diff -Nru qemu-kvm-0.14.1+noroms/acl.c qemu-kvm-0.15.0+noroms/acl.c --- qemu-kvm-0.14.1+noroms/acl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/acl.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include "qemu-common.h" -#include "sysemu.h" #include "acl.h" #ifdef CONFIG_FNMATCH diff -Nru qemu-kvm-0.14.1+noroms/alpha-dis.c qemu-kvm-0.15.0+noroms/alpha-dis.c --- qemu-kvm-0.14.1+noroms/alpha-dis.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/alpha-dis.c 2011-08-09 07:40:29.000000000 -0500 @@ -238,10 +238,6 @@ #define AXP_REG_SP 30 #define AXP_REG_ZERO 31 -#define bfd_mach_alpha_ev4 0x10 -#define bfd_mach_alpha_ev5 0x20 -#define bfd_mach_alpha_ev6 0x30 - enum bfd_reloc_code_real { BFD_RELOC_23_PCREL_S2, BFD_RELOC_ALPHA_HINT diff -Nru qemu-kvm-0.14.1+noroms/arch_init.c qemu-kvm-0.15.0+noroms/arch_init.c --- qemu-kvm-0.14.1+noroms/arch_init.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/arch_init.c 2011-08-09 07:40:29.000000000 -0500 @@ -64,6 +64,8 @@ #define QEMU_ARCH QEMU_ARCH_I386 #elif defined(TARGET_M68K) #define QEMU_ARCH QEMU_ARCH_M68K +#elif defined(TARGET_LM32) +#define QEMU_ARCH QEMU_ARCH_LM32 #elif defined(TARGET_MICROBLAZE) #define QEMU_ARCH QEMU_ARCH_MICROBLAZE #elif defined(TARGET_MIPS) @@ -707,6 +709,11 @@ #endif } +int tcg_available(void) +{ + return 1; +} + int kvm_available(void) { #ifdef CONFIG_KVM diff -Nru qemu-kvm-0.14.1+noroms/arch_init.h qemu-kvm-0.15.0+noroms/arch_init.h --- qemu-kvm-0.14.1+noroms/arch_init.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/arch_init.h 2011-08-09 07:40:29.000000000 -0500 @@ -10,24 +10,24 @@ 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, + QEMU_ARCH_LM32 = 32, + QEMU_ARCH_MICROBLAZE = 64, + QEMU_ARCH_MIPS = 128, + QEMU_ARCH_PPC = 256, + QEMU_ARCH_S390X = 512, + QEMU_ARCH_SH4 = 1024, + QEMU_ARCH_SPARC = 2048, }; 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 tcg_available(void); int kvm_available(void); int xen_available(void); diff -Nru qemu-kvm-0.14.1+noroms/arm-semi.c qemu-kvm-0.15.0+noroms/arm-semi.c --- qemu-kvm-0.14.1+noroms/arm-semi.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/arm-semi.c 2011-08-09 07:40:29.000000000 -0500 @@ -33,8 +33,8 @@ #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024) #else #include "qemu-common.h" -#include "sysemu.h" #include "gdbstub.h" +#include "hw/arm-misc.h" #endif #define SYS_OPEN 0x01 @@ -370,68 +370,88 @@ return syscall_err; #endif case SYS_GET_CMDLINE: -#ifdef CONFIG_USER_ONLY - /* Build a commandline from the original argv. */ { - char *arm_cmdline_buffer; - const char *host_cmdline_buffer; + /* Build a command-line from the original argv. + * + * The inputs are: + * * ARG(0), pointer to a buffer of at least the size + * specified in ARG(1). + * * ARG(1), size of the buffer pointed to by ARG(0) in + * bytes. + * + * The outputs are: + * * ARG(0), pointer to null-terminated string of the + * command line. + * * ARG(1), length of the string pointed to by ARG(0). + */ + + char *output_buffer; + size_t input_size = ARG(1); + size_t output_size; + int status = 0; + /* Compute the size of the output string. */ +#if !defined(CONFIG_USER_ONLY) + output_size = strlen(ts->boot_info->kernel_filename) + + 1 /* Separating space. */ + + strlen(ts->boot_info->kernel_cmdline) + + 1; /* Terminating null byte. */ +#else unsigned int i; - unsigned int arm_cmdline_len = ARG(1); - unsigned int host_cmdline_len = - ts->info->arg_end-ts->info->arg_start; - if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) { - return -1; /* not enough space to store command line */ - } - - if (!host_cmdline_len) { + output_size = ts->info->arg_end - ts->info->arg_start; + if (!output_size) { /* 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; + output_size = 1; } +#endif - /* 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); + if (output_size > input_size) { + /* Not enough space to store command-line arguments. */ + return -1; } - /* 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); + /* Adjust the command-line length. */ + SET_ARG(1, output_size - 1); - /* Return success if we could return a commandline. */ - return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1; - } + /* Lock the buffer on the ARM side. */ + output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0); + if (!output_buffer) { + return -1; + } + + /* Copy the command-line arguments. */ +#if !defined(CONFIG_USER_ONLY) + pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename); + pstrcat(output_buffer, output_size, " "); + pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline); #else - return -1; + if (output_size == 1) { + /* Empty command-line. */ + output_buffer[0] = '\0'; + goto out; + } + + if (copy_from_user(output_buffer, ts->info->arg_start, + output_size)) { + status = -1; + goto out; + } + + /* Separate arguments by white spaces. */ + for (i = 0; i < output_size - 1; i++) { + if (output_buffer[i] == 0) { + output_buffer[i] = ' '; + } + } + out: #endif + /* Unlock the buffer on the ARM side. */ + unlock_user(output_buffer, ARG(0), output_size); + + return status; + } case SYS_HEAPINFO: { uint32_t *ptr; @@ -441,15 +461,16 @@ /* Some C libraries assume the heap immediately follows .bss, so allocate it using sbrk. */ if (!ts->heap_limit) { - long ret; + abi_ulong ret; ts->heap_base = do_brk(0); limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE; /* Try a big heap, and reduce the size if that fails. */ for (;;) { ret = do_brk(limit); - if (ret != -1) + if (ret >= limit) { break; + } limit = (ts->heap_base >> 1) + (limit >> 1); } ts->heap_limit = limit; diff -Nru qemu-kvm-0.14.1+noroms/async.c qemu-kvm-0.15.0+noroms/async.c --- qemu-kvm-0.14.1+noroms/async.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/async.c 2011-08-09 07:40:29.000000000 -0500 @@ -137,11 +137,12 @@ int qemu_bh_poll(void) { - QEMUBH *bh, **bhp; + QEMUBH *bh, **bhp, *next; int ret; ret = 0; - for (bh = async_context->first_bh; bh; bh = bh->next) { + for (bh = async_context->first_bh; bh; bh = next) { + next = bh->next; if (!bh->deleted && bh->scheduled) { bh->scheduled = 0; if (!bh->idle) diff -Nru qemu-kvm-0.14.1+noroms/audio/audio.c qemu-kvm-0.15.0+noroms/audio/audio.c --- qemu-kvm-0.14.1+noroms/audio/audio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/audio.c 2011-08-09 07:40:29.000000000 -0500 @@ -1114,7 +1114,7 @@ static void audio_reset_timer (AudioState *s) { if (audio_is_timer_needed ()) { - qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); + qemu_mod_timer (s->ts, qemu_get_clock_ns (vm_clock) + 1); } else { qemu_del_timer (s->ts); @@ -1820,7 +1820,7 @@ QLIST_INIT (&s->cap_head); atexit (audio_atexit); - s->ts = qemu_new_timer (vm_clock, audio_timer, s); + s->ts = qemu_new_timer_ns (vm_clock, audio_timer, s); if (!s->ts) { hw_error("Could not create audio timer\n"); } diff -Nru qemu-kvm-0.14.1+noroms/audio/audio_pt_int.c qemu-kvm-0.15.0+noroms/audio/audio_pt_int.c --- qemu-kvm-0.14.1+noroms/audio/audio_pt_int.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/audio_pt_int.c 2011-08-09 07:40:29.000000000 -0500 @@ -6,8 +6,6 @@ #include "audio_int.h" #include "audio_pt_int.h" -#include - static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err, const char *fmt, ...) { diff -Nru qemu-kvm-0.14.1+noroms/audio/coreaudio.c qemu-kvm-0.15.0+noroms/audio/coreaudio.c --- qemu-kvm-0.14.1+noroms/audio/coreaudio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/coreaudio.c 2011-08-09 07:40:29.000000000 -0500 @@ -56,7 +56,7 @@ static void coreaudio_logstatus (OSStatus status) { - char *str = "BUG"; + const char *str = "BUG"; switch(status) { case kAudioHardwareNoError: @@ -104,7 +104,7 @@ break; default: - AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); + AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); return; } @@ -360,8 +360,8 @@ &core->audioDevicePropertyBufferFrameSize); if (status != kAudioHardwareNoError) { coreaudio_logerr2 (status, typ, - "Could not set device buffer frame size %ld\n", - core->audioDevicePropertyBufferFrameSize); + "Could not set device buffer frame size %" PRIu32 "\n", + (uint32_t)core->audioDevicePropertyBufferFrameSize); return -1; } diff -Nru qemu-kvm-0.14.1+noroms/audio/mixeng_template.h qemu-kvm-0.15.0+noroms/audio/mixeng_template.h --- qemu-kvm-0.14.1+noroms/audio/mixeng_template.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/mixeng_template.h 2011-08-09 07:40:29.000000000 -0500 @@ -46,7 +46,7 @@ #endif #else /* !RECIPROCAL */ #ifdef SIGNED - return nv / (mixeng_real) (IN_MAX - IN_MIN); + return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN); #else return (nv - HALF) / (mixeng_real) IN_MAX; #endif @@ -63,7 +63,7 @@ } #ifdef SIGNED - return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN))); + return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN))); #else return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF)); #endif diff -Nru qemu-kvm-0.14.1+noroms/audio/noaudio.c qemu-kvm-0.15.0+noroms/audio/noaudio.c --- qemu-kvm-0.14.1+noroms/audio/noaudio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/noaudio.c 2011-08-09 07:40:29.000000000 -0500 @@ -46,7 +46,7 @@ int64_t ticks; int64_t bytes; - now = qemu_get_clock (vm_clock); + now = qemu_get_clock_ns (vm_clock); ticks = now - no->old_ticks; bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); bytes = audio_MIN (bytes, INT_MAX); @@ -102,7 +102,7 @@ int samples = 0; if (dead) { - int64_t now = qemu_get_clock (vm_clock); + int64_t now = qemu_get_clock_ns (vm_clock); int64_t ticks = now - no->old_ticks; int64_t bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); diff -Nru qemu-kvm-0.14.1+noroms/audio/sdlaudio.c qemu-kvm-0.15.0+noroms/audio/sdlaudio.c --- qemu-kvm-0.14.1+noroms/audio/sdlaudio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/sdlaudio.c 2011-08-09 07:40:29.000000000 -0500 @@ -32,7 +32,6 @@ #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) #include #endif -#include #endif #define AUDIO_CAP "sdl" @@ -139,36 +138,36 @@ } } -static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess) +static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) { switch (sdlfmt) { case AUDIO_S8: - *endianess = 0; + *endianness = 0; *fmt = AUD_FMT_S8; break; case AUDIO_U8: - *endianess = 0; + *endianness = 0; *fmt = AUD_FMT_U8; break; case AUDIO_S16LSB: - *endianess = 0; + *endianness = 0; *fmt = AUD_FMT_S16; break; case AUDIO_U16LSB: - *endianess = 0; + *endianness = 0; *fmt = AUD_FMT_U16; break; case AUDIO_S16MSB: - *endianess = 1; + *endianness = 1; *fmt = AUD_FMT_S16; break; case AUDIO_U16MSB: - *endianess = 1; + *endianness = 1; *fmt = AUD_FMT_U16; break; @@ -338,7 +337,7 @@ SDLVoiceOut *sdl = (SDLVoiceOut *) hw; SDLAudioState *s = &glob_sdl; SDL_AudioSpec req, obt; - int endianess; + int endianness; int err; audfmt_e effective_fmt; struct audsettings obt_as; @@ -354,7 +353,7 @@ return -1; } - err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess); + err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); if (err) { sdl_close (s); return -1; @@ -363,7 +362,7 @@ obt_as.freq = obt.freq; obt_as.nchannels = obt.channels; obt_as.fmt = effective_fmt; - obt_as.endianness = endianess; + obt_as.endianness = endianness; audio_pcm_init_info (&hw->info, &obt_as); hw->samples = obt.samples; diff -Nru qemu-kvm-0.14.1+noroms/audio/spiceaudio.c qemu-kvm-0.15.0+noroms/audio/spiceaudio.c --- qemu-kvm-0.14.1+noroms/audio/spiceaudio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/spiceaudio.c 2011-08-09 07:40:29.000000000 -0500 @@ -81,7 +81,7 @@ static void rate_start (SpiceRateCtl *rate) { memset (rate, 0, sizeof (*rate)); - rate->start_ticks = qemu_get_clock (vm_clock); + rate->start_ticks = qemu_get_clock_ns (vm_clock); } static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) @@ -91,7 +91,7 @@ int64_t bytes; int64_t samples; - now = qemu_get_clock (vm_clock); + now = qemu_get_clock_ns (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; diff -Nru qemu-kvm-0.14.1+noroms/audio/wavaudio.c qemu-kvm-0.15.0+noroms/audio/wavaudio.c --- qemu-kvm-0.14.1+noroms/audio/wavaudio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/audio/wavaudio.c 2011-08-09 07:40:29.000000000 -0500 @@ -52,7 +52,7 @@ int rpos, decr, samples; uint8_t *dst; struct st_sample *src; - int64_t now = qemu_get_clock (vm_clock); + int64_t now = qemu_get_clock_ns (vm_clock); int64_t ticks = now - wav->old_ticks; int64_t bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); diff -Nru qemu-kvm-0.14.1+noroms/balloon.c qemu-kvm-0.15.0+noroms/balloon.c --- qemu-kvm-0.14.1+noroms/balloon.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/balloon.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,7 +1,9 @@ /* - * QEMU System Emulator + * Generic Balloon handlers and management * * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 Amit Shah * * 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,7 +24,6 @@ * THE SOFTWARE. */ -#include "sysemu.h" #include "monitor.h" #include "qjson.h" #include "qint.h" @@ -31,44 +32,53 @@ #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; +static QEMUBalloonEvent *balloon_event_fn; +static QEMUBalloonStatus *balloon_stat_fn; +static void *balloon_opaque; + +int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, + QEMUBalloonStatus *stat_func, void *opaque) +{ + if (balloon_event_fn || balloon_stat_fn || balloon_opaque) { + /* We're already registered one balloon handler. How many can + * a guest really have? + */ + error_report("Another balloon device already registered"); + return -1; + } + balloon_event_fn = event_func; + balloon_stat_fn = stat_func; + balloon_opaque = opaque; + return 0; } -int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) +static int qemu_balloon(ram_addr_t target) { - 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 { + if (!balloon_event_fn) { return 0; } + trace_balloon_event(balloon_opaque, target); + balloon_event_fn(balloon_opaque, target); + return 1; } -int qemu_balloon_status(MonitorCompletion cb, void *opaque) +static 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 { + if (!balloon_stat_fn) { return 0; } + balloon_stat_fn(balloon_opaque, cb, opaque); + return 1; } static void print_balloon_stat(const char *key, QObject *obj, void *opaque) { Monitor *mon = opaque; - if (strcmp(key, "actual")) + 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) @@ -76,9 +86,9 @@ QDict *qdict; qdict = qobject_to_qdict(data); - if (!qdict_haskey(qdict, "actual")) + 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); @@ -130,6 +140,7 @@ int do_balloon(Monitor *mon, const QDict *params, MonitorCompletion cb, void *opaque) { + int64_t target; int ret; if (kvm_enabled() && !kvm_has_sync_mmu()) { @@ -137,7 +148,12 @@ return -1; } - ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque); + target = qdict_get_int(params, "value"); + if (target <= 0) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size"); + return -1; + } + ret = qemu_balloon(target); if (ret == 0) { qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); return -1; diff -Nru qemu-kvm-0.14.1+noroms/balloon.h qemu-kvm-0.15.0+noroms/balloon.h --- qemu-kvm-0.14.1+noroms/balloon.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/balloon.h 2011-08-09 07:40:29.000000000 -0500 @@ -16,14 +16,12 @@ #include "monitor.h" -typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target, - MonitorCompletion cb, void *cb_data); +typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target); +typedef void (QEMUBalloonStatus)(void *opaque, MonitorCompletion cb, + void *cb_data); -void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); - -int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque); - -int qemu_balloon_status(MonitorCompletion cb, void *opaque); +int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, + QEMUBalloonStatus *stat_func, void *opaque); void monitor_print_balloon(Monitor *mon, const QObject *data); int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque); diff -Nru qemu-kvm-0.14.1+noroms/bitmap.c qemu-kvm-0.15.0+noroms/bitmap.c --- qemu-kvm-0.14.1+noroms/bitmap.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/bitmap.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,256 @@ +/* + * Bitmap Module + * + * Stolen from linux/src/lib/bitmap.c + * + * Copyright (C) 2010 Corentin Chary + * + * This source code is licensed under the GNU General Public License, + * Version 2. + */ + +#include "bitops.h" +#include "bitmap.h" + +/* + * bitmaps provide an array of bits, implemented using an an + * array of unsigned longs. The number of valid bits in a + * given bitmap does _not_ need to be an exact multiple of + * BITS_PER_LONG. + * + * The possible unused bits in the last, partially used word + * of a bitmap are 'don't care'. The implementation makes + * no particular effort to keep them zero. It ensures that + * their value will not affect the results of any operation. + * The bitmap operations that return Boolean (bitmap_empty, + * for example) or scalar (bitmap_weight, for example) results + * carefully filter out these unused bits from impacting their + * results. + * + * These operations actually hold to a slightly stronger rule: + * if you don't input any bitmaps to these ops that have some + * unused bits set, then they won't output any set unused bits + * in output bitmaps. + * + * The byte ordering of bitmaps is more natural on little + * endian architectures. + */ + +int slow_bitmap_empty(const unsigned long *bitmap, int bits) +{ + int k, lim = bits/BITS_PER_LONG; + + for (k = 0; k < lim; ++k) { + if (bitmap[k]) { + return 0; + } + } + if (bits % BITS_PER_LONG) { + if (bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) { + return 0; + } + } + + return 1; +} + +int slow_bitmap_full(const unsigned long *bitmap, int bits) +{ + int k, lim = bits/BITS_PER_LONG; + + for (k = 0; k < lim; ++k) { + if (~bitmap[k]) { + return 0; + } + } + + if (bits % BITS_PER_LONG) { + if (~bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) { + return 0; + } + } + + return 1; +} + +int slow_bitmap_equal(const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k, lim = bits/BITS_PER_LONG; + + for (k = 0; k < lim; ++k) { + if (bitmap1[k] != bitmap2[k]) { + return 0; + } + } + + if (bits % BITS_PER_LONG) { + if ((bitmap1[k] ^ bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) { + return 0; + } + } + + return 1; +} + +void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, + int bits) +{ + int k, lim = bits/BITS_PER_LONG; + + for (k = 0; k < lim; ++k) { + dst[k] = ~src[k]; + } + + if (bits % BITS_PER_LONG) { + dst[k] = ~src[k] & BITMAP_LAST_WORD_MASK(bits); + } +} + +int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + unsigned long result = 0; + + for (k = 0; k < nr; k++) { + result |= (dst[k] = bitmap1[k] & bitmap2[k]); + } + return result != 0; +} + +void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + + for (k = 0; k < nr; k++) { + dst[k] = bitmap1[k] | bitmap2[k]; + } +} + +void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + + for (k = 0; k < nr; k++) { + dst[k] = bitmap1[k] ^ bitmap2[k]; + } +} + +int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k; + int nr = BITS_TO_LONGS(bits); + unsigned long result = 0; + + for (k = 0; k < nr; k++) { + result |= (dst[k] = bitmap1[k] & ~bitmap2[k]); + } + return result != 0; +} + +#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG)) + +void bitmap_set(unsigned long *map, int start, int nr) +{ + unsigned long *p = map + BIT_WORD(start); + const int size = start + nr; + int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); + + while (nr - bits_to_set >= 0) { + *p |= mask_to_set; + nr -= bits_to_set; + bits_to_set = BITS_PER_LONG; + mask_to_set = ~0UL; + p++; + } + if (nr) { + mask_to_set &= BITMAP_LAST_WORD_MASK(size); + *p |= mask_to_set; + } +} + +void bitmap_clear(unsigned long *map, int start, int nr) +{ + unsigned long *p = map + BIT_WORD(start); + const int size = start + nr; + int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); + + while (nr - bits_to_clear >= 0) { + *p &= ~mask_to_clear; + nr -= bits_to_clear; + bits_to_clear = BITS_PER_LONG; + mask_to_clear = ~0UL; + p++; + } + if (nr) { + mask_to_clear &= BITMAP_LAST_WORD_MASK(size); + *p &= ~mask_to_clear; + } +} + +#define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) + +/** + * bitmap_find_next_zero_area - find a contiguous aligned zero area + * @map: The address to base the search on + * @size: The bitmap size in bits + * @start: The bitnumber to start searching at + * @nr: The number of zeroed bits we're looking for + * @align_mask: Alignment mask for zero area + * + * The @align_mask should be one less than a power of 2; the effect is that + * the bit offset of all zero areas this function finds is multiples of that + * power of 2. A @align_mask of 0 means no alignment is required. + */ +unsigned long bitmap_find_next_zero_area(unsigned long *map, + unsigned long size, + unsigned long start, + unsigned int nr, + unsigned long align_mask) +{ + unsigned long index, end, i; +again: + index = find_next_zero_bit(map, size, start); + + /* Align allocation */ + index = ALIGN_MASK(index, align_mask); + + end = index + nr; + if (end > size) { + return end; + } + i = find_next_bit(map, end, index); + if (i < end) { + start = i + 1; + goto again; + } + return index; +} + +int slow_bitmap_intersects(const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits) +{ + int k, lim = bits/BITS_PER_LONG; + + for (k = 0; k < lim; ++k) { + if (bitmap1[k] & bitmap2[k]) { + return 1; + } + } + + if (bits % BITS_PER_LONG) { + if ((bitmap1[k] & bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) { + return 1; + } + } + return 0; +} diff -Nru qemu-kvm-0.14.1+noroms/bitmap.h qemu-kvm-0.15.0+noroms/bitmap.h --- qemu-kvm-0.14.1+noroms/bitmap.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/bitmap.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,222 @@ +/* + * Bitmap Module + * + * Copyright (C) 2010 Corentin Chary + * + * Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h + * + * 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 BITMAP_H +#define BITMAP_H + +#include "qemu-common.h" +#include "bitops.h" + +/* + * The available bitmap operations and their rough meaning in the + * case that the bitmap is a single unsigned long are thus: + * + * Note that nbits should be always a compile time evaluable constant. + * Otherwise many inlines will generate horrible code. + * + * bitmap_zero(dst, nbits) *dst = 0UL + * bitmap_fill(dst, nbits) *dst = ~0UL + * bitmap_copy(dst, src, nbits) *dst = *src + * bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2 + * bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2 + * bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2 + * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) + * bitmap_complement(dst, src, nbits) *dst = ~(*src) + * bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal? + * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? + * bitmap_empty(src, nbits) Are all bits zero in *src? + * bitmap_full(src, nbits) Are all bits set in *src? + * bitmap_set(dst, pos, nbits) Set specified bit area + * bitmap_clear(dst, pos, nbits) Clear specified bit area + * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area + */ + +/* + * Also the following operations apply to bitmaps. + * + * set_bit(bit, addr) *addr |= bit + * clear_bit(bit, addr) *addr &= ~bit + * change_bit(bit, addr) *addr ^= bit + * test_bit(bit, addr) Is bit set in *addr? + * test_and_set_bit(bit, addr) Set bit and return old value + * test_and_clear_bit(bit, addr) Clear bit and return old value + * test_and_change_bit(bit, addr) Change bit and return old value + * find_first_zero_bit(addr, nbits) Position first zero bit in *addr + * find_first_bit(addr, nbits) Position first set bit in *addr + * find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit + * find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit + */ + +#define BITMAP_LAST_WORD_MASK(nbits) \ + ( \ + ((nbits) % BITS_PER_LONG) ? \ + (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ + ) + +#define DECLARE_BITMAP(name,bits) \ + unsigned long name[BITS_TO_LONGS(bits)] + +#define small_nbits(nbits) \ + ((nbits) <= BITS_PER_LONG) + +int slow_bitmap_empty(const unsigned long *bitmap, int bits); +int slow_bitmap_full(const unsigned long *bitmap, int bits); +int slow_bitmap_equal(const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); +void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, + int bits); +void slow_bitmap_shift_right(unsigned long *dst, + const unsigned long *src, int shift, int bits); +void slow_bitmap_shift_left(unsigned long *dst, + const unsigned long *src, int shift, int bits); +int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); +void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); +void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); +int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); +int slow_bitmap_intersects(const unsigned long *bitmap1, + const unsigned long *bitmap2, int bits); + +static inline unsigned long *bitmap_new(int nbits) +{ + int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + return qemu_mallocz(len); +} + +static inline void bitmap_zero(unsigned long *dst, int nbits) +{ + if (small_nbits(nbits)) { + *dst = 0UL; + } else { + int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + memset(dst, 0, len); + } +} + +static inline void bitmap_fill(unsigned long *dst, int nbits) +{ + size_t nlongs = BITS_TO_LONGS(nbits); + if (!small_nbits(nbits)) { + int len = (nlongs - 1) * sizeof(unsigned long); + memset(dst, 0xff, len); + } + dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); +} + +static inline void bitmap_copy(unsigned long *dst, const unsigned long *src, + int nbits) +{ + if (small_nbits(nbits)) { + *dst = *src; + } else { + int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + memcpy(dst, src, len); + } +} + +static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_nbits(nbits)) { + return (*dst = *src1 & *src2) != 0; + } + return slow_bitmap_and(dst, src1, src2, nbits); +} + +static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_nbits(nbits)) { + *dst = *src1 | *src2; + } else { + slow_bitmap_or(dst, src1, src2, nbits); + } +} + +static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_nbits(nbits)) { + *dst = *src1 ^ *src2; + } else { + slow_bitmap_xor(dst, src1, src2, nbits); + } +} + +static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_nbits(nbits)) { + return (*dst = *src1 & ~(*src2)) != 0; + } + return slow_bitmap_andnot(dst, src1, src2, nbits); +} + +static inline void bitmap_complement(unsigned long *dst, const unsigned long *src, + int nbits) +{ + if (small_nbits(nbits)) { + *dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits); + } else { + slow_bitmap_complement(dst, src, nbits); + } +} + +static inline int bitmap_equal(const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_nbits(nbits)) { + return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits)); + } else { + return slow_bitmap_equal(src1, src2, nbits); + } +} + +static inline int bitmap_empty(const unsigned long *src, int nbits) +{ + if (small_nbits(nbits)) { + return ! (*src & BITMAP_LAST_WORD_MASK(nbits)); + } else { + return slow_bitmap_empty(src, nbits); + } +} + +static inline int bitmap_full(const unsigned long *src, int nbits) +{ + if (small_nbits(nbits)) { + return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits)); + } else { + return slow_bitmap_full(src, nbits); + } +} + +static inline int bitmap_intersects(const unsigned long *src1, + const unsigned long *src2, int nbits) +{ + if (small_nbits(nbits)) { + return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0; + } else { + return slow_bitmap_intersects(src1, src2, nbits); + } +} + +void bitmap_set(unsigned long *map, int i, int len); +void bitmap_clear(unsigned long *map, int start, int nr); +unsigned long bitmap_find_next_zero_area(unsigned long *map, + unsigned long size, + unsigned long start, + unsigned int nr, + unsigned long align_mask); + +#endif /* BITMAP_H */ diff -Nru qemu-kvm-0.14.1+noroms/bitops.c qemu-kvm-0.15.0+noroms/bitops.c --- qemu-kvm-0.14.1+noroms/bitops.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/bitops.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Copyright (C) 2008 IBM Corporation + * Written by Rusty Russell + * (Inspired by David Howell's find_next_bit implementation) + * + * 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. + */ + +#include "bitops.h" + +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/* + * Find the next set bit in a memory region. + */ +unsigned long find_next_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) { + return size; + } + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < BITS_PER_LONG) { + goto found_first; + } + if (tmp) { + goto found_middle; + } + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if ((tmp = *(p++))) { + goto found_middle; + } + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) { + return result; + } + tmp = *p; + +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) { /* Are any bits set? */ + return result + size; /* Nope. */ + } +found_middle: + return result + bitops_ffsl(tmp); +} + +/* + * This implementation of find_{first,next}_zero_bit was stolen from + * Linus' asm-alpha/bitops.h. + */ +unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) { + return size; + } + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (BITS_PER_LONG - offset); + if (size < BITS_PER_LONG) { + goto found_first; + } + if (~tmp) { + goto found_middle; + } + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if (~(tmp = *(p++))) { + goto found_middle; + } + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) { + return result; + } + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) { /* Are any bits zero? */ + return result + size; /* Nope. */ + } +found_middle: + return result + ffz(tmp); +} + +unsigned long find_last_bit(const unsigned long *addr, unsigned long size) +{ + unsigned long words; + unsigned long tmp; + + /* Start at final word. */ + words = size / BITS_PER_LONG; + + /* Partial final word? */ + if (size & (BITS_PER_LONG-1)) { + tmp = (addr[words] & (~0UL >> (BITS_PER_LONG + - (size & (BITS_PER_LONG-1))))); + if (tmp) { + goto found; + } + } + + while (words) { + tmp = addr[--words]; + if (tmp) { + found: + return words * BITS_PER_LONG + bitops_flsl(tmp); + } + } + + /* Not found */ + return size; +} diff -Nru qemu-kvm-0.14.1+noroms/bitops.h qemu-kvm-0.15.0+noroms/bitops.h --- qemu-kvm-0.14.1+noroms/bitops.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/bitops.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,272 @@ +/* + * Bitops Module + * + * Copyright (C) 2010 Corentin Chary + * + * Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h + * + * 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 BITOPS_H +#define BITOPS_H + +#include "qemu-common.h" + +#define BITS_PER_BYTE CHAR_BIT +#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) + +#define BIT(nr) (1UL << (nr)) +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) + +/** + * bitops_ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static unsigned long bitops_ffsl(unsigned long word) +{ + int num = 0; + +#if LONG_MAX > 0x7FFFFFFF + if ((word & 0xffffffff) == 0) { + num += 32; + word >>= 32; + } +#endif + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) { + num += 1; + } + return num; +} + +/** + * bitops_fls - find last (most-significant) set bit in a long word + * @word: the word to search + * + * Undefined if no set bit exists, so code should check against 0 first. + */ +static inline unsigned long bitops_flsl(unsigned long word) +{ + int num = BITS_PER_LONG - 1; + +#if LONG_MAX > 0x7FFFFFFF + if (!(word & (~0ul << 32))) { + num -= 32; + word <<= 32; + } +#endif + if (!(word & (~0ul << (BITS_PER_LONG-16)))) { + num -= 16; + word <<= 16; + } + if (!(word & (~0ul << (BITS_PER_LONG-8)))) { + num -= 8; + word <<= 8; + } + if (!(word & (~0ul << (BITS_PER_LONG-4)))) { + num -= 4; + word <<= 4; + } + if (!(word & (~0ul << (BITS_PER_LONG-2)))) { + num -= 2; + + word <<= 2; + } + if (!(word & (~0ul << (BITS_PER_LONG-1)))) + num -= 1; + return num; +} + +/** + * ffz - find first zero in word. + * @word: The word to search + * + * Undefined if no zero exists, so code should check against ~0UL first. + */ +static inline unsigned long ffz(unsigned long word) +{ + return bitops_ffsl(~word); +} + +/** + * set_bit - Set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + */ +static inline void set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p |= mask; +} + +/** + * clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + */ +static inline void clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p &= ~mask; +} + +/** + * change_bit - Toggle a bit in memory + * @nr: Bit to change + * @addr: Address to start counting from + */ +static inline void change_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + + *p ^= mask; +} + +/** + * test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + */ +static inline int test_and_set_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long old = *p; + + *p = old | mask; + return (old & mask) != 0; +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + */ +static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long old = *p; + + *p = old & ~mask; + return (old & mask) != 0; +} + +/** + * test_and_change_bit - Change a bit and return its old value + * @nr: Bit to change + * @addr: Address to count from + */ +static inline int test_and_change_bit(int nr, volatile unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + unsigned long old = *p; + + *p = old ^ mask; + return (old & mask) != 0; +} + +/** + * test_bit - Determine whether a bit is set + * @nr: bit number to test + * @addr: Address to start counting from + */ +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +/** + * find_last_bit - find the last set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit, or size. + */ +unsigned long find_last_bit(const unsigned long *addr, + unsigned long size); + +/** + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + */ +unsigned long find_next_bit(const unsigned long *addr, + unsigned long size, unsigned long offset); + +/** + * find_next_zero_bit - find the next cleared bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The bitmap size in bits + */ + +unsigned long find_next_zero_bit(const unsigned long *addr, + unsigned long size, + unsigned long offset); + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit. + */ +static inline unsigned long find_first_bit(const unsigned long *addr, + unsigned long size) +{ + return find_next_bit(addr, size, 0); +} + +/** + * find_first_zero_bit - find the first cleared bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first cleared bit. + */ +static inline unsigned long find_first_zero_bit(const unsigned long *addr, + unsigned long size) +{ + return find_next_zero_bit(addr, size, 0); +} + +static inline unsigned long hweight_long(unsigned long w) +{ + unsigned long count; + + for (count = 0; w; w >>= 1) { + count += w & 1; + } + return count; +} + +#endif diff -Nru qemu-kvm-0.14.1+noroms/block/nbd.c qemu-kvm-0.15.0+noroms/block/nbd.c --- qemu-kvm-0.14.1+noroms/block/nbd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/nbd.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,96 +29,152 @@ #include "qemu-common.h" #include "nbd.h" #include "module.h" +#include "qemu_socket.h" #include #include #define EN_OPTSTR ":exportname=" +/* #define DEBUG_NBD */ + +#if defined(DEBUG_NBD) +#define logout(fmt, ...) \ + fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__) +#else +#define logout(fmt, ...) ((void)0) +#endif + typedef struct BDRVNBDState { int sock; off_t size; size_t blocksize; + char *export_name; /* An NBD server may export several devices */ + + /* If it begins with '/', this is a UNIX domain socket. Otherwise, + * it's a string of the form :port + */ + char *host_spec; } BDRVNBDState; -static int nbd_open(BlockDriverState *bs, const char* filename, int flags) +static int nbd_config(BDRVNBDState *s, const char *filename, int flags) { - BDRVNBDState *s = bs->opaque; - uint32_t nbdflags; - char *file; - char *name; - const char *host; + char *export_name; + const char *host_spec; const char *unixpath; - int sock; - off_t size; - size_t blocksize; - int ret; int err = -EINVAL; file = qemu_strdup(filename); - name = strstr(file, EN_OPTSTR); - if (name) { - if (name[strlen(EN_OPTSTR)] == 0) { + export_name = strstr(file, EN_OPTSTR); + if (export_name) { + if (export_name[strlen(EN_OPTSTR)] == 0) { goto out; } - name[0] = 0; - name += strlen(EN_OPTSTR); + export_name[0] = 0; /* truncate 'file' */ + export_name += strlen(EN_OPTSTR); + s->export_name = qemu_strdup(export_name); } - if (!strstart(file, "nbd:", &host)) { + /* extract the host_spec - fail if it's not nbd:... */ + if (!strstart(file, "nbd:", &host_spec)) { goto out; } - if (strstart(host, "unix:", &unixpath)) { - - if (unixpath[0] != '/') { + /* are we a UNIX or TCP socket? */ + if (strstart(host_spec, "unix:", &unixpath)) { + if (unixpath[0] != '/') { /* We demand an absolute path*/ goto out; } + s->host_spec = qemu_strdup(unixpath); + } else { + s->host_spec = qemu_strdup(host_spec); + } - sock = unix_socket_outgoing(unixpath); + err = 0; - } else { - uint16_t port = NBD_DEFAULT_PORT; - char *p, *r; - char hostname[128]; - - pstrcpy(hostname, 128, host); - - p = strchr(hostname, ':'); - if (p != NULL) { - *p = '\0'; - p++; - - port = strtol(p, &r, 0); - if (r == p) { - goto out; - } - } +out: + qemu_free(file); + if (err != 0) { + qemu_free(s->export_name); + qemu_free(s->host_spec); + } + return err; +} - sock = tcp_socket_outgoing(hostname, port); +static int nbd_establish_connection(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + int sock; + int ret; + off_t size; + size_t blocksize; + uint32_t nbdflags; + + if (s->host_spec[0] == '/') { + sock = unix_socket_outgoing(s->host_spec); + } else { + sock = tcp_socket_outgoing_spec(s->host_spec); } + /* Failed to establish connection */ if (sock == -1) { - err = -errno; - goto out; + logout("Failed to establish connection to NBD server\n"); + return -errno; } - ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize); + /* NBD handshake */ + ret = nbd_receive_negotiate(sock, s->export_name, &nbdflags, &size, + &blocksize); if (ret == -1) { - err = -errno; - goto out; + logout("Failed to negotiate with the NBD server\n"); + closesocket(sock); + return -errno; } + /* Now that we're connected, set the socket to be non-blocking */ + socket_set_nonblock(sock); + s->sock = sock; s->size = size; s->blocksize = blocksize; - err = 0; -out: - qemu_free(file); - return err; + logout("Established connection with NBD server\n"); + return 0; +} + +static void nbd_teardown_connection(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + struct nbd_request request; + + request.type = NBD_CMD_DISC; + request.handle = (uint64_t)(intptr_t)bs; + request.from = 0; + request.len = 0; + nbd_send_request(s->sock, &request); + + closesocket(s->sock); +} + +static int nbd_open(BlockDriverState *bs, const char* filename, int flags) +{ + BDRVNBDState *s = bs->opaque; + int result; + + /* Pop the config into our state object. Exit if invalid. */ + result = nbd_config(s, filename, flags); + if (result != 0) { + return result; + } + + /* establish TCP connection, return error if it fails + * TODO: Configurable retry-until-timeout behaviour. + */ + result = nbd_establish_connection(bs); + + return result; } static int nbd_read(BlockDriverState *bs, int64_t sector_num, @@ -184,15 +240,10 @@ static void nbd_close(BlockDriverState *bs) { BDRVNBDState *s = bs->opaque; - struct nbd_request request; - - request.type = NBD_CMD_DISC; - request.handle = (uint64_t)(intptr_t)bs; - request.from = 0; - request.len = 0; - nbd_send_request(s->sock, &request); + qemu_free(s->export_name); + qemu_free(s->host_spec); - close(s->sock); + nbd_teardown_connection(bs); } static int64_t nbd_getlength(BlockDriverState *bs) diff -Nru qemu-kvm-0.14.1+noroms/block/qcow2.c qemu-kvm-0.15.0+noroms/block/qcow2.c --- qemu-kvm-0.14.1+noroms/block/qcow2.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qcow2.c 2011-08-09 07:40:29.000000000 -0500 @@ -229,7 +229,7 @@ } /* alloc L2 table/refcount block cache */ - writethrough = ((flags & BDRV_O_CACHE_MASK) == 0); + writethrough = ((flags & BDRV_O_CACHE_WB) == 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); @@ -378,6 +378,7 @@ uint64_t bytes_done; uint64_t cluster_offset; uint8_t *cluster_data; + bool is_write; BlockDriverAIOCB *hd_aiocb; QEMUIOVector hd_qiov; QEMUBH *bh; @@ -399,12 +400,19 @@ }; static void qcow2_aio_read_cb(void *opaque, int ret); -static void qcow2_aio_read_bh(void *opaque) +static void qcow2_aio_write_cb(void *opaque, int ret); + +static void qcow2_aio_rw_bh(void *opaque) { QCowAIOCB *acb = opaque; qemu_bh_delete(acb->bh); acb->bh = NULL; - qcow2_aio_read_cb(opaque, 0); + + if (acb->is_write) { + qcow2_aio_write_cb(opaque, 0); + } else { + qcow2_aio_read_cb(opaque, 0); + } } static int qcow2_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb) @@ -493,14 +501,14 @@ goto done; } } else { - ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb); + ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); if (ret < 0) goto done; } } else { /* Note: in this case, no need to wait */ qemu_iovec_memset(&acb->hd_qiov, 0, 512 * acb->cur_nr_sectors); - ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb); + ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); if (ret < 0) goto done; } @@ -515,7 +523,7 @@ s->cluster_cache + index_in_cluster * 512, 512 * acb->cur_nr_sectors); - ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb); + ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); if (ret < 0) goto done; } else { @@ -572,6 +580,7 @@ acb->hd_aiocb = NULL; acb->sector_num = sector_num; acb->qiov = qiov; + acb->is_write = is_write; qemu_iovec_init(&acb->hd_qiov, qiov->niov); @@ -591,17 +600,22 @@ void *opaque) { QCowAIOCB *acb; + int ret; acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); if (!acb) return NULL; - qcow2_aio_read_cb(acb, 0); + ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); + if (ret < 0) { + qemu_iovec_destroy(&acb->hd_qiov); + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } -static void qcow2_aio_write_cb(void *opaque, int ret); - static void run_dependent_requests(QCowL2Meta *m) { QCowAIOCB *req; @@ -724,6 +738,7 @@ { BDRVQcowState *s = bs->opaque; QCowAIOCB *acb; + int ret; s->cluster_cache_offset = -1; /* disable compressed cache */ @@ -731,7 +746,13 @@ if (!acb) return NULL; - qcow2_aio_write_cb(acb, 0); + ret = qcow2_schedule_bh(qcow2_aio_rw_bh, acb); + if (ret < 0) { + qemu_iovec_destroy(&acb->hd_qiov); + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } @@ -915,7 +936,7 @@ (1 << cluster_bits) != cluster_size) { error_report( - "Cluster size must be a power of two between %d and %dk\n", + "Cluster size must be a power of two between %d and %dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); return -EINVAL; } @@ -1036,7 +1057,7 @@ const char *backing_fmt = NULL; uint64_t sectors = 0; int flags = 0; - size_t cluster_size = 65536; + size_t cluster_size = DEFAULT_CLUSTER_SIZE; int prealloc = 0; /* Read out options */ @@ -1343,7 +1364,8 @@ { .name = BLOCK_OPT_CLUSTER_SIZE, .type = OPT_SIZE, - .help = "qcow2 cluster size" + .help = "qcow2 cluster size", + .value = { .n = DEFAULT_CLUSTER_SIZE }, }, { .name = BLOCK_OPT_PREALLOC, diff -Nru qemu-kvm-0.14.1+noroms/block/qcow2-cache.c qemu-kvm-0.15.0+noroms/block/qcow2-cache.c --- qemu-kvm-0.14.1+noroms/block/qcow2-cache.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qcow2-cache.c 2011-08-09 07:40:29.000000000 -0500 @@ -312,3 +312,15 @@ c->entries[i].dirty = true; } +bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c, + bool enable) +{ + bool old = c->writethrough; + + if (!old && enable) { + qcow2_cache_flush(bs, c); + } + + c->writethrough = enable; + return old; +} diff -Nru qemu-kvm-0.14.1+noroms/block/qcow2-cluster.c qemu-kvm-0.15.0+noroms/block/qcow2-cluster.c --- qemu-kvm-0.14.1+noroms/block/qcow2-cluster.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qcow2-cluster.c 2011-08-09 07:40:29.000000000 -0500 @@ -70,7 +70,7 @@ ret = qcow2_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { - return ret; + goto fail; } BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); @@ -796,8 +796,8 @@ m->depends_on = old_alloc; m->nb_clusters = 0; *num = 0; - ret = 0; - goto fail; + + goto out_wait_dependency; } } } @@ -812,7 +812,6 @@ cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); if (cluster_offset < 0) { - QLIST_REMOVE(m, next_in_flight); ret = cluster_offset; goto fail; } @@ -825,7 +824,7 @@ out: ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); if (ret < 0) { - return ret; + goto fail_put; } m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end); @@ -835,8 +834,13 @@ return 0; +out_wait_dependency: + return qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + fail: qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); +fail_put: + QLIST_REMOVE(m, next_in_flight); return ret; } diff -Nru qemu-kvm-0.14.1+noroms/block/qcow2.h qemu-kvm-0.15.0+noroms/block/qcow2.h --- qemu-kvm-0.14.1+noroms/block/qcow2.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qcow2.h 2011-08-09 07:40:29.000000000 -0500 @@ -54,6 +54,8 @@ /* Must be at least 4 to cover all cases of refcount table growth */ #define REFCOUNT_CACHE_SIZE 4 +#define DEFAULT_CLUSTER_SIZE 65536 + typedef struct QCowHeader { uint32_t magic; uint32_t version; @@ -226,6 +228,8 @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, bool writethrough); int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); +bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c, + bool enable); void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); diff -Nru qemu-kvm-0.14.1+noroms/block/qcow2-refcount.c qemu-kvm-0.15.0+noroms/block/qcow2-refcount.c --- qemu-kvm-0.14.1+noroms/block/qcow2-refcount.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qcow2-refcount.c 2011-08-09 07:40:29.000000000 -0500 @@ -705,8 +705,15 @@ 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 i, j, l1_modified, nb_csectors, refcount; + int i, j, l1_modified = 0, nb_csectors, refcount; int ret; + bool old_l2_writethrough, old_refcount_writethrough; + + /* Switch caches to writeback mode during update */ + old_l2_writethrough = + qcow2_cache_set_writethrough(bs, s->l2_table_cache, false); + old_refcount_writethrough = + qcow2_cache_set_writethrough(bs, s->refcount_block_cache, false); l2_table = NULL; l1_table = NULL; @@ -720,7 +727,11 @@ l1_allocated = 1; if (bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2) != l1_size2) + { + ret = -EIO; goto fail; + } + for(i = 0;i < l1_size; i++) be64_to_cpus(&l1_table[i]); } else { @@ -729,7 +740,6 @@ l1_allocated = 0; } - l1_modified = 0; for(i = 0; i < l1_size; i++) { l2_offset = l1_table[i]; if (l2_offset) { @@ -773,6 +783,7 @@ } if (refcount < 0) { + ret = -EIO; goto fail; } } @@ -803,6 +814,7 @@ refcount = get_refcount(bs, l2_offset >> s->cluster_bits); } if (refcount < 0) { + ret = -EIO; goto fail; } else if (refcount == 1) { l2_offset |= QCOW_OFLAG_COPIED; @@ -813,6 +825,18 @@ } } } + + ret = 0; +fail: + if (l2_table) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + } + + /* Enable writethrough cache mode again */ + qcow2_cache_set_writethrough(bs, s->l2_table_cache, old_l2_writethrough); + qcow2_cache_set_writethrough(bs, s->refcount_block_cache, + old_refcount_writethrough); + if (l1_modified) { for(i = 0; i < l1_size; i++) cpu_to_be64s(&l1_table[i]); @@ -824,15 +848,7 @@ } if (l1_allocated) qemu_free(l1_table); - return 0; - fail: - if (l2_table) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); - } - - if (l1_allocated) - qemu_free(l1_table); - return -EIO; + return ret; } @@ -1063,7 +1079,7 @@ * Checks an image for refcount consistency. * * 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. + * detected as corrupted, and -errno when an internal error occurred. */ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res) { @@ -1086,7 +1102,7 @@ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, s->l1_table_offset, s->l1_size, 1); if (ret < 0) { - return ret; + goto fail; } /* snapshots */ @@ -1095,7 +1111,7 @@ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, sn->l1_table_offset, sn->l1_size, 0); if (ret < 0) { - return ret; + goto fail; } } inc_refcounts(bs, res, refcount_table, nb_clusters, @@ -1159,8 +1175,11 @@ } } + ret = 0; + +fail: qemu_free(refcount_table); - return 0; + return ret; } diff -Nru qemu-kvm-0.14.1+noroms/block/qcow2-snapshot.c qemu-kvm-0.15.0+noroms/block/qcow2-snapshot.c --- qemu-kvm-0.14.1+noroms/block/qcow2-snapshot.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qcow2-snapshot.c 2011-08-09 07:40:29.000000000 -0500 @@ -317,7 +317,8 @@ { BDRVQcowState *s = bs->opaque; QCowSnapshot *sn; - int i, snapshot_index, l1_size2; + int i, snapshot_index; + int cur_l1_bytes, sn_l1_bytes; snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); if (snapshot_index < 0) @@ -330,14 +331,19 @@ 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); + cur_l1_bytes = s->l1_size * sizeof(uint64_t); + sn_l1_bytes = sn->l1_size * sizeof(uint64_t); + + if (cur_l1_bytes > sn_l1_bytes) { + memset(s->l1_table + sn->l1_size, 0, cur_l1_bytes - sn_l1_bytes); + } + /* copy the snapshot l1 table to the current l1 table */ if (bdrv_pread(bs->file, sn->l1_table_offset, - s->l1_table, l1_size2) != l1_size2) + s->l1_table, sn_l1_bytes) < 0) goto fail; if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, - s->l1_table, l1_size2) < 0) + s->l1_table, cur_l1_bytes) < 0) goto fail; for(i = 0;i < s->l1_size; i++) { be64_to_cpus(&s->l1_table[i]); diff -Nru qemu-kvm-0.14.1+noroms/block/qcow.c qemu-kvm-0.15.0+noroms/block/qcow.c --- qemu-kvm-0.14.1+noroms/block/qcow.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qcow.c 2011-08-09 07:40:29.000000000 -0500 @@ -496,6 +496,8 @@ uint64_t cluster_offset; uint8_t *cluster_data; struct iovec hd_iov; + bool is_write; + QEMUBH *bh; QEMUIOVector hd_qiov; BlockDriverAIOCB *hd_aiocb; } QCowAIOCB; @@ -525,6 +527,8 @@ acb->hd_aiocb = NULL; acb->sector_num = sector_num; acb->qiov = qiov; + acb->is_write = is_write; + if (qiov->niov > 1) { acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size); if (is_write) @@ -538,6 +542,38 @@ return acb; } +static void qcow_aio_read_cb(void *opaque, int ret); +static void qcow_aio_write_cb(void *opaque, int ret); + +static void qcow_aio_rw_bh(void *opaque) +{ + QCowAIOCB *acb = opaque; + qemu_bh_delete(acb->bh); + acb->bh = NULL; + + if (acb->is_write) { + qcow_aio_write_cb(opaque, 0); + } else { + qcow_aio_read_cb(opaque, 0); + } +} + +static int qcow_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb) +{ + if (acb->bh) { + return -EIO; + } + + acb->bh = qemu_bh_new(cb, acb); + if (!acb->bh) { + return -EIO; + } + + qemu_bh_schedule(acb->bh); + + return 0; +} + static void qcow_aio_read_cb(void *opaque, int ret) { QCowAIOCB *acb = opaque; @@ -589,8 +625,10 @@ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); 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) + if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; + } } else { /* Note: in this case, no need to wait */ memset(acb->buf, 0, 512 * acb->n); @@ -598,8 +636,10 @@ } } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ - if (decompress_cluster(bs, acb->cluster_offset) < 0) + if (decompress_cluster(bs, acb->cluster_offset) < 0) { + ret = -EIO; goto done; + } memcpy(acb->buf, s->cluster_cache + index_in_cluster * 512, 512 * acb->n); goto redo; @@ -614,8 +654,10 @@ 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) + if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; + } } return; @@ -634,12 +676,21 @@ BlockDriverCompletionFunc *cb, void *opaque) { QCowAIOCB *acb; + int ret; acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); if (!acb) return NULL; - qcow_aio_read_cb(acb, 0); + ret = qcow_schedule_bh(qcow_aio_rw_bh, acb); + if (ret < 0) { + if (acb->qiov->niov > 1) { + qemu_vfree(acb->orig_buf); + } + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } @@ -700,8 +751,10 @@ (cluster_offset >> 9) + index_in_cluster, &acb->hd_qiov, acb->n, qcow_aio_write_cb, acb); - if (acb->hd_aiocb == NULL) + if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; + } return; done: @@ -717,6 +770,7 @@ { BDRVQcowState *s = bs->opaque; QCowAIOCB *acb; + int ret; s->cluster_cache_offset = -1; /* disable compressed cache */ @@ -725,7 +779,15 @@ return NULL; - qcow_aio_write_cb(acb, 0); + ret = qcow_schedule_bh(qcow_aio_rw_bh, acb); + if (ret < 0) { + if (acb->qiov->niov > 1) { + qemu_vfree(acb->orig_buf); + } + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } diff -Nru qemu-kvm-0.14.1+noroms/block/qed.c qemu-kvm-0.15.0+noroms/block/qed.c --- qemu-kvm-0.14.1+noroms/block/qed.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qed.c 2011-08-09 07:40:29.000000000 -0500 @@ -12,6 +12,7 @@ * */ +#include "qemu-timer.h" #include "trace.h" #include "qed.h" #include "qerror.h" @@ -291,6 +292,88 @@ static void qed_aio_next_io(void *opaque, int ret); +static void qed_plug_allocating_write_reqs(BDRVQEDState *s) +{ + assert(!s->allocating_write_reqs_plugged); + + s->allocating_write_reqs_plugged = true; +} + +static void qed_unplug_allocating_write_reqs(BDRVQEDState *s) +{ + QEDAIOCB *acb; + + assert(s->allocating_write_reqs_plugged); + + s->allocating_write_reqs_plugged = false; + + acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); + if (acb) { + qed_aio_next_io(acb, 0); + } +} + +static void qed_finish_clear_need_check(void *opaque, int ret) +{ + /* Do nothing */ +} + +static void qed_flush_after_clear_need_check(void *opaque, int ret) +{ + BDRVQEDState *s = opaque; + + bdrv_aio_flush(s->bs, qed_finish_clear_need_check, s); + + /* No need to wait until flush completes */ + qed_unplug_allocating_write_reqs(s); +} + +static void qed_clear_need_check(void *opaque, int ret) +{ + BDRVQEDState *s = opaque; + + if (ret) { + qed_unplug_allocating_write_reqs(s); + return; + } + + s->header.features &= ~QED_F_NEED_CHECK; + qed_write_header(s, qed_flush_after_clear_need_check, s); +} + +static void qed_need_check_timer_cb(void *opaque) +{ + BDRVQEDState *s = opaque; + + /* The timer should only fire when allocating writes have drained */ + assert(!QSIMPLEQ_FIRST(&s->allocating_write_reqs)); + + trace_qed_need_check_timer_cb(s); + + qed_plug_allocating_write_reqs(s); + + /* Ensure writes are on disk before clearing flag */ + bdrv_aio_flush(s->bs, qed_clear_need_check, s); +} + +static void qed_start_need_check_timer(BDRVQEDState *s) +{ + trace_qed_start_need_check_timer(s); + + /* Use vm_clock so we don't alter the image file while suspended for + * migration. + */ + qemu_mod_timer(s->need_check_timer, qemu_get_clock_ns(vm_clock) + + get_ticks_per_sec() * QED_NEED_CHECK_TIMEOUT); +} + +/* It's okay to call this multiple times or when no timer is started */ +static void qed_cancel_need_check_timer(BDRVQEDState *s) +{ + trace_qed_cancel_need_check_timer(s); + qemu_del_timer(s->need_check_timer); +} + static int bdrv_qed_open(BlockDriverState *bs, int flags) { BDRVQEDState *s = bs->opaque; @@ -406,7 +489,10 @@ BdrvCheckResult result = {0}; ret = qed_check(s, &result, true); - if (!ret && !result.corruptions && !result.check_errors) { + if (ret) { + goto out; + } + if (!result.corruptions && !result.check_errors) { /* Ensure fixes reach storage before clearing check bit */ bdrv_flush(s->bs); @@ -416,6 +502,9 @@ } } + s->need_check_timer = qemu_new_timer_ns(vm_clock, + qed_need_check_timer_cb, s); + out: if (ret) { qed_free_l2_cache(&s->l2_cache); @@ -428,6 +517,9 @@ { BDRVQEDState *s = bs->opaque; + qed_cancel_need_check_timer(s); + qemu_free_timer(s->need_check_timer); + /* Ensure writes reach stable storage */ bdrv_flush(bs->file); @@ -573,7 +665,7 @@ { QEDIsAllocatedCB *cb = opaque; *cb->pnum = len / BDRV_SECTOR_SIZE; - cb->is_allocated = ret == QED_CLUSTER_FOUND; + cb->is_allocated = (ret == QED_CLUSTER_FOUND || ret == QED_CLUSTER_ZERO); } static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num, @@ -745,7 +837,10 @@ * @table: L2 table * @index: First cluster index * @n: Number of contiguous clusters - * @cluster: First cluster byte offset in image file + * @cluster: First cluster offset + * + * The cluster offset may be an allocated byte offset in the image file, the + * zero cluster marker, or the unallocated cluster marker. */ static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index, unsigned int n, uint64_t cluster) @@ -753,7 +848,10 @@ int i; for (i = index; i < index + n; i++) { table->offsets[i] = cluster; - cluster += s->header.cluster_size; + if (!qed_offset_is_unalloc_cluster(cluster) && + !qed_offset_is_zero_cluster(cluster)) { + cluster += s->header.cluster_size; + } } } @@ -803,6 +901,8 @@ acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); if (acb) { qed_aio_next_io(acb, 0); + } else if (s->header.features & QED_F_NEED_CHECK) { + qed_start_need_check_timer(s); } } } @@ -1008,11 +1108,17 @@ { BDRVQEDState *s = acb_to_s(acb); + /* Cancel timer when the first allocating request comes in */ + if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) { + qed_cancel_need_check_timer(s); + } + /* 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)) { + if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs) || + s->allocating_write_reqs_plugged) { return; /* wait for existing request to finish */ } @@ -1075,6 +1181,7 @@ case QED_CLUSTER_L2: case QED_CLUSTER_L1: + case QED_CLUSTER_ZERO: qed_aio_write_alloc(acb, len); break; @@ -1114,8 +1221,12 @@ 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) { + /* Handle zero cluster and backing file reads */ + if (ret == QED_CLUSTER_ZERO) { + qemu_iovec_memset(&acb->cur_qiov, 0, acb->cur_qiov.size); + qed_aio_next_io(acb, 0); + return; + } else if (ret != QED_CLUSTER_FOUND) { qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov, qed_aio_next_io, acb); return; @@ -1222,7 +1333,27 @@ static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset) { - return -ENOTSUP; + BDRVQEDState *s = bs->opaque; + uint64_t old_image_size; + int ret; + + if (!qed_is_image_size_valid(offset, s->header.cluster_size, + s->header.table_size)) { + return -EINVAL; + } + + /* Shrinking is currently not supported */ + if ((uint64_t)offset < s->header.image_size) { + return -ENOTSUP; + } + + old_image_size = s->header.image_size; + s->header.image_size = offset; + ret = qed_write_header_sync(s); + if (ret < 0) { + s->header.image_size = old_image_size; + } + return ret; } static int64_t bdrv_qed_getlength(BlockDriverState *bs) @@ -1333,7 +1464,8 @@ }, { .name = BLOCK_OPT_CLUSTER_SIZE, .type = OPT_SIZE, - .help = "Cluster size (in bytes)" + .help = "Cluster size (in bytes)", + .value = { .n = QED_DEFAULT_CLUSTER_SIZE }, }, { .name = BLOCK_OPT_TABLE_SIZE, .type = OPT_SIZE, diff -Nru qemu-kvm-0.14.1+noroms/block/qed-check.c qemu-kvm-0.15.0+noroms/block/qed-check.c --- qemu-kvm-0.14.1+noroms/block/qed-check.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qed-check.c 2011-08-09 07:40:29.000000000 -0500 @@ -72,7 +72,8 @@ for (i = 0; i < s->table_nelems; i++) { uint64_t offset = table->offsets[i]; - if (!offset) { + if (qed_offset_is_unalloc_cluster(offset) || + qed_offset_is_zero_cluster(offset)) { continue; } @@ -111,7 +112,7 @@ unsigned int num_invalid_l2; uint64_t offset = table->offsets[i]; - if (!offset) { + if (qed_offset_is_unalloc_cluster(offset)) { continue; } diff -Nru qemu-kvm-0.14.1+noroms/block/qed-cluster.c qemu-kvm-0.15.0+noroms/block/qed-cluster.c --- qemu-kvm-0.14.1+noroms/block/qed-cluster.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qed-cluster.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,7 +23,8 @@ * @n: Maximum number of clusters * @offset: Set to first cluster offset * - * This function scans tables for contiguous allocated or free clusters. + * This function scans tables for contiguous clusters. A contiguous run of + * clusters may be allocated, unallocated, or zero. */ static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s, QEDTable *table, @@ -38,9 +39,14 @@ *offset = last; for (i = index + 1; i < end; i++) { - if (last == 0) { - /* Counting free clusters */ - if (table->offsets[i] != 0) { + if (qed_offset_is_unalloc_cluster(last)) { + /* Counting unallocated clusters */ + if (!qed_offset_is_unalloc_cluster(table->offsets[i])) { + break; + } + } else if (qed_offset_is_zero_cluster(last)) { + /* Counting zero clusters */ + if (!qed_offset_is_zero_cluster(table->offsets[i])) { break; } } else { @@ -87,14 +93,19 @@ 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)) { + if (qed_offset_is_unalloc_cluster(offset)) { + ret = QED_CLUSTER_L2; + } else if (qed_offset_is_zero_cluster(offset)) { + ret = QED_CLUSTER_ZERO; + } else if (qed_check_cluster_offset(s, offset)) { + ret = QED_CLUSTER_FOUND; + } else { ret = -EINVAL; } + len = MIN(find_cluster_cb->len, n * s->header.cluster_size - + qed_offset_into_cluster(s, find_cluster_cb->pos)); + out: find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len); qemu_free(find_cluster_cb); @@ -132,7 +143,7 @@ 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) { + if (qed_offset_is_unalloc_cluster(l2_offset)) { cb(opaque, QED_CLUSTER_L1, 0, len); return; } diff -Nru qemu-kvm-0.14.1+noroms/block/qed.h qemu-kvm-0.15.0+noroms/block/qed.h --- qemu-kvm-0.14.1+noroms/block/qed.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/qed.h 2011-08-09 07:40:29.000000000 -0500 @@ -78,6 +78,9 @@ QED_MIN_TABLE_SIZE = 1, /* in clusters */ QED_MAX_TABLE_SIZE = 16, QED_DEFAULT_TABLE_SIZE = 4, + + /* Delay to flush and clean image after last allocating write completes */ + QED_NEED_CHECK_TIMEOUT = 5, /* in seconds */ }; typedef struct { @@ -157,10 +160,15 @@ /* Allocating write request queue */ QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs; + bool allocating_write_reqs_plugged; + + /* Periodic flush and clear need check flag */ + QEMUTimer *need_check_timer; } BDRVQEDState; enum { QED_CLUSTER_FOUND, /* cluster found */ + QED_CLUSTER_ZERO, /* zero cluster found */ QED_CLUSTER_L2, /* cluster missing in L2 */ QED_CLUSTER_L1, /* cluster missing in L1 */ }; @@ -298,4 +306,29 @@ qed_check_cluster_offset(s, end_offset); } +static inline bool qed_offset_is_cluster_aligned(BDRVQEDState *s, + uint64_t offset) +{ + if (qed_offset_into_cluster(s, offset)) { + return false; + } + return true; +} + +static inline bool qed_offset_is_unalloc_cluster(uint64_t offset) +{ + if (offset == 0) { + return true; + } + return false; +} + +static inline bool qed_offset_is_zero_cluster(uint64_t offset) +{ + if (offset == 1) { + return true; + } + return false; +} + #endif /* BLOCK_QED_H */ diff -Nru qemu-kvm-0.14.1+noroms/block/raw-posix.c qemu-kvm-0.15.0+noroms/block/raw-posix.c --- qemu-kvm-0.14.1+noroms/block/raw-posix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/raw-posix.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,8 +27,6 @@ #include "qemu-log.h" #include "block_int.h" #include "module.h" -#include "compatfd.h" -#include #include "block/raw-posix-aio.h" #ifdef CONFIG_COCOA @@ -45,17 +43,17 @@ #ifdef __sun__ #define _POSIX_PTHREAD_SEMANTICS 1 -#include #include #endif #ifdef __linux__ +#include +#include #include #include #include #include #endif #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) -#include #include #include #endif @@ -66,6 +64,13 @@ #include #endif +#ifdef __NetBSD__ +#include +#include +#include +#include +#endif + #ifdef __DragonFly__ #include #include @@ -138,12 +143,55 @@ static int cdrom_reopen(BlockDriverState *bs); #endif +#if defined(__NetBSD__) +static int raw_normalize_devicepath(const char **filename) +{ + static char namebuf[PATH_MAX]; + const char *dp, *fname; + struct stat sb; + + fname = *filename; + dp = strrchr(fname, '/'); + if (lstat(fname, &sb) < 0) { + fprintf(stderr, "%s: stat failed: %s\n", + fname, strerror(errno)); + return -errno; + } + + if (!S_ISBLK(sb.st_mode)) { + return 0; + } + + if (dp == NULL) { + snprintf(namebuf, PATH_MAX, "r%s", fname); + } else { + snprintf(namebuf, PATH_MAX, "%.*s/r%s", + (int)(dp - fname), fname, dp + 1); + } + fprintf(stderr, "%s is a block device", fname); + *filename = namebuf; + fprintf(stderr, ", using %s\n", *filename); + + return 0; +} +#else +static int raw_normalize_devicepath(const char **filename) +{ + return 0; +} +#endif + static int raw_open_common(BlockDriverState *bs, const char *filename, int bdrv_flags, int open_flags) { BDRVRawState *s = bs->opaque; int fd, ret; + ret = raw_normalize_devicepath(&filename); + if (ret != 0) { + return ret; + } + s->open_flags = open_flags | O_BINARY; s->open_flags &= ~O_ACCMODE; if (bdrv_flags & BDRV_O_RDWR) { @@ -156,7 +204,7 @@ * and O_DIRECT for no caching. */ if ((bdrv_flags & BDRV_O_NOCACHE)) s->open_flags |= O_DIRECT; - else if (!(bdrv_flags & BDRV_O_CACHE_WB)) + if (!(bdrv_flags & BDRV_O_CACHE_WB)) s->open_flags |= O_DSYNC; s->fd = -1; @@ -624,6 +672,31 @@ } else return st.st_size; } +#elif defined(__NetBSD__) +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int fd = s->fd; + struct stat st; + + if (fstat(fd, &st)) + return -1; + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + struct dkwedge_info dkw; + + if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) { + return dkw.dkw_size * 512; + } else { + struct disklabel dl; + + if (ioctl(fd, DIOCGDINFO, &dl)) + return -1; + return (uint64_t)dl.d_secsize * + dl.d_partitions[DISKPART(st.st_rdev)].p_size; + } + } else + return st.st_size; +} #elif defined(__sun__) static int64_t raw_getlength(BlockDriverState *bs) { @@ -720,6 +793,17 @@ } #endif +static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +{ + struct stat st; + BDRVRawState *s = bs->opaque; + + if (fstat(s->fd, &st) < 0) { + return -errno; + } + return (int64_t)st.st_blocks * 512; +} + static int raw_create(const char *filename, QEMUOptionParameter *options) { int fd; @@ -815,6 +899,8 @@ .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, .create_options = raw_create_options, }; @@ -1083,6 +1169,8 @@ .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* generic scsi device */ #ifdef __linux__ @@ -1117,6 +1205,7 @@ int fd, ret; int prio = 0; struct floppy_struct fdparam; + struct stat st; if (strstart(filename, "/dev/fd", NULL)) prio = 50; @@ -1125,12 +1214,17 @@ if (fd < 0) { goto out; } + ret = fstat(fd, &st); + if (ret == -1 || !S_ISBLK(st.st_mode)) { + goto outc; + } /* Attempt to detect via a floppy specific ioctl */ ret = ioctl(fd, FDGETPRM, &fdparam); if (ret >= 0) prio = 100; +outc: close(fd); out: return prio; @@ -1198,6 +1292,8 @@ .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* removable device support */ .bdrv_is_inserted = floppy_is_inserted, @@ -1219,17 +1315,23 @@ { int fd, ret; int prio = 0; + struct stat st; fd = open(filename, O_RDONLY | O_NONBLOCK); if (fd < 0) { goto out; } + ret = fstat(fd, &st); + if (ret == -1 || !S_ISBLK(st.st_mode)) { + goto outc; + } /* Attempt to detect via a CDROM specific ioctl */ ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); if (ret >= 0) prio = 100; +outc: close(fd); out: return prio; @@ -1295,6 +1397,8 @@ .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* removable device support */ .bdrv_is_inserted = cdrom_is_inserted, @@ -1418,6 +1522,8 @@ .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* removable device support */ .bdrv_is_inserted = cdrom_is_inserted, diff -Nru qemu-kvm-0.14.1+noroms/block/raw-win32.c qemu-kvm-0.15.0+noroms/block/raw-win32.c --- qemu-kvm-0.14.1+noroms/block/raw-win32.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/raw-win32.c 2011-08-09 07:40:29.000000000 -0500 @@ -88,9 +88,9 @@ } overlapped = FILE_ATTRIBUTE_NORMAL; - if ((flags & BDRV_O_NOCACHE)) - overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; - else if (!(flags & BDRV_O_CACHE_WB)) + if (flags & BDRV_O_NOCACHE) + overlapped |= FILE_FLAG_NO_BUFFERING; + if (!(flags & BDRV_O_CACHE_WB)) overlapped |= FILE_FLAG_WRITE_THROUGH; s->hfile = CreateFile(filename, access_flags, FILE_SHARE_READ, NULL, @@ -213,6 +213,31 @@ return l.QuadPart; } +static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +{ + typedef DWORD (WINAPI * get_compressed_t)(const char *filename, + DWORD * high); + get_compressed_t get_compressed; + struct _stati64 st; + const char *filename = bs->filename; + /* WinNT support GetCompressedFileSize to determine allocate size */ + get_compressed = + (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), + "GetCompressedFileSizeA"); + if (get_compressed) { + DWORD high, low; + low = get_compressed(filename, &high); + if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) { + return (((int64_t) high) << 32) + low; + } + } + + if (_stati64(filename, &st) < 0) { + return -1; + } + return st.st_size; +} + static int raw_create(const char *filename, QEMUOptionParameter *options) { int fd; @@ -257,6 +282,8 @@ .bdrv_write = raw_write, .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, .create_options = raw_create_options, }; @@ -349,9 +376,9 @@ create_flags = OPEN_EXISTING; overlapped = FILE_ATTRIBUTE_NORMAL; - if ((flags & BDRV_O_NOCACHE)) - overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; - else if (!(flags & BDRV_O_CACHE_WB)) + if (flags & BDRV_O_NOCACHE) + overlapped |= FILE_FLAG_NO_BUFFERING; + if (!(flags & BDRV_O_CACHE_WB)) overlapped |= FILE_FLAG_WRITE_THROUGH; s->hfile = CreateFile(filename, access_flags, FILE_SHARE_READ, NULL, @@ -419,6 +446,8 @@ .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, }; static void bdrv_file_init(void) diff -Nru qemu-kvm-0.14.1+noroms/block/rbd.c qemu-kvm-0.15.0+noroms/block/rbd.c --- qemu-kvm-0.14.1+noroms/block/rbd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/rbd.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,33 +1,39 @@ /* * QEMU Block driver for RADOS (Ceph) * - * Copyright (C) 2010 Christian Brunner + * Copyright (C) 2010-2011 Christian Brunner , + * Josh Durgin * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. * */ +#include + #include "qemu-common.h" #include "qemu-error.h" -#include "rbd_types.h" #include "block_int.h" -#include +#include /* * When specifying the image filename use: * - * rbd:poolname/devicename + * rbd:poolname/devicename[@snapshotname][:option1=value1[:option2=value2...]] * * poolname must be the name of an existing rados pool * * devicename is the basename for all objects used to * emulate the raw device. * + * Each option given is used to configure rados, and may be + * any Ceph option, or "conf". The "conf" option specifies + * a Ceph configuration file to read. + * * Metadata information (image size, ...) is stored in an * object with the name "devicename.rbd". * @@ -40,6 +46,13 @@ #define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER) +#define RBD_MAX_CONF_NAME_SIZE 128 +#define RBD_MAX_CONF_VAL_SIZE 512 +#define RBD_MAX_CONF_SIZE 1024 +#define RBD_MAX_POOL_NAME_SIZE 128 +#define RBD_MAX_SNAP_NAME_SIZE 128 +#define RBD_MAX_SNAPS 100 + typedef struct RBDAIOCB { BlockDriverAIOCB common; QEMUBH *bh; @@ -48,7 +61,6 @@ char *bounce; int write; int64_t sector_num; - int aiocnt; int error; struct BDRVRBDState *s; int cancelled; @@ -59,7 +71,7 @@ RBDAIOCB *acb; struct BDRVRBDState *s; int done; - int64_t segsize; + int64_t size; char *buf; int ret; } RADOSCB; @@ -69,25 +81,22 @@ 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; + rados_t cluster; + rados_ioctx_t io_ctx; + rbd_image_t image; + char name[RBD_MAX_IMAGE_NAME_SIZE]; int qemu_aio_count; + char *snap; 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) +static int qemu_rbd_next_tok(char *dst, int dst_len, + char *src, char delim, + const char *name, + char **p) { int l; char *end; @@ -115,10 +124,11 @@ return 0; } -static int rbd_parsename(const char *filename, - char *pool, int pool_len, - char *snap, int snap_len, - char *name, int name_len) +static int qemu_rbd_parsename(const char *filename, + char *pool, int pool_len, + char *snap, int snap_len, + char *name, int name_len, + char *conf, int conf_len) { const char *start; char *p, *buf; @@ -130,137 +140,103 @@ buf = qemu_strdup(start); p = buf; + *snap = '\0'; + *conf = '\0'; - ret = rbd_next_tok(pool, pool_len, p, '/', "pool name", &p); + ret = qemu_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 (strchr(p, '@')) { + ret = qemu_rbd_next_tok(name, name_len, p, '@', "object name", &p); + if (ret < 0) { + goto done; + } + ret = qemu_rbd_next_tok(snap, snap_len, p, ':', "snap name", &p); + } else { + ret = qemu_rbd_next_tok(name, name_len, p, ':', "object name", &p); } - if (!p) { - *snap = '\0'; + if (ret < 0 || !p) { goto done; } - ret = rbd_next_tok(snap, snap_len, p, '\0', "snap name", &p); + ret = qemu_rbd_next_tok(conf, conf_len, p, '\0', "configuration", &p); done: qemu_free(buf); return ret; } -static int create_tmap_op(uint8_t op, const char *name, char **tmap_desc) +static int qemu_rbd_set_conf(rados_t cluster, const char *conf) { - 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; + char *p, *buf; + char name[RBD_MAX_CONF_NAME_SIZE]; + char value[RBD_MAX_CONF_VAL_SIZE]; + int ret = 0; - *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); + buf = qemu_strdup(conf); + p = buf; - return (char *)desc - *tmap_desc; -} + while (p) { + ret = qemu_rbd_next_tok(name, sizeof(name), p, + '=', "conf option name", &p); + if (ret < 0) { + break; + } -static void free_tmap_op(char *tmap_desc) -{ - qemu_free(tmap_desc); -} + if (!p) { + error_report("conf option %s has no value", name); + ret = -EINVAL; + break; + } -static int rbd_register_image(rados_pool_t pool, const char *name) -{ - char *tmap_desc; - const char *dir = RBD_DIRECTORY; - int ret; + ret = qemu_rbd_next_tok(value, sizeof(value), p, + ':', "conf option value", &p); + if (ret < 0) { + break; + } - ret = create_tmap_op(CEPH_OSD_TMAP_SET, name, &tmap_desc); - if (ret < 0) { - return ret; + if (strcmp(name, "conf")) { + ret = rados_conf_set(cluster, name, value); + if (ret < 0) { + error_report("invalid conf option %s", name); + ret = -EINVAL; + break; + } + } else { + ret = rados_conf_read_file(cluster, value); + if (ret < 0) { + error_report("error reading conf file %s", value); + break; + } + } } - ret = rados_tmap_update(pool, dir, tmap_desc, ret); - free_tmap_op(tmap_desc); - + qemu_free(buf); 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) +static int qemu_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 obj_order = 0; + char pool[RBD_MAX_POOL_NAME_SIZE]; + char name[RBD_MAX_IMAGE_NAME_SIZE]; + char snap_buf[RBD_MAX_SNAP_NAME_SIZE]; + char conf[RBD_MAX_CONF_SIZE]; + rados_t cluster; + rados_ioctx_t io_ctx; int ret; - if (rbd_parsename(filename, - pool, sizeof(pool), - snap_buf, sizeof(snap_buf), - name, sizeof(name)) < 0) { + if (qemu_rbd_parsename(filename, pool, sizeof(pool), + snap_buf, sizeof(snap_buf), + name, sizeof(name), + conf, sizeof(conf)) < 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) { @@ -277,82 +253,64 @@ error_report("obj size too small"); return -EINVAL; } - obj_order = ffs(objsize) - 1; + 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) { + if (rados_create(&cluster, 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; + if (strstr(conf, "conf=") == NULL) { + if (rados_conf_read_file(cluster, NULL) < 0) { + error_report("error reading config file"); + rados_shutdown(cluster); + return -EIO; + } } - /* check for existing rbd header file */ - ret = rados_stat(p, n, &size, &mtime); - if (ret == 0) { - ret=-EEXIST; - goto done; + if (conf[0] != '\0' && + qemu_rbd_set_conf(cluster, conf) < 0) { + error_report("error setting config options"); + rados_shutdown(cluster); + return -EIO; } - ret = rbd_assign_bid(p, &bid); - if (ret < 0) { - error_report("failed assigning block id"); - rados_deinitialize(); + if (rados_connect(cluster) < 0) { + error_report("error connecting"); + rados_shutdown(cluster); 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; + if (rados_ioctx_create(cluster, pool, &io_ctx) < 0) { + error_report("error opening pool %s", pool); + rados_shutdown(cluster); + return -EIO; } - ret = rbd_register_image(p, name); -done: - rados_close_pool(p); - rados_deinitialize(); + ret = rbd_create(io_ctx, name, bytes, &obj_order); + rados_ioctx_destroy(io_ctx); + rados_shutdown(cluster); 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 + * This aio completion is being called from qemu_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) +static void qemu_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); - } + qemu_vfree(acb->bounce); + qemu_aio_release(acb); goto done; } @@ -363,32 +321,25 @@ acb->ret = r; acb->error = 1; } else if (!acb->error) { - acb->ret += rcb->segsize; + acb->ret = rcb->size; } } 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); + if (r < 0) { + memset(rcb->buf, 0, rcb->size); acb->ret = r; acb->error = 1; - } else if (r < rcb->segsize) { - memset(rcb->buf + r, 0, rcb->segsize - r); + } else if (r < rcb->size) { + memset(rcb->buf + r, 0, rcb->size - r); if (!acb->error) { - acb->ret += rcb->segsize; + acb->ret = rcb->size; } } else if (!acb->error) { - acb->ret += r; + 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); - } + acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb); + qemu_bh_schedule(acb->bh); done: qemu_free(rcb); } @@ -397,7 +348,7 @@ * 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) +static void qemu_rbd_aio_event_reader(void *opaque) { BDRVRBDState *s = opaque; @@ -413,176 +364,87 @@ 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 --; + qemu_rbd_complete_aio(s->event_rcb); + s->qemu_aio_count--; } } } } while (ret < 0 && errno == EINTR); } -static int rbd_aio_flush_cb(void *opaque) +static int qemu_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) +static int qemu_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; + char pool[RBD_MAX_POOL_NAME_SIZE]; + char snap_buf[RBD_MAX_SNAP_NAME_SIZE]; + char conf[RBD_MAX_CONF_SIZE]; int r; - if (rbd_parsename(filename, pool, sizeof(pool), - snap_buf, sizeof(snap_buf), - s->name, sizeof(s->name)) < 0) { + if (qemu_rbd_parsename(filename, pool, sizeof(pool), + snap_buf, sizeof(snap_buf), + s->name, sizeof(s->name), + conf, sizeof(conf)) < 0) { return -EINVAL; } + s->snap = NULL; if (snap_buf[0] != '\0') { - snap = snap_buf; + s->snap = qemu_strdup(snap_buf); } - if ((r = rados_initialize(0, NULL)) < 0) { + r = rados_create(&s->cluster, NULL); + if (r < 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 (strstr(conf, "conf=") == NULL) { + r = rados_conf_read_file(s->cluster, NULL); + if (r < 0) { + error_report("error reading config file"); + rados_shutdown(s->cluster); + return r; + } } - if ((r = rbd_read_header(s, &hbuf)) < 0) { - error_report("error reading header from %s", s->name); - goto failed; + if (conf[0] != '\0') { + r = qemu_rbd_set_conf(s->cluster, conf); + if (r < 0) { + error_report("error setting config options"); + rados_shutdown(s->cluster); + return r; + } } - if (memcmp(hbuf + 64, RBD_HEADER_SIGNATURE, 4)) { - error_report("Invalid header signature"); - r = -EMEDIUMTYPE; - goto failed; + r = rados_connect(s->cluster); + if (r < 0) { + error_report("error connecting"); + rados_shutdown(s->cluster); + return r; } - if (memcmp(hbuf + 68, RBD_HEADER_VERSION, 8)) { - error_report("Unknown image version"); - r = -EMEDIUMTYPE; - goto failed; + r = rados_ioctx_create(s->cluster, pool, &s->io_ctx); + if (r < 0) { + error_report("error opening pool %s", pool); + rados_shutdown(s->cluster); + return r; } - 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); + r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); if (r < 0) { - error_report("failed setting snap context: %s", strerror(-r)); - goto failed; + error_report("error reading header from %s", s->name); + rados_ioctx_destroy(s->io_ctx); + rados_shutdown(s->cluster); + return r; } - bs->read_only = (snap != NULL); + bs->read_only = (s->snap != NULL); s->event_reader_pos = 0; r = qemu_pipe(s->fds); @@ -592,23 +454,20 @@ } 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_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader, + NULL, qemu_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(); + rbd_close(s->image); + rados_ioctx_destroy(s->io_ctx); + rados_shutdown(s->cluster); return r; } -static void rbd_close(BlockDriverState *bs) +static void qemu_rbd_close(BlockDriverState *bs) { BDRVRBDState *s = bs->opaque; @@ -617,16 +476,17 @@ 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(); + rbd_close(s->image); + rados_ioctx_destroy(s->io_ctx); + qemu_free(s->snap); + rados_shutdown(s->cluster); } /* * 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) +static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb) { RBDAIOCB *acb = (RBDAIOCB *) blockacb; acb->cancelled = 1; @@ -634,39 +494,28 @@ static AIOPool rbd_aio_pool = { .aiocb_size = sizeof(RBDAIOCB), - .cancel = rbd_aio_cancel, + .cancel = qemu_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) +static int qemu_rbd_send_pipe(BDRVRBDState *s, RADOSCB *rcb) { - int ret; - rcb->ret = rados_aio_get_return_value(c); - rados_aio_release(c); + int ret = 0; while (1) { fd_set wfd; - int fd = rcb->s->fds[RBD_FD_WRITE]; + int fd = 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 */ + /* send the op pointer to the qemu thread that is responsible + for the aio/op 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); @@ -675,13 +524,31 @@ } while (ret < 0 && errno == EINTR); } + return ret; +} + +/* + * This is the callback function for rbd_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 qemu_rbd_aio_event_reader() which + * runs in a qemu context. + */ +static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb) +{ + int ret; + rcb->ret = rbd_aio_get_return_value(c); + rbd_aio_release(c); + ret = qemu_rbd_send_pipe(rcb->s, rcb); if (ret < 0) { - error_report("failed writing to acb->s->fds\n"); + error_report("failed writing to acb->s->fds"); qemu_free(rcb); } } -/* Callback when all queued rados_aio requests are complete */ +/* Callback when all queued rbd_aio requests are complete */ static void rbd_aio_bh_cb(void *opaque) { @@ -707,19 +574,20 @@ { RBDAIOCB *acb; RADOSCB *rcb; - rados_completion_t c; - char n[RBD_MAX_SEG_NAME_SIZE]; - int64_t segnr, segoffs, segsize, last_segnr; + rbd_completion_t c; int64_t off, size; char *buf; + int r; BDRVRBDState *s = bs->opaque; acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque); + if (!acb) { + return NULL; + } 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; @@ -734,95 +602,106 @@ 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); + s->qemu_aio_count++; /* All the RADOSCB */ - 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); - } + rcb = qemu_malloc(sizeof(RADOSCB)); + rcb->done = 0; + rcb->acb = acb; + rcb->buf = buf; + rcb->s = acb->s; + rcb->size = size; + r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c); + if (r < 0) { + goto failed; + } + + if (write) { + r = rbd_aio_write(s->image, off, size, buf, c); + } else { + r = rbd_aio_read(s->image, off, size, buf, c); + } - buf += segsize; - size -= segsize; - segoffs = 0; - segsize = s->objsize; - segnr++; + if (r < 0) { + goto failed; } return &acb->common; + +failed: + qemu_free(rcb); + s->qemu_aio_count--; + qemu_aio_release(acb); + return NULL; } -static BlockDriverAIOCB *rbd_aio_readv(BlockDriverState * bs, - int64_t sector_num, QEMUIOVector * qiov, - int nb_sectors, - BlockDriverCompletionFunc * cb, - void *opaque) +static BlockDriverAIOCB *qemu_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) +static BlockDriverAIOCB *qemu_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) +static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVRBDState *s = bs->opaque; - bdi->cluster_size = s->objsize; + rbd_image_info_t info; + int r; + + r = rbd_stat(s->image, &info, sizeof(info)); + if (r < 0) { + return r; + } + + bdi->cluster_size = info.obj_size; return 0; } -static int64_t rbd_getlength(BlockDriverState * bs) +static int64_t qemu_rbd_getlength(BlockDriverState *bs) +{ + BDRVRBDState *s = bs->opaque; + rbd_image_info_t info; + int r; + + r = rbd_stat(s->image, &info, sizeof(info)); + if (r < 0) { + return r; + } + + return info.size; +} + +static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset) { BDRVRBDState *s = bs->opaque; + int r; + + r = rbd_resize(s->image, offset); + if (r < 0) { + return r; + } - return s->size; + return 0; } -static int rbd_snap_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) +static int qemu_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 */ @@ -841,185 +720,57 @@ 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)); + r = rbd_snap_create(s->image, sn_info->name); if (r < 0) { - error_report("rbd.snap_add execution failed failed: %s", strerror(-r)); + error_report("failed to create snap: %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) +static int qemu_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); + int i, snap_count; + rbd_snap_info_t *snaps; + int max_snaps = RBD_MAX_SNAPS; - 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; + do { + snaps = qemu_malloc(sizeof(*snaps) * max_snaps); + snap_count = rbd_snap_list(s->image, snaps, &max_snaps); + if (snap_count < 0) { + qemu_free(snaps); } - if (r != len) { - break; - } + } while (snap_count == -ERANGE); - /* 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; + if (snap_count <= 0) { + return snap_count; } 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; - } + for (i = 0; i < snap_count; i++) { + const char *snap_name = snaps[i].name; 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->vm_state_size = snaps[i].size; sn_info->date_sec = 0; sn_info->date_nsec = 0; sn_info->vm_clock_nsec = 0; } + rbd_snap_list_end(snaps); + *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[] = { +static QEMUOptionParameter qemu_rbd_create_options[] = { { .name = BLOCK_OPT_SIZE, .type = OPT_SIZE, @@ -1036,19 +787,20 @@ 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, + .bdrv_file_open = qemu_rbd_open, + .bdrv_close = qemu_rbd_close, + .bdrv_create = qemu_rbd_create, + .bdrv_get_info = qemu_rbd_getinfo, + .create_options = qemu_rbd_create_options, + .bdrv_getlength = qemu_rbd_getlength, + .bdrv_truncate = qemu_rbd_truncate, .protocol_name = "rbd", - .bdrv_aio_readv = rbd_aio_readv, - .bdrv_aio_writev = rbd_aio_writev, + .bdrv_aio_readv = qemu_rbd_aio_readv, + .bdrv_aio_writev = qemu_rbd_aio_writev, - .bdrv_snapshot_create = rbd_snap_create, - .bdrv_snapshot_list = rbd_snap_list, + .bdrv_snapshot_create = qemu_rbd_snap_create, + .bdrv_snapshot_list = qemu_rbd_snap_list, }; static void bdrv_rbd_init(void) diff -Nru qemu-kvm-0.14.1+noroms/block/rbd_types.h qemu-kvm-0.15.0+noroms/block/rbd_types.h --- qemu-kvm-0.14.1+noroms/block/rbd_types.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/rbd_types.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,71 +0,0 @@ -/* - * 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.14.1+noroms/block/sheepdog.c qemu-kvm-0.15.0+noroms/block/sheepdog.c --- qemu-kvm-0.14.1+noroms/block/sheepdog.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/sheepdog.c 2011-08-09 07:40:29.000000000 -0500 @@ -13,6 +13,7 @@ #include "qemu-error.h" #include "qemu_socket.h" #include "block_int.h" +#include "bitops.h" #define SD_PROTO_VER 0x01 @@ -195,7 +196,7 @@ return hval; } -static inline int is_data_obj_writeable(SheepdogInode *inode, unsigned int idx) +static inline int is_data_obj_writable(SheepdogInode *inode, unsigned int idx) { return inode->vdi_id == inode->data_vdi_id[idx]; } @@ -443,18 +444,12 @@ static int sd_schedule_bh(QEMUBHFunc *cb, SheepdogAIOCB *acb) { if (acb->bh) { - error_report("bug: %d %d\n", acb->aiocb_type, acb->aiocb_type); + error_report("bug: %d %d", 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; } @@ -501,7 +496,7 @@ } buf = qemu_malloc(size); - ret = recv(s, buf, size, flags); + ret = qemu_recv(s, buf, size, flags); if (ret < 0) { goto out; } @@ -597,7 +592,7 @@ ret = getaddrinfo(addr, port, &hints, &res0); if (ret) { - error_report("unable to get address info %s, %s\n", + error_report("unable to get address info %s, %s", addr, strerror(errno)); return -1; } @@ -627,7 +622,7 @@ goto success; } fd = -1; - error_report("failed connect to %s:%s\n", addr, port); + error_report("failed connect to %s:%s", addr, port); success: freeaddrinfo(res0); return fd; @@ -643,7 +638,7 @@ if (errno == EINTR || errno == EAGAIN) { goto again; } - error_report("failed to recv a rsp, %s\n", strerror(errno)); + error_report("failed to recv a rsp, %s", strerror(errno)); return 1; } @@ -702,7 +697,7 @@ ret = do_writev(sockfd, iov, sizeof(*hdr) + *wlen, 0); if (ret) { - error_report("failed to send a req, %s\n", strerror(errno)); + error_report("failed to send a req, %s", strerror(errno)); ret = -1; } @@ -722,7 +717,7 @@ ret = do_read(sockfd, hdr, sizeof(*hdr)); if (ret) { - error_report("failed to get a rsp, %s\n", strerror(errno)); + error_report("failed to get a rsp, %s", strerror(errno)); ret = -1; goto out; } @@ -734,7 +729,7 @@ if (*rlen) { ret = do_read(sockfd, data, *rlen); if (ret) { - error_report("failed to get the data, %s\n", strerror(errno)); + error_report("failed to get the data, %s", strerror(errno)); ret = -1; goto out; } @@ -771,7 +766,7 @@ 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"); + error_report("add_aio_request is failed"); free_aio_req(s, aio_req); if (QLIST_EMPTY(&acb->aioreq_head)) { sd_finish_aiocb(acb); @@ -804,7 +799,7 @@ /* read a header */ ret = do_read(fd, &rsp, sizeof(rsp)); if (ret) { - error_report("failed to get the header, %s\n", strerror(errno)); + error_report("failed to get the header, %s", strerror(errno)); return; } @@ -815,7 +810,7 @@ } } if (!aio_req) { - error_report("cannot find aio_req %x\n", rsp.id); + error_report("cannot find aio_req %x", rsp.id); return; } @@ -851,7 +846,7 @@ 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)); + error_report("failed to get the data, %s", strerror(errno)); return; } break; @@ -859,7 +854,7 @@ if (rsp.result != SD_RES_SUCCESS) { acb->ret = -EIO; - error_report("%s\n", sd_strerror(rsp.result)); + error_report("%s", sd_strerror(rsp.result)); } rest = free_aio_req(s, aio_req); @@ -916,7 +911,7 @@ fd = connect_to_sdog(s->addr, s->port); if (fd < 0) { - error_report("%s\n", strerror(errno)); + error_report("%s", strerror(errno)); return -1; } @@ -924,7 +919,7 @@ ret = set_nodelay(fd); if (ret) { - error_report("%s\n", strerror(errno)); + error_report("%s", strerror(errno)); closesocket(fd); return -1; } @@ -1040,7 +1035,7 @@ } if (rsp->result != SD_RES_SUCCESS) { - error_report("cannot get vdi info, %s, %s %d %s\n", + error_report("cannot get vdi info, %s, %s %d %s", sd_strerror(rsp->result), filename, snapid, tag); ret = -1; goto out; @@ -1068,7 +1063,7 @@ uint64_t old_oid = aio_req->base_oid; if (!nr_copies) { - error_report("bug\n"); + error_report("bug"); } memset(&hdr, 0, sizeof(hdr)); @@ -1101,14 +1096,14 @@ /* send a header */ ret = do_write(s->fd, &hdr, sizeof(hdr)); if (ret) { - error_report("failed to send a req, %s\n", strerror(errno)); + error_report("failed to send a req, %s", 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)); + error_report("failed to send a data, %s", strerror(errno)); return -EIO; } } @@ -1150,7 +1145,7 @@ ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen); if (ret) { - error_report("failed to send a request to the sheep\n"); + error_report("failed to send a request to the sheep"); return -1; } @@ -1158,7 +1153,7 @@ case SD_RES_SUCCESS: return 0; default: - error_report("%s\n", sd_strerror(rsp->result)); + error_report("%s", sd_strerror(rsp->result)); return -1; } } @@ -1211,7 +1206,7 @@ fd = connect_to_sdog(s->addr, s->port); if (fd < 0) { - error_report("failed to connect\n"); + error_report("failed to connect"); goto out; } @@ -1280,7 +1275,7 @@ } if (rsp->result != SD_RES_SUCCESS) { - error_report("%s, %s\n", sd_strerror(rsp->result), filename); + error_report("%s, %s", sd_strerror(rsp->result), filename); return -EIO; } @@ -1291,6 +1286,49 @@ return 0; } +static int sd_prealloc(const char *filename) +{ + BlockDriverState *bs = NULL; + uint32_t idx, max_idx; + int64_t vdi_size; + void *buf = qemu_mallocz(SD_DATA_OBJ_SIZE); + int ret; + + ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR); + if (ret < 0) { + goto out; + } + + vdi_size = bdrv_getlength(bs); + if (vdi_size < 0) { + ret = vdi_size; + goto out; + } + max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE); + + for (idx = 0; idx < max_idx; idx++) { + /* + * The created image can be a cloned image, so we need to read + * a data from the source image. + */ + ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + if (ret < 0) { + goto out; + } + ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + if (ret < 0) { + goto out; + } + } +out: + if (bs) { + bdrv_delete(bs); + } + qemu_free(buf); + + return ret; +} + static int sd_create(const char *filename, QEMUOptionParameter *options) { int ret; @@ -1300,14 +1338,16 @@ BDRVSheepdogState s; char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; + int prealloc = 0; + const char *vdiname; - strstart(filename, "sheepdog:", (const char **)&filename); + strstart(filename, "sheepdog:", &vdiname); 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"); + if (parse_vdiname(&s, vdiname, vdi, &snapid, tag) < 0) { + error_report("invalid filename"); return -EINVAL; } @@ -1316,12 +1356,22 @@ vdi_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_PREALLOC)) { + if (!options->value.s || !strcmp(options->value.s, "off")) { + prealloc = 0; + } else if (!strcmp(options->value.s, "full")) { + prealloc = 1; + } else { + error_report("Invalid preallocation mode: '%s'", + options->value.s); + return -EINVAL; + } } options++; } if (vdi_size > SD_MAX_VDI_SIZE) { - error_report("too big image size\n"); + error_report("too big image size"); return -EINVAL; } @@ -1333,7 +1383,7 @@ /* 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"); + error_report("backing_file must be a sheepdog image"); return -EINVAL; } @@ -1344,7 +1394,7 @@ s = bs->opaque; if (!is_snapshot(&s->inode)) { - error_report("cannot clone from a non snapshot vdi\n"); + error_report("cannot clone from a non snapshot vdi"); bdrv_delete(bs); return -EINVAL; } @@ -1353,7 +1403,12 @@ bdrv_delete(bs); } - return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port); + ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port); + if (!prealloc || ret) { + return ret; + } + + return sd_prealloc(filename); } static void sd_close(BlockDriverState *bs) @@ -1384,7 +1439,7 @@ 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); + error_report("%s, %s", sd_strerror(rsp->result), s->name); } qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL); @@ -1406,10 +1461,10 @@ unsigned int datalen; if (offset < s->inode.vdi_size) { - error_report("shrinking is not supported\n"); + error_report("shrinking is not supported"); return -EINVAL; } else if (offset > SD_MAX_VDI_SIZE) { - error_report("too big image size\n"); + error_report("too big image size"); return -EINVAL; } @@ -1426,7 +1481,7 @@ close(fd); if (ret < 0) { - error_report("failed to update an inode.\n"); + error_report("failed to update an inode."); return -EIO; } @@ -1499,7 +1554,7 @@ fd = connect_to_sdog(s->addr, s->port); if (fd < 0) { - error_report("failed to connect\n"); + error_report("failed to connect"); goto out; } @@ -1576,7 +1631,7 @@ create = 1; } else if (acb->aiocb_type == AIOCB_WRITE_UDATA - && !is_data_obj_writeable(inode, idx)) { + && !is_data_obj_writable(inode, idx)) { /* Copy-On-Write */ create = 1; old_oid = oid; @@ -1617,7 +1672,7 @@ 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"); + error_report("add_aio_request is failed"); free_aio_req(s, aio_req); acb->ret = -EIO; goto out; @@ -1694,7 +1749,7 @@ 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); + "%s (%" PRIu32 ").", s->name, s->inode.vdi_id); return -EINVAL; } @@ -1717,7 +1772,7 @@ 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"); + error_report("failed to write snapshot's inode."); ret = -EIO; goto cleanup; } @@ -1725,7 +1780,7 @@ 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", + error_report("failed to create inode for snapshot. %s", strerror(errno)); ret = -EIO; goto cleanup; @@ -1737,7 +1792,7 @@ s->inode.nr_copies, datalen, 0); if (ret < 0) { - error_report("failed to read new inode info. %s\n", strerror(errno)); + error_report("failed to read new inode info. %s", strerror(errno)); ret = -EIO; goto cleanup; } @@ -1776,14 +1831,14 @@ ret = find_vdi_name(s, vdi, snapid, tag, &vid, 1); if (ret) { - error_report("Failed to find_vdi_name\n"); + error_report("Failed to find_vdi_name"); ret = -ENOENT; goto out; } fd = connect_to_sdog(s->addr, s->port); if (fd < 0) { - error_report("failed to connect\n"); + error_report("failed to connect"); goto out; } @@ -1801,7 +1856,7 @@ memcpy(&s->inode, buf, sizeof(s->inode)); if (!s->inode.vm_state_size) { - error_report("Invalid snapshot\n"); + error_report("Invalid snapshot"); ret = -ENOENT; goto out; } @@ -1818,7 +1873,7 @@ qemu_free(buf); qemu_free(old_s); - error_report("failed to open. recover old bdrv_sd_state.\n"); + error_report("failed to open. recover old bdrv_sd_state."); return ret; } @@ -1829,20 +1884,6 @@ 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; @@ -1887,7 +1928,7 @@ fd = connect_to_sdog(s->addr, s->port); if (fd < 0) { - error_report("failed to connect\n"); + error_report("failed to connect"); goto out; } @@ -1961,7 +2002,7 @@ } if (ret < 0) { - error_report("failed to save vmstate %s\n", strerror(errno)); + error_report("failed to save vmstate %s", strerror(errno)); ret = -EIO; goto cleanup; } @@ -2003,6 +2044,11 @@ .type = OPT_STRING, .help = "File name of a base image" }, + { + .name = BLOCK_OPT_PREALLOC, + .type = OPT_STRING, + .help = "Preallocation mode (allowed values: off, full)" + }, { NULL } }; diff -Nru qemu-kvm-0.14.1+noroms/block/vdi.c qemu-kvm-0.15.0+noroms/block/vdi.c --- qemu-kvm-0.14.1+noroms/block/vdi.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/vdi.c 2011-08-09 07:40:29.000000000 -0500 @@ -87,6 +87,7 @@ #define MiB (KiB * KiB) #define SECTOR_SIZE 512 +#define DEFAULT_CLUSTER_SIZE (1 * MiB) #if defined(CONFIG_VDI_DEBUG) #define logout(fmt, ...) \ @@ -113,7 +114,7 @@ */ #define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n" -/* Unallocated blocks use this index (no need to convert endianess). */ +/* Unallocated blocks use this index (no need to convert endianness). */ #define VDI_UNALLOCATED UINT32_MAX #if !defined(CONFIG_UUID) @@ -151,6 +152,7 @@ /* Buffer for new allocated block. */ void *block_buffer; void *orig_buf; + bool is_write; int header_modified; BlockDriverAIOCB *hd_aiocb; struct iovec hd_iov; @@ -194,7 +196,7 @@ uint32_t block_sectors; /* First sector of block map. */ uint32_t bmap_sector; - /* VDI header (converted to host endianess). */ + /* VDI header (converted to host endianness). */ VdiHeader header; } BDRVVdiState; @@ -503,6 +505,8 @@ acb->hd_aiocb = NULL; acb->sector_num = sector_num; acb->qiov = qiov; + acb->is_write = is_write; + if (qiov->niov > 1) { acb->buf = qemu_blockalign(bs, qiov->size); acb->orig_buf = acb->buf; @@ -541,14 +545,20 @@ } static void vdi_aio_read_cb(void *opaque, int ret); +static void vdi_aio_write_cb(void *opaque, int ret); -static void vdi_aio_read_bh(void *opaque) +static void vdi_aio_rw_bh(void *opaque) { VdiAIOCB *acb = opaque; logout("\n"); qemu_bh_delete(acb->bh); acb->bh = NULL; - vdi_aio_read_cb(opaque, 0); + + if (acb->is_write) { + vdi_aio_write_cb(opaque, 0); + } else { + vdi_aio_read_cb(opaque, 0); + } } static void vdi_aio_read_cb(void *opaque, int ret) @@ -596,7 +606,7 @@ if (bmap_entry == VDI_UNALLOCATED) { /* Block not allocated, return zeros, no need to wait. */ memset(acb->buf, 0, n_sectors * SECTOR_SIZE); - ret = vdi_schedule_bh(vdi_aio_read_bh, acb); + ret = vdi_schedule_bh(vdi_aio_rw_bh, acb); if (ret < 0) { goto done; } @@ -610,6 +620,7 @@ acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_read_cb, acb); if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; } } @@ -628,12 +639,23 @@ BlockDriverCompletionFunc *cb, void *opaque) { VdiAIOCB *acb; + int ret; + logout("\n"); acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); if (!acb) { return NULL; } - vdi_aio_read_cb(acb, 0); + + ret = vdi_schedule_bh(vdi_aio_rw_bh, acb); + if (ret < 0) { + if (acb->qiov->niov > 1) { + qemu_vfree(acb->orig_buf); + } + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } @@ -673,6 +695,7 @@ acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; } return; @@ -702,6 +725,7 @@ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; } return; @@ -752,6 +776,7 @@ &acb->hd_qiov, s->block_sectors, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; } } else { @@ -764,6 +789,7 @@ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; } } @@ -783,12 +809,23 @@ BlockDriverCompletionFunc *cb, void *opaque) { VdiAIOCB *acb; + int ret; + logout("\n"); acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); if (!acb) { return NULL; } - vdi_aio_write_cb(acb, 0); + + ret = vdi_schedule_bh(vdi_aio_rw_bh, acb); + if (ret < 0) { + if (acb->qiov->niov > 1) { + qemu_vfree(acb->orig_buf); + } + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } @@ -798,7 +835,7 @@ int result = 0; uint64_t bytes = 0; uint32_t blocks; - size_t block_size = 1 * MiB; + size_t block_size = DEFAULT_CLUSTER_SIZE; uint32_t image_type = VDI_TYPE_DYNAMIC; VdiHeader header; size_t i; @@ -916,7 +953,8 @@ { .name = BLOCK_OPT_CLUSTER_SIZE, .type = OPT_SIZE, - .help = "VDI cluster (block) size" + .help = "VDI cluster (block) size", + .value = { .n = DEFAULT_CLUSTER_SIZE }, }, #endif #if defined(CONFIG_VDI_STATIC_IMAGE) diff -Nru qemu-kvm-0.14.1+noroms/block/vmdk.c qemu-kvm-0.15.0+noroms/block/vmdk.c --- qemu-kvm-0.14.1+noroms/block/vmdk.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/vmdk.c 2011-08-09 07:40:29.000000000 -0500 @@ -60,7 +60,12 @@ #define L2_CACHE_SIZE 16 -typedef struct BDRVVmdkState { +typedef struct VmdkExtent { + BlockDriverState *file; + bool flat; + int64_t sectors; + int64_t end_sector; + int64_t flat_start_offset; int64_t l1_table_offset; int64_t l1_backup_table_offset; uint32_t *l1_table; @@ -74,7 +79,15 @@ uint32_t l2_cache_counts[L2_CACHE_SIZE]; unsigned int cluster_sectors; +} VmdkExtent; + +typedef struct BDRVVmdkState { + int desc_offset; + bool cid_updated; uint32_t parent_cid; + int num_extents; + /* Extent array with num_extents entries, ascend ordered by address */ + VmdkExtent *extents; } BDRVVmdkState; typedef struct VmdkMetaData { @@ -89,21 +102,77 @@ { uint32_t magic; - if (buf_size < 4) + if (buf_size < 4) { return 0; + } magic = be32_to_cpu(*(uint32_t *)buf); if (magic == VMDK3_MAGIC || - magic == VMDK4_MAGIC) + magic == VMDK4_MAGIC) { return 100; - else + } else { + const char *p = (const char *)buf; + const char *end = p + buf_size; + while (p < end) { + if (*p == '#') { + /* skip comment line */ + while (p < end && *p != '\n') { + p++; + } + p++; + continue; + } + if (*p == ' ') { + while (p < end && *p == ' ') { + p++; + } + /* skip '\r' if windows line endings used. */ + if (p < end && *p == '\r') { + p++; + } + /* only accept blank lines before 'version=' line */ + if (p == end || *p != '\n') { + return 0; + } + p++; + continue; + } + if (end - p >= strlen("version=X\n")) { + if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 || + strncmp("version=2\n", p, strlen("version=2\n")) == 0) { + return 100; + } + } + if (end - p >= strlen("version=X\r\n")) { + if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 || + strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) { + return 100; + } + } + return 0; + } return 0; + } } #define CHECK_CID 1 #define SECTOR_SIZE 512 -#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each -#define HEADER_SIZE 512 // first sector of 512 bytes +#define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */ +#define BUF_SIZE 4096 +#define HEADER_SIZE 512 /* first sector of 512 bytes */ + +static void vmdk_free_extents(BlockDriverState *bs) +{ + int i; + BDRVVmdkState *s = bs->opaque; + + for (i = 0; i < s->num_extents; i++) { + qemu_free(s->extents[i].l1_table); + qemu_free(s->extents[i].l2_cache); + qemu_free(s->extents[i].l1_backup_table); + } + qemu_free(s->extents); +} static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) { @@ -111,10 +180,11 @@ uint32_t cid; const char *p_name, *cid_str; size_t cid_str_size; + BDRVVmdkState *s = bs->opaque; - /* the descriptor offset = 0x200 */ - if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) + if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { return 0; + } if (parent) { cid_str = "parentCID"; @@ -124,9 +194,10 @@ cid_str_size = sizeof("CID"); } - if ((p_name = strstr(desc,cid_str)) != NULL) { + p_name = strstr(desc, cid_str); + if (p_name != NULL) { p_name += cid_str_size; - sscanf(p_name,"%x",&cid); + sscanf(p_name, "%x", &cid); } return cid; @@ -136,21 +207,25 @@ { char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; char *p_name, *tmp_str; + BDRVVmdkState *s = bs->opaque; - /* the descriptor offset = 0x200 */ - if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) - return -1; + memset(desc, 0, sizeof(desc)); + if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { + return -EIO; + } - tmp_str = strstr(desc,"parentCID"); + tmp_str = strstr(desc, "parentCID"); pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); - if ((p_name = strstr(desc,"CID")) != NULL) { + p_name = strstr(desc, "CID"); + if (p_name != NULL) { p_name += sizeof("CID"); snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid); pstrcat(desc, sizeof(desc), tmp_desc); } - if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0) - return -1; + if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) { + return -EIO; + } return 0; } @@ -162,302 +237,387 @@ uint32_t cur_pcid; if (p_bs) { - cur_pcid = vmdk_read_cid(p_bs,0); - if (s->parent_cid != cur_pcid) - // CID not valid + cur_pcid = vmdk_read_cid(p_bs, 0); + if (s->parent_cid != cur_pcid) { + /* CID not valid */ return 0; + } } #endif - // CID valid + /* CID valid */ return 1; } -static int vmdk_snapshot_create(const char *filename, const char *backing_file) +static int vmdk_parent_open(BlockDriverState *bs) { - int snp_fd, p_fd; - int ret; - uint32_t p_cid; - char *p_name, *gd_buf, *rgd_buf; - const char *real_filename, *temp_str; - VMDK4Header header; - uint32_t gde_entries, gd_size; - int64_t gd_offset, rgd_offset, capacity, gt_size; - char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE]; - static const char desc_template[] = - "# Disk DescriptorFile\n" - "version=1\n" - "CID=%x\n" - "parentCID=%x\n" - "createType=\"monolithicSparse\"\n" - "parentFileNameHint=\"%s\"\n" - "\n" - "# Extent description\n" - "RW %u SPARSE \"%s\"\n" - "\n" - "# The Disk Data Base \n" - "#DDB\n" - "\n"; + char *p_name; + char desc[DESC_SIZE + 1]; + BDRVVmdkState *s = bs->opaque; - snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); - if (snp_fd < 0) - return -errno; - p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); - if (p_fd < 0) { - close(snp_fd); - return -errno; + desc[DESC_SIZE] = '\0'; + if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { + return -1; } - /* read the header */ - if (lseek(p_fd, 0x0, SEEK_SET) == -1) { - ret = -errno; - goto fail; - } - if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) { - ret = -errno; - goto fail; - } + p_name = strstr(desc, "parentFileNameHint"); + if (p_name != NULL) { + char *end_name; - /* write the header */ - if (lseek(snp_fd, 0x0, SEEK_SET) == -1) { - ret = -errno; - goto fail; - } - if (write(snp_fd, hdr, HEADER_SIZE) == -1) { - ret = -errno; - goto fail; + p_name += sizeof("parentFileNameHint") + 1; + end_name = strchr(p_name, '\"'); + if (end_name == NULL) { + return -1; + } + if ((end_name - p_name) > sizeof(bs->backing_file) - 1) { + return -1; + } + + pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); } - memset(&header, 0, sizeof(header)); - memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC + return 0; +} - if (ftruncate(snp_fd, header.grain_offset << 9)) { - ret = -errno; - goto fail; +/* Create and append extent to the extent array. Return the added VmdkExtent + * address. return NULL if allocation failed. */ +static VmdkExtent *vmdk_add_extent(BlockDriverState *bs, + BlockDriverState *file, bool flat, int64_t sectors, + int64_t l1_offset, int64_t l1_backup_offset, + uint32_t l1_size, + int l2_size, unsigned int cluster_sectors) +{ + VmdkExtent *extent; + BDRVVmdkState *s = bs->opaque; + + s->extents = qemu_realloc(s->extents, + (s->num_extents + 1) * sizeof(VmdkExtent)); + extent = &s->extents[s->num_extents]; + s->num_extents++; + + memset(extent, 0, sizeof(VmdkExtent)); + extent->file = file; + extent->flat = flat; + extent->sectors = sectors; + extent->l1_table_offset = l1_offset; + extent->l1_backup_table_offset = l1_backup_offset; + extent->l1_size = l1_size; + extent->l1_entry_sectors = l2_size * cluster_sectors; + extent->l2_size = l2_size; + extent->cluster_sectors = cluster_sectors; + + if (s->num_extents > 1) { + extent->end_sector = (*(extent - 1)).end_sector + extent->sectors; + } else { + extent->end_sector = extent->sectors; } - /* the descriptor offset = 0x200 */ - if (lseek(p_fd, 0x200, SEEK_SET) == -1) { - ret = -errno; - goto fail; + bs->total_sectors = extent->end_sector; + return extent; +} + +static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent) +{ + int ret; + int l1_size, i; + + /* read the L1 table */ + l1_size = extent->l1_size * sizeof(uint32_t); + extent->l1_table = qemu_malloc(l1_size); + ret = bdrv_pread(extent->file, + extent->l1_table_offset, + extent->l1_table, + l1_size); + if (ret < 0) { + goto fail_l1; } - if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) { - ret = -errno; - goto fail; + for (i = 0; i < extent->l1_size; i++) { + le32_to_cpus(&extent->l1_table[i]); } - if ((p_name = strstr(p_desc,"CID")) != NULL) { - p_name += sizeof("CID"); - sscanf(p_name,"%x",&p_cid); + if (extent->l1_backup_table_offset) { + extent->l1_backup_table = qemu_malloc(l1_size); + ret = bdrv_pread(extent->file, + extent->l1_backup_table_offset, + extent->l1_backup_table, + l1_size); + if (ret < 0) { + goto fail_l1b; + } + for (i = 0; i < extent->l1_size; i++) { + le32_to_cpus(&extent->l1_backup_table[i]); + } } - real_filename = filename; - if ((temp_str = strrchr(real_filename, '\\')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, '/')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, ':')) != NULL) - real_filename = temp_str + 1; + extent->l2_cache = + qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); + return 0; + fail_l1b: + qemu_free(extent->l1_backup_table); + fail_l1: + qemu_free(extent->l1_table); + return ret; +} - snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file, - (uint32_t)header.capacity, real_filename); +static int vmdk_open_vmdk3(BlockDriverState *bs, int flags) +{ + int ret; + uint32_t magic; + VMDK3Header header; + BDRVVmdkState *s = bs->opaque; + VmdkExtent *extent; - /* write the descriptor */ - if (lseek(snp_fd, 0x200, SEEK_SET) == -1) { - ret = -errno; + s->desc_offset = 0x200; + ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); + if (ret < 0) { goto fail; } - if (write(snp_fd, s_desc, strlen(s_desc)) == -1) { - ret = -errno; + extent = vmdk_add_extent(bs, + bs->file, false, + le32_to_cpu(header.disk_sectors), + le32_to_cpu(header.l1dir_offset) << 9, + 0, 1 << 6, 1 << 9, + le32_to_cpu(header.granularity)); + ret = vmdk_init_tables(bs, extent); + if (ret) { + /* vmdk_init_tables cleans up on fail, so only free allocation of + * vmdk_add_extent here. */ goto fail; } + return 0; + fail: + vmdk_free_extents(bs); + return ret; +} - gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table - rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table - capacity = header.capacity * SECTOR_SIZE; // Extent size - /* - * Each GDE span 32M disk, means: - * 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) { - ret = -EINVAL; - goto fail; - } - gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde - gd_size = gde_entries * sizeof(uint32_t); +static int vmdk_open_vmdk4(BlockDriverState *bs, int flags) +{ + int ret; + uint32_t magic; + uint32_t l1_size, l1_entry_sectors; + VMDK4Header header; + BDRVVmdkState *s = bs->opaque; + VmdkExtent *extent; - /* write RGD */ - rgd_buf = qemu_malloc(gd_size); - if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_rgd; + s->desc_offset = 0x200; + ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); + if (ret < 0) { + goto fail; } - if (read(p_fd, rgd_buf, gd_size) != gd_size) { - ret = -errno; - goto fail_rgd; + l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) + * le64_to_cpu(header.granularity); + l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) + / l1_entry_sectors; + extent = vmdk_add_extent(bs, bs->file, false, + le64_to_cpu(header.capacity), + le64_to_cpu(header.gd_offset) << 9, + le64_to_cpu(header.rgd_offset) << 9, + l1_size, + le32_to_cpu(header.num_gtes_per_gte), + le64_to_cpu(header.granularity)); + if (extent->l1_entry_sectors <= 0) { + ret = -EINVAL; + goto fail; } - if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_rgd; + /* try to open parent images, if exist */ + ret = vmdk_parent_open(bs); + if (ret) { + goto fail; } - if (write(snp_fd, rgd_buf, gd_size) == -1) { - ret = -errno; - goto fail_rgd; + s->parent_cid = vmdk_read_cid(bs, 1); + ret = vmdk_init_tables(bs, extent); + if (ret) { + goto fail; } + return 0; + fail: + vmdk_free_extents(bs); + return ret; +} - /* write GD */ - gd_buf = qemu_malloc(gd_size); - if (lseek(p_fd, gd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_gd; +/* find an option value out of descriptor file */ +static int vmdk_parse_description(const char *desc, const char *opt_name, + char *buf, int buf_size) +{ + char *opt_pos, *opt_end; + const char *end = desc + strlen(desc); + + opt_pos = strstr(desc, opt_name); + if (!opt_pos) { + return -1; } - if (read(p_fd, gd_buf, gd_size) != gd_size) { - ret = -errno; - goto fail_gd; + /* Skip "=\"" following opt_name */ + opt_pos += strlen(opt_name) + 2; + if (opt_pos >= end) { + return -1; } - if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_gd; + opt_end = opt_pos; + while (opt_end < end && *opt_end != '"') { + opt_end++; } - if (write(snp_fd, gd_buf, gd_size) == -1) { - ret = -errno; - goto fail_gd; + if (opt_end == end || buf_size < opt_end - opt_pos + 1) { + return -1; } - ret = 0; - -fail_gd: - qemu_free(gd_buf); -fail_rgd: - qemu_free(rgd_buf); -fail: - close(p_fd); - close(snp_fd); - return ret; + pstrcpy(buf, opt_end - opt_pos + 1, opt_pos); + return 0; } -static int vmdk_parent_open(BlockDriverState *bs) +static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, + const char *desc_file_path) { - char *p_name; - char desc[DESC_SIZE]; + int ret; + char access[11]; + char type[11]; + char fname[512]; + const char *p = desc; + int64_t sectors = 0; + int64_t flat_offset; + + while (*p) { + /* parse extent line: + * RW [size in sectors] FLAT "file-name.vmdk" OFFSET + * or + * RW [size in sectors] SPARSE "file-name.vmdk" + */ + flat_offset = -1; + ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64, + access, §ors, type, fname, &flat_offset); + if (ret < 4 || strcmp(access, "RW")) { + goto next_line; + } else if (!strcmp(type, "FLAT")) { + if (ret != 5 || flat_offset < 0) { + return -EINVAL; + } + } else if (ret != 4) { + return -EINVAL; + } - /* the descriptor offset = 0x200 */ - if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) - return -1; + /* trim the quotation marks around */ + if (fname[0] == '"') { + memmove(fname, fname + 1, strlen(fname)); + if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') { + return -EINVAL; + } + fname[strlen(fname) - 1] = '\0'; + } + if (sectors <= 0 || + (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) || + (strcmp(access, "RW"))) { + goto next_line; + } - if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { - char *end_name; + /* save to extents array */ + if (!strcmp(type, "FLAT")) { + /* FLAT extent */ + char extent_path[PATH_MAX]; + BlockDriverState *extent_file; + VmdkExtent *extent; + + path_combine(extent_path, sizeof(extent_path), + desc_file_path, fname); + ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags); + if (ret) { + return ret; + } + extent = vmdk_add_extent(bs, extent_file, true, sectors, + 0, 0, 0, 0, sectors); + extent->flat_start_offset = flat_offset; + } else { + /* SPARSE extent, not supported for now */ + fprintf(stderr, + "VMDK: Not supported extent type \"%s\""".\n", type); + return -ENOTSUP; + } +next_line: + /* move to next line */ + while (*p && *p != '\n') { + p++; + } + p++; + } + return 0; +} - p_name += sizeof("parentFileNameHint") + 1; - if ((end_name = strchr(p_name,'\"')) == NULL) - return -1; - if ((end_name - p_name) > sizeof (bs->backing_file) - 1) - return -1; +static int vmdk_open_desc_file(BlockDriverState *bs, int flags) +{ + int ret; + char buf[2048]; + char ct[128]; + BDRVVmdkState *s = bs->opaque; - pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); + ret = bdrv_pread(bs->file, 0, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + buf[2047] = '\0'; + if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { + return -EINVAL; + } + if (strcmp(ct, "monolithicFlat")) { + fprintf(stderr, + "VMDK: Not supported image type \"%s\""".\n", ct); + return -ENOTSUP; + } + s->desc_offset = 0; + ret = vmdk_parse_extents(buf, bs, bs->file->filename); + if (ret) { + return ret; } + /* try to open parent images, if exist */ + if (vmdk_parent_open(bs)) { + qemu_free(s->extents); + return -EINVAL; + } + s->parent_cid = vmdk_read_cid(bs, 1); return 0; } static int vmdk_open(BlockDriverState *bs, int flags) { - BDRVVmdkState *s = bs->opaque; uint32_t magic; - int l1_size, i; - if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) - goto fail; + if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) { + return -EIO; + } magic = be32_to_cpu(magic); if (magic == VMDK3_MAGIC) { - VMDK3Header 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; - s->l1_size = 1 << 6; - bs->total_sectors = le32_to_cpu(header.disk_sectors); - s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9; - s->l1_backup_table_offset = 0; - s->l1_entry_sectors = s->l2_size * s->cluster_sectors; + return vmdk_open_vmdk3(bs, flags); } else if (magic == VMDK4_MAGIC) { - VMDK4Header 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); - s->l2_size = le32_to_cpu(header.num_gtes_per_gte); - s->l1_entry_sectors = s->l2_size * s->cluster_sectors; - if (s->l1_entry_sectors <= 0) - goto fail; - s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) - / s->l1_entry_sectors; - s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; - s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; - - // try to open parent images, if exist - if (vmdk_parent_open(bs) != 0) - goto fail; - // write the CID once after the image creation - s->parent_cid = vmdk_read_cid(bs,1); + return vmdk_open_vmdk4(bs, flags); } else { - goto fail; + return vmdk_open_desc_file(bs, flags); } - - /* read the L1 table */ - l1_size = s->l1_size * sizeof(uint32_t); - s->l1_table = qemu_malloc(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]); - } - - if (s->l1_backup_table_offset) { - s->l1_backup_table = qemu_malloc(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]); - } - } - - s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); - return 0; - fail: - qemu_free(s->l1_backup_table); - qemu_free(s->l1_table); - qemu_free(s->l2_cache); - return -1; } -static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, - uint64_t offset, int allocate); - -static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, - uint64_t offset, int allocate) +static int get_whole_cluster(BlockDriverState *bs, + VmdkExtent *extent, + uint64_t cluster_offset, + uint64_t offset, + bool allocate) { - BDRVVmdkState *s = bs->opaque; - uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB + /* 128 sectors * 512 bytes each = grain size 64KB */ + uint8_t whole_grain[extent->cluster_sectors * 512]; - // we will be here if it's first write on non-exist grain(cluster). - // try to read from parent image, if exist + /* we will be here if it's first write on non-exist grain(cluster). + * try to read from parent image, if exist */ if (bs->backing_hd) { int ret; - if (!vmdk_is_cid_valid(bs)) + if (!vmdk_is_cid_valid(bs)) { return -1; + } + /* floor offset to cluster */ + offset -= offset % (extent->cluster_sectors * 512); ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain, - s->cluster_sectors); + extent->cluster_sectors); if (ret < 0) { return -1; } - //Write grain only into the active image - ret = bdrv_write(bs->file, cluster_offset, whole_grain, - s->cluster_sectors); + /* Write grain only into the active image */ + ret = bdrv_write(extent->file, cluster_offset, whole_grain, + extent->cluster_sectors); if (ret < 0) { return -1; } @@ -465,85 +625,112 @@ return 0; } -static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data) +static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data) { - BDRVVmdkState *s = bs->opaque; - /* update L2 table */ - 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) + if (bdrv_pwrite_sync( + extent->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(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) + if (extent->l1_backup_table_offset != 0) { + m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; + if (bdrv_pwrite_sync( + extent->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; + } } return 0; } -static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, - uint64_t offset, int allocate) +static int get_cluster_offset(BlockDriverState *bs, + VmdkExtent *extent, + VmdkMetaData *m_data, + uint64_t offset, + int allocate, + uint64_t *cluster_offset) { - BDRVVmdkState *s = bs->opaque; unsigned int l1_index, l2_offset, l2_index; int min_index, i, j; uint32_t min_count, *l2_table, tmp = 0; - uint64_t cluster_offset; - if (m_data) + if (m_data) { m_data->valid = 0; - - l1_index = (offset >> 9) / s->l1_entry_sectors; - if (l1_index >= s->l1_size) - return 0; - l2_offset = s->l1_table[l1_index]; - if (!l2_offset) + } + if (extent->flat) { + *cluster_offset = extent->flat_start_offset; return 0; - for(i = 0; i < L2_CACHE_SIZE; i++) { - if (l2_offset == s->l2_cache_offsets[i]) { + } + + l1_index = (offset >> 9) / extent->l1_entry_sectors; + if (l1_index >= extent->l1_size) { + return -1; + } + l2_offset = extent->l1_table[l1_index]; + if (!l2_offset) { + return -1; + } + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == extent->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; + if (++extent->l2_cache_counts[i] == 0xffffffff) { + for (j = 0; j < L2_CACHE_SIZE; j++) { + extent->l2_cache_counts[j] >>= 1; } } - l2_table = s->l2_cache + (i * s->l2_size); + l2_table = extent->l2_cache + (i * extent->l2_size); goto found; } } /* not found: load 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]; + for (i = 0; i < L2_CACHE_SIZE; i++) { + if (extent->l2_cache_counts[i] < min_count) { + min_count = extent->l2_cache_counts[i]; min_index = i; } } - l2_table = s->l2_cache + (min_index * s->l2_size); - 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; + l2_table = extent->l2_cache + (min_index * extent->l2_size); + if (bdrv_pread( + extent->file, + (int64_t)l2_offset * 512, + l2_table, + extent->l2_size * sizeof(uint32_t) + ) != extent->l2_size * sizeof(uint32_t)) { + return -1; + } - s->l2_cache_offsets[min_index] = l2_offset; - s->l2_cache_counts[min_index] = 1; + extent->l2_cache_offsets[min_index] = l2_offset; + extent->l2_cache_counts[min_index] = 1; found: - l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; - cluster_offset = le32_to_cpu(l2_table[l2_index]); + l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; + *cluster_offset = le32_to_cpu(l2_table[l2_index]); - if (!cluster_offset) { - if (!allocate) - return 0; + if (!*cluster_offset) { + if (!allocate) { + return -1; + } - // Avoid the L2 tables update for the images that have snapshots. - cluster_offset = bdrv_getlength(bs->file); - bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9)); + /* Avoid the L2 tables update for the images that have snapshots. */ + *cluster_offset = bdrv_getlength(extent->file); + bdrv_truncate( + extent->file, + *cluster_offset + (extent->cluster_sectors << 9) + ); - cluster_offset >>= 9; - tmp = cpu_to_le32(cluster_offset); + *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 @@ -551,8 +738,10 @@ * This problem may occur because of insufficient space on host disk * or inappropriate VM shutdown. */ - if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1) - return 0; + if (get_whole_cluster( + bs, extent, *cluster_offset, offset, allocate) == -1) { + return -1; + } if (m_data) { m_data->offset = tmp; @@ -562,53 +751,95 @@ m_data->valid = 1; } } - cluster_offset <<= 9; - return cluster_offset; + *cluster_offset <<= 9; + return 0; +} + +static VmdkExtent *find_extent(BDRVVmdkState *s, + int64_t sector_num, VmdkExtent *start_hint) +{ + VmdkExtent *extent = start_hint; + + if (!extent) { + extent = &s->extents[0]; + } + while (extent < &s->extents[s->num_extents]) { + if (sector_num < extent->end_sector) { + return extent; + } + extent++; + } + return NULL; } static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { BDRVVmdkState *s = bs->opaque; - int index_in_cluster, n; - uint64_t cluster_offset; + int64_t index_in_cluster, n, ret; + uint64_t offset; + VmdkExtent *extent; - cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0); - index_in_cluster = sector_num % s->cluster_sectors; - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) + extent = find_extent(s, sector_num, NULL); + if (!extent) { + return 0; + } + ret = get_cluster_offset(bs, extent, NULL, + sector_num * 512, 0, &offset); + /* get_cluster_offset returning 0 means success */ + ret = !ret; + + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { n = nb_sectors; + } *pnum = n; - return (cluster_offset != 0); + return ret; } static int vmdk_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { BDRVVmdkState *s = bs->opaque; - int index_in_cluster, n, ret; + int ret; + uint64_t n, index_in_cluster; + VmdkExtent *extent = NULL; uint64_t cluster_offset; while (nb_sectors > 0) { - cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0); - index_in_cluster = sector_num % s->cluster_sectors; - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) + extent = find_extent(s, sector_num, extent); + if (!extent) { + return -EIO; + } + ret = get_cluster_offset( + bs, extent, NULL, + sector_num << 9, 0, &cluster_offset); + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { n = nb_sectors; - if (!cluster_offset) { - // try to read from parent image, if exist + } + if (ret) { + /* if not allocated, try to read from parent image, if exist */ if (bs->backing_hd) { - if (!vmdk_is_cid_valid(bs)) - return -1; + if (!vmdk_is_cid_valid(bs)) { + return -EINVAL; + } ret = bdrv_read(bs->backing_hd, sector_num, buf, n); - if (ret < 0) - return -1; + if (ret < 0) { + return ret; + } } else { memset(buf, 0, 512 * n); } } else { - if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) - return -1; + ret = bdrv_pread(extent->file, + cluster_offset + index_in_cluster * 512, + buf, n * 512); + if (ret < 0) { + return ret; + } } nb_sectors -= n; sector_num += n; @@ -621,110 +852,101 @@ const uint8_t *buf, int nb_sectors) { BDRVVmdkState *s = bs->opaque; - VmdkMetaData m_data; - int index_in_cluster, n; + VmdkExtent *extent = NULL; + int n, ret; + int64_t index_in_cluster; uint64_t cluster_offset; - static int cid_update = 0; + VmdkMetaData m_data; if (sector_num > bs->total_sectors) { fprintf(stderr, "(VMDK) Wrong offset: sector_num=0x%" PRIx64 " total_sectors=0x%" PRIx64 "\n", sector_num, bs->total_sectors); - return -1; + return -EIO; } while (nb_sectors > 0) { - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) + extent = find_extent(s, sector_num, extent); + if (!extent) { + return -EIO; + } + ret = get_cluster_offset( + bs, + extent, + &m_data, + sector_num << 9, 1, + &cluster_offset); + if (ret) { + return -EINVAL; + } + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { n = nb_sectors; - cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1); - if (!cluster_offset) - return -1; + } - if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) - return -1; + ret = bdrv_pwrite(extent->file, + cluster_offset + index_in_cluster * 512, + buf, + n * 512); + if (ret < 0) { + return ret; + } if (m_data.valid) { /* update L2 tables */ - if (vmdk_L2update(bs, &m_data) == -1) - return -1; + if (vmdk_L2update(extent, &m_data) == -1) { + return -EIO; + } } nb_sectors -= n; sector_num += n; buf += n * 512; - // update CID on the first write every time the virtual disk is opened - if (!cid_update) { + /* update CID on the first write every time the virtual disk is + * opened */ + if (!s->cid_updated) { vmdk_write_cid(bs, time(NULL)); - cid_update++; + s->cid_updated = true; } } return 0; } -static int vmdk_create(const char *filename, QEMUOptionParameter *options) + +static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat) { - int fd, i; + int ret, i; + int fd = 0; VMDK4Header header; uint32_t tmp, magic, grains, gd_size, gt_size, gt_count; - static const char desc_template[] = - "# Disk DescriptorFile\n" - "version=1\n" - "CID=%x\n" - "parentCID=ffffffff\n" - "createType=\"monolithicSparse\"\n" - "\n" - "# Extent description\n" - "RW %" PRId64 " SPARSE \"%s\"\n" - "\n" - "# The Disk Data Base \n" - "#DDB\n" - "\n" - "ddb.virtualHWVersion = \"%d\"\n" - "ddb.geometry.cylinders = \"%" PRId64 "\"\n" - "ddb.geometry.heads = \"16\"\n" - "ddb.geometry.sectors = \"63\"\n" - "ddb.adapterType = \"ide\"\n"; - char desc[1024]; - const char *real_filename, *temp_str; - int64_t total_size = 0; - const char *backing_file = NULL; - int flags = 0; - int ret; - // Read out options - while (options && options->name) { - if (!strcmp(options->name, BLOCK_OPT_SIZE)) { - total_size = options->value.n / 512; - } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { - backing_file = options->value.s; - } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { - flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0; - } - options++; + fd = open( + filename, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) { + return -errno; } - - /* XXX: add support for backing file */ - if (backing_file) { - return vmdk_snapshot_create(filename, backing_file); + if (flat) { + ret = ftruncate(fd, filesize); + if (ret < 0) { + ret = -errno; + } + goto exit; } - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, - 0644); - if (fd < 0) - return -errno; magic = cpu_to_be32(VMDK4_MAGIC); memset(&header, 0, sizeof(header)); - header.version = cpu_to_le32(1); - header.flags = cpu_to_le32(3); /* ?? */ - header.capacity = cpu_to_le64(total_size); - header.granularity = cpu_to_le64(128); - header.num_gtes_per_gte = cpu_to_le32(512); + header.version = 1; + header.flags = 3; /* ?? */ + header.capacity = filesize / 512; + header.granularity = 128; + header.num_gtes_per_gte = 512; - grains = (total_size + header.granularity - 1) / header.granularity; + grains = (filesize / 512 + header.granularity - 1) / header.granularity; gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; - gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; + gt_count = + (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; header.desc_offset = 1; @@ -735,7 +957,12 @@ ((header.gd_offset + gd_size + (gt_size * gt_count) + header.granularity - 1) / header.granularity) * header.granularity; - + /* swap endianness for all header fields */ + header.version = cpu_to_le32(header.version); + header.flags = cpu_to_le32(header.flags); + header.capacity = cpu_to_le64(header.capacity); + header.granularity = cpu_to_le64(header.granularity); + header.num_gtes_per_gte = cpu_to_le32(header.num_gtes_per_gte); header.desc_offset = cpu_to_le64(header.desc_offset); header.desc_size = cpu_to_le64(header.desc_size); header.rgd_offset = cpu_to_le64(header.rgd_offset); @@ -759,7 +986,7 @@ goto exit; } - ret = ftruncate(fd, header.grain_offset << 9); + ret = ftruncate(fd, le64_to_cpu(header.grain_offset) << 9); if (ret < 0) { ret = -errno; goto exit; @@ -767,7 +994,7 @@ /* write grain directory */ lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); - for (i = 0, tmp = header.rgd_offset + gd_size; + for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_size; i < gt_count; i++, tmp += gt_size) { ret = qemu_write_full(fd, &tmp, sizeof(tmp)); if (ret != sizeof(tmp)) { @@ -778,7 +1005,7 @@ /* write backup grain directory */ lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); - for (i = 0, tmp = header.gd_offset + gd_size; + for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_size; i < gt_count; i++, tmp += gt_size) { ret = qemu_write_full(fd, &tmp, sizeof(tmp)); if (ret != sizeof(tmp)) { @@ -787,27 +1014,255 @@ } } - /* compose the descriptor */ - real_filename = filename; - if ((temp_str = strrchr(real_filename, '\\')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, '/')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, ':')) != NULL) - real_filename = temp_str + 1; - snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL), - total_size, real_filename, - (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), - total_size / (int64_t)(63 * 16)); + ret = 0; + exit: + close(fd); + return ret; +} + +static int filename_decompose(const char *filename, char *path, char *prefix, + char *postfix, size_t buf_len) +{ + const char *p, *q; + + if (filename == NULL || !strlen(filename)) { + fprintf(stderr, "Vmdk: no filename provided.\n"); + return -1; + } + p = strrchr(filename, '/'); + if (p == NULL) { + p = strrchr(filename, '\\'); + } + if (p == NULL) { + p = strrchr(filename, ':'); + } + if (p != NULL) { + p++; + if (p - filename >= buf_len) { + return -1; + } + pstrcpy(path, p - filename + 1, filename); + } else { + p = filename; + path[0] = '\0'; + } + q = strrchr(p, '.'); + if (q == NULL) { + pstrcpy(prefix, buf_len, p); + postfix[0] = '\0'; + } else { + if (q - p >= buf_len) { + return -1; + } + pstrcpy(prefix, q - p + 1, p); + pstrcpy(postfix, buf_len, q); + } + return 0; +} + +static int relative_path(char *dest, int dest_size, + const char *base, const char *target) +{ + int i = 0; + int n = 0; + const char *p, *q; +#ifdef _WIN32 + const char *sep = "\\"; +#else + const char *sep = "/"; +#endif + + if (!(dest && base && target)) { + return -1; + } + if (path_is_absolute(target)) { + dest[dest_size - 1] = '\0'; + strncpy(dest, target, dest_size - 1); + return 0; + } + while (base[i] == target[i]) { + i++; + } + p = &base[i]; + q = &target[i]; + while (*p) { + if (*p == *sep) { + n++; + } + p++; + } + dest[0] = '\0'; + for (; n; n--) { + pstrcat(dest, dest_size, ".."); + pstrcat(dest, dest_size, sep); + } + pstrcat(dest, dest_size, q); + return 0; +} + +static int vmdk_create(const char *filename, QEMUOptionParameter *options) +{ + int fd, idx = 0; + char desc[BUF_SIZE]; + int64_t total_size = 0, filesize; + const char *backing_file = NULL; + const char *fmt = NULL; + int flags = 0; + int ret = 0; + bool flat, split; + char ext_desc_lines[BUF_SIZE] = ""; + char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX]; + const int64_t split_size = 0x80000000; /* VMDK has constant split size */ + const char *desc_extent_line; + char parent_desc_line[BUF_SIZE] = ""; + uint32_t parent_cid = 0xffffffff; + const char desc_template[] = + "# Disk DescriptorFile\n" + "version=1\n" + "CID=%x\n" + "parentCID=%x\n" + "createType=\"%s\"\n" + "%s" + "\n" + "# Extent description\n" + "%s" + "\n" + "# The Disk Data Base\n" + "#DDB\n" + "\n" + "ddb.virtualHWVersion = \"%d\"\n" + "ddb.geometry.cylinders = \"%" PRId64 "\"\n" + "ddb.geometry.heads = \"16\"\n" + "ddb.geometry.sectors = \"63\"\n" + "ddb.adapterType = \"ide\"\n"; - /* write the descriptor */ - lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); + if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { + return -EINVAL; + } + /* Read out options */ + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + total_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_COMPAT6)) { + flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0; + } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) { + fmt = options->value.s; + } + options++; + } + if (!fmt) { + /* Default format to monolithicSparse */ + fmt = "monolithicSparse"; + } else if (strcmp(fmt, "monolithicFlat") && + strcmp(fmt, "monolithicSparse") && + strcmp(fmt, "twoGbMaxExtentSparse") && + strcmp(fmt, "twoGbMaxExtentFlat")) { + fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt); + return -EINVAL; + } + split = !(strcmp(fmt, "twoGbMaxExtentFlat") && + strcmp(fmt, "twoGbMaxExtentSparse")); + flat = !(strcmp(fmt, "monolithicFlat") && + strcmp(fmt, "twoGbMaxExtentFlat")); + if (flat) { + desc_extent_line = "RW %lld FLAT \"%s\" 0\n"; + } else { + desc_extent_line = "RW %lld SPARSE \"%s\"\n"; + } + if (flat && backing_file) { + /* not supporting backing file for flat image */ + return -ENOTSUP; + } + if (backing_file) { + char parent_filename[PATH_MAX]; + BlockDriverState *bs = bdrv_new(""); + ret = bdrv_open(bs, backing_file, 0, NULL); + if (ret != 0) { + bdrv_delete(bs); + return ret; + } + if (strcmp(bs->drv->format_name, "vmdk")) { + bdrv_delete(bs); + return -EINVAL; + } + filesize = bdrv_getlength(bs); + parent_cid = vmdk_read_cid(bs, 0); + bdrv_delete(bs); + relative_path(parent_filename, sizeof(parent_filename), + filename, backing_file); + snprintf(parent_desc_line, sizeof(parent_desc_line), + "parentFileNameHint=\"%s\"", parent_filename); + } + + /* Create extents */ + filesize = total_size; + while (filesize > 0) { + char desc_line[BUF_SIZE]; + char ext_filename[PATH_MAX]; + char desc_filename[PATH_MAX]; + int64_t size = filesize; + + if (split && size > split_size) { + size = split_size; + } + if (split) { + snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s", + prefix, flat ? 'f' : 's', ++idx, postfix); + } else if (flat) { + snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s", + prefix, postfix); + } else { + snprintf(desc_filename, sizeof(desc_filename), "%s%s", + prefix, postfix); + } + snprintf(ext_filename, sizeof(ext_filename), "%s%s", + path, desc_filename); + + if (vmdk_create_extent(ext_filename, size, flat)) { + return -EINVAL; + } + filesize -= size; + + /* Format description line */ + snprintf(desc_line, sizeof(desc_line), + desc_extent_line, size / 512, desc_filename); + pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line); + } + /* generate descriptor file */ + snprintf(desc, sizeof(desc), desc_template, + (unsigned int)time(NULL), + parent_cid, + fmt, + parent_desc_line, + ext_desc_lines, + (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), + total_size / (int64_t)(63 * 16 * 512)); + if (split || flat) { + fd = open( + filename, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + } else { + fd = open( + filename, + O_WRONLY | O_BINARY | O_LARGEFILE, + 0644); + } + if (fd < 0) { + return -errno; + } + /* the descriptor offset = 0x200 */ + if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) { + ret = -errno; + goto exit; + } ret = qemu_write_full(fd, desc, strlen(desc)); if (ret != strlen(desc)) { ret = -errno; goto exit; } - ret = 0; exit: close(fd); @@ -816,17 +1271,47 @@ static void vmdk_close(BlockDriverState *bs) { - BDRVVmdkState *s = bs->opaque; - - qemu_free(s->l1_table); - qemu_free(s->l2_cache); + vmdk_free_extents(bs); } static int vmdk_flush(BlockDriverState *bs) { - return bdrv_flush(bs->file); + int i, ret, err; + BDRVVmdkState *s = bs->opaque; + + ret = bdrv_flush(bs->file); + for (i = 0; i < s->num_extents; i++) { + err = bdrv_flush(s->extents[i].file); + if (err < 0) { + ret = err; + } + } + return ret; } +static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) +{ + int i; + int64_t ret = 0; + int64_t r; + BDRVVmdkState *s = bs->opaque; + + ret = bdrv_get_allocated_file_size(bs->file); + if (ret < 0) { + return ret; + } + for (i = 0; i < s->num_extents; i++) { + if (s->extents[i].file == bs->file) { + continue; + } + r = bdrv_get_allocated_file_size(s->extents[i].file); + if (r < 0) { + return r; + } + ret += r; + } + return ret; +} static QEMUOptionParameter vmdk_create_options[] = { { @@ -844,20 +1329,28 @@ .type = OPT_FLAG, .help = "VMDK version 6 image" }, + { + .name = BLOCK_OPT_SUBFMT, + .type = OPT_STRING, + .help = + "VMDK flat extent format, can be one of " + "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} " + }, { NULL } }; static BlockDriver bdrv_vmdk = { - .format_name = "vmdk", - .instance_size = sizeof(BDRVVmdkState), - .bdrv_probe = vmdk_probe, + .format_name = "vmdk", + .instance_size = sizeof(BDRVVmdkState), + .bdrv_probe = vmdk_probe, .bdrv_open = vmdk_open, - .bdrv_read = vmdk_read, - .bdrv_write = vmdk_write, - .bdrv_close = vmdk_close, - .bdrv_create = vmdk_create, - .bdrv_flush = vmdk_flush, - .bdrv_is_allocated = vmdk_is_allocated, + .bdrv_read = vmdk_read, + .bdrv_write = vmdk_write, + .bdrv_close = vmdk_close, + .bdrv_create = vmdk_create, + .bdrv_flush = vmdk_flush, + .bdrv_is_allocated = vmdk_is_allocated, + .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, .create_options = vmdk_create_options, }; diff -Nru qemu-kvm-0.14.1+noroms/block/vpc.c qemu-kvm-0.15.0+noroms/block/vpc.c --- qemu-kvm-0.14.1+noroms/block/vpc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block/vpc.c 2011-08-09 07:40:29.000000000 -0500 @@ -505,12 +505,8 @@ int ret = -EIO; // Read out options - while (options && options->name) { - if (!strcmp(options->name, "size")) { - total_sectors = options->value.n / 512; - } - options++; - } + total_sectors = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n / + BDRV_SECTOR_SIZE; // Create the file fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); diff -Nru qemu-kvm-0.14.1+noroms/block.c qemu-kvm-0.15.0+noroms/block.c --- qemu-kvm-0.14.1+noroms/block.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block.c 2011-08-09 07:40:29.000000000 -0500 @@ -439,13 +439,7 @@ 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)) + if (flags & BDRV_O_CACHE_WB) bs->enable_write_cache = 1; /* @@ -455,7 +449,7 @@ open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); /* - * Snapshots should be writeable. + * Snapshots should be writable. */ if (bs->is_temporary) { open_flags |= BDRV_O_RDWR; @@ -747,7 +741,7 @@ * Run consistency checks on an image * * 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 + * free of errors) or -errno when an internal error occurred. The results of the * check are stored in res. */ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res) @@ -1153,6 +1147,25 @@ } /** + * Length of a allocated file in bytes. Sparse files are counted by actual + * allocated space. Return < 0 if error or unknown. + */ +int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_get_allocated_file_size) { + return drv->bdrv_get_allocated_file_size(bs); + } + if (bs->file) { + return bdrv_get_allocated_file_size(bs->file); + } + return -ENOTSUP; +} + +/** * Length of a file in bytes. Return < 0 if error or unknown. */ int64_t bdrv_getlength(BlockDriverState *bs) @@ -1161,14 +1174,12 @@ if (!drv) return -ENOMEDIUM; - /* 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; + if (bs->growable || bs->removable) { + if (drv->bdrv_getlength) { + return drv->bdrv_getlength(bs); + } } - return drv->bdrv_getlength(bs); + return bs->total_sectors * BDRV_SECTOR_SIZE; } /* return 0 as number of sectors if no device present or error */ @@ -1307,13 +1318,6 @@ bs->secs = secs; } -void bdrv_set_type_hint(BlockDriverState *bs, int type) -{ - bs->type = type; - bs->removable = ((type == BDRV_TYPE_CDROM || - type == BDRV_TYPE_FLOPPY)); -} - void bdrv_set_translation_hint(BlockDriverState *bs, int translation) { bs->translation = translation; @@ -1327,9 +1331,107 @@ *psecs = bs->secs; } -int bdrv_get_type_hint(BlockDriverState *bs) -{ - return bs->type; +/* Recognize floppy formats */ +typedef struct FDFormat { + FDriveType drive; + uint8_t last_sect; + uint8_t max_track; + uint8_t max_head; +} FDFormat; + +static const FDFormat fd_formats[] = { + /* First entry is default format */ + /* 1.44 MB 3"1/2 floppy disks */ + { FDRIVE_DRV_144, 18, 80, 1, }, + { FDRIVE_DRV_144, 20, 80, 1, }, + { FDRIVE_DRV_144, 21, 80, 1, }, + { FDRIVE_DRV_144, 21, 82, 1, }, + { FDRIVE_DRV_144, 21, 83, 1, }, + { FDRIVE_DRV_144, 22, 80, 1, }, + { FDRIVE_DRV_144, 23, 80, 1, }, + { FDRIVE_DRV_144, 24, 80, 1, }, + /* 2.88 MB 3"1/2 floppy disks */ + { FDRIVE_DRV_288, 36, 80, 1, }, + { FDRIVE_DRV_288, 39, 80, 1, }, + { FDRIVE_DRV_288, 40, 80, 1, }, + { FDRIVE_DRV_288, 44, 80, 1, }, + { FDRIVE_DRV_288, 48, 80, 1, }, + /* 720 kB 3"1/2 floppy disks */ + { FDRIVE_DRV_144, 9, 80, 1, }, + { FDRIVE_DRV_144, 10, 80, 1, }, + { FDRIVE_DRV_144, 10, 82, 1, }, + { FDRIVE_DRV_144, 10, 83, 1, }, + { FDRIVE_DRV_144, 13, 80, 1, }, + { FDRIVE_DRV_144, 14, 80, 1, }, + /* 1.2 MB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 15, 80, 1, }, + { FDRIVE_DRV_120, 18, 80, 1, }, + { FDRIVE_DRV_120, 18, 82, 1, }, + { FDRIVE_DRV_120, 18, 83, 1, }, + { FDRIVE_DRV_120, 20, 80, 1, }, + /* 720 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 9, 80, 1, }, + { FDRIVE_DRV_120, 11, 80, 1, }, + /* 360 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 9, 40, 1, }, + { FDRIVE_DRV_120, 9, 40, 0, }, + { FDRIVE_DRV_120, 10, 41, 1, }, + { FDRIVE_DRV_120, 10, 42, 1, }, + /* 320 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 8, 40, 1, }, + { FDRIVE_DRV_120, 8, 40, 0, }, + /* 360 kB must match 5"1/4 better than 3"1/2... */ + { FDRIVE_DRV_144, 9, 80, 0, }, + /* end */ + { FDRIVE_DRV_NONE, -1, -1, 0, }, +}; + +void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads, + int *max_track, int *last_sect, + FDriveType drive_in, FDriveType *drive) +{ + const FDFormat *parse; + uint64_t nb_sectors, size; + int i, first_match, match; + + bdrv_get_geometry_hint(bs, nb_heads, max_track, last_sect); + if (*nb_heads != 0 && *max_track != 0 && *last_sect != 0) { + /* User defined disk */ + } else { + bdrv_get_geometry(bs, &nb_sectors); + match = -1; + first_match = -1; + for (i = 0; ; i++) { + parse = &fd_formats[i]; + if (parse->drive == FDRIVE_DRV_NONE) { + break; + } + if (drive_in == parse->drive || + drive_in == FDRIVE_DRV_NONE) { + size = (parse->max_head + 1) * parse->max_track * + parse->last_sect; + if (nb_sectors == size) { + match = i; + break; + } + if (first_match == -1) { + first_match = i; + } + } + } + if (match == -1) { + if (first_match == -1) { + match = 1; + } else { + match = first_match; + } + parse = &fd_formats[match]; + } + *nb_heads = parse->max_head + 1; + *max_track = parse->max_track; + *last_sect = parse->last_sect; + *drive = parse->drive; + } } int bdrv_get_translation_hint(BlockDriverState *bs) @@ -1603,9 +1705,8 @@ bs_dict = qobject_to_qdict(obj); - monitor_printf(mon, "%s: type=%s removable=%d", + monitor_printf(mon, "%s: removable=%d", qdict_get_str(bs_dict, "device"), - qdict_get_str(bs_dict, "type"), qdict_get_bool(bs_dict, "removable")); if (qdict_get_bool(bs_dict, "removable")) { @@ -1646,23 +1747,10 @@ QTAILQ_FOREACH(bs, &bdrv_states, list) { QObject *bs_obj; - const char *type = "unknown"; - switch(bs->type) { - case BDRV_TYPE_HD: - type = "hd"; - break; - case BDRV_TYPE_CDROM: - type = "cdrom"; - break; - case BDRV_TYPE_FLOPPY: - type = "floppy"; - break; - } - - bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': %s, " + bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': 'unknown', " "'removable': %i, 'locked': %i }", - bs->device_name, type, bs->removable, + bs->device_name, bs->removable, bs->locked); if (bs->drv) { @@ -2390,6 +2478,8 @@ { BlockDriver *drv = bs->drv; + trace_bdrv_aio_flush(bs, opaque); + if (bs->open_flags & BDRV_O_NO_FLUSH) { return bdrv_aio_noop_em(bs, cb, opaque); } @@ -2712,6 +2802,8 @@ { BlockDriver *drv = bs->drv; + trace_bdrv_set_locked(bs, locked); + bs->locked = locked; if (drv && drv->bdrv_set_locked) { drv->bdrv_set_locked(bs, locked); @@ -2808,7 +2900,7 @@ char *options, uint64_t img_size, int flags) { QEMUOptionParameter *param = NULL, *create_options = NULL; - QEMUOptionParameter *backing_fmt, *backing_file; + QEMUOptionParameter *backing_fmt, *backing_file, *size; BlockDriverState *bs = NULL; BlockDriver *drv, *proto_drv; BlockDriver *backing_drv = NULL; @@ -2891,7 +2983,8 @@ // 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) { + size = get_option_parameter(param, BLOCK_OPT_SIZE); + if (size && size->value.n == -1) { if (backing_file && backing_file->value.s) { uint64_t size; char buf[32]; diff -Nru qemu-kvm-0.14.1+noroms/blockdev.c qemu-kvm-0.15.0+noroms/blockdev.c --- qemu-kvm-0.14.1+noroms/blockdev.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/blockdev.c 2011-08-09 07:40:29.000000000 -0500 @@ -243,14 +243,6 @@ 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 */ @@ -276,7 +268,11 @@ error_report("unsupported bus type '%s'", buf); return NULL; } + } else { + type = default_to_scsi ? IF_SCSI : IF_IDE; + pstrcpy(devname, sizeof(devname), if_name[type]); } + max_devs = if_max_devs[type]; if (cyls || heads || secs) { @@ -296,7 +292,7 @@ if ((buf = qemu_opt_get(opts, "trans")) != NULL) { if (!cyls) { - error_report("'%s' trans must be used with cyls,heads and secs", + error_report("'%s' trans must be used with cyls, heads and secs", buf); return NULL; } @@ -317,7 +313,7 @@ media = MEDIA_DISK; } else if (!strcmp(buf, "cdrom")) { if (cyls || secs || heads) { - error_report("'%s' invalid physical CHS format", buf); + error_report("CHS can't be set with media=%s", buf); return NULL; } media = MEDIA_CDROM; @@ -329,7 +325,7 @@ if ((buf = qemu_opt_get(opts, "cache")) != NULL) { if (!strcmp(buf, "off") || !strcmp(buf, "none")) { - bdrv_flags |= BDRV_O_NOCACHE; + bdrv_flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; } else if (!strcmp(buf, "writeback")) { bdrv_flags |= BDRV_O_CACHE_WB; } else if (!strcmp(buf, "unsafe")) { @@ -500,7 +496,8 @@ } break; case MEDIA_CDROM: - bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM); + bdrv_set_removable(dinfo->bdrv, 1); + dinfo->media_cd = 1; break; } break; @@ -508,7 +505,7 @@ /* 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); + bdrv_set_removable(dinfo->bdrv, 1); break; case IF_PFLASH: case IF_MTD: @@ -516,7 +513,7 @@ 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, "driver", "virtio-blk"); qemu_opt_set(opts, "drive", dinfo->id); if (devaddr) qemu_opt_set(opts, "addr", devaddr); @@ -584,15 +581,16 @@ 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 *filename = qdict_get_try_str(qdict, "snapshot-file"); const char *format = qdict_get_try_str(qdict, "format"); BlockDriverState *bs; - BlockDriver *drv, *proto_drv; + BlockDriver *drv, *old_drv, *proto_drv; int ret = 0; int flags; + char old_filename[1024]; if (!filename) { - qerror_report(QERR_MISSING_PARAMETER, "snapshot_file"); + qerror_report(QERR_MISSING_PARAMETER, "snapshot-file"); ret = -1; goto out; } @@ -604,6 +602,11 @@ goto out; } + pstrcpy(old_filename, sizeof(old_filename), bs->filename); + + old_drv = bs->drv; + flags = bs->open_flags; + if (!format) { format = "qcow2"; } @@ -623,7 +626,7 @@ } ret = bdrv_img_create(filename, format, bs->filename, - bs->drv->format_name, NULL, -1, bs->open_flags); + bs->drv->format_name, NULL, -1, flags); if (ret) { goto out; } @@ -631,15 +634,20 @@ 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 reopening the image file we just created fails, fall back + * and try to re-open the original image. If that fails too, we + * are in serious trouble. */ if (ret != 0) { - abort(); + ret = bdrv_open(bs, old_filename, flags, old_drv); + if (ret != 0) { + qerror_report(QERR_OPEN_FILE_FAILED, old_filename); + } else { + qerror_report(QERR_OPEN_FILE_FAILED, filename); + } } out: if (ret) { diff -Nru qemu-kvm-0.14.1+noroms/blockdev.h qemu-kvm-0.15.0+noroms/blockdev.h --- qemu-kvm-0.14.1+noroms/blockdev.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/blockdev.h 2011-08-09 07:40:29.000000000 -0500 @@ -33,6 +33,7 @@ int bus; int unit; int auto_del; /* see blockdev_mark_auto_del() */ + int media_cd; QemuOpts *opts; char serial[BLOCK_SERIAL_STRLEN + 1]; QTAILQ_ENTRY(DriveInfo) next; diff -Nru qemu-kvm-0.14.1+noroms/block.h qemu-kvm-0.15.0+noroms/block.h --- qemu-kvm-0.14.1+noroms/block.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block.h 2011-08-09 07:40:29.000000000 -0500 @@ -89,6 +89,7 @@ const uint8_t *buf, int nb_sectors); int bdrv_truncate(BlockDriverState *bs, int64_t offset); int64_t bdrv_getlength(BlockDriverState *bs); +int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); 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); @@ -110,7 +111,7 @@ typedef struct BlockDriverAIOCB BlockDriverAIOCB; typedef void BlockDriverCompletionFunc(void *opaque, int ret); typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector, - int sector_num); + int sector_num); BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); @@ -118,7 +119,7 @@ QEMUIOVector *iov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs, - BlockDriverCompletionFunc *cb, void *opaque); + BlockDriverCompletionFunc *cb, void *opaque); void bdrv_aio_cancel(BlockDriverAIOCB *acb); typedef struct BlockRequest { @@ -150,11 +151,8 @@ 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); + int *pnum); -#define BDRV_TYPE_HD 0 -#define BDRV_TYPE_CDROM 1 -#define BDRV_TYPE_FLOPPY 2 #define BIOS_ATA_TRANSLATION_AUTO 0 #define BIOS_ATA_TRANSLATION_NONE 1 #define BIOS_ATA_TRANSLATION_LBA 2 @@ -163,11 +161,19 @@ void bdrv_set_geometry_hint(BlockDriverState *bs, int cyls, int heads, int secs); -void bdrv_set_type_hint(BlockDriverState *bs, int type); void bdrv_set_translation_hint(BlockDriverState *bs, int translation); void bdrv_get_geometry_hint(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); -int bdrv_get_type_hint(BlockDriverState *bs); +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 */ +} FDriveType; + +void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads, + int *max_track, int *last_sect, + FDriveType drive_in, FDriveType *drive); int bdrv_get_translation_hint(BlockDriverState *bs); void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, BlockErrorAction on_write_error); diff -Nru qemu-kvm-0.14.1+noroms/block_int.h qemu-kvm-0.15.0+noroms/block_int.h --- qemu-kvm-0.14.1+noroms/block_int.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block_int.h 2011-08-09 07:40:29.000000000 -0500 @@ -39,6 +39,7 @@ #define BLOCK_OPT_CLUSTER_SIZE "cluster_size" #define BLOCK_OPT_TABLE_SIZE "table_size" #define BLOCK_OPT_PREALLOC "preallocation" +#define BLOCK_OPT_SUBFMT "subformat" typedef struct AIOPool { void (*cancel)(BlockDriverAIOCB *acb); @@ -85,6 +86,7 @@ const char *protocol_name; int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); int64_t (*bdrv_getlength)(BlockDriverState *bs); + int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs); int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); @@ -194,7 +196,6 @@ /* NOTE: the following infos are only hints for real hardware 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; @@ -204,8 +205,8 @@ void *private; }; -#define CHANGE_MEDIA 0x01 -#define CHANGE_SIZE 0x02 +#define CHANGE_MEDIA 0x01 +#define CHANGE_SIZE 0x02 struct BlockDriverAIOCB { AIOPool *pool; diff -Nru qemu-kvm-0.14.1+noroms/block-migration.c qemu-kvm-0.15.0+noroms/block-migration.c --- qemu-kvm-0.14.1+noroms/block-migration.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/block-migration.c 2011-08-09 07:40:29.000000000 -0500 @@ -62,7 +62,6 @@ QEMUIOVector qiov; BlockDriverAIOCB *aiocb; int ret; - int64_t time; QSIMPLEQ_ENTRY(BlkMigBlock) entry; } BlkMigBlock; @@ -78,6 +77,7 @@ int prev_progress; int bulk_completed; long double total_time; + long double prev_time_offset; int reads; } BlkMigState; @@ -131,16 +131,10 @@ 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; + return (block_mig_state.reads / block_mig_state.total_time) * BLOCK_SIZE; } static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) @@ -191,13 +185,14 @@ static void blk_mig_read_cb(void *opaque, int ret) { + long double curr_time = qemu_get_clock_ns(rt_clock); BlkMigBlock *blk = opaque; blk->ret = ret; - blk->time = qemu_get_clock_ns(rt_clock) - blk->time; - - add_avg_read_time(blk->time); + block_mig_state.reads++; + block_mig_state.total_time += (curr_time - block_mig_state.prev_time_offset); + block_mig_state.prev_time_offset = curr_time; QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0); @@ -250,7 +245,9 @@ 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); + if (block_mig_state.submitted == 0) { + block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock); + } blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, nr_sectors, blk_mig_read_cb, blk); @@ -409,7 +406,9 @@ 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); + if (block_mig_state.submitted == 0) { + block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock); + } blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov, nr_sectors, blk_mig_read_cb, blk); @@ -672,7 +671,7 @@ 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", + error_report("Error getting length of block device %s", device_name); return -EINVAL; } diff -Nru qemu-kvm-0.14.1+noroms/bsd-user/main.c qemu-kvm-0.15.0+noroms/bsd-user/main.c --- qemu-kvm-0.14.1+noroms/bsd-user/main.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/bsd-user/main.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,7 +29,7 @@ #include "qemu.h" #include "qemu-common.h" /* For tb_lock */ -#include "exec-all.h" +#include "cpu.h" #include "tcg.h" #include "qemu-timer.h" #include "envlist.h" @@ -237,7 +237,7 @@ break; #ifndef TARGET_ABI32 case EXCP_SYSCALL: - /* syscall from syscall intruction */ + /* syscall from syscall instruction */ if (bsd_type == target_freebsd) env->regs[R_EAX] = do_freebsd_syscall(env, env->regs[R_EAX], @@ -690,7 +690,8 @@ "-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n" "\n" "Debug options:\n" - "-d options activate log (logfile=%s)\n" + "-d options activate log (default logfile=%s)\n" + "-D logfile override default logfile location\n" "-p pagesize set the host page size to 'pagesize'\n" "-singlestep always run in singlestep mode\n" "-strace log system calls\n" @@ -731,6 +732,8 @@ { const char *filename; const char *cpu_model; + const char *log_file = DEBUG_LOGFILE; + const char *log_mask = NULL; struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; TaskState ts1, *ts = &ts1; @@ -745,9 +748,6 @@ if (argc <= 1) usage(); - /* init debug */ - cpu_set_log_filename(DEBUG_LOGFILE); - if ((envlist = envlist_create()) == NULL) { (void) fprintf(stderr, "Unable to allocate envlist\n"); exit(1); @@ -775,22 +775,15 @@ if (!strcmp(r, "-")) { break; } else if (!strcmp(r, "d")) { - int mask; - const CPULogItem *item; - - if (optind >= argc) + if (optind >= argc) { + break; + } + log_mask = argv[optind++]; + } else if (!strcmp(r, "D")) { + if (optind >= argc) { break; - - r = argv[optind++]; - mask = cpu_str_to_log_mask(r); - 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); + log_file = argv[optind++]; } else if (!strcmp(r, "E")) { r = argv[optind++]; if (envlist_setenv(envlist, r) != 0) @@ -863,8 +856,27 @@ usage(); } } - if (optind >= argc) + + /* init debug */ + cpu_set_log_filename(log_file); + if (log_mask) { + int mask; + const CPULogItem *item; + + mask = cpu_str_to_log_mask(log_mask); + 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); + } + + if (optind >= argc) { usage(); + } filename = argv[optind]; /* Zero out regs */ diff -Nru qemu-kvm-0.14.1+noroms/bsd-user/qemu.h qemu-kvm-0.15.0+noroms/bsd-user/qemu.h --- qemu-kvm-0.14.1+noroms/bsd-user/qemu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/bsd-user/qemu.h 2011-08-09 07:40:29.000000000 -0500 @@ -323,7 +323,7 @@ abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len); /* Functions for accessing guest memory. The tget and tput functions - read/write single values, byteswapping as neccessary. The lock_user + read/write single values, byteswapping as necessary. The lock_user gets a pointer to a contiguous area of guest memory, but does not perform and byteswapping. lock_user may return either a pointer to the guest memory, or a temporary buffer. */ diff -Nru qemu-kvm-0.14.1+noroms/bsd-user/syscall.c qemu-kvm-0.15.0+noroms/bsd-user/syscall.c --- qemu-kvm-0.14.1+noroms/bsd-user/syscall.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/bsd-user/syscall.c 2011-08-09 07:40:29.000000000 -0500 @@ -31,7 +31,6 @@ #include #include #include -#include #include #include "qemu.h" diff -Nru qemu-kvm-0.14.1+noroms/bt-host.c qemu-kvm-0.15.0+noroms/bt-host.c --- qemu-kvm-0.14.1+noroms/bt-host.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/bt-host.c 2011-08-09 07:40:29.000000000 -0500 @@ -19,7 +19,6 @@ #include "qemu-common.h" #include "qemu-char.h" -#include "sysemu.h" #include "net.h" #include "bt-host.h" diff -Nru qemu-kvm-0.14.1+noroms/bt-vhci.c qemu-kvm-0.15.0+noroms/bt-vhci.c --- qemu-kvm-0.14.1+noroms/bt-vhci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/bt-vhci.c 2011-08-09 07:40:29.000000000 -0500 @@ -19,7 +19,6 @@ #include "qemu-common.h" #include "qemu-char.h" -#include "sysemu.h" #include "net.h" #include "hw/bt.h" diff -Nru qemu-kvm-0.14.1+noroms/buffered_file.c qemu-kvm-0.15.0+noroms/buffered_file.c --- qemu-kvm-0.14.1+noroms/buffered_file.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/buffered_file.c 2011-08-09 07:40:29.000000000 -0500 @@ -14,7 +14,6 @@ #include "qemu-common.h" #include "hw/hw.h" #include "qemu-timer.h" -#include "sysemu.h" #include "qemu-char.h" #include "buffered_file.h" @@ -238,7 +237,7 @@ return; } - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100); if (s->freeze_output) return; @@ -274,9 +273,9 @@ buffered_set_rate_limit, buffered_get_rate_limit); - s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s); + s->timer = qemu_new_timer_ms(rt_clock, buffered_rate_tick, s); - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100); return s->file; } diff -Nru qemu-kvm-0.14.1+noroms/Changelog qemu-kvm-0.15.0+noroms/Changelog --- qemu-kvm-0.14.1+noroms/Changelog 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/Changelog 2011-08-09 07:40:29.000000000 -0500 @@ -1,3 +1,9 @@ +This file documents changes for QEMU releases 0.12 and earlier. +For changelog information for later releases, see +http://wiki.qemu.org/ChangeLog or look at the git history for +more detailed information. + + version 0.12.0: - Update to SeaBIOS 0.5.0 @@ -525,7 +531,7 @@ - ppc64 support + personality() patch (Rusty Russell) - first Alpha CPU patches (Falk Hueffner) - - removed bfd.h dependancy + - removed bfd.h dependency - fixed shrd, shld, idivl and divl on PowerPC. - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC). diff -Nru qemu-kvm-0.14.1+noroms/check-qdict.c qemu-kvm-0.15.0+noroms/check-qdict.c --- qemu-kvm-0.14.1+noroms/check-qdict.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/check-qdict.c 2011-08-09 07:40:29.000000000 -0500 @@ -267,8 +267,9 @@ { char value[128]; - if (fscanf(file, "%s%s", key, value) == EOF) + if (fscanf(file, "%127s%127s", key, value) == EOF) { return NULL; + } remove_dots(key); return qstring_from_str(value); } diff -Nru qemu-kvm-0.14.1+noroms/cmd.c qemu-kvm-0.15.0+noroms/cmd.c --- qemu-kvm-0.14.1+noroms/cmd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cmd.c 2011-08-09 07:40:29.000000000 -0500 @@ -486,7 +486,7 @@ snprintf(ts, size, "%u:%02u.%02u", (unsigned int) MINUTES(tv->tv_sec), (unsigned int) SECONDS(tv->tv_sec), - (unsigned int) usec * 100); + (unsigned int) (usec * 100)); return; } format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */ @@ -497,9 +497,9 @@ (unsigned int) HOURS(tv->tv_sec), (unsigned int) MINUTES(tv->tv_sec), (unsigned int) SECONDS(tv->tv_sec), - (unsigned int) usec * 100); + (unsigned int) (usec * 100)); } else { - snprintf(ts, size, "0.%04u sec", (unsigned int) usec * 10000); + snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000)); } } diff -Nru qemu-kvm-0.14.1+noroms/compat/sys/eventfd.h qemu-kvm-0.15.0+noroms/compat/sys/eventfd.h --- qemu-kvm-0.14.1+noroms/compat/sys/eventfd.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/compat/sys/eventfd.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,13 +0,0 @@ -#ifndef _COMPAT_SYS_EVENTFD -#define _COMPAT_SYS_EVENTFD - -#include -#include - - -static inline int eventfd (int count, int flags) -{ - return syscall(SYS_eventfd, count, flags); -} - -#endif diff -Nru qemu-kvm-0.14.1+noroms/compatfd.c qemu-kvm-0.15.0+noroms/compatfd.c --- qemu-kvm-0.14.1+noroms/compatfd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/compatfd.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,45 +26,45 @@ static void *sigwait_compat(void *opaque) { struct sigfd_compat_info *info = opaque; - int err; sigset_t all; sigfillset(&all); - sigprocmask(SIG_BLOCK, &all, NULL); + pthread_sigmask(SIG_BLOCK, &all, NULL); - do { - siginfo_t siginfo; - - err = sigwaitinfo(&info->mask, &siginfo); - if (err == -1 && errno == EINTR) { - err = 0; - continue; - } - - if (err > 0) { - char buffer[128]; + while (1) { + int sig; + int err; + + err = sigwait(&info->mask, &sig); + if (err != 0) { + if (errno == EINTR) { + continue; + } else { + return NULL; + } + } else { + struct qemu_signalfd_siginfo buffer; size_t offset = 0; - memcpy(buffer, &err, sizeof(err)); + memset(&buffer, 0, sizeof(buffer)); + buffer.ssi_signo = sig; + while (offset < sizeof(buffer)) { ssize_t len; - len = write(info->fd, buffer + offset, + len = write(info->fd, (char *)&buffer + offset, sizeof(buffer) - offset); if (len == -1 && errno == EINTR) continue; if (len <= 0) { - err = -1; - break; + return NULL; } offset += len; } } - } while (err >= 0); - - return NULL; + } } static int qemu_signalfd_compat(const sigset_t *mask) @@ -115,3 +115,14 @@ return qemu_signalfd_compat(mask); } + +bool qemu_signalfd_available(void) +{ +#ifdef CONFIG_SIGNALFD + errno = 0; + syscall(SYS_signalfd, -1, NULL, _NSIG / 8); + return errno != ENOSYS; +#else + return false; +#endif +} diff -Nru qemu-kvm-0.14.1+noroms/compatfd.h qemu-kvm-0.15.0+noroms/compatfd.h --- qemu-kvm-0.14.1+noroms/compatfd.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/compatfd.h 2011-08-09 07:40:29.000000000 -0500 @@ -39,5 +39,6 @@ }; int qemu_signalfd(const sigset_t *mask); +bool qemu_signalfd_available(void); #endif diff -Nru qemu-kvm-0.14.1+noroms/compiler.h qemu-kvm-0.15.0+noroms/compiler.h --- qemu-kvm-0.14.1+noroms/compiler.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/compiler.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,34 @@ +/* public domain */ + +#ifndef COMPILER_H +#define COMPILER_H + +#include "config-host.h" + +#define QEMU_NORETURN __attribute__ ((__noreturn__)) +#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT +#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define QEMU_WARN_UNUSED_RESULT +#endif + +#define QEMU_BUILD_BUG_ON(x) \ + typedef char qemu_build_bug_on__##__LINE__[(x)?-1:1]; + +#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 + +#endif /* COMPILER_H */ diff -Nru qemu-kvm-0.14.1+noroms/configure qemu-kvm-0.15.0+noroms/configure --- qemu-kvm-0.14.1+noroms/configure 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/configure 2011-08-09 07:40:29.000000000 -0500 @@ -124,9 +124,9 @@ docs="" fdt="" kvm="" -kvm_para="" nptl="" sdl="" +vnc="yes" sparse="no" uuid="" vde="" @@ -136,6 +136,7 @@ vnc_png="" vnc_thread="no" xen="" +xen_ctrl_version="" linux_aio="" attr="" vhost_net="" @@ -154,6 +155,7 @@ datadir="\${prefix}/share/qemu" docdir="\${prefix}/share/doc/qemu" bindir="\${prefix}/bin" +libdir="\${prefix}/lib" sysconfdir="\${prefix}/etc" confsuffix="/qemu" slirp="yes" @@ -171,11 +173,10 @@ bsd_user="no" guest_base="" uname_release="" -io_thread="no" +io_thread="yes" mixemu="no" -kvm_cap_pit="" -kvm_cap_device_assignment="" -kerneldir="" +kvm_cap_pit="yes" +kvm_cap_device_assignment="yes" aix="no" blobs="yes" pkgversion=" ($(kvm_version))" @@ -187,6 +188,11 @@ trace_file="trace" spice="" rbd="" +smartcard="" +smartcard_nss="" +usb_redir="" +opengl="" +guest_agent="yes" # parse CC options first for opt do @@ -237,7 +243,7 @@ # default flags for all hosts QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS" CFLAGS="-g $CFLAGS" -QEMU_CFLAGS="-Wall -Wundef -Wendif-labels -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" +QEMU_CFLAGS="-Wall -Wundef -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="-D_FORTIFY_SOURCE=2 $QEMU_CFLAGS" @@ -293,7 +299,7 @@ fi case "$cpu" in - alpha|cris|ia64|m68k|microblaze|ppc|ppc64|sparc64) + alpha|cris|ia64|lm32|m68k|microblaze|ppc|ppc64|sparc64|unicore32) cpu="$cpu" ;; i386|i486|i586|i686|i86pc|BePC) @@ -466,6 +472,8 @@ linux="yes" linux_user="yes" usb="linux" + kvm="yes" + vhost_net="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then audio_possible_drivers="$audio_possible_drivers fmod" fi @@ -488,13 +496,14 @@ : ${make=${MAKE-make}} : ${install=${INSTALL-install}} +: ${python=${PYTHON-python}} if test "$mingw32" = "yes" ; then EXESUF=".exe" QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS" # enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS" - LIBS="-lwinmm -lws2_32 -liphlpapi $LIBS" + LIBS="-lwinmm -lws2_32 -liberty -liphlpapi $LIBS" prefix="c:/Program Files/Qemu" mandir="\${prefix}" datadir="\${prefix}" @@ -511,6 +520,8 @@ case "$opt" in --help|-h) show_help=yes ;; + --version|-V) exec cat $source_path/VERSION + ;; --prefix=*) prefix="$optarg" ;; --interp-prefix=*) interp_prefix="$optarg" @@ -527,6 +538,8 @@ ;; --install=*) install="$optarg" ;; + --python=*) python="$optarg" + ;; --extra-cflags=*) ;; --extra-ldflags=*) @@ -549,6 +562,8 @@ ;; --bindir=*) bindir="$optarg" ;; + --libdir=*) libdir="$optarg" + ;; --datadir=*) datadir="$optarg" ;; --docdir=*) docdir="$optarg" @@ -559,6 +574,10 @@ ;; --enable-sdl) sdl="yes" ;; + --disable-vnc) vnc="no" + ;; + --enable-vnc) vnc="yes" + ;; --fmod-lib=*) fmod_lib="$optarg" ;; --fmod-inc=*) fmod_inc="$optarg" @@ -729,8 +748,6 @@ ;; --disable-blobs) blobs="no" ;; - --kerneldir=*) kerneldir="$optarg" - ;; --with-pkgversion=*) pkgversion=" ($optarg)" ;; --disable-docs) docs="no" @@ -743,12 +760,32 @@ ;; --enable-vhost-net) vhost_net="yes" ;; + --disable-opengl) opengl="no" + ;; + --enable-opengl) opengl="yes" + ;; --*dir) ;; --disable-rbd) rbd="no" ;; --enable-rbd) rbd="yes" ;; + --disable-smartcard) smartcard="no" + ;; + --enable-smartcard) smartcard="yes" + ;; + --disable-smartcard-nss) smartcard_nss="no" + ;; + --enable-smartcard-nss) smartcard_nss="yes" + ;; + --disable-usb-redir) usb_redir="no" + ;; + --enable-usb-redir) usb_redir="yes" + ;; + --enable-guest-agent) guest_agent="yes" + ;; + --disable-guest-agent) guest_agent="no" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -823,10 +860,81 @@ hppa*) host_guest_base="yes" ;; + unicore32*) + host_guest_base="yes" + ;; esac [ -z "$guest_base" ] && guest_base="$host_guest_base" + +default_target_list="" + +# these targets are portable +if [ "$softmmu" = "yes" ] ; then + default_target_list="\ +i386-softmmu \ +x86_64-softmmu \ +arm-softmmu \ +cris-softmmu \ +lm32-softmmu \ +m68k-softmmu \ +microblaze-softmmu \ +microblazeel-softmmu \ +mips-softmmu \ +mipsel-softmmu \ +mips64-softmmu \ +mips64el-softmmu \ +ppc-softmmu \ +ppcemb-softmmu \ +ppc64-softmmu \ +sh4-softmmu \ +sh4eb-softmmu \ +sparc-softmmu \ +sparc64-softmmu \ +s390x-softmmu \ +" +fi +# the following are Linux specific +if [ "$linux_user" = "yes" ] ; then + default_target_list="${default_target_list}\ +i386-linux-user \ +x86_64-linux-user \ +alpha-linux-user \ +arm-linux-user \ +armeb-linux-user \ +cris-linux-user \ +m68k-linux-user \ +microblaze-linux-user \ +microblazeel-linux-user \ +mips-linux-user \ +mipsel-linux-user \ +ppc-linux-user \ +ppc64-linux-user \ +ppc64abi32-linux-user \ +sh4-linux-user \ +sh4eb-linux-user \ +sparc-linux-user \ +sparc64-linux-user \ +sparc32plus-linux-user \ +unicore32-linux-user \ +s390x-linux-user \ +" +fi +# the following are Darwin specific +if [ "$darwin_user" = "yes" ] ; then + default_target_list="$default_target_list i386-darwin-user ppc-darwin-user " +fi +# the following are BSD specific +if [ "$bsd_user" = "yes" ] ; then + default_target_list="${default_target_list}\ +i386-bsd-user \ +x86_64-bsd-user \ +sparc-bsd-user \ +sparc64-bsd-user \ +" +fi + if test x"$show_help" = x"yes" ; then cat << EOF @@ -839,7 +947,9 @@ echo " --prefix=PREFIX install in PREFIX [$prefix]" echo " --interp-prefix=PREFIX where to find shared libraries, etc." echo " use %M for cpu name [$interp_prefix]" -echo " --target-list=LIST set target list [$target_list]" +echo " --target-list=LIST set target list (default: build everything)" +echo "Available targets: $default_target_list" | \ + fold -s -w 53 | sed -e 's/^/ /' echo "" echo "Advanced options (experts only):" echo " --source-path=PATH path of source code [$source_path]" @@ -851,6 +961,7 @@ echo " --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS" echo " --make=MAKE use specified make [$make]" echo " --install=INSTALL use specified install [$install]" +echo " --python=PYTHON use specified python [$python]" echo " --static enable static build [$static]" echo " --mandir=PATH install man pages in PATH" echo " --datadir=PATH install firmware in PATH" @@ -866,6 +977,8 @@ echo " --disable-werror disable compilation abort on warning" echo " --disable-sdl disable SDL" echo " --enable-sdl enable SDL" +echo " --disable-vnc disable VNC" +echo " --enable-vnc enable VNC" echo " --enable-cocoa enable COCOA (Mac OS X only)" echo " --audio-drv-list=LIST set audio drivers list:" echo " Available drivers: $audio_possible_drivers" @@ -898,6 +1011,7 @@ echo " --enable-check-utests enable check unit-tests" echo " --disable-bluez disable bluez stack connectivity" echo " --enable-bluez enable bluez stack connectivity" +echo " --disable-slirp disable SLIRP userspace network connectivity" echo " --disable-kvm disable KVM acceleration support" echo " --enable-kvm enable KVM acceleration support" echo " --disable-kvm-pit disable KVM pit support" @@ -936,7 +1050,6 @@ echo " --enable-attr enable attr and xattr support" echo " --enable-io-thread enable IO thread" echo " --disable-blobs disable installing provided firmware blobs" -echo " --kerneldir=PATH look for kernel includes in PATH" echo " --disable-cpu-emulation disables use of qemu cpu emulation code" echo " --enable-docs enable documentation build" echo " --disable-docs disable documentation build" @@ -949,6 +1062,14 @@ echo " --disable-spice disable spice" echo " --enable-spice enable spice" echo " --enable-rbd enable building the rados block device (rbd)" +echo " --disable-smartcard disable smartcard support" +echo " --enable-smartcard enable smartcard support" +echo " --disable-smartcard-nss disable smartcard nss support" +echo " --enable-smartcard-nss enable smartcard nss support" +echo " --disable-usb-redir disable usb network redirection support" +echo " --enable-usb-redir enable usb network redirection support" +echo " --disable-guest-agent disable building of the QEMU Guest Agent" +echo " --enable-guest-agent enable building of the QEMU Guest Agent" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -969,7 +1090,7 @@ gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits" gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags" gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" -gcc_flags="-fstack-protector-all $gcc_flags" +gcc_flags="-fstack-protector-all -Wendif-labels $gcc_flags" cat > $TMPC << EOF int main(void) { return 0; } EOF @@ -1008,66 +1129,17 @@ fi fi +if test "$guest_agent" != "no" ; then + if has $python; then + : + else + echo "Python not found. Use --python=/path/to/python" + exit 1 + fi +fi if test -z "$target_list" ; then -# these targets are portable - if [ "$softmmu" = "yes" ] ; then - target_list="\ -i386-softmmu \ -x86_64-softmmu \ -arm-softmmu \ -cris-softmmu \ -m68k-softmmu \ -microblaze-softmmu \ -mips-softmmu \ -mipsel-softmmu \ -mips64-softmmu \ -mips64el-softmmu \ -ppc-softmmu \ -ppcemb-softmmu \ -ppc64-softmmu \ -sh4-softmmu \ -sh4eb-softmmu \ -sparc-softmmu \ -sparc64-softmmu \ -" - fi -# the following are Linux specific - if [ "$linux_user" = "yes" ] ; then - target_list="${target_list}\ -i386-linux-user \ -x86_64-linux-user \ -alpha-linux-user \ -arm-linux-user \ -armeb-linux-user \ -cris-linux-user \ -m68k-linux-user \ -microblaze-linux-user \ -mips-linux-user \ -mipsel-linux-user \ -ppc-linux-user \ -ppc64-linux-user \ -ppc64abi32-linux-user \ -sh4-linux-user \ -sh4eb-linux-user \ -sparc-linux-user \ -sparc64-linux-user \ -sparc32plus-linux-user \ -" - fi -# the following are Darwin specific - if [ "$darwin_user" = "yes" ] ; then - target_list="$target_list i386-darwin-user ppc-darwin-user " - fi -# the following are BSD specific - if [ "$bsd_user" = "yes" ] ; then - target_list="${target_list}\ -i386-bsd-user \ -x86_64-bsd-user \ -sparc-bsd-user \ -sparc64-bsd-user \ -" - fi + target_list="$default_target_list" else target_list=`echo "$target_list" | sed -e 's/,/ /g'` fi @@ -1180,20 +1252,108 @@ if test "$xen" != "no" ; then xen_libs="-lxenstore -lxenctrl -lxenguest" + + # Xen unstable cat > $TMPC < #include -int main(void) { xs_daemon_open(); xc_interface_open(); return 0; } +#include +#include +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + xc_interface *xc; + xs_daemon_open(); + xc = xc_interface_open(0, 0, 0); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_gnttab_open(NULL, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + return 0; +} EOF if compile_prog "" "$xen_libs" ; then + xen_ctrl_version=410 xen=yes - libs_softmmu="$xen_libs $libs_softmmu" + + # Xen 4.0.0 + elif ( + cat > $TMPC < +#include +#include +#include +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + struct xen_add_to_physmap xatp = { + .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0, + }; + xs_daemon_open(); + xc_interface_open(); + xc_gnttab_open(); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_memory_op(0, XENMEM_add_to_physmap, &xatp); + return 0; +} +EOF + compile_prog "" "$xen_libs" + ) ; then + xen_ctrl_version=400 + xen=yes + + # Xen 3.4.0 + elif ( + cat > $TMPC < +#include +int main(void) { + struct xen_add_to_physmap xatp = { + .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0, + }; + xs_daemon_open(); + xc_interface_open(); + xc_gnttab_open(); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_memory_op(0, XENMEM_add_to_physmap, &xatp); + return 0; +} +EOF + compile_prog "" "$xen_libs" + ) ; then + xen_ctrl_version=340 + xen=yes + + # Xen 3.3.0 + elif ( + cat > $TMPC < +#include +int main(void) { + xs_daemon_open(); + xc_interface_open(); + xc_gnttab_open(); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + return 0; +} +EOF + compile_prog "" "$xen_libs" + ) ; then + xen_ctrl_version=330 + xen=yes + + # Xen not found or unsupported else if test "$xen" = "yes" ; then feature_not_found "xen" fi xen=no fi + + if test "$xen" = yes; then + libs_softmmu="$xen_libs $libs_softmmu" + fi fi ########################################## @@ -1205,6 +1365,15 @@ fi ########################################## +# libtool probe + +if ! has libtool; then + libtool= +else + libtool=libtool +fi + +########################################## # Sparse probe if test "$sparse" != "no" ; then if has cgcc; then @@ -1238,7 +1407,7 @@ fi sdl=no fi -if test -n "$cross_prefix" && test "`basename $sdlconfig`" = sdl-config; then +if test -n "$cross_prefix" && test "$(basename "$sdlconfig")" = sdl-config; then echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2 fi @@ -1305,7 +1474,7 @@ ########################################## # VNC TLS detection -if test "$vnc_tls" != "no" ; then +if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then cat > $TMPC < int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } @@ -1325,7 +1494,7 @@ ########################################## # VNC SASL detection -if test "$vnc_sasl" != "no" ; then +if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then cat > $TMPC < #include @@ -1347,7 +1516,7 @@ ########################################## # VNC JPEG detection -if test "$vnc_jpeg" != "no" ; then +if test "$vnc" = "yes" -a "$vnc_jpeg" != "no" ; then cat > $TMPC < #include @@ -1368,7 +1537,7 @@ ########################################## # VNC PNG detection -if test "$vnc_png" != "no" ; then +if test "$vnc" = "yes" -a "$vnc_png" != "no" ; then cat > $TMPC < #include @@ -1532,7 +1701,7 @@ pa) audio_drv_probe $drv pulse/simple.h "-lpulse-simple -lpulse" \ - "pa_simple *s = NULL; pa_simple_free(s); return 0;" + "pa_simple *s = 0; pa_simple_free(s); return 0;" libs_softmmu="-lpulse -lpulse-simple $libs_softmmu" audio_pt_int="yes" ;; @@ -1594,7 +1763,11 @@ ########################################## # curses probe -curses_list="-lncurses -lcurses" +if test "$mingw32" = "yes" ; then + curses_list="-lpdcurses" +else + curses_list="-lncurses -lcurses" +fi if test "$curses" != "no" ; then curses_found=no @@ -1634,7 +1807,7 @@ if test "$curl" != "no" ; then cat > $TMPC << EOF #include -int main(void) { return curl_easy_init(); } +int main(void) { curl_easy_init(); curl_multi_setopt(0, 0, 0); return 0; } EOF curl_cflags=`$curlconfig --cflags 2>/dev/null` curl_libs=`$curlconfig --libs 2>/dev/null` @@ -1691,145 +1864,17 @@ fi ########################################## -# kvm probe -if test "$kvm" != "no" ; then - cat > $TMPC < -#if !defined(KVM_API_VERSION) || KVM_API_VERSION < 12 || KVM_API_VERSION > 12 -#error Invalid KVM version -#endif -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" - ;; - ppc) - kvm_arch="powerpc" - ;; - *) - kvm_arch="$cpu" - ;; - esac - 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 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." - fi - fi - feature_not_found "kvm" - fi - kvm=no - fi -fi - -########################################## -# test for KVM_CAP_PIT - -if test "$kvm_cap_pit" != "no" ; then - if test "$kvm" = "no" -a "$kvm_cap_pit" = "yes" ; then - feature_not_found "kvm_cap_pit (kvm is not enabled)" - fi - cat > $TMPC < -#ifndef KVM_CAP_PIT -#error "kvm no pit capability" -#endif -int main(void) { return 0; } -EOF - if compile_prog "$kvm_cflags" ""; then - kvm_cap_pit=yes - else - if test "$kvm_cap_pit" = "yes" ; then - feature_not_found "kvm_cap_pit" - fi - kvm_cap_pit=no - fi -fi - -########################################## -# test for KVM_CAP_DEVICE_ASSIGNMENT - -if test "$kvm_cap_device_assignment" != "no" ; then - if test "$kvm" = "no" -a "$kvm_cap_device_assignment" = "yes" ; then - feature_not_found "kvm_cap_device_assignment (kvm is not enabled)" - fi - cat > $TMPC < -#ifndef KVM_CAP_DEVICE_ASSIGNMENT -#error "kvm no device assignment capability" -#endif -int main(void) { return 0; } -EOF - if compile_prog "$kvm_cflags" "" ; then - kvm_cap_device_assignment=yes - else - if test "$kvm_cap_device_assignment" = "yes" ; then - feature_not_found "kvm_cap_device_assigment" +# glib support probe +if test "$guest_agent" != "no" ; then + if $pkg_config --modversion glib-2.0 > /dev/null 2>&1 ; then + glib_cflags=`$pkg_config --cflags glib-2.0 2>/dev/null` + glib_libs=`$pkg_config --libs glib-2.0 2>/dev/null` + libs_softmmu="$glib_libs $libs_softmmu" + libs_tools="$glib_libs $libs_tools" + else + echo "glib-2.0 required to compile QEMU" + exit 1 fi - kvm_cap_device_assignment=no - fi fi ########################################## @@ -1854,32 +1899,6 @@ 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" @@ -1913,41 +1932,24 @@ 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 +#include int main(void) { - int (*func)(const rados_pool_t pool, uint64_t *snapid) = rados_selfmanaged_snap_create; - rados_initialize(0, NULL); + rados_t cluster; + rados_create(&cluster, 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 + rbd_libs="-lrbd -lrados" + if compile_prog "" "$rbd_libs" ; then + rbd=yes + libs_tools="$rbd_libs $libs_tools" + libs_softmmu="$rbd_libs $libs_softmmu" 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 ########################################## @@ -2028,15 +2030,36 @@ EOF if compile_prog "" "$fdt_libs" ; then fdt=yes - libs_softmmu="$fdt_libs $libs_softmmu" else if test "$fdt" = "yes" ; then feature_not_found "fdt" fi + fdt_libs= fdt=no fi fi +########################################## +# opengl probe, used by milkymist-tmu2 +if test "$opengl" != "no" ; then + opengl_libs="-lGL" + cat > $TMPC << EOF +#include +#include +#include +int main(void) { GL_VERSION; return 0; } +EOF + if compile_prog "" "-lGL" ; then + opengl=yes + else + if test "$opengl" = "yes" ; then + feature_not_found "opengl" + fi + opengl_libs= + opengl=no + fi +fi + # # Check for xxxat() functions when we are building linux-user # emulator. This is done because older glibc versions don't @@ -2178,6 +2201,12 @@ if compile_prog "" "" ; then signalfd=yes +elif test "$kvm" = "yes" -a "$io_thread" != "yes"; then + echo + echo "ERROR: Host kernel lacks signalfd() support," + echo "but KVM depends on it when the IO thread is disabled." + echo + exit 1 fi # check if eventfd is supported @@ -2257,6 +2286,59 @@ dup3=yes fi +# check for epoll support +epoll=no +cat > $TMPC << EOF +#include + +int main(void) +{ + epoll_create(0); + return 0; +} +EOF +if compile_prog "$ARCH_CFLAGS" "" ; then + epoll=yes +fi + +# epoll_create1 and epoll_pwait are later additions +# so we must check separately for their presence +epoll_create1=no +cat > $TMPC << EOF +#include + +int main(void) +{ + /* Note that we use epoll_create1 as a value, not as + * a function being called. This is necessary so that on + * old SPARC glibc versions where the function was present in + * the library but not declared in the header file we will + * fail the configure check. (Otherwise we will get a compiler + * warning but not an error, and will proceed to fail the + * qemu compile where we compile with -Werror.) + */ + epoll_create1; + return 0; +} +EOF +if compile_prog "$ARCH_CFLAGS" "" ; then + epoll_create1=yes +fi + +epoll_pwait=no +cat > $TMPC << EOF +#include + +int main(void) +{ + epoll_pwait(0, 0, 0, 0, 0); + return 0; +} +EOF +if compile_prog "$ARCH_CFLAGS" "" ; then + epoll_pwait=yes +fi + # Check if tools are available to build documentation. if test "$docs" != "no" ; then if has makeinfo && has pod2man; then @@ -2347,7 +2429,7 @@ 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 && \ + if $pkg_config --atleast-version=0.6.0 spice-server >/dev/null 2>&1 && \ compile_prog "$spice_cflags" "$spice_libs" ; then spice="yes" libs_softmmu="$libs_softmmu $spice_libs" @@ -2360,6 +2442,47 @@ fi fi +# check for libcacard for smartcard support +if test "$smartcard" != "no" ; then + smartcard="yes" + smartcard_cflags="" + # TODO - what's the minimal nss version we support? + if test "$smartcard_nss" != "no"; then + if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 ; then + smartcard_nss="yes" + smartcard_cflags="-I\$(SRC_PATH)/libcacard" + libcacard_libs=$($pkg_config --libs nss 2>/dev/null) + libcacard_cflags=$($pkg_config --cflags nss 2>/dev/null) + QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags" + LIBS="$libcacard_libs $LIBS" + else + if test "$smartcard_nss" = "yes"; then + feature_not_found "nss" + fi + smartcard_nss="no" + fi + fi +fi +if test "$smartcard" = "no" ; then + smartcard_nss="no" +fi + +# check for usbredirparser for usb network redirection support +if test "$usb_redir" != "no" ; then + if $pkg_config libusbredirparser >/dev/null 2>&1 ; then + usb_redir="yes" + usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) + usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) + QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags" + LIBS="$LIBS $usb_redir_libs" + else + if test "$usb_redir" = "yes"; then + feature_not_found "usb-redir" + fi + usb_redir="no" + fi +fi + ########################################## ########################################## @@ -2368,7 +2491,13 @@ fdatasync=no cat > $TMPC << EOF #include -int main(void) { return fdatasync(0); } +int main(void) { +#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 +return fdatasync(0); +#else +#abort Not supported +#endif +} EOF if compile_prog "" "" ; then fdatasync=yes @@ -2447,6 +2576,29 @@ fi ########################################## +# __sync_fetch_and_and requires at least -march=i486. Many toolchains +# use i686 as default anyway, but for those that don't, an explicit +# specification is necessary +if test $vhost_net = "yes" && test $cpu = "i386"; then + cat > $TMPC << EOF +int sfaa(unsigned *ptr) +{ + return __sync_fetch_and_and(ptr, 0); +} + +int main(int argc, char **argv) +{ + int val = 42; + sfaa(&val); + return val; +} +EOF + if ! compile_prog "" "" ; then + CFLAGS+="-march=i486" + fi +fi + +########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -2503,6 +2655,9 @@ tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then tools="qemu-nbd\$(EXESUF) $tools" + if [ "$guest_agent" = "yes" ]; then + tools="qemu-ga\$(EXESUF) $tools" + fi if [ "$check_utests" = "yes" ]; then tools="check-qint check-qstring check-qdict check-qlist $tools" tools="check-qfloat check-qjson $tools" @@ -2517,11 +2672,14 @@ "$softmmu" = yes ; then roms="optionrom" fi - +if test "$cpu" = "ppc64" -a "$targetos" != "Darwin" ; then + roms="$roms spapr-rtas" +fi echo "Install prefix $prefix" echo "BIOS directory `eval echo $datadir`" echo "binary directory `eval echo $bindir`" +echo "library directory `eval echo $libdir`" echo "config directory `eval echo $sysconfdir`" if test "$mingw32" = "no" ; then echo "Manual directory `eval echo $mandir`" @@ -2535,6 +2693,7 @@ echo "LDFLAGS $LDFLAGS" echo "make $make" echo "install $install" +echo "python $python" echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -2558,11 +2717,14 @@ echo "Extra audio cards $audio_card_list" echo "Block whitelist $block_drv_whitelist" 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" +echo "VNC support $vnc" +if test "$vnc" = "yes" ; then + 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" +fi if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi @@ -2596,6 +2758,10 @@ echo "spice support $spice" echo "rbd support $rbd" echo "xfsctl support $xfs" +echo "nss used $smartcard_nss" +echo "usb net redir $usb_redir" +echo "OpenGL support $opengl" +echo "build guest agent $guest_agent" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -2612,6 +2778,7 @@ echo all: >> $config_host_mak echo "prefix=$prefix" >> $config_host_mak echo "bindir=$bindir" >> $config_host_mak +echo "libdir=$libdir" >> $config_host_mak echo "mandir=$mandir" >> $config_host_mak echo "datadir=$datadir" >> $config_host_mak echo "sysconfdir=$sysconfdir" >> $config_host_mak @@ -2619,7 +2786,7 @@ 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) + i386|x86_64|alpha|cris|hppa|ia64|lm32|m68k|microblaze|mips|mips64|ppc|ppc64|s390|s390x|sparc|sparc64|unicore32) ARCH=$cpu ;; armv4b|armv4l) @@ -2715,6 +2882,9 @@ if test "$mixemu" = "yes" ; then echo "CONFIG_MIXEMU=y" >> $config_host_mak fi +if test "$vnc" = "yes" ; then + echo "CONFIG_VNC=y" >> $config_host_mak +fi if test "$vnc_tls" = "yes" ; then echo "CONFIG_VNC_TLS=y" >> $config_host_mak echo "VNC_TLS_CFLAGS=$vnc_tls_cflags" >> $config_host_mak @@ -2723,17 +2893,16 @@ 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 +if test "$vnc_jpeg" = "yes" ; 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 +if test "$vnc_png" = "yes" ; 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 +if test "$vnc_thread" = "yes" ; 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 @@ -2792,6 +2961,15 @@ if test "$dup3" = "yes" ; then echo "CONFIG_DUP3=y" >> $config_host_mak fi +if test "$epoll" = "yes" ; then + echo "CONFIG_EPOLL=y" >> $config_host_mak +fi +if test "$epoll_create1" = "yes" ; then + echo "CONFIG_EPOLL_CREATE1=y" >> $config_host_mak +fi +if test "$epoll_pwait" = "yes" ; then + echo "CONFIG_EPOLL_PWAIT=y" >> $config_host_mak +fi if test "$inotify" = "yes" ; then echo "CONFIG_INOTIFY=y" >> $config_host_mak fi @@ -2815,12 +2993,13 @@ echo "CONFIG_BLUEZ=y" >> $config_host_mak echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi +echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak if test "$xen" = "yes" ; then - echo "CONFIG_XEN=y" >> $config_host_mak + echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak + echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak 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 @@ -2873,6 +3052,22 @@ echo "CONFIG_SPICE=y" >> $config_host_mak fi +if test "$smartcard" = "yes" ; then + echo "CONFIG_SMARTCARD=y" >> $config_host_mak +fi + +if test "$smartcard_nss" = "yes" ; then + echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak +fi + +if test "$usb_redir" = "yes" ; then + echo "CONFIG_USB_REDIR=y" >> $config_host_mak +fi + +if test "$opengl" = "yes" ; then + echo "CONFIG_OPENGL=y" >> $config_host_mak +fi + # XXX: suppress that if [ "$bsd" = "yes" ] ; then echo "CONFIG_BSD=y" >> $config_host_mak @@ -2906,7 +3101,7 @@ fi # Set the appropriate trace file. if test "$trace_backend" = "simple"; then - trace_file="\"$trace_file-%u\"" + trace_file="\"$trace_file-\" FMT_pid" fi if test "$trace_backend" = "dtrace" -a "$trace_backend_stap" = "yes" ; then echo "CONFIG_SYSTEMTAP_TRACE=y" >> $config_host_mak @@ -2920,6 +3115,7 @@ 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 "PYTHON=$python" >> $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 @@ -2927,6 +3123,7 @@ echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak echo "WINDRES=$windres" >> $config_host_mak +echo "LIBTOOL=$libtool" >> $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 @@ -2971,7 +3168,7 @@ target_bigendian="no" case "$target_arch2" in - armeb|m68k|microblaze|mips|mipsn32|mips64|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus) + armeb|lm32|m68k|microblaze|mips|mipsn32|mips64|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus) target_bigendian=yes ;; esac @@ -3018,6 +3215,7 @@ mkdir -p $target_dir/fpu mkdir -p $target_dir/tcg mkdir -p $target_dir/ide +mkdir -p $target_dir/9pfs 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 @@ -3031,6 +3229,11 @@ interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_arch2/g"` echo "CONFIG_QEMU_INTERP_PREFIX=\"$interp_prefix1\"" >> $config_target_mak gdb_xml_files="" +target_short_alignment=2 +target_int_alignment=4 +target_long_alignment=4 +target_llong_alignment=8 +target_libs_softmmu= TARGET_ARCH="$target_arch2" TARGET_BASE_ARCH="" @@ -3038,17 +3241,19 @@ case "$target_arch2" in i386) - target_phys_bits=32 + target_phys_bits=64 ;; x86_64) TARGET_BASE_ARCH=i386 target_phys_bits=64 + target_long_alignment=8 ;; ia64) target_phys_bits=64 ;; alpha) target_phys_bits=64 + target_long_alignment=8 target_nptl="yes" ;; arm|armeb) @@ -3057,20 +3262,30 @@ target_nptl="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" target_phys_bits=32 + target_llong_alignment=4 ;; cris) target_nptl="yes" target_phys_bits=32 ;; + lm32) + target_phys_bits=32 + target_libs_softmmu="$opengl_libs" + ;; m68k) bflt="yes" gdb_xml_files="cf-core.xml cf-fp.xml" target_phys_bits=32 + target_int_alignment=2 + target_long_alignment=2 + target_llong_alignment=2 ;; - microblaze) + microblaze|microblazeel) + TARGET_ARCH=microblaze bflt="yes" target_nptl="yes" target_phys_bits=32 + target_libs_softmmu="$fdt_libs" ;; mips|mipsel) TARGET_ARCH=mips @@ -3089,11 +3304,13 @@ TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak target_phys_bits=64 + target_long_alignment=8 ;; ppc) gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_phys_bits=32 target_nptl="yes" + target_libs_softmmu="$fdt_libs" ;; ppcemb) TARGET_BASE_ARCH=ppc @@ -3101,12 +3318,15 @@ gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_phys_bits=64 target_nptl="yes" + target_libs_softmmu="$fdt_libs" ;; ppc64) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_phys_bits=64 + target_long_alignment=8 + target_libs_softmmu="$fdt_libs" ;; ppc64abi32) TARGET_ARCH=ppc64 @@ -3115,6 +3335,7 @@ echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_phys_bits=64 + target_libs_softmmu="$fdt_libs" ;; sh4|sh4eb) TARGET_ARCH=sh4 @@ -3128,6 +3349,7 @@ sparc64) TARGET_BASE_ARCH=sparc target_phys_bits=64 + target_long_alignment=8 ;; sparc32plus) TARGET_ARCH=sparc64 @@ -3137,13 +3359,22 @@ target_phys_bits=64 ;; s390x) + target_nptl="yes" target_phys_bits=64 + target_long_alignment=8 + ;; + unicore32) + target_phys_bits=32 ;; *) echo "Unsupported target CPU" exit 1 ;; esac +echo "TARGET_SHORT_ALIGNMENT=$target_short_alignment" >> $config_target_mak +echo "TARGET_INT_ALIGNMENT=$target_int_alignment" >> $config_target_mak +echo "TARGET_LONG_ALIGNMENT=$target_long_alignment" >> $config_target_mak +echo "TARGET_LLONG_ALIGNMENT=$target_llong_alignment" >> $config_target_mak echo "TARGET_ARCH=$TARGET_ARCH" >> $config_target_mak target_arch_name="`echo $TARGET_ARCH | tr '[:lower:]' '[:upper:]'`" echo "TARGET_$target_arch_name=y" >> $config_target_mak @@ -3160,7 +3391,11 @@ case "$target_arch2" in i386|x86_64) if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then + target_phys_bits=64 echo "CONFIG_XEN=y" >> $config_target_mak + if test "$cpu" = "i386" -o "$cpu" = "x86_64"; then + echo "CONFIG_XEN_MAPCACHE=y" >> $config_target_mak + fi fi esac case "$target_arch2" in @@ -3170,12 +3405,13 @@ \( "$target_arch2" = "$cpu" -o \ \( "$target_arch2" = "ppcemb" -a "$cpu" = "ppc" \) -o \ \( "$target_arch2" = "ppc64" -a "$cpu" = "ppc" \) -o \ + \( "$target_arch2" = "ppc" -a "$cpu" = "ppc64" \) -o \ + \( "$target_arch2" = "ppcemb" -a "$cpu" = "ppc64" \) -o \ \( "$target_arch2" = "x86_64" -a "$cpu" = "i386" \) -o \ \( "$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 + if test $vhost_net = "yes" ; then + echo "CONFIG_VHOST_NET=y" >> $config_target_mak fi if test $kvm_cap_pit = "yes" ; then echo "CONFIG_KVM_PIT=y" >> $config_target_mak @@ -3183,9 +3419,6 @@ 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 if test "$target_bigendian" = "yes" ; then @@ -3194,7 +3427,7 @@ 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 "LIBS+=$libs_softmmu $target_libs_softmmu" >> $config_target_mak echo "HWDIR=../libhw$target_phys_bits" >> $config_target_mak echo "subdir-$target: subdir-libhw$target_phys_bits" >> $config_host_mak fi @@ -3207,6 +3440,11 @@ if test "$target_darwin_user" = "yes" ; then echo "CONFIG_DARWIN_USER=y" >> $config_target_mak fi +if test "$smartcard_nss" = "yes" ; then + echo "subdir-$target: subdir-libcacard" >> $config_host_mak + echo "libcacard_libs=$libcacard_libs" >> $config_host_mak + echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak +fi list="" if test ! -z "$gdb_xml_files" ; then for x in $gdb_xml_files; do @@ -3215,15 +3453,6 @@ echo "TARGET_XML_FILES=$list" >> $config_target_mak fi -case "$target_arch2" in - i386|x86_64) - echo "CONFIG_NOSOFTFLOAT=y" >> $config_target_mak - ;; - *) - echo "CONFIG_SOFTFLOAT=y" >> $config_target_mak - ;; -esac - if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then echo "TARGET_HAS_BFLT=y" >> $config_target_mak fi @@ -3292,7 +3521,7 @@ echo "CONFIG_M68K_DIS=y" >> $config_target_mak echo "CONFIG_M68K_DIS=y" >> $libdis_config_mak ;; - microblaze) + microblaze*) echo "CONFIG_MICROBLAZE_DIS=y" >> $config_target_mak echo "CONFIG_MICROBLAZE_DIS=y" >> $libdis_config_mak ;; @@ -3331,6 +3560,9 @@ arm) cflags="-DHAS_AUDIO $cflags" ;; + lm32) + cflags="-DHAS_AUDIO $cflags" + ;; i386|mips|ppc) cflags="-DHAS_AUDIO -DHAS_AUDIO_CHOICE $cflags" ;; @@ -3377,6 +3609,23 @@ esac fi +# use included Linux headers +if test "$linux" = "yes" ; then + includes="-I\$(SRC_PATH)/linux-headers $includes" + mkdir -p linux-headers + case "$cpu" in + i386|x86_64) + symlink $source_path/linux-headers/asm-x86 linux-headers/asm + ;; + ppcemb|ppc|ppc64) + symlink $source_path/linux-headers/asm-powerpc linux-headers/asm + ;; + s390x) + symlink $source_path/linux-headers/asm-s390 linux-headers/asm + ;; + esac +fi + echo "LDFLAGS+=$ldflags" >> $config_target_mak echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak echo "QEMU_INCLUDES+=$includes" >> $config_target_mak @@ -3385,22 +3634,28 @@ # 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 pc-bios/spapr-rtas" DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS fsdev ui" +DIRS="$DIRS qapi" +DIRS="$DIRS qga" FILES="Makefile tests/Makefile" FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" +FILES="$FILES pc-bios/spapr-rtas/Makefile" 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 +for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.rom $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 + if [ -e "$source_path/$f" ] && ! [ -e "$f" ]; then + symlink "$source_path/$f" "$f" + fi done # temporary config to build submodules -for rom in seabios vgabios; do +for rom in seabios vgabios ; do config_mak=roms/$rom/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak @@ -3417,9 +3672,17 @@ mkdir -p $d mkdir -p $d/ide symlink $source_path/Makefile.hw $d/Makefile + mkdir -p $d/9pfs echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak done +if [ "$source_path" != `pwd` ]; then + # out of tree build + mkdir -p libcacard + rm -f libcacard/Makefile + symlink "$source_path/libcacard/Makefile" libcacard/Makefile +fi + d=libuser mkdir -p $d symlink $source_path/Makefile.user $d/Makefile diff -Nru qemu-kvm-0.14.1+noroms/console.c qemu-kvm-0.15.0+noroms/console.c --- qemu-kvm-0.14.1+noroms/console.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/console.c 2011-08-09 07:40:29.000000000 -0500 @@ -180,7 +180,7 @@ active_console = consoles[0]; /* There is currently no way of specifying which screen we want to dump, so always dump the first one. */ - if (consoles[0]->hw_screen_dump) + if (consoles[0] && consoles[0]->hw_screen_dump) consoles[0]->hw_screen_dump(consoles[0]->hw, filename); active_console = previous_active_console; } @@ -1135,7 +1135,7 @@ /* characters are pending: we send them a bit later (XXX: horrible, should change char device API) */ if (s->out_fifo.count > 0) { - qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1); + qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1); } } @@ -1278,38 +1278,40 @@ { 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); - + int linesize = width * 4; + qemu_alloc_display(surface, width, height, linesize, + qemu_default_pixelformat(32), 0); return surface; } static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface, int width, int height) { + int linesize = width * 4; + qemu_alloc_display(surface, width, height, linesize, + qemu_default_pixelformat(32), 0); + return surface; +} + +void qemu_alloc_display(DisplaySurface *surface, int width, int height, + int linesize, PixelFormat pf, int newflags) +{ + void *data; 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); + surface->linesize = linesize; + surface->pf = pf; + if (surface->flags & QEMU_ALLOCATED_FLAG) { + data = qemu_realloc(surface->data, + surface->linesize * surface->height); + } else { + data = qemu_malloc(surface->linesize * surface->height); + } + surface->data = (uint8_t *)data; + surface->flags = newflags | QEMU_ALLOCATED_FLAG; #ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; -#else - surface->flags = QEMU_ALLOCATED_FLAG; + surface->flags |= QEMU_BIG_ENDIAN_FLAG; #endif - - return surface; } DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, @@ -1347,8 +1349,15 @@ static void dumb_display_init(void) { DisplayState *ds = qemu_mallocz(sizeof(DisplayState)); + int width = 640; + int height = 480; + ds->allocator = &default_allocator; - ds->surface = qemu_create_displaysurface(ds, 640, 480); + if (is_fixedsize_console()) { + width = active_console->g_width; + height = active_console->g_height; + } + ds->surface = qemu_create_displaysurface(ds, width, height); register_displaystate(ds); } @@ -1457,7 +1466,7 @@ 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); + s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s); s->ds = ds; if (!color_inited) { @@ -1505,7 +1514,7 @@ chr->init(chr); } -CharDriverState *text_console_init(QemuOpts *opts) +int text_console_init(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; TextConsole *s; @@ -1537,7 +1546,7 @@ if (!s) { free(chr); - return NULL; + return -EBUSY; } s->chr = chr; @@ -1545,7 +1554,9 @@ s->g_height = height; chr->opaque = s; chr->chr_set_echo = text_console_set_echo; - return chr; + + *_chr = chr; + return 0; } void text_consoles_set_display(DisplayState *ds) diff -Nru qemu-kvm-0.14.1+noroms/console.h qemu-kvm-0.15.0+noroms/console.h --- qemu-kvm-0.14.1+noroms/console.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/console.h 2011-08-09 07:40:29.000000000 -0500 @@ -4,6 +4,8 @@ #include "qemu-char.h" #include "qdict.h" #include "notify.h" +#include "qerror.h" +#include "monitor.h" /* keyboard/mouse support */ @@ -189,6 +191,8 @@ DisplayState *get_displaystate(void); DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, int linesize, uint8_t *data); +void qemu_alloc_display(DisplaySurface *surface, int width, int height, + int linesize, PixelFormat pf, int newflags); PixelFormat qemu_different_endianness_pixelformat(int bpp); PixelFormat qemu_default_pixelformat(int bpp); @@ -350,7 +354,7 @@ int is_graphic_console(void); int is_fixedsize_console(void); -CharDriverState *text_console_init(QemuOpts *opts); +int text_console_init(QemuOpts *opts, CharDriverState **_chr); void text_consoles_set_display(DisplayState *ds); void console_select(unsigned int index); void console_color_init(DisplayState *ds); @@ -368,12 +372,33 @@ void vnc_display_init(DisplayState *ds); void vnc_display_close(DisplayState *ds); int vnc_display_open(DisplayState *ds, const char *display); -int vnc_display_password(DisplayState *ds, const char *password); +void vnc_display_add_client(DisplayState *ds, int csock, int skipauth); int vnc_display_disable_login(DisplayState *ds); +char *vnc_display_local_addr(DisplayState *ds); +#ifdef CONFIG_VNC +int vnc_display_password(DisplayState *ds, const char *password); 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); +#else +static inline int vnc_display_password(DisplayState *ds, const char *password) +{ + qerror_report(QERR_FEATURE_DISABLED, "vnc"); + return -ENODEV; +} +static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires) +{ + qerror_report(QERR_FEATURE_DISABLED, "vnc"); + return -ENODEV; +}; +static inline void do_info_vnc(Monitor *mon, QObject **ret_data) +{ +}; +static inline void do_info_vnc_print(Monitor *mon, const QObject *data) +{ + monitor_printf(mon, "VNC support disabled\n"); +}; +#endif /* curses.c */ void curses_display_init(DisplayState *ds, int full_screen); diff -Nru qemu-kvm-0.14.1+noroms/cpu-all.h qemu-kvm-0.15.0+noroms/cpu-all.h --- qemu-kvm-0.14.1+noroms/cpu-all.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cpu-all.h 2011-08-09 07:40:29.000000000 -0500 @@ -123,8 +123,7 @@ endian ! */ typedef union { float64 d; -#if defined(HOST_WORDS_BIGENDIAN) \ - || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT)) +#if defined(HOST_WORDS_BIGENDIAN) struct { uint32_t upper; uint32_t lower; @@ -138,11 +137,17 @@ uint64_t ll; } CPU_DoubleU; -#ifdef TARGET_SPARC +typedef union { + floatx80 d; + struct { + uint64_t lower; + uint16_t upper; + } l; +} CPU_LDoubleU; + typedef union { float128 q; -#if defined(HOST_WORDS_BIGENDIAN) \ - || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT)) +#if defined(HOST_WORDS_BIGENDIAN) struct { uint32_t upmost; uint32_t upper; @@ -166,7 +171,6 @@ } ll; #endif } CPU_QuadU; -#endif /* CPU memory access without any memory or io remapping */ @@ -777,25 +781,73 @@ extern CPUState *first_cpu; extern CPUState *cpu_single_env; -#define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */ -#define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */ -#define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */ -#define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */ -#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */ -#define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */ -#define CPU_INTERRUPT_DEBUG 0x80 /* Debug event occured. */ -#define CPU_INTERRUPT_VIRQ 0x100 /* virtual interrupt pending. */ -#define CPU_INTERRUPT_NMI 0x200 /* NMI pending. */ -#define CPU_INTERRUPT_INIT 0x400 /* INIT pending. */ -#define CPU_INTERRUPT_SIPI 0x800 /* SIPI pending. */ -#define CPU_INTERRUPT_MCE 0x1000 /* (x86 only) MCE pending. */ +/* Flags for use in ENV->INTERRUPT_PENDING. + + The numbers assigned here are non-sequential in order to preserve + binary compatibility with the vmstate dump. Bit 0 (0x0001) was + previously used for CPU_INTERRUPT_EXIT, and is cleared when loading + the vmstate dump. */ + +/* External hardware interrupt pending. This is typically used for + interrupts from devices. */ +#define CPU_INTERRUPT_HARD 0x0002 + +/* Exit the current TB. This is typically used when some system-level device + makes some change to the memory mapping. E.g. the a20 line change. */ +#define CPU_INTERRUPT_EXITTB 0x0004 + +/* Halt the CPU. */ +#define CPU_INTERRUPT_HALT 0x0020 + +/* Debug event pending. */ +#define CPU_INTERRUPT_DEBUG 0x0080 + +/* Several target-specific external hardware interrupts. Each target/cpu.h + should define proper names based on these defines. */ +#define CPU_INTERRUPT_TGT_EXT_0 0x0008 +#define CPU_INTERRUPT_TGT_EXT_1 0x0010 +#define CPU_INTERRUPT_TGT_EXT_2 0x0040 +#define CPU_INTERRUPT_TGT_EXT_3 0x0200 +#define CPU_INTERRUPT_TGT_EXT_4 0x1000 + +/* Several target-specific internal interrupts. These differ from the + preceeding target-specific interrupts in that they are intended to + originate from within the cpu itself, typically in response to some + instruction being executed. These, therefore, are not masked while + single-stepping within the debugger. */ +#define CPU_INTERRUPT_TGT_INT_0 0x0100 +#define CPU_INTERRUPT_TGT_INT_1 0x0400 +#define CPU_INTERRUPT_TGT_INT_2 0x0800 + +/* First unused bit: 0x2000. */ + +/* The set of all bits that should be masked when single-stepping. */ +#define CPU_INTERRUPT_SSTEP_MASK \ + (CPU_INTERRUPT_HARD \ + | CPU_INTERRUPT_TGT_EXT_0 \ + | CPU_INTERRUPT_TGT_EXT_1 \ + | CPU_INTERRUPT_TGT_EXT_2 \ + | CPU_INTERRUPT_TGT_EXT_3 \ + | CPU_INTERRUPT_TGT_EXT_4) + +#ifndef CONFIG_USER_ONLY +typedef void (*CPUInterruptHandler)(CPUState *, int); + +extern CPUInterruptHandler cpu_interrupt_handler; + +static inline void cpu_interrupt(CPUState *s, int mask) +{ + cpu_interrupt_handler(s, mask); +} +#else /* USER_ONLY */ +void cpu_interrupt(CPUState *env, int mask); +#endif /* USER_ONLY */ -void cpu_interrupt(CPUState *s, int mask); void cpu_reset_interrupt(CPUState *env, int mask); void cpu_exit(CPUState *s); -int qemu_cpu_has_work(CPUState *env); +bool qemu_cpu_has_work(CPUState *env); /* Breakpoint/watchpoint flags */ #define BP_MEM_READ 0x01 @@ -863,10 +915,14 @@ extern int phys_ram_fd; extern ram_addr_t ram_size; +/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */ +#define RAM_PREALLOC_MASK (1 << 0) + typedef struct RAMBlock { uint8_t *host; ram_addr_t offset; ram_addr_t length; + uint32_t flags; char idstr[256]; QLIST_ENTRY(RAMBlock) next; #if defined(__linux__) && !defined(TARGET_S390X) @@ -959,14 +1015,16 @@ int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr); +int cpu_physical_log_start(target_phys_addr_t start_addr, + ram_addr_t size); + +int cpu_physical_log_stop(target_phys_addr_t start_addr, + ram_addr_t size); + void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); #endif /* !CONFIG_USER_ONLY */ 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, - int broadcast); - #endif /* CPU_ALL_H */ diff -Nru qemu-kvm-0.14.1+noroms/cpu-common.h qemu-kvm-0.15.0+noroms/cpu-common.h --- qemu-kvm-0.14.1+noroms/cpu-common.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cpu-common.h 2011-08-09 07:40:29.000000000 -0500 @@ -34,10 +34,21 @@ typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value); typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr); -void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, - ram_addr_t size, - ram_addr_t phys_offset, - ram_addr_t region_offset); +void cpu_register_physical_memory_log(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset, + ram_addr_t region_offset, + bool log_dirty); + +static inline void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset, + ram_addr_t region_offset) +{ + cpu_register_physical_memory_log(start_addr, size, phys_offset, + region_offset, false); +} + static inline void cpu_register_physical_memory(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset) @@ -49,13 +60,16 @@ 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); +void qemu_ram_free_from_ptr(ram_addr_t addr); +void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); /* This should only be used for ram local to a device. */ void *qemu_get_ram_ptr(ram_addr_t addr); +void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size); /* Same but slower, to use for migration, where the order of * RAMBlocks must not change. */ void *qemu_safe_ram_ptr(ram_addr_t addr); +void qemu_put_ram_ptr(void *addr); /* This should not be used by devices. */ int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr); ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr); @@ -68,14 +82,14 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, int len, int is_write); static inline void cpu_physical_memory_read(target_phys_addr_t addr, - uint8_t *buf, int len) + void *buf, int len) { cpu_physical_memory_rw(addr, buf, len, 0); } static inline void cpu_physical_memory_write(target_phys_addr_t addr, - const uint8_t *buf, int len) + const void *buf, int len) { - cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1); + cpu_physical_memory_rw(addr, (void *)buf, len, 1); } void *cpu_physical_memory_map(target_phys_addr_t addr, target_phys_addr_t *plen, @@ -91,12 +105,17 @@ void (*set_memory)(struct CPUPhysMemoryClient *client, target_phys_addr_t start_addr, ram_addr_t size, - ram_addr_t phys_offset); + ram_addr_t phys_offset, + bool log_dirty); 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); + int (*log_start)(struct CPUPhysMemoryClient *client, + target_phys_addr_t phys_addr, ram_addr_t size); + int (*log_stop)(struct CPUPhysMemoryClient *client, + target_phys_addr_t phys_addr, ram_addr_t size); QLIST_ENTRY(CPUPhysMemoryClient) list; }; @@ -115,15 +134,30 @@ void qemu_flush_coalesced_mmio_buffer(void); uint32_t ldub_phys(target_phys_addr_t addr); +uint32_t lduw_le_phys(target_phys_addr_t addr); +uint32_t lduw_be_phys(target_phys_addr_t addr); +uint32_t ldl_le_phys(target_phys_addr_t addr); +uint32_t ldl_be_phys(target_phys_addr_t addr); +uint64_t ldq_le_phys(target_phys_addr_t addr); +uint64_t ldq_be_phys(target_phys_addr_t addr); +void stb_phys(target_phys_addr_t addr, uint32_t val); +void stw_le_phys(target_phys_addr_t addr, uint32_t val); +void stw_be_phys(target_phys_addr_t addr, uint32_t val); +void stl_le_phys(target_phys_addr_t addr, uint32_t val); +void stl_be_phys(target_phys_addr_t addr, uint32_t val); +void stq_le_phys(target_phys_addr_t addr, uint64_t val); +void stq_be_phys(target_phys_addr_t addr, uint64_t val); + +#ifdef NEED_CPU_H uint32_t lduw_phys(target_phys_addr_t addr); uint32_t ldl_phys(target_phys_addr_t addr); uint64_t ldq_phys(target_phys_addr_t addr); void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val); void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val); -void stb_phys(target_phys_addr_t addr, uint32_t val); void stw_phys(target_phys_addr_t addr, uint32_t val); void stl_phys(target_phys_addr_t addr, uint32_t val); void stq_phys(target_phys_addr_t addr, uint64_t val); +#endif void cpu_physical_memory_write_rom(target_phys_addr_t addr, const uint8_t *buf, int len); diff -Nru qemu-kvm-0.14.1+noroms/cpu-defs.h qemu-kvm-0.15.0+noroms/cpu-defs.h --- qemu-kvm-0.14.1+noroms/cpu-defs.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cpu-defs.h 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ #include #include #include -#include #include "osdep.h" #include "qemu-queue.h" #include "targphys.h" @@ -38,16 +37,22 @@ #define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8) +typedef int16_t target_short __attribute__ ((aligned(TARGET_SHORT_ALIGNMENT))); +typedef uint16_t target_ushort __attribute__((aligned(TARGET_SHORT_ALIGNMENT))); +typedef int32_t target_int __attribute__((aligned(TARGET_INT_ALIGNMENT))); +typedef uint32_t target_uint __attribute__((aligned(TARGET_INT_ALIGNMENT))); +typedef int64_t target_llong __attribute__((aligned(TARGET_LLONG_ALIGNMENT))); +typedef uint64_t target_ullong __attribute__((aligned(TARGET_LLONG_ALIGNMENT))); /* target_ulong is the type of a virtual address */ #if TARGET_LONG_SIZE == 4 -typedef int32_t target_long; -typedef uint32_t target_ulong; +typedef int32_t target_long __attribute__((aligned(TARGET_LONG_ALIGNMENT))); +typedef uint32_t target_ulong __attribute__((aligned(TARGET_LONG_ALIGNMENT))); #define TARGET_FMT_lx "%08x" #define TARGET_FMT_ld "%d" #define TARGET_FMT_lu "%u" #elif TARGET_LONG_SIZE == 8 -typedef int64_t target_long; -typedef uint64_t target_ulong; +typedef int64_t target_long __attribute__((aligned(TARGET_LONG_ALIGNMENT))); +typedef uint64_t target_ulong __attribute__((aligned(TARGET_LONG_ALIGNMENT))); #define TARGET_FMT_lx "%016" PRIx64 #define TARGET_FMT_ld "%" PRId64 #define TARGET_FMT_lu "%" PRIu64 @@ -148,15 +153,6 @@ QTAILQ_ENTRY(CPUWatchpoint) entry; } CPUWatchpoint; -/* forward decleration */ -struct qemu_work_item; - -struct KVMCPUState { - pthread_t thread; - int signalled; - struct qemu_work_item *queued_work_first, *queued_work_last; -}; - #define CPU_TEMP_BUF_NLONGS 128 #define CPU_COMMON \ struct TranslationBlock *current_tb; /* currently executing TB */ \ @@ -207,7 +203,7 @@ int nr_cores; /* number of cores within this CPU package */ \ int nr_threads;/* number of threads within this CPU */ \ int running; /* Nonzero if cpu is currently running(usermode). */ \ - int thread_id; \ + int thread_id; \ /* user data */ \ void *opaque; \ \ @@ -216,12 +212,12 @@ uint32_t stopped; /* Artificially stopped */ \ struct QemuThread *thread; \ struct QemuCond *halt_cond; \ + int thread_kicked; \ 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; \ - int kvm_vcpu_dirty; \ - struct KVMCPUState kvm_cpu_state; + int kvm_vcpu_dirty; #endif diff -Nru qemu-kvm-0.14.1+noroms/cpu-exec.c qemu-kvm-0.15.0+noroms/cpu-exec.c --- qemu-kvm-0.14.1+noroms/cpu-exec.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cpu-exec.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,49 +17,23 @@ * License along with this library; if not, see . */ #include "config.h" -#include "exec.h" +#include "cpu.h" #include "disas.h" #if !defined(TARGET_IA64) #include "tcg.h" #endif -#include "kvm.h" #include "qemu-barrier.h" -#if !defined(CONFIG_SOFTMMU) -#undef EAX -#undef ECX -#undef EDX -#undef EBX -#undef ESP -#undef EBP -#undef ESI -#undef EDI -#undef EIP -#include -#ifdef __linux__ -#include -#endif -#endif - -#include "qemu-kvm.h" - -#if defined(__sparc__) && !defined(CONFIG_SOLARIS) -// Work around ugly bugs in glibc that mangle global register contents -#undef env -#define env cpu_single_env -#endif - int tb_invalidated_flag; //#define CONFIG_DEBUG_EXEC -//#define DEBUG_SIGNAL -int qemu_cpu_has_work(CPUState *env) +bool qemu_cpu_has_work(CPUState *env) { return cpu_has_work(env); } -void cpu_loop_exit(void) +void cpu_loop_exit(CPUState *env) { env->current_tb = NULL; longjmp(env->jmp_env, 1); @@ -68,41 +42,20 @@ /* exit the current TB from a signal handler. The host registers are restored in a state compatible with the CPU emulator */ -void cpu_resume_from_signal(CPUState *env1, void *puc) +#if defined(CONFIG_SOFTMMU) +void cpu_resume_from_signal(CPUState *env, void *puc) { -#if !defined(CONFIG_SOFTMMU) -#ifdef __linux__ - struct ucontext *uc = puc; -#elif defined(__OpenBSD__) - struct sigcontext *uc = puc; -#endif -#endif - - env = env1; - /* XXX: restore cpu registers saved in host registers */ -#if !defined(CONFIG_SOFTMMU) - 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 - } -#endif env->exception_index = -1; longjmp(env->jmp_env, 1); } +#endif /* Execute the code without caching the generated code. An interpreter could be used if available. */ -static void cpu_exec_nocache(int max_cycles, TranslationBlock *orig_tb) +static void cpu_exec_nocache(CPUState *env, int max_cycles, + TranslationBlock *orig_tb) { unsigned long next_tb; TranslationBlock *tb; @@ -116,7 +69,7 @@ max_cycles); env->current_tb = tb; /* execute the generated code */ - next_tb = tcg_qemu_tb_exec(tb->tc_ptr); + next_tb = tcg_qemu_tb_exec(env, tb->tc_ptr); env->current_tb = NULL; if ((next_tb & 3) == 2) { @@ -128,7 +81,8 @@ tb_free(tb); } -static TranslationBlock *tb_find_slow(target_ulong pc, +static TranslationBlock *tb_find_slow(CPUState *env, + target_ulong pc, target_ulong cs_base, uint64_t flags) { @@ -182,7 +136,7 @@ return tb; } -static inline TranslationBlock *tb_find_fast(void) +static inline TranslationBlock *tb_find_fast(CPUState *env) { TranslationBlock *tb; target_ulong cs_base, pc; @@ -195,7 +149,7 @@ tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || tb->flags != flags)) { - tb = tb_find_slow(pc, cs_base, flags); + tb = tb_find_slow(env, pc, cs_base, flags); } return tb; } @@ -214,51 +168,47 @@ { CPUWatchpoint *wp; - if (!env->watchpoint_hit) - QTAILQ_FOREACH(wp, &env->watchpoints, entry) + if (!env->watchpoint_hit) { + QTAILQ_FOREACH(wp, &env->watchpoints, entry) { wp->flags &= ~BP_WATCHPOINT_HIT; - - if (debug_excp_handler) + } + } + if (debug_excp_handler) { debug_excp_handler(env); + } } /* main execution loop */ volatile sig_atomic_t exit_request; -int cpu_exec(CPUState *env1) +int cpu_exec(CPUState *env) { - volatile host_reg_t saved_env_reg; int ret, interrupt_request; TranslationBlock *tb; uint8_t *tc_ptr; unsigned long next_tb; - if (cpu_halted(env1) == EXCP_HALTED) - return EXCP_HALTED; + if (env->halted) { + if (!cpu_has_work(env)) { + return EXCP_HALTED; + } - cpu_single_env = env1; + env->halted = 0; + } - /* 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; + cpu_single_env = env; if (unlikely(exit_request)) { env->exit_request = 1; } #if defined(TARGET_I386) - if (!kvm_enabled()) { - /* put eflags in CPU temporary format */ - CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - DF = 1 - (2 * ((env->eflags >> 10) & 1)); - CC_OP = CC_OP_EFLAGS; - env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); - } + /* put eflags in CPU temporary format */ + CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((env->eflags >> 10) & 1)); + CC_OP = CC_OP_EFLAGS; + env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); #elif defined(TARGET_SPARC) #elif defined(TARGET_M68K) env->cc_op = CC_OP_FLAGS; @@ -266,7 +216,9 @@ env->cc_x = (env->sr >> 4) & 1; #elif defined(TARGET_ALPHA) #elif defined(TARGET_ARM) +#elif defined(TARGET_UNICORE32) #elif defined(TARGET_PPC) +#elif defined(TARGET_LM32) #elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) #elif defined(TARGET_SH4) @@ -282,18 +234,14 @@ /* prepare setjmp context for exception handling */ for(;;) { if (setjmp(env->jmp_env) == 0) { -#if defined(__sparc__) && !defined(CONFIG_SOLARIS) -#undef env - env = cpu_single_env; -#define env cpu_single_env -#endif /* if an exception is pending, we execute it here */ if (env->exception_index >= 0) { if (env->exception_index >= EXCP_INTERRUPT) { /* exit request from the cpu execution loop */ ret = env->exception_index; - if (ret == EXCP_DEBUG) + if (ret == EXCP_DEBUG) { cpu_handle_debug_exception(env); + } break; } else { #if defined(CONFIG_USER_ONLY) @@ -301,107 +249,64 @@ which will be handled outside the cpu execution loop */ #if defined(TARGET_I386) - do_interrupt_user(env->exception_index, - env->exception_is_int, - env->error_code, - env->exception_next_eip); - /* successfully delivered */ - env->old_exception = -1; + do_interrupt(env); #endif ret = env->exception_index; break; #else -#if defined(TARGET_I386) - /* simulate a real cpu exception. On i386, it can - trigger new exceptions, but we do not handle - double or triple faults yet. */ - do_interrupt(env->exception_index, - env->exception_is_int, - env->error_code, - env->exception_next_eip, 0); - /* successfully delivered */ - env->old_exception = -1; -#elif defined(TARGET_PPC) - do_interrupt(env); -#elif defined(TARGET_MICROBLAZE) - do_interrupt(env); -#elif defined(TARGET_MIPS) - do_interrupt(env); -#elif defined(TARGET_SPARC) - do_interrupt(env); -#elif defined(TARGET_ARM) - do_interrupt(env); -#elif defined(TARGET_SH4) - do_interrupt(env); -#elif defined(TARGET_ALPHA) do_interrupt(env); -#elif defined(TARGET_CRIS) - do_interrupt(env); -#elif defined(TARGET_M68K) - do_interrupt(0); -#elif defined(TARGET_IA64) - do_interrupt(env); -#endif env->exception_index = -1; #endif } } - if (kvm_enabled()) { - kvm_cpu_exec(env); - longjmp(env->jmp_env, 1); - } - next_tb = 0; /* force lookup of first TB */ for(;;) { interrupt_request = env->interrupt_request; if (unlikely(interrupt_request)) { if (unlikely(env->singlestep_enabled & SSTEP_NOIRQ)) { /* Mask out external interrupts for this step. */ - interrupt_request &= ~(CPU_INTERRUPT_HARD | - CPU_INTERRUPT_FIQ | - CPU_INTERRUPT_SMI | - CPU_INTERRUPT_NMI); + interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; } if (interrupt_request & CPU_INTERRUPT_DEBUG) { env->interrupt_request &= ~CPU_INTERRUPT_DEBUG; env->exception_index = EXCP_DEBUG; - cpu_loop_exit(); + cpu_loop_exit(env); } #if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \ defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \ - defined(TARGET_MICROBLAZE) + defined(TARGET_MICROBLAZE) || defined(TARGET_LM32) || defined(TARGET_UNICORE32) if (interrupt_request & CPU_INTERRUPT_HALT) { env->interrupt_request &= ~CPU_INTERRUPT_HALT; env->halted = 1; env->exception_index = EXCP_HLT; - cpu_loop_exit(); + cpu_loop_exit(env); } #endif #if defined(TARGET_I386) if (interrupt_request & CPU_INTERRUPT_INIT) { - svm_check_intercept(SVM_EXIT_INIT); + svm_check_intercept(env, SVM_EXIT_INIT); do_cpu_init(env); env->exception_index = EXCP_HALTED; - cpu_loop_exit(); + cpu_loop_exit(env); } else if (interrupt_request & CPU_INTERRUPT_SIPI) { do_cpu_sipi(env); } else if (env->hflags2 & HF2_GIF_MASK) { if ((interrupt_request & CPU_INTERRUPT_SMI) && !(env->hflags & HF_SMM_MASK)) { - svm_check_intercept(SVM_EXIT_SMI); + svm_check_intercept(env, SVM_EXIT_SMI); env->interrupt_request &= ~CPU_INTERRUPT_SMI; - do_smm_enter(); + do_smm_enter(env); next_tb = 0; } else if ((interrupt_request & CPU_INTERRUPT_NMI) && !(env->hflags2 & HF2_NMI_MASK)) { env->interrupt_request &= ~CPU_INTERRUPT_NMI; env->hflags2 |= HF2_NMI_MASK; - do_interrupt(EXCP02_NMI, 0, 0, 0, 1); + do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); next_tb = 0; } else if (interrupt_request & CPU_INTERRUPT_MCE) { env->interrupt_request &= ~CPU_INTERRUPT_MCE; - do_interrupt(EXCP12_MCHK, 0, 0, 0, 0); + do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); next_tb = 0; } else if ((interrupt_request & CPU_INTERRUPT_HARD) && (((env->hflags2 & HF2_VINTR_MASK) && @@ -410,16 +315,11 @@ (env->eflags & IF_MASK && !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { int intno; - svm_check_intercept(SVM_EXIT_INTR); + svm_check_intercept(env, SVM_EXIT_INTR); env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); intno = cpu_get_pic_interrupt(env); qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing hardware INT=0x%02x\n", intno); -#if defined(__sparc__) && !defined(CONFIG_SOLARIS) -#undef env - env = cpu_single_env; -#define env cpu_single_env -#endif - do_interrupt(intno, 0, 0, 0, 1); + do_interrupt_x86_hardirq(env, intno, 1); /* ensure that no TB jump will be modified as the program flow was changed */ next_tb = 0; @@ -429,10 +329,10 @@ !(env->hflags & HF_INHIBIT_IRQ_MASK)) { int intno; /* FIXME: this should respect TPR */ - svm_check_intercept(SVM_EXIT_VINTR); + svm_check_intercept(env, SVM_EXIT_VINTR); intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno); - do_interrupt(intno, 0, 0, 0, 1); + do_interrupt_x86_hardirq(env, intno, 1); env->interrupt_request &= ~CPU_INTERRUPT_VIRQ; next_tb = 0; #endif @@ -450,6 +350,13 @@ env->interrupt_request &= ~CPU_INTERRUPT_HARD; next_tb = 0; } +#elif defined(TARGET_LM32) + if ((interrupt_request & CPU_INTERRUPT_HARD) + && (env->ie & IE_IE)) { + env->exception_index = EXCP_IRQ; + do_interrupt(env); + next_tb = 0; + } #elif defined(TARGET_MICROBLAZE) if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->sregs[SR_MSR] & MSR_IE) @@ -483,9 +390,6 @@ next_tb = 0; } } - } else if (interrupt_request & CPU_INTERRUPT_TIMER) { - //do_interrupt(0, 0, 0, 0, 0); - env->interrupt_request &= ~CPU_INTERRUPT_TIMER; } #elif defined(TARGET_ARM) if (interrupt_request & CPU_INTERRUPT_FIQ @@ -500,7 +404,7 @@ jump normally, then does the exception return when the CPU tries to execute code at the magic address. This will cause the magic PC value to be pushed to - the stack if an interrupt occured at the wrong time. + the stack if an interrupt occurred at the wrong time. We avoid this by disabling interrupts when pc contains a magic address. */ if (interrupt_request & CPU_INTERRUPT_HARD @@ -510,16 +414,49 @@ do_interrupt(env); next_tb = 0; } -#elif defined(TARGET_SH4) - if (interrupt_request & CPU_INTERRUPT_HARD) { +#elif defined(TARGET_UNICORE32) + if (interrupt_request & CPU_INTERRUPT_HARD + && !(env->uncached_asr & ASR_I)) { do_interrupt(env); next_tb = 0; } -#elif defined(TARGET_ALPHA) +#elif defined(TARGET_SH4) if (interrupt_request & CPU_INTERRUPT_HARD) { do_interrupt(env); next_tb = 0; } +#elif defined(TARGET_ALPHA) + { + int idx = -1; + /* ??? This hard-codes the OSF/1 interrupt levels. */ + switch (env->pal_mode ? 7 : env->ps & PS_INT_MASK) { + case 0 ... 3: + if (interrupt_request & CPU_INTERRUPT_HARD) { + idx = EXCP_DEV_INTERRUPT; + } + /* FALLTHRU */ + case 4: + if (interrupt_request & CPU_INTERRUPT_TIMER) { + idx = EXCP_CLK_INTERRUPT; + } + /* FALLTHRU */ + case 5: + if (interrupt_request & CPU_INTERRUPT_SMP) { + idx = EXCP_SMP_INTERRUPT; + } + /* FALLTHRU */ + case 6: + if (interrupt_request & CPU_INTERRUPT_MCHK) { + idx = EXCP_MCHK; + } + } + if (idx >= 0) { + env->exception_index = idx; + env->error_code = 0; + do_interrupt(env); + next_tb = 0; + } + } #elif defined(TARGET_CRIS) if (interrupt_request & CPU_INTERRUPT_HARD && (env->pregs[PR_CCS] & I_FLAG) @@ -544,11 +481,17 @@ provide/save the vector when the interrupt is first signalled. */ env->exception_index = env->pending_vector; - do_interrupt(1); + do_interrupt_m68k_hardirq(env); + next_tb = 0; + } +#elif defined(TARGET_S390X) && !defined(CONFIG_USER_ONLY) + if ((interrupt_request & CPU_INTERRUPT_HARD) && + (env->psw.mask & PSW_MASK_EXT)) { + do_interrupt(env); next_tb = 0; } #endif - /* Don't use the cached interupt_request value, + /* Don't use the cached interrupt_request value, do_interrupt may have updated the EXITTB flag. */ if (env->interrupt_request & CPU_INTERRUPT_EXITTB) { env->interrupt_request &= ~CPU_INTERRUPT_EXITTB; @@ -560,13 +503,14 @@ if (unlikely(env->exit_request)) { env->exit_request = 0; env->exception_index = EXCP_INTERRUPT; - cpu_loop_exit(); + cpu_loop_exit(env); } #if defined(DEBUG_DISAS) || defined(CONFIG_DEBUG_EXEC) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { /* restore flags in standard format */ #if defined(TARGET_I386) - env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); + env->eflags = env->eflags | cpu_cc_compute_all(env, 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_M68K) @@ -581,7 +525,7 @@ } #endif /* DEBUG_DISAS || CONFIG_DEBUG_EXEC */ spin_lock(&tb_lock); - tb = tb_find_fast(); + tb = tb_find_fast(env); /* Note: we do it here to avoid a gcc bug on Mac OS X when doing it in tb_find_slow */ if (tb_invalidated_flag) { @@ -613,12 +557,7 @@ if (likely(!env->exit_request)) { tc_ptr = tb->tc_ptr; /* execute the generated code */ -#if defined(__sparc__) && !defined(CONFIG_SOLARIS) -#undef env - env = cpu_single_env; -#define env cpu_single_env -#endif - next_tb = tcg_qemu_tb_exec(tc_ptr); + next_tb = tcg_qemu_tb_exec(env, tc_ptr); if ((next_tb & 3) == 2) { /* Instruction counter expired. */ int insns_left; @@ -639,11 +578,11 @@ } else { if (insns_left > 0) { /* Execute remaining instructions. */ - cpu_exec_nocache(insns_left, tb); + cpu_exec_nocache(env, insns_left, tb); } env->exception_index = EXCP_INTERRUPT; next_tb = 0; - cpu_loop_exit(); + cpu_loop_exit(env); } } } @@ -651,17 +590,24 @@ /* reset soft MMU for next block (it can currently only be set by a memory fault) */ } /* for(;;) */ + } else { + /* Reload env after longjmp - the compiler may have smashed all + * local variables as longjmp is marked 'noreturn'. */ + env = cpu_single_env; } } /* for(;;) */ #if defined(TARGET_I386) /* restore flags in standard format */ - env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); + env->eflags = env->eflags | cpu_cc_compute_all(env, CC_OP) + | (DF & DF_MASK); #elif defined(TARGET_ARM) /* XXX: Save/restore host fpu exception state?. */ +#elif defined(TARGET_UNICORE32) #elif defined(TARGET_SPARC) #elif defined(TARGET_PPC) +#elif defined(TARGET_LM32) #elif defined(TARGET_M68K) cpu_m68k_flush_flags(env, env->cc_op); env->cc_op = CC_OP_FLAGS; @@ -679,601 +625,7 @@ #error unsupported target CPU #endif - /* restore global registers */ - barrier(); - env = (void *) saved_env_reg; - /* fail safe : never use cpu_single_env outside cpu_exec() */ cpu_single_env = NULL; return ret; } - -/* must only be called from the generated code as an exception can be - generated */ -void tb_invalidate_page_range(target_ulong start, target_ulong end) -{ - /* XXX: cannot enable it yet because it yields to MMU exception - where NIP != read address on PowerPC */ -#if 0 - target_ulong phys_addr; - phys_addr = get_phys_addr_code(env, start); - tb_invalidate_phys_page_range(phys_addr, phys_addr + end - start, 0); -#endif -} - -#if defined(TARGET_I386) && defined(CONFIG_USER_ONLY) - -void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) -{ - CPUX86State *saved_env; - - saved_env = env; - env = s; - if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { - selector &= 0xffff; - cpu_x86_load_seg_cache(env, seg_reg, selector, - (selector << 4), 0xffff, 0); - } else { - helper_load_seg(seg_reg, selector); - } - env = saved_env; -} - -void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32) -{ - CPUX86State *saved_env; - - saved_env = env; - env = s; - - helper_fsave(ptr, data32); - - env = saved_env; -} - -void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32) -{ - CPUX86State *saved_env; - - saved_env = env; - env = s; - - helper_frstor(ptr, data32); - - env = saved_env; -} - -#endif /* TARGET_I386 */ - -#if !defined(CONFIG_SOFTMMU) - -#if defined(TARGET_I386) -#define EXCEPTION_ACTION raise_exception_err(env->exception_index, env->error_code) -#else -#define EXCEPTION_ACTION cpu_loop_exit() -#endif - -/* 'pc' is the host PC at which the exception was raised. 'address' is - the effective address of the memory exception. 'is_write' is 1 if a - write caused the exception and otherwise 0'. 'old_set' is the - signal set which should be restored */ -static inline int handle_cpu_signal(unsigned long pc, unsigned long address, - int is_write, sigset_t *old_set, - void *puc) -{ - TranslationBlock *tb; - int ret; - - if (cpu_single_env) - env = cpu_single_env; /* XXX: find a correct solution for multithread */ -#if defined(DEBUG_SIGNAL) - qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", - pc, address, is_write, *(unsigned long *)old_set); -#endif - /* XXX: locking issue */ - if (is_write && page_unprotect(h2g(address), pc, puc)) { - return 1; - } - - /* see if it is an MMU fault */ - ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); - if (ret < 0) - return 0; /* not an MMU fault */ - if (ret == 0) - return 1; /* the MMU fault was handled without causing real CPU fault */ - /* now we have a real cpu fault */ - 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, puc); - } - - /* we restore the process signal mask as the sigreturn should - do it (XXX: use sigsetjmp) */ - sigprocmask(SIG_SETMASK, old_set, NULL); - EXCEPTION_ACTION; - - /* never comes here */ - return 1; -} - -#if defined(__i386__) - -#if defined(__APPLE__) -# include - -# define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext->ss.eip)) -# define TRAP_sig(context) ((context)->uc_mcontext->es.trapno) -# define ERROR_sig(context) ((context)->uc_mcontext->es.err) -# define MASK_sig(context) ((context)->uc_sigmask) -#elif defined (__NetBSD__) -# include - -# define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP]) -# define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) -# define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) -# define MASK_sig(context) ((context)->uc_sigmask) -#elif defined (__FreeBSD__) || defined(__DragonFly__) -# include - -# define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_eip)) -# define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) -# define ERROR_sig(context) ((context)->uc_mcontext.mc_err) -# define MASK_sig(context) ((context)->uc_sigmask) -#elif defined(__OpenBSD__) -# define EIP_sig(context) ((context)->sc_eip) -# define TRAP_sig(context) ((context)->sc_trapno) -# define ERROR_sig(context) ((context)->sc_err) -# define MASK_sig(context) ((context)->sc_mask) -#else -# define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP]) -# define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) -# define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) -# define MASK_sig(context) ((context)->uc_sigmask) -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; -#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__) - ucontext_t *uc = puc; -#elif defined(__OpenBSD__) - struct sigcontext *uc = puc; -#else - struct ucontext *uc = puc; -#endif - unsigned long pc; - int trapno; - -#ifndef REG_EIP -/* for glibc 2.1 */ -#define REG_EIP EIP -#define REG_ERR ERR -#define REG_TRAPNO TRAPNO -#endif - pc = EIP_sig(uc); - trapno = TRAP_sig(uc); - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - trapno == 0xe ? - (ERROR_sig(uc) >> 1) & 1 : 0, - &MASK_sig(uc), puc); -} - -#elif defined(__x86_64__) - -#ifdef __NetBSD__ -#define PC_sig(context) _UC_MACHINE_PC(context) -#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) -#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) -#define MASK_sig(context) ((context)->uc_sigmask) -#elif defined(__OpenBSD__) -#define PC_sig(context) ((context)->sc_rip) -#define TRAP_sig(context) ((context)->sc_trapno) -#define ERROR_sig(context) ((context)->sc_err) -#define MASK_sig(context) ((context)->sc_mask) -#elif defined (__FreeBSD__) || defined(__DragonFly__) -#include - -#define PC_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_rip)) -#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) -#define ERROR_sig(context) ((context)->uc_mcontext.mc_err) -#define MASK_sig(context) ((context)->uc_sigmask) -#else -#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP]) -#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) -#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) -#define MASK_sig(context) ((context)->uc_sigmask) -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - unsigned long pc; -#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__) - ucontext_t *uc = puc; -#elif defined(__OpenBSD__) - struct sigcontext *uc = puc; -#else - struct ucontext *uc = puc; -#endif - - pc = PC_sig(uc); - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - TRAP_sig(uc) == 0xe ? - (ERROR_sig(uc) >> 1) & 1 : 0, - &MASK_sig(uc), puc); -} - -#elif defined(_ARCH_PPC) - -/*********************************************************************** - * signal context platform-specific definitions - * From Wine - */ -#ifdef linux -/* All Registers access - only for local access */ -# define REG_sig(reg_name, context) ((context)->uc_mcontext.regs->reg_name) -/* Gpr Registers access */ -# define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context) -# define IAR_sig(context) REG_sig(nip, context) /* Program counter */ -# define MSR_sig(context) REG_sig(msr, context) /* Machine State Register (Supervisor) */ -# define CTR_sig(context) REG_sig(ctr, context) /* Count register */ -# define XER_sig(context) REG_sig(xer, context) /* User's integer exception register */ -# define LR_sig(context) REG_sig(link, context) /* Link register */ -# define CR_sig(context) REG_sig(ccr, context) /* Condition register */ -/* Float Registers access */ -# define FLOAT_sig(reg_num, context) (((double*)((char*)((context)->uc_mcontext.regs+48*4)))[reg_num]) -# define FPSCR_sig(context) (*(int*)((char*)((context)->uc_mcontext.regs+(48+32*2)*4))) -/* Exception Registers access */ -# define DAR_sig(context) REG_sig(dar, context) -# define DSISR_sig(context) REG_sig(dsisr, context) -# 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; -/* All Registers access - only for local access */ -# define REG_sig(reg_name, context) ((context)->uc_mcontext->ss.reg_name) -# define FLOATREG_sig(reg_name, context) ((context)->uc_mcontext->fs.reg_name) -# define EXCEPREG_sig(reg_name, context) ((context)->uc_mcontext->es.reg_name) -# define VECREG_sig(reg_name, context) ((context)->uc_mcontext->vs.reg_name) -/* Gpr Registers access */ -# define GPR_sig(reg_num, context) REG_sig(r##reg_num, context) -# define IAR_sig(context) REG_sig(srr0, context) /* Program counter */ -# define MSR_sig(context) REG_sig(srr1, context) /* Machine State Register (Supervisor) */ -# define CTR_sig(context) REG_sig(ctr, context) -# define XER_sig(context) REG_sig(xer, context) /* Link register */ -# define LR_sig(context) REG_sig(lr, context) /* User's integer exception register */ -# define CR_sig(context) REG_sig(cr, context) /* Condition register */ -/* Float Registers access */ -# define FLOAT_sig(reg_num, context) FLOATREG_sig(fpregs[reg_num], context) -# define FPSCR_sig(context) ((double)FLOATREG_sig(fpscr, context)) -/* Exception Registers access */ -# define DAR_sig(context) EXCEPREG_sig(dar, context) /* Fault registers for coredump */ -# define DSISR_sig(context) EXCEPREG_sig(dsisr, context) -# define TRAP_sig(context) EXCEPREG_sig(exception, context) /* number of powerpc exception taken */ -#endif /* __APPLE__ */ - -int cpu_signal_handler(int host_signum, void *pinfo, - 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; - - pc = IAR_sig(uc); - is_write = 0; -#if 0 - /* ppc 4xx case */ - if (DSISR_sig(uc) & 0x00800000) - is_write = 1; -#else - if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) - is_write = 1; -#endif - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, &uc->uc_sigmask, puc); -} - -#elif defined(__alpha__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - struct ucontext *uc = puc; - uint32_t *pc = uc->uc_mcontext.sc_pc; - uint32_t insn = *pc; - int is_write = 0; - - /* XXX: need kernel patch to get write flag faster */ - switch (insn >> 26) { - case 0x0d: // stw - case 0x0e: // stb - case 0x0f: // stq_u - case 0x24: // stf - case 0x25: // stg - case 0x26: // sts - case 0x27: // stt - case 0x2c: // stl - case 0x2d: // stq - case 0x2e: // stl_c - case 0x2f: // stq_c - is_write = 1; - } - - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, &uc->uc_sigmask, puc); -} -#elif defined(__sparc__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - int is_write; - uint32_t insn; -#if !defined(__arch64__) || defined(CONFIG_SOLARIS) - uint32_t *regs = (uint32_t *)(info + 1); - void *sigmask = (regs + 20); - /* XXX: is there a standard glibc define ? */ - unsigned long pc = regs[1]; -#else -#ifdef __linux__ - struct sigcontext *sc = puc; - unsigned long pc = sc->sigc_regs.tpc; - void *sigmask = (void *)sc->sigc_mask; -#elif defined(__OpenBSD__) - struct sigcontext *uc = puc; - unsigned long pc = uc->sc_pc; - void *sigmask = (void *)(long)uc->sc_mask; -#endif -#endif - - /* XXX: need kernel patch to get write flag faster */ - is_write = 0; - insn = *(uint32_t *)pc; - if ((insn >> 30) == 3) { - switch((insn >> 19) & 0x3f) { - case 0x05: // stb - case 0x15: // stba - case 0x06: // sth - case 0x16: // stha - case 0x04: // st - case 0x14: // sta - case 0x07: // std - case 0x17: // stda - case 0x0e: // stx - case 0x1e: // stxa - case 0x24: // stf - case 0x34: // stfa - case 0x27: // stdf - case 0x37: // stdfa - case 0x26: // stqf - case 0x36: // stqfa - case 0x25: // stfsr - case 0x3c: // casa - case 0x3e: // casxa - is_write = 1; - break; - } - } - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, sigmask, NULL); -} - -#elif defined(__arm__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - struct ucontext *uc = puc; - unsigned long pc; - int is_write; - -#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) - pc = uc->uc_mcontext.gregs[R15]; -#else - pc = uc->uc_mcontext.arm_pc; -#endif - /* XXX: compute is_write */ - is_write = 0; - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, - &uc->uc_sigmask, puc); -} - -#elif defined(__mc68000) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - struct ucontext *uc = puc; - unsigned long pc; - int is_write; - - pc = uc->uc_mcontext.gregs[16]; - /* XXX: compute is_write */ - is_write = 0; - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, - &uc->uc_sigmask, puc); -} - -#elif defined(__ia64) - -#ifndef __ISR_VALID - /* This ought to be in ... */ -# define __ISR_VALID 1 -#endif - -int cpu_signal_handler(int host_signum, void *pinfo, void *puc) -{ - siginfo_t *info = pinfo; - struct ucontext *uc = puc; - unsigned long ip; - int is_write = 0; - - ip = uc->uc_mcontext.sc_ip; - switch (host_signum) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - case SIGTRAP: - if (info->si_code && (info->si_segvflags & __ISR_VALID)) - /* ISR.W (write-access) is bit 33: */ - is_write = (info->si_isr >> 33) & 1; - break; - - default: - break; - } - return handle_cpu_signal(ip, (unsigned long)info->si_addr, - is_write, - (sigset_t *)&uc->uc_sigmask, puc); -} - -#elif defined(__s390__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - struct ucontext *uc = puc; - unsigned long pc; - uint16_t *pinsn; - int is_write = 0; - - pc = uc->uc_mcontext.psw.addr; - - /* ??? 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); -} - -#elif defined(__mips__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - siginfo_t *info = pinfo; - struct ucontext *uc = puc; - greg_t pc = uc->uc_mcontext.pc; - int is_write; - - /* XXX: compute is_write */ - is_write = 0; - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, &uc->uc_sigmask, puc); -} - -#elif defined(__hppa__) - -int cpu_signal_handler(int host_signum, void *pinfo, - void *puc) -{ - struct siginfo *info = pinfo; - struct ucontext *uc = puc; - 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; - } - - return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, &uc->uc_sigmask, puc); -} - -#else - -#error host CPU specific signal handler needed - -#endif - -#endif /* !defined(CONFIG_SOFTMMU) */ diff -Nru qemu-kvm-0.14.1+noroms/cpus.c qemu-kvm-0.15.0+noroms/cpus.c --- qemu-kvm-0.14.1+noroms/cpus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cpus.c 2011-08-09 07:40:29.000000000 -0500 @@ -30,12 +30,12 @@ #include "gdbstub.h" #include "dma.h" #include "kvm.h" -#include "exec-all.h" +#include "qemu-thread.h" #include "cpus.h" + +#ifndef _WIN32 #include "compatfd.h" -#ifdef CONFIG_LINUX -#include #endif #ifdef SIGRTMIN @@ -44,10 +44,24 @@ #define SIG_IPI SIGUSR1 #endif +#ifdef CONFIG_LINUX + +#include + #ifndef PR_MCE_KILL #define PR_MCE_KILL 33 #endif +#ifndef PR_MCE_KILL_SET +#define PR_MCE_KILL_SET 1 +#endif + +#ifndef PR_MCE_KILL_EARLY +#define PR_MCE_KILL_EARLY 1 +#endif + +#endif /* CONFIG_LINUX */ + static CPUState *next_cpu; /***********************************************************/ @@ -119,44 +133,153 @@ static int cpu_can_run(CPUState *env) { - if (env->stop) + if (env->stop) { return 0; - if (env->stopped || !vm_running) + } + if (env->stopped || !vm_running) { return 0; + } return 1; } -static int cpu_has_work(CPUState *env) +static bool cpu_thread_is_idle(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; + if (env->stop || env->queued_work_first) { + return false; + } + if (env->stopped || !vm_running) { + return true; + } + if (!env->halted || qemu_cpu_has_work(env) || + (kvm_enabled() && kvm_irqchip_in_kernel())) { + return false; + } + return true; } -static int any_cpu_has_work(void) +bool all_cpu_threads_idle(void) { CPUState *env; - for (env = first_cpu; env != NULL; env = env->next_cpu) - if (cpu_has_work(env)) - return 1; - return 0; + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (!cpu_thread_is_idle(env)) { + return false; + } + } + return true; } -static void cpu_debug_handler(CPUState *env) +static void cpu_handle_guest_debug(CPUState *env) { gdb_set_stop_cpu(env); - debug_requested = EXCP_DEBUG; - vm_stop(EXCP_DEBUG); + qemu_system_debug_request(); +#ifdef CONFIG_IOTHREAD + env->stopped = 1; +#endif +} + +#ifdef CONFIG_IOTHREAD +static void cpu_signal(int sig) +{ + if (cpu_single_env) { + cpu_exit(cpu_single_env); + } + exit_request = 1; } +#endif + +#ifdef CONFIG_LINUX +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 (kvm_on_sigbus(siginfo->ssi_code, + (void *)(intptr_t)siginfo->ssi_addr)) { + sigbus_reraise(); + } +} + +static void qemu_init_sigbus(void) +{ + struct sigaction action; + + 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, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0); +} + +static void qemu_kvm_eat_signals(CPUState *env) +{ + struct timespec ts = { 0, 0 }; + siginfo_t siginfo; + sigset_t waitset; + sigset_t chkset; + int r; + + sigemptyset(&waitset); + sigaddset(&waitset, SIG_IPI); + sigaddset(&waitset, SIGBUS); + + do { + r = sigtimedwait(&waitset, &siginfo, &ts); + if (r == -1 && !(errno == EAGAIN || errno == EINTR)) { + perror("sigtimedwait"); + exit(1); + } + + switch (r) { + case SIGBUS: + if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) { + sigbus_reraise(); + } + break; + default: + break; + } + + r = sigpending(&chkset); + if (r == -1) { + perror("sigpending"); + exit(1); + } + } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS)); + +#ifndef CONFIG_IOTHREAD + if (sigismember(&chkset, SIGIO) || sigismember(&chkset, SIGALRM)) { + qemu_notify_event(); + } +#endif +} + +#else /* !CONFIG_LINUX */ + +static void qemu_init_sigbus(void) +{ +} + +static void qemu_kvm_eat_signals(CPUState *env) +{ +} +#endif /* !CONFIG_LINUX */ #ifndef _WIN32 static int io_thread_fd = -1; @@ -167,16 +290,16 @@ static const uint64_t val = 1; ssize_t ret; - if (io_thread_fd == -1) + 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", + fprintf(stderr, "qemu_event_increment: write() failed: %s\n", strerror(errno)); exit (1); } @@ -184,7 +307,7 @@ static void qemu_event_read(void *opaque) { - int fd = (unsigned long)opaque; + int fd = (intptr_t)opaque; ssize_t len; char buffer[512]; @@ -200,19 +323,19 @@ int fds[2]; err = qemu_eventfd(fds); - if (err == -1) + if (err == -1) { return -errno; - + } err = fcntl_setfl(fds[0], O_NONBLOCK); - if (err < 0) + if (err < 0) { goto fail; - + } err = fcntl_setfl(fds[1], O_NONBLOCK); - if (err < 0) + if (err < 0) { goto fail; - + } qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, - (void *)(unsigned long)fds[0]); + (void *)(intptr_t)fds[0]); io_thread_fd = fds[1]; return 0; @@ -222,7 +345,155 @@ close(fds[1]); return err; } + +static void dummy_signal(int sig) +{ +} + +/* 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 = (intptr_t)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_signal_init(void) +{ + int sigfd; + sigset_t set; + +#ifdef CONFIG_IOTHREAD + /* SIGUSR2 used by posix-aio-compat.c */ + sigemptyset(&set); + sigaddset(&set, SIGUSR2); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + /* + * SIG_IPI must be blocked in the main thread and must not be caught + * by sigwait() in the signal thread. Otherwise, the cpu thread will + * not catch it reliably. + */ + sigemptyset(&set); + sigaddset(&set, SIG_IPI); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + sigemptyset(&set); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGBUS); #else + sigemptyset(&set); + sigaddset(&set, SIGBUS); + if (kvm_enabled()) { + /* + * We need to process timer signals synchronously to avoid a race + * between exit_request check and KVM vcpu entry. + */ + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + } +#endif + pthread_sigmask(SIG_BLOCK, &set, NULL); + + sigfd = qemu_signalfd(&set); + 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 *)(intptr_t)sigfd); + + return 0; +} + +static void qemu_kvm_init_cpu_signals(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); + +#ifdef CONFIG_IOTHREAD + 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); + } +#else + sigemptyset(&set); + sigaddset(&set, SIG_IPI); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + pthread_sigmask(SIG_BLOCK, NULL, &set); + sigdelset(&set, SIGIO); + sigdelset(&set, SIGALRM); +#endif + 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 void qemu_tcg_init_cpu_signals(void) +{ +#ifdef CONFIG_IOTHREAD + 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); +#endif +} + +#else /* _WIN32 */ + HANDLE qemu_event_handle; static void dummy_event_handler(void *opaque) @@ -248,12 +519,33 @@ exit (1); } } -#endif + +static int qemu_signal_init(void) +{ + return 0; +} + +static void qemu_kvm_init_cpu_signals(CPUState *env) +{ + abort(); +} + +static void qemu_tcg_init_cpu_signals(void) +{ +} +#endif /* _WIN32 */ #ifndef CONFIG_IOTHREAD int qemu_init_main_loop(void) { - cpu_set_debug_excp_handler(cpu_debug_handler); + int ret; + + ret = qemu_signal_init(); + if (ret) { + return ret; + } + + qemu_init_sigbus(); return qemu_event_init(); } @@ -265,15 +557,24 @@ void qemu_init_vcpu(void *_env) { CPUState *env = _env; + int r; env->nr_cores = smp_cores; env->nr_threads = smp_threads; - if (kvm_enabled()) - kvm_init_vcpu(env); - return; + + if (kvm_enabled()) { + r = kvm_init_vcpu(env); + if (r < 0) { + fprintf(stderr, "kvm_init_vcpu failed: %s\n", strerror(-r)); + exit(1); + } + qemu_kvm_init_cpu_signals(env); + } else { + qemu_tcg_init_cpu_signals(); + } } -int qemu_cpu_self(void *env) +int qemu_cpu_is_self(void *env) { return 1; } @@ -293,7 +594,17 @@ void qemu_cpu_kick(void *env) { - return; +} + +void qemu_cpu_kick_self(void) +{ +#ifndef _WIN32 + assert(cpu_single_env); + + raise(SIG_IPI); +#else + abort(); +#endif } void qemu_notify_event(void) @@ -307,12 +618,15 @@ if (next_cpu && env != next_cpu) { cpu_exit(next_cpu); } + exit_request = 1; } -#if defined(OBSOLETE_KVM_IMPL) || !defined(CONFIG_KVM) void qemu_mutex_lock_iothread(void) {} void qemu_mutex_unlock_iothread(void) {} -#endif + +void cpu_stop_current(void) +{ +} void vm_stop(int reason) { @@ -321,10 +635,9 @@ #else /* CONFIG_IOTHREAD */ -#include "qemu-thread.h" - QemuMutex qemu_global_mutex; -static QemuMutex qemu_fair_mutex; +static QemuCond qemu_io_proceeded_cond; +static bool iothread_requesting_mutex; static QemuThread io_thread; @@ -339,88 +652,32 @@ 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); + qemu_init_sigbus(); - blocked_signals = block_io_signals(); - - ret = qemu_signalfd_init(blocked_signals); - if (ret) + ret = qemu_signal_init(); + if (ret) { return ret; + } /* Note eventfd must be drained before signalfd handlers run */ ret = qemu_event_init(); - if (ret) + if (ret) { return ret; + } - qemu_cond_init(&qemu_pause_cond); + qemu_cond_init(&qemu_cpu_cond); qemu_cond_init(&qemu_system_cond); - qemu_mutex_init(&qemu_fair_mutex); + qemu_cond_init(&qemu_pause_cond); + qemu_cond_init(&qemu_work_cond); + qemu_cond_init(&qemu_io_proceeded_cond); qemu_mutex_init(&qemu_global_mutex); qemu_mutex_lock(&qemu_global_mutex); - qemu_thread_self(&io_thread); + qemu_thread_get_self(&io_thread); return 0; } @@ -431,21 +688,27 @@ qemu_cond_broadcast(&qemu_system_cond); } +bool qemu_system_is_ready(void) +{ + return qemu_system_ready; +} + void run_on_cpu(CPUState *env, void (*func)(void *data), void *data) { struct qemu_work_item wi; - if (qemu_cpu_self(env)) { + if (qemu_cpu_is_self(env)) { func(data); return; } wi.func = func; wi.data = data; - if (!env->queued_work_first) + if (!env->queued_work_first) { env->queued_work_first = &wi; - else + } else { env->queued_work_last->next = &wi; + } env->queued_work_last = &wi; wi.next = NULL; wi.done = false; @@ -463,8 +726,9 @@ { struct qemu_work_item *wi; - if (!env->queued_work_first) + if (!env->queued_work_first) { return; + } while ((wi = env->queued_work_first)) { env->queued_work_first = wi->next; @@ -483,255 +747,158 @@ qemu_cond_signal(&qemu_pause_cond); } flush_queued_work(env); + env->thread_kicked = false; } 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); + while (all_cpu_threads_idle()) { + /* Start accounting real time to the virtual clock if the CPUs + are idle. */ + qemu_clock_warp(vm_clock); + qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex); + } - qemu_mutex_lock(&qemu_global_mutex); + while (iothread_requesting_mutex) { + qemu_cond_wait(&qemu_io_proceeded_cond, &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); + while (cpu_thread_is_idle(env)) { + qemu_cond_wait(env->halt_cond, &qemu_global_mutex); + } - qemu_kvm_eat_signal(env, 0); + qemu_kvm_eat_signals(env); qemu_wait_io_event_common(env); } -static int qemu_cpu_exec(CPUState *env); - -static void *kvm_cpu_thread_fn(void *arg) +static void *qemu_kvm_cpu_thread_fn(void *arg) { CPUState *env = arg; + int r; qemu_mutex_lock(&qemu_global_mutex); - qemu_thread_self(env->thread); - if (kvm_enabled()) - kvm_init_vcpu(env); + qemu_thread_get_self(env->thread); + env->thread_id = qemu_get_thread_id(); - kvm_init_ipi(env); + r = kvm_init_vcpu(env); + if (r < 0) { + fprintf(stderr, "kvm_init_vcpu failed: %s\n", strerror(-r)); + exit(1); + } + + qemu_kvm_init_cpu_signals(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 (!qemu_system_ready) { + qemu_cond_wait(&qemu_system_cond, &qemu_global_mutex); + } while (1) { - if (cpu_can_run(env)) - qemu_cpu_exec(env); + if (cpu_can_run(env)) { + r = kvm_cpu_exec(env); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(env); + } + } qemu_kvm_wait_io_event(env); } return NULL; } -static void *tcg_cpu_thread_fn(void *arg) +static void *qemu_tcg_cpu_thread_fn(void *arg) { CPUState *env = arg; - tcg_init_ipi(); - qemu_thread_self(env->thread); + qemu_tcg_init_cpu_signals(); + qemu_thread_get_self(env->thread); /* signal CPU creation */ qemu_mutex_lock(&qemu_global_mutex); - for (env = first_cpu; env != NULL; env = env->next_cpu) + for (env = first_cpu; env != NULL; env = env->next_cpu) { + env->thread_id = qemu_get_thread_id(); 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 (!qemu_system_ready) { + qemu_cond_wait(&qemu_system_cond, &qemu_global_mutex); + } while (1) { cpu_exec_all(); + if (use_icount && qemu_next_icount_deadline() <= 0) { + qemu_notify_event(); + } 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) +static void qemu_cpu_kick_thread(CPUState *env) { - CPUState *env = _env; - QemuThread this; - - qemu_thread_self(&this); - - return qemu_thread_equal(&this, env->thread); -} +#ifndef _WIN32 + int err; -static void cpu_signal(int sig) -{ - if (cpu_single_env) - cpu_exit(cpu_single_env); - exit_request = 1; + err = pthread_kill(env->thread->thread, SIG_IPI); + if (err) { + fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); + exit(1); + } +#else /* _WIN32 */ + if (!qemu_cpu_is_self(env)) { + SuspendThread(env->thread->thread); + cpu_signal(0); + ResumeThread(env->thread->thread); + } +#endif } -static void tcg_init_ipi(void) +void qemu_cpu_kick(void *_env) { - 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); -} + CPUState *env = _env; -static void dummy_signal(int sig) -{ + qemu_cond_broadcast(env->halt_cond); + if (!env->thread_kicked) { + qemu_cpu_kick_thread(env); + env->thread_kicked = true; + } } -static void kvm_init_ipi(CPUState *env) +void qemu_cpu_kick_self(void) { - int r; - sigset_t set; - struct sigaction sigact; - - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = dummy_signal; - sigaction(SIG_IPI, &sigact, NULL); +#ifndef _WIN32 + assert(cpu_single_env); - 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); + if (!cpu_single_env->thread_kicked) { + qemu_cpu_kick_thread(cpu_single_env); + cpu_single_env->thread_kicked = true; } +#else + abort(); +#endif } -static sigset_t block_io_signals(void) +int qemu_cpu_is_self(void *_env) { - 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); + CPUState *env = _env; - return set; + return qemu_thread_is_self(env->thread); } void qemu_mutex_lock_iothread(void) @@ -739,12 +906,13 @@ if (kvm_enabled()) { qemu_mutex_lock(&qemu_global_mutex); } else { - qemu_mutex_lock(&qemu_fair_mutex); + iothread_requesting_mutex = true; if (qemu_mutex_trylock(&qemu_global_mutex)) { - qemu_thread_signal(tcg_cpu_thread, SIG_IPI); + qemu_cpu_kick_thread(first_cpu); qemu_mutex_lock(&qemu_global_mutex); } - qemu_mutex_unlock(&qemu_fair_mutex); + iothread_requesting_mutex = false; + qemu_cond_broadcast(&qemu_io_proceeded_cond); } } @@ -758,8 +926,9 @@ CPUState *penv = first_cpu; while (penv) { - if (!penv->stopped) + if (!penv->stopped) { return 0; + } penv = (CPUState *)penv->next_cpu; } @@ -777,7 +946,7 @@ } while (!all_vcpus_paused()) { - qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); + qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex); penv = first_cpu; while (penv) { qemu_cpu_kick(penv); @@ -798,17 +967,19 @@ } } -static void tcg_init_vcpu(void *_env) +static void qemu_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); + qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env); + while (env->created == 0) { + qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); + } tcg_cpu_thread = env->thread; tcg_halt_cond = env->halt_cond; } else { @@ -817,14 +988,15 @@ } } -static void kvm_start_vcpu(CPUState *env) +static void qemu_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); + qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env); + while (env->created == 0) { + qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); + } } void qemu_init_vcpu(void *_env) @@ -833,10 +1005,11 @@ env->nr_cores = smp_cores; env->nr_threads = smp_threads; - if (kvm_enabled()) - kvm_start_vcpu(env); - else - tcg_init_vcpu(env); + if (kvm_enabled()) { + qemu_kvm_start_vcpu(env); + } else { + qemu_tcg_init_vcpu(env); + } } void qemu_notify_event(void) @@ -844,27 +1017,25 @@ qemu_event_increment(); } -static void qemu_system_vmstop_request(int reason) +void cpu_stop_current(void) { - vmstop_requested = reason; - qemu_notify_event(); + if (cpu_single_env) { + cpu_single_env->stop = 0; + cpu_single_env->stopped = 1; + cpu_exit(cpu_single_env); + qemu_cond_signal(&qemu_pause_cond); + } } void vm_stop(int reason) { - QemuThread me; - qemu_thread_self(&me); - - if (!qemu_thread_equal(&me, &io_thread)) { + if (!qemu_thread_is_self(&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; - } + cpu_stop_current(); return; } do_vm_stop(reason); @@ -872,7 +1043,7 @@ #endif -static int qemu_cpu_exec(CPUState *env) +static int tcg_cpu_exec(CPUState *env) { int ret; #ifdef CONFIG_PROFILER @@ -888,7 +1059,7 @@ 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()); + count = qemu_icount_round(qemu_next_icount_deadline()); qemu_icount += count; decr = (count > 0xffff) ? 0xffff : count; count -= decr; @@ -912,26 +1083,42 @@ bool cpu_exec_all(void) { - if (next_cpu == NULL) + int r; + + /* Account partial waits to the vm_clock. */ + qemu_clock_warp(vm_clock); + + 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()) +#ifndef CONFIG_IOTHREAD + if (qemu_alarm_pending()) { break; + } +#endif if (cpu_can_run(env)) { - if (qemu_cpu_exec(env) == EXCP_DEBUG) { + if (kvm_enabled()) { + r = kvm_cpu_exec(env); + qemu_kvm_eat_signals(env); + } else { + r = tcg_cpu_exec(env); + } + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(env); break; } - } else if (env->stop) { + } else if (env->stop || env->stopped) { break; } } exit_request = 0; - return any_cpu_has_work(); + return !all_cpu_threads_idle(); } void set_numa_modes(void) @@ -964,6 +1151,11 @@ cpu_set_log(mask); } +void set_cpu_log_filename(const char *optarg) +{ + cpu_set_log_filename(optarg); +} + /* Return the virtual CPU time, based on the instruction counter. */ int64_t cpu_get_icount(void) { diff -Nru qemu-kvm-0.14.1+noroms/cpus.h qemu-kvm-0.15.0+noroms/cpus.h --- qemu-kvm-0.14.1+noroms/cpus.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cpus.h 2011-08-09 07:40:29.000000000 -0500 @@ -6,16 +6,21 @@ void qemu_main_loop_start(void); void resume_all_vcpus(void); void pause_all_vcpus(void); +void cpu_stop_current(void); +bool qemu_system_is_ready(void); + +void cpu_synchronize_all_states(void); +void cpu_synchronize_all_post_reset(void); +void cpu_synchronize_all_post_init(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 set_cpu_log_filename(const char *optarg); void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg); #endif diff -Nru qemu-kvm-0.14.1+noroms/cutils.c qemu-kvm-0.15.0+noroms/cutils.c --- qemu-kvm-0.14.1+noroms/cutils.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/cutils.c 2011-08-09 07:40:29.000000000 -0500 @@ -239,11 +239,6 @@ } } -/* - * No dma flushing needed here, as the aio code will call dma_bdrv_cb() - * on completion as well, which will result in a call to - * dma_bdrv_unmap() which will do the flushing .... - */ void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count) { const uint8_t *p = (const uint8_t *)buf; diff -Nru qemu-kvm-0.14.1+noroms/darwin-user/commpage.c qemu-kvm-0.15.0+noroms/darwin-user/commpage.c --- qemu-kvm-0.14.1+noroms/darwin-user/commpage.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/darwin-user/commpage.c 2011-08-09 07:40:29.000000000 -0500 @@ -211,7 +211,7 @@ uint32_t *value = (uint32_t*)((CPUX86State*)cpu_env)->regs[R_ECX]; DPRINTF("commpage: compare_and_swap32(%x,new,%p)\n", old, value); - if(value && old == tswap32(*value)) + if(old == tswap32(*value)) { uint32_t new = ((CPUX86State*)cpu_env)->regs[R_EDX]; *value = tswap32(new); diff -Nru qemu-kvm-0.14.1+noroms/darwin-user/main.c qemu-kvm-0.15.0+noroms/darwin-user/main.c --- qemu-kvm-0.14.1+noroms/darwin-user/main.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/darwin-user/main.c 2011-08-09 07:40:29.000000000 -0500 @@ -738,6 +738,8 @@ int main(int argc, char **argv) { const char *filename; + const char *log_file = DEBUG_LOGFILE; + const char *log_mask = NULL; struct target_pt_regs regs1, *regs = ®s1; TaskState ts1, *ts = &ts1; CPUState *env; @@ -749,9 +751,6 @@ if (argc <= 1) usage(); - /* init debug */ - cpu_set_log_filename(DEBUG_LOGFILE); - optind = 1; for(;;) { if (optind >= argc) @@ -764,22 +763,15 @@ if (!strcmp(r, "-")) { break; } else if (!strcmp(r, "d")) { - int mask; - CPULogItem *item; - - if (optind >= argc) - break; - - r = argv[optind++]; - mask = cpu_str_to_log_mask(r); - 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); + if (optind >= argc) { + break; + } + log_mask = argv[optind++]; + } else if (!strcmp(r, "D")) { + if (optind >= argc) { + break; } - cpu_set_log(mask); + log_file = argv[optind++]; } else if (!strcmp(r, "s")) { r = argv[optind++]; stack_size = strtol(r, (char **)&r, 0); @@ -817,8 +809,27 @@ usage(); } } - if (optind >= argc) + + /* init debug */ + cpu_set_log_filename(log_file); + if (log_mask) { + int mask; + CPULogItem *item; + + mask = cpu_str_to_log_mask(log_mask); + 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); + } + + if (optind >= argc) { usage(); + } filename = argv[optind]; /* Zero out regs */ diff -Nru qemu-kvm-0.14.1+noroms/darwin-user/signal.c qemu-kvm-0.15.0+noroms/darwin-user/signal.c --- qemu-kvm-0.14.1+noroms/darwin-user/signal.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/darwin-user/signal.c 2011-08-09 07:40:29.000000000 -0500 @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -32,8 +31,6 @@ #undef uc_link #endif -#include - #include "qemu.h" #include "qemu-common.h" diff -Nru qemu-kvm-0.14.1+noroms/darwin-user/syscall.c qemu-kvm-0.15.0+noroms/darwin-user/syscall.c --- qemu-kvm-0.14.1+noroms/darwin-user/syscall.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/darwin-user/syscall.c 2011-08-09 07:40:29.000000000 -0500 @@ -977,7 +977,7 @@ #elif TARGET_PPC { int i; - /* XXX: not really needed those regs are volatile accross calls */ + /* XXX: not really needed those regs are volatile across calls */ uint32_t **regs = ((CPUPPCState*)cpu_env)->gpr; for(i = 11; i > 3; i--) *regs[i] = *regs[i-1]; diff -Nru qemu-kvm-0.14.1+noroms/debian/changelog qemu-kvm-0.15.0+noroms/debian/changelog --- qemu-kvm-0.14.1+noroms/debian/changelog 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/changelog 2011-08-18 13:44:52.000000000 -0500 @@ -1,3 +1,23 @@ +qemu-kvm (0.15.0+noroms-0ubuntu1) oneiric; urgency=low + + * New upstream release + * Remaining changes from upstream: + - removed all binary roms and tests/pi_10.com + * Removed Detect-and-use-GCC-atomic-builtins-for-locking.patch - non-NPTL + implementations were removed with commit + 02615337ef295443daa03233e492194e289a807e + * Drop spice-qxl-locking-fix-for-qemu-kvm.patch - should be unnecessary + as of commit 196a778428989217b82de042725dc8eb29c8f8d8 + * drop patches applied upstream: + - CVE-2011-1751.diff + - virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff + - CVE-2011-2527.patch + * Refreshed the two remaining patches: + - larger_default_ram_size.patch + - CVE-2011-2212-virtqueue-indirect-overflow.patch + + -- Serge Hallyn Thu, 18 Aug 2011 11:12:27 -0500 + qemu-kvm (0.14.1+noroms-0ubuntu2) oneiric; urgency=low * debian/{control,rules}: build with PIE. diff -Nru qemu-kvm-0.14.1+noroms/debian/patches/CVE-2011-1751.diff qemu-kvm-0.15.0+noroms/debian/patches/CVE-2011-1751.diff --- qemu-kvm-0.14.1+noroms/debian/patches/CVE-2011-1751.diff 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/CVE-2011-1751.diff 1969-12-31 18:00:00.000000000 -0600 @@ -1,32 +0,0 @@ -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.14.1+noroms/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch qemu-kvm-0.15.0+noroms/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch --- qemu-kvm-0.14.1+noroms/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch 2011-08-18 13:44:52.000000000 -0500 @@ -5,11 +5,11 @@ 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 +Index: qemu-kvm-0.15.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 @@ +--- qemu-kvm-0.15.0+noroms.orig/hw/virtio.c 2011-08-09 07:40:29.000000000 -0500 ++++ qemu-kvm-0.15.0+noroms/hw/virtio.c 2011-08-18 13:13:19.824009373 -0500 +@@ -369,6 +369,11 @@ max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc); num_bufs = i = 0; desc_pa = vring_desc_addr(desc_pa, i); @@ -21,7 +21,7 @@ } do { -@@ -406,6 +411,11 @@ +@@ -442,6 +447,11 @@ max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc); desc_pa = vring_desc_addr(desc_pa, i); i = 0; diff -Nru qemu-kvm-0.14.1+noroms/debian/patches/CVE-2011-2527.patch qemu-kvm-0.15.0+noroms/debian/patches/CVE-2011-2527.patch --- qemu-kvm-0.14.1+noroms/debian/patches/CVE-2011-2527.patch 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/CVE-2011-2527.patch 1969-12-31 18:00:00.000000000 -0600 @@ -1,52 +0,0 @@ -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.14.1+noroms/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch qemu-kvm-0.15.0+noroms/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch --- qemu-kvm-0.14.1+noroms/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch 1969-12-31 18:00:00.000000000 -0600 @@ -1,84 +0,0 @@ -From de01f17a2cb88dc5ff53cc321342b888c33b120a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Lo=C3=AFc=20Minier?= -Date: Thu, 11 Feb 2010 17:42:33 +0100 -Subject: [PATCH] Detect and use GCC atomic builtins for locking - ---- - configure | 17 +++++++++++++++++ - qemu-lock.h | 13 +++++++++++++ - 2 files changed, 30 insertions(+), 0 deletions(-) - -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 - - ########################################## -+# check if we have gcc atomic built-ins -+gcc_atomic_builtins=no -+cat > $TMPC << EOF -+int main(void) { -+ int i; -+ __sync_lock_test_and_set(&i, 1); -+ __sync_lock_release(&i); -+} -+EOF -+if compile_prog "" ""; then -+ gcc_atomic_builtins=yes -+fi -+ -+########################################## - # check if we have fdatasync - - fdatasync=no -@@ -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 -+if test "$gcc_atomic_builtins" = "yes" ; then -+ echo "CONFIG_GCC_ATOMIC_BUILTINS=y" >> $config_host_mak -+fi - if test "$fdatasync" = "yes" ; then - echo "CONFIG_FDATASYNC=y" >> $config_host_mak - fi -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 - -+#ifdef CONFIG_GCC_ATOMIC_BUILTINS -+typedef int spinlock_t; -+ -+#define SPIN_LOCK_UNLOCKED 0 -+ -+#define resetlock(p) __sync_lock_release((p)) -+#else /* CONFIG_GCC_ATOMIC_BUILTINS */ -+ - #if defined(__hppa__) - - typedef int spinlock_t[4]; -@@ -56,7 +64,11 @@ - } - - #endif -+#endif /* !CONFIG_GCC_ATOMIC_BUILTINS */ - -+#ifdef CONFIG_GCC_ATOMIC_BUILTINS -+#define testandset(p) __sync_lock_test_and_set((p), 1) -+#else /* CONFIG_GCC_ATOMIC_BUILTINS */ - #if defined(_ARCH_PPC) - static inline int testandset (int *p) - { -@@ -213,6 +225,7 @@ - #else - #error unimplemented CPU support - #endif -+#endif /* !CONFIG_GCC_ATOMIC_BUILTINS */ - - #if defined(CONFIG_USER_ONLY) - static inline void spin_lock(spinlock_t *lock) diff -Nru qemu-kvm-0.14.1+noroms/debian/patches/larger_default_ram_size.patch qemu-kvm-0.15.0+noroms/debian/patches/larger_default_ram_size.patch --- qemu-kvm-0.14.1+noroms/debian/patches/larger_default_ram_size.patch 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/larger_default_ram_size.patch 2011-08-18 13:44:52.000000000 -0500 @@ -1,8 +1,8 @@ -Index: qemu-kvm-0.13.0-rc1/vl.c +Index: qemu-kvm-0.15.0+noroms/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 @@ +--- qemu-kvm-0.15.0+noroms.orig/vl.c 2011-08-09 07:40:29.000000000 -0500 ++++ qemu-kvm-0.15.0+noroms/vl.c 2011-08-18 11:17:12.343852747 -0500 +@@ -167,7 +167,7 @@ //#define DEBUG_NET //#define DEBUG_SLIRP diff -Nru qemu-kvm-0.14.1+noroms/debian/patches/series qemu-kvm-0.15.0+noroms/debian/patches/series --- qemu-kvm-0.14.1+noroms/debian/patches/series 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/series 2011-08-18 13:44:52.000000000 -0500 @@ -1,7 +1,2 @@ larger_default_ram_size.patch -Detect-and-use-GCC-atomic-builtins-for-locking.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.14.1+noroms/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch qemu-kvm-0.15.0+noroms/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch --- qemu-kvm-0.14.1+noroms/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch 1969-12-31 18:00:00.000000000 -0600 @@ -1,150 +0,0 @@ -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.14.1+noroms/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff qemu-kvm-0.15.0+noroms/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff --- qemu-kvm-0.14.1+noroms/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff 2011-08-18 13:44:51.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff 1969-12-31 18:00:00.000000000 -0600 @@ -1,70 +0,0 @@ -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.14.1+noroms/default-configs/alpha-softmmu.mak qemu-kvm-0.15.0+noroms/default-configs/alpha-softmmu.mak --- qemu-kvm-0.14.1+noroms/default-configs/alpha-softmmu.mak 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/default-configs/alpha-softmmu.mak 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,9 @@ +# Default configuration for alpha-softmmu + +include pci.mak +CONFIG_SERIAL=y +CONFIG_I8254=y +CONFIG_VGA_PCI=y +CONFIG_IDE_CORE=y +CONFIG_IDE_QDEV=y +CONFIG_VMWARE_VGA=y diff -Nru qemu-kvm-0.14.1+noroms/default-configs/i386-softmmu.mak qemu-kvm-0.15.0+noroms/default-configs/i386-softmmu.mak --- qemu-kvm-0.14.1+noroms/default-configs/i386-softmmu.mak 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/default-configs/i386-softmmu.mak 2011-08-09 07:40:29.000000000 -0500 @@ -4,6 +4,7 @@ CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VMWARE_VGA=y +CONFIG_VMMOUSE=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -18,3 +19,5 @@ CONFIG_NE2000_ISA=y CONFIG_PIIX_PCI=y CONFIG_SOUND=y +CONFIG_HPET=y +CONFIG_APPLESMC=y diff -Nru qemu-kvm-0.14.1+noroms/default-configs/lm32-softmmu.mak qemu-kvm-0.15.0+noroms/default-configs/lm32-softmmu.mak --- qemu-kvm-0.14.1+noroms/default-configs/lm32-softmmu.mak 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/default-configs/lm32-softmmu.mak 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,6 @@ +# Default configuration for lm32-softmmu + +CONFIG_PTIMER=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_SD=y diff -Nru qemu-kvm-0.14.1+noroms/default-configs/microblazeel-linux-user.mak qemu-kvm-0.15.0+noroms/default-configs/microblazeel-linux-user.mak --- qemu-kvm-0.14.1+noroms/default-configs/microblazeel-linux-user.mak 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/default-configs/microblazeel-linux-user.mak 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1 @@ +# Default configuration for microblazeel-linux-user diff -Nru qemu-kvm-0.14.1+noroms/default-configs/microblazeel-softmmu.mak qemu-kvm-0.15.0+noroms/default-configs/microblazeel-softmmu.mak --- qemu-kvm-0.14.1+noroms/default-configs/microblazeel-softmmu.mak 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/default-configs/microblazeel-softmmu.mak 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,5 @@ +# Default configuration for microblazeel-softmmu + +CONFIG_PTIMER=y +CONFIG_PFLASH_CFI01=y +CONFIG_SERIAL=y diff -Nru qemu-kvm-0.14.1+noroms/default-configs/microblaze-softmmu.mak qemu-kvm-0.15.0+noroms/default-configs/microblaze-softmmu.mak --- qemu-kvm-0.14.1+noroms/default-configs/microblaze-softmmu.mak 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/default-configs/microblaze-softmmu.mak 2011-08-09 07:40:29.000000000 -0500 @@ -2,3 +2,4 @@ CONFIG_PTIMER=y CONFIG_PFLASH_CFI01=y +CONFIG_SERIAL=y diff -Nru qemu-kvm-0.14.1+noroms/default-configs/pci.mak qemu-kvm-0.15.0+noroms/default-configs/pci.mak --- qemu-kvm-0.14.1+noroms/default-configs/pci.mak 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/default-configs/pci.mak 2011-08-09 07:40:29.000000000 -0500 @@ -3,6 +3,7 @@ CONFIG_VIRTIO=y CONFIG_USB_UHCI=y CONFIG_USB_OHCI=y +CONFIG_USB_EHCI=y CONFIG_NE2000_PCI=y CONFIG_EEPRO100_PCI=y CONFIG_PCNET_PCI=y diff -Nru qemu-kvm-0.14.1+noroms/default-configs/s390x-linux-user.mak qemu-kvm-0.15.0+noroms/default-configs/s390x-linux-user.mak --- qemu-kvm-0.14.1+noroms/default-configs/s390x-linux-user.mak 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/default-configs/s390x-linux-user.mak 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1 @@ +# Default configuration for s390x-linux-user diff -Nru qemu-kvm-0.14.1+noroms/default-configs/unicore32-linux-user.mak qemu-kvm-0.15.0+noroms/default-configs/unicore32-linux-user.mak --- qemu-kvm-0.14.1+noroms/default-configs/unicore32-linux-user.mak 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/default-configs/unicore32-linux-user.mak 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1 @@ +# Default configuration for unicore32-linux-user diff -Nru qemu-kvm-0.14.1+noroms/default-configs/x86_64-softmmu.mak qemu-kvm-0.15.0+noroms/default-configs/x86_64-softmmu.mak --- qemu-kvm-0.14.1+noroms/default-configs/x86_64-softmmu.mak 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/default-configs/x86_64-softmmu.mak 2011-08-09 07:40:29.000000000 -0500 @@ -4,6 +4,7 @@ CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VMWARE_VGA=y +CONFIG_VMMOUSE=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y @@ -18,3 +19,5 @@ CONFIG_NE2000_ISA=y CONFIG_PIIX_PCI=y CONFIG_SOUND=y +CONFIG_HPET=y +CONFIG_APPLESMC=y diff -Nru qemu-kvm-0.14.1+noroms/device_tree.c qemu-kvm-0.15.0+noroms/device_tree.c --- qemu-kvm-0.14.1+noroms/device_tree.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/device_tree.c 2011-08-09 07:40:29.000000000 -0500 @@ -20,7 +20,6 @@ #include "config.h" #include "qemu-common.h" -#include "sysemu.h" #include "device_tree.h" #include "hw/loader.h" @@ -74,7 +73,7 @@ } int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, uint32_t *val_array, int size) + const char *property, void *val_array, int size) { int offset; diff -Nru qemu-kvm-0.14.1+noroms/device_tree.h qemu-kvm-0.15.0+noroms/device_tree.h --- qemu-kvm-0.14.1+noroms/device_tree.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/device_tree.h 2011-08-09 07:40:29.000000000 -0500 @@ -17,7 +17,7 @@ void *load_device_tree(const char *filename_path, int *sizep); int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, uint32_t *val_array, int size); + const char *property, void *val_array, int size); int qemu_devtree_setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val); int qemu_devtree_setprop_string(void *fdt, const char *node_path, diff -Nru qemu-kvm-0.14.1+noroms/disas.c qemu-kvm-0.15.0+noroms/disas.c --- qemu-kvm-0.14.1+noroms/disas.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/disas.c 2011-08-09 07:40:29.000000000 -0500 @@ -5,7 +5,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" /* Filled in by elfload.c. Simplistic, but will do for now. */ @@ -205,7 +204,7 @@ disasm_info.mach = bfd_mach_sh4; print_insn = print_insn_sh; #elif defined(TARGET_ALPHA) - disasm_info.mach = bfd_mach_alpha; + disasm_info.mach = bfd_mach_alpha_ev6; print_insn = print_insn_alpha; #elif defined(TARGET_CRIS) if (flags != 32) { @@ -215,6 +214,9 @@ disasm_info.mach = bfd_mach_cris_v32; print_insn = print_insn_crisv32; } +#elif defined(TARGET_S390X) + disasm_info.mach = bfd_mach_s390_64; + print_insn = print_insn_s390; #elif defined(TARGET_MICROBLAZE) disasm_info.mach = bfd_arch_microblaze; print_insn = print_insn_microblaze; @@ -342,7 +344,7 @@ struct disassemble_info *info) { if (monitor_disas_is_physical) { - cpu_physical_memory_rw(memaddr, myaddr, length, 0); + cpu_physical_memory_read(memaddr, myaddr, length); } else { cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0); } @@ -414,6 +416,9 @@ #elif defined(TARGET_SH4) disasm_info.mach = bfd_mach_sh4; print_insn = print_insn_sh; +#elif defined(TARGET_S390X) + disasm_info.mach = bfd_mach_s390_64; + print_insn = print_insn_s390; #else monitor_printf(mon, "0x" TARGET_FMT_lx ": Asm output not supported on this arch\n", pc); diff -Nru qemu-kvm-0.14.1+noroms/dis-asm.h qemu-kvm-0.15.0+noroms/dis-asm.h --- qemu-kvm-0.14.1+noroms/dis-asm.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/dis-asm.h 2011-08-09 07:40:29.000000000 -0500 @@ -184,6 +184,9 @@ #define bfd_mach_sh5 0x50 bfd_arch_alpha, /* Dec Alpha */ #define bfd_mach_alpha 1 +#define bfd_mach_alpha_ev4 0x10 +#define bfd_mach_alpha_ev5 0x20 +#define bfd_mach_alpha_ev6 0x30 bfd_arch_arm, /* Advanced Risc Machines ARM */ #define bfd_mach_arm_unknown 0 #define bfd_mach_arm_2 1 diff -Nru qemu-kvm-0.14.1+noroms/dma.h qemu-kvm-0.15.0+noroms/dma.h --- qemu-kvm-0.14.1+noroms/dma.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/dma.h 2011-08-09 07:40:29.000000000 -0500 @@ -32,6 +32,14 @@ target_phys_addr_t len); void qemu_sglist_destroy(QEMUSGList *qsg); +typedef BlockDriverAIOCB *DMAIOFunc(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *iov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque); + +BlockDriverAIOCB *dma_bdrv_io(BlockDriverState *bs, + QEMUSGList *sg, uint64_t sector_num, + DMAIOFunc *io_func, BlockDriverCompletionFunc *cb, + void *opaque, int is_write); BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs, QEMUSGList *sg, uint64_t sector, BlockDriverCompletionFunc *cb, void *opaque); diff -Nru qemu-kvm-0.14.1+noroms/dma-helpers.c qemu-kvm-0.15.0+noroms/dma-helpers.c --- qemu-kvm-0.14.1+noroms/dma-helpers.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/dma-helpers.c 2011-08-09 07:40:29.000000000 -0500 @@ -47,6 +47,7 @@ target_phys_addr_t sg_cur_byte; QEMUIOVector iov; QEMUBH *bh; + DMAIOFunc *io_func; } DMAAIOCB; static void dma_bdrv_cb(void *opaque, int ret); @@ -116,13 +117,8 @@ return; } - if (dbs->is_write) { - dbs->acb = bdrv_aio_writev(dbs->bs, dbs->sector_num, &dbs->iov, - dbs->iov.size / 512, dma_bdrv_cb, dbs); - } else { - dbs->acb = bdrv_aio_readv(dbs->bs, dbs->sector_num, &dbs->iov, - dbs->iov.size / 512, dma_bdrv_cb, dbs); - } + dbs->acb = dbs->io_func(dbs->bs, dbs->sector_num, &dbs->iov, + dbs->iov.size / 512, dma_bdrv_cb, dbs); if (!dbs->acb) { dma_bdrv_unmap(dbs); qemu_iovec_destroy(&dbs->iov); @@ -144,12 +140,12 @@ .cancel = dma_aio_cancel, }; -static BlockDriverAIOCB *dma_bdrv_io( +BlockDriverAIOCB *dma_bdrv_io( BlockDriverState *bs, QEMUSGList *sg, uint64_t sector_num, - BlockDriverCompletionFunc *cb, void *opaque, - int is_write) + DMAIOFunc *io_func, BlockDriverCompletionFunc *cb, + void *opaque, int is_write) { - DMAAIOCB *dbs = qemu_aio_get(&dma_aio_pool, bs, cb, opaque); + DMAAIOCB *dbs = qemu_aio_get(&dma_aio_pool, bs, cb, opaque); dbs->acb = NULL; dbs->bs = bs; @@ -158,12 +154,9 @@ dbs->sg_cur_index = 0; dbs->sg_cur_byte = 0; dbs->is_write = is_write; + dbs->io_func = io_func; dbs->bh = NULL; qemu_iovec_init(&dbs->iov, sg->nsg); - /* - * DMA flushing is handled in dma_bdrv_cb() calling dma_bdrv_unmap() - * so we don't need to do that here. - */ dma_bdrv_cb(dbs, 0); if (!dbs->acb) { qemu_aio_release(dbs); @@ -177,12 +170,12 @@ QEMUSGList *sg, uint64_t sector, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_bdrv_io(bs, sg, sector, cb, opaque, 0); + return dma_bdrv_io(bs, sg, sector, bdrv_aio_readv, cb, opaque, 0); } BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs, QEMUSGList *sg, uint64_t sector, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_bdrv_io(bs, sg, sector, cb, opaque, 1); + return dma_bdrv_io(bs, sg, sector, bdrv_aio_writev, cb, opaque, 1); } diff -Nru qemu-kvm-0.14.1+noroms/docs/ccid.txt qemu-kvm-0.15.0+noroms/docs/ccid.txt --- qemu-kvm-0.14.1+noroms/docs/ccid.txt 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/docs/ccid.txt 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,135 @@ +Qemu CCID Device Documentation. + +Contents +1. USB CCID device +2. Building +3. Using ccid-card-emulated with hardware +4. Using ccid-card-emulated with certificates +5. Using ccid-card-passthru with client side hardware +6. Using ccid-card-passthru with client side certificates +7. Passthrough protocol scenario +8. libcacard + +1. USB CCID device + +The USB CCID device is a USB device implementing the CCID specification, which +lets one connect smart card readers that implement the same spec. For more +information see the specification: + + Universal Serial Bus + Device Class: Smart Card + CCID + Specification for + Integrated Circuit(s) Cards Interface Devices + Revision 1.1 + April 22rd, 2005 + +Smartcard are used for authentication, single sign on, decryption in +public/private schemes and digital signatures. A smartcard reader on the client +cannot be used on a guest with simple usb passthrough since it will then not be +available on the client, possibly locking the computer when it is "removed". On +the other hand this device can let you use the smartcard on both the client and +the guest machine. It is also possible to have a completely virtual smart card +reader and smart card (i.e. not backed by a physical device) using this device. + +2. Building + +The cryptographic functions and access to the physical card is done via NSS. + +Installing NSS: + +In redhat/fedora: + yum install nss-devel +In ubuntu/debian: + apt-get install libnss3-dev + (not tested on ubuntu) + +Configuring and building: + ./configure --enable-smartcard && make + +3. Using ccid-card-emulated with hardware + +Assuming you have a working smartcard on the host with the current +user, using NSS, qemu acts as another NSS client using ccid-card-emulated: + + qemu -usb -device usb-ccid -device ccid-card-emualated + +4. Using ccid-card-emulated with certificates + +You must create the certificates. This is a one time process. We use NSS +certificates: + + certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=cert1" -n cert1 + +Note: you must have exactly three certificates. + +Assuming the current user can access the certificates (use certutil -L to +verify), you can use the emulated card type with the certificates backend: + + qemu -usb -device usb-ccid -device ccid-card-emulated,backend=certificates,cert1=cert1,cert2=cert2,cert3=cert3 + +5. Using ccid-card-passthru with client side hardware + +on the host specify the ccid-card-passthru device with a suitable chardev: + + qemu -chardev socket,server,host=0.0.0.0,port=2001,id=ccid,nowait -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid + +on the client run vscclient, built when you built the libcacard library: + libcacard/vscclient 2001 + +6. Using ccid-card-passthru with client side certificates + +Run qemu as per #5, and run vscclient as follows: +(Note: vscclient command line interface is in a state of change) + + libcacard/vscclient -e "db=\"/etc/pki/nssdb\" use_hw=no soft=(,Test,CAC,,cert1,cert2,cert3)" 2001 + +7. Passthrough protocol scenario + +This is a typical interchange of messages when using the passthru card device. +usb-ccid is a usb device. It defaults to an unattached usb device on startup. +usb-ccid expects a chardev and expects the protocol defined in +cac_card/vscard_common.h to be passed over that. +The usb-ccid device can be in one of three modes: + * detached + * attached with no card + * attached with card + +A typical interchange is: (the arrow shows who started each exchange, it can be client +originated or guest originated) + +client event | vscclient | passthru | usb-ccid | guest event +---------------------------------------------------------------------------------------------- + | VSC_Init | | | + | VSC_ReaderAdd | | attach | + | | | | sees new usb device. +card inserted -> | | | | + | VSC_ATR | insert | insert | see new card + | | | | + | VSC_APDU | VSC_APDU | | <- guest sends APDU +client<->physical | | | | +card APDU exchange| | | | +client response ->| VSC_APDU | VSC_APDU | | receive APDU response + ... + [APDU<->APDU repeats several times] + ... +card removed -> | | | | + | VSC_CardRemove | remove | remove | card removed + ... + [(card insert, apdu's, card remove) repeat] + ... +kill/quit | | | | + vscclient | | | | + | VSC_ReaderRemove | | detach | + | | | | usb device removed. + + +8. libcacard + +ccid-card-passthru and vscclient use libcacard as the card emulator. +libcacard implements a completely virtual CAC (DoD standard for smart cards) +compliant card and uses NSS to actually retrive certificates and do any +encryption using the backend (real reader + card or file backed certificates). + +For documentation of cac_card see README in libcacard subdirectory. + diff -Nru qemu-kvm-0.14.1+noroms/docs/ich9-ehci-uhci.cfg qemu-kvm-0.15.0+noroms/docs/ich9-ehci-uhci.cfg --- qemu-kvm-0.14.1+noroms/docs/ich9-ehci-uhci.cfg 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/docs/ich9-ehci-uhci.cfg 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,37 @@ +########################################################################### +# +# You can pass this file directly to qemu using the -readconfig +# command line switch. +# +# This config file creates a EHCI adapter with companion UHCI +# controllers as multifunction device in PCI slot "1d". +# +# Specify "bus=ehci.0" when creating usb devices to hook them up +# there. +# + +[device "ehci"] + driver = "ich9-usb-ehci1" + addr = "1d.7" + multifunction = "on" + +[device "uhci-1"] + driver = "ich9-usb-uhci1" + addr = "1d.0" + multifunction = "on" + masterbus = "ehci.0" + firstport = "0" + +[device "uhci-2"] + driver = "ich9-usb-uhci2" + addr = "1d.1" + multifunction = "on" + masterbus = "ehci.0" + firstport = "2" + +[device "uhci-3"] + driver = "ich9-usb-uhci3" + addr = "1d.2" + multifunction = "on" + masterbus = "ehci.0" + firstport = "4" diff -Nru qemu-kvm-0.14.1+noroms/docs/libcacard.txt qemu-kvm-0.15.0+noroms/docs/libcacard.txt --- qemu-kvm-0.14.1+noroms/docs/libcacard.txt 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/docs/libcacard.txt 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,483 @@ +This file documents the CAC (Common Access Card) library in the libcacard +subdirectory. + +Virtual Smart Card Emulator + +This emulator is designed to provide emulation of actual smart cards to a +virtual card reader running in a guest virtual machine. The emulated smart +cards can be representations of real smart cards, where the necessary functions +such as signing, card removal/insertion, etc. are mapped to real, physical +cards which are shared with the client machine the emulator is running on, or +the cards could be pure software constructs. + +The emulator is structured to allow multiple replacable or additional pieces, +so it can be easily modified for future requirements. The primary envisioned +modifications are: + +1) The socket connection to the virtual card reader (presumably a CCID reader, +but other ISO-7816 compatible readers could be used). The code that handles +this is in vscclient.c. + +2) The virtual card low level emulation. This is currently supplied by using +NSS. This emulation could be replaced by implementations based on other +security libraries, including but not limitted to openssl+pkcs#11 library, +raw pkcs#11, Microsoft CAPI, direct opensc calls, etc. The code that handles +this is in vcard_emul_nss.c. + +3) Emulation for new types of cards. The current implementation emulates the +original DoD CAC standard with separate pki containers. This emulator lives in +cac.c. More than one card type emulator could be included. Other cards could +be emulated as well, including PIV, newer versions of CAC, PKCS #15, etc. + +-------------------- +Replacing the Socket Based Virtual Reader Interface. + +The current implementation contains a replacable module vscclient.c. The +current vscclient.c implements a sockets interface to the virtual ccid reader +on the guest. CCID commands that are pertinent to emulation are passed +across the socket, and their responses are passed back along that same socket. +The protocol that vscclient uses is defined in vscard_common.h and connects +to a qemu ccid usb device. Since this socket runs as a client, vscclient.c +implements a program with a main entry. It also handles argument parsing for +the emulator. + +An application that wants to use the virtual reader can replace vscclient.c +with it's own implementation that connects to it's own CCID reader. The calls +that the CCID reader can call are: + + VReaderList * vreader_get_reader_list(); + + This function returns a list of virtual readers. These readers may map to + physical devices, or simulated devices depending on vcard the back end. Each + reader in the list should represent a reader to the virtual machine. Virtual + USB address mapping is left to the CCID reader front end. This call can be + made any time to get an updated list. The returned list is a copy of the + internal list that can be referenced by the caller without locking. This copy + must be freed by the caller with vreader_list_delete when it is no longer + needed. + + VReaderListEntry *vreader_list_get_first(VReaderList *); + + This function gets the first entry on the reader list. Along with + vreader_list_get_next(), vreader_list_get_first() can be used to walk the + reader list returned from vreader_get_reader_list(). VReaderListEntries are + part of the list themselves and do not need to be freed separately from the + list. If there are no entries on the list, it will return NULL. + + VReaderListEntry *vreader_list_get_next(VReaderListEntry *); + + This function gets the next entry in the list. If there are no more entries + it will return NULL. + + VReader * vreader_list_get_reader(VReaderListEntry *) + + This function returns the reader stored in the reader List entry. Caller gets + a new reference to a reader. The caller must free it's reference when it is + finished with vreader_free(). + + void vreader_free(VReader *reader); + + This function frees a reference to a reader. Reader's are reference counted + and are automatically deleted when the last reference is freed. + + void vreader_list_delete(VReaderList *list); + + This function frees the list, all the elements on the list, and all the + reader references held by the list. + + VReaderStatus vreader_power_on(VReader *reader, char *atr, int *len); + + This functions simulates a card power on. Virtual cards do not care about + the actual voltage and other physical parameters, but it does care that the + card is actually on or off. Cycling the card causes the card to reset. If + the caller provides enough space, vreader_power_on will return the ATR of + the virtual card. The amount of space provided in atr should be indicated + in *len. The function modifies *len to be the actual length of of the + returned ATR. + + VReaderStatus vreader_power_off(VReader *reader); + + This function simulates a power off of a virtual card. + + VReaderStatus vreader_xfer_bytes(VReader *reader, unsigne char *send_buf, + int send_buf_len, + unsigned char *receive_buf, + int receive_buf_len); + + This functions send a raw apdu to a card and returns the card's response. + The CCID front end should return the response back. Most of the emulation + is driven from these APDUs. + + VReaderStatus vreader_card_is_present(VReader *reader); + + This function returns whether or not the reader has a card inserted. The + vreader_power_on, vreader_power_off, and vreader_xfer_bytes will return + VREADER_NO_CARD. + + const char *vreader_get_name(VReader *reader); + + This function returns the name of the reader. The name comes from the card + emulator level and is usually related to the name of the physical reader. + + VReaderID vreader_get_id(VReader *reader); + + This function returns the id of a reader. All readers start out with an id + of -1. The application can set the id with vreader_set_id. + + VReaderStatus vreader_get_id(VReader *reader, VReaderID id); + + This function sets the reader id. The application is responsible for making + sure that the id is unique for all readers it is actively using. + + VReader *vreader_find_reader_by_id(VReaderID id); + + This function returns the reader which matches the id. If two readers match, + only one is returned. The function returns NULL if the id is -1. + + Event *vevent_wait_next_vevent(); + + This function blocks waiting for reader and card insertion events. There + will be one event for each card insertion, each card removal, each reader + insertion and each reader removal. At start up, events are created for all + the initial readers found, as well as all the cards that are inserted. + + Event *vevent_get_next_vevent(); + + This function returns a pending event if it exists, otherwise it returns + NULL. It does not block. + +---------------- +Card Type Emulator: Adding a New Virtual Card Type + +The ISO 7816 card spec describes 2 types of cards: + 1) File system cards, where the smartcard is managed by reading and writing +data to files in a file system. There is currently only boiler plate +implemented for file system cards. + 2) VM cards, where the card has loadable applets which perform the card +functions. The current implementation supports VM cards. + +In the case of VM cards, the difference between various types of cards is +really what applets have been installed in that card. This structure is +mirrored in card type emulators. The 7816 emulator already handles the basic +ISO 7186 commands. Card type emulators simply need to add the virtual applets +which emulate the real card applets. Card type emulators have exactly one +public entry point: + + VCARDStatus xxx_card_init(VCard *card, const char *flags, + const unsigned char *cert[], + int cert_len[], + VCardKey *key[], + int cert_count); + + The parameters for this are: + card - the virtual card structure which will prepresent this card. + flags - option flags that may be specific to this card type. + cert - array of binary certificates. + cert_len - array of lengths of each of the certificates specified in cert. + key - array of opaque key structures representing the private keys on + the card. + cert_count - number of entries in cert, cert_len, and key arrays. + + Any cert, cert_len, or key with the same index are matching sets. That is + cert[0] is cert_len[0] long and has the corresponsing private key of key[0]. + +The card type emulator is expected to own the VCardKeys, but it should copy +any raw cert data it wants to save. It can create new applets and add them to +the card using the following functions: + + VCardApplet *vcard_new_applet(VCardProcessAPDU apdu_func, + VCardResetApplet reset_func, + const unsigned char *aid, + int aid_len); + + This function creates a new applet. Applet structures store the following + information: + 1) the AID of the applet (set by aid and aid_len). + 2) a function to handle APDUs for this applet. (set by apdu_func, more on + this below). + 3) a function to reset the applet state when the applet is selected. + (set by reset_func, more on this below). + 3) applet private data, a data pointer used by the card type emulator to + store any data or state it needs to complete requests. (set by a + separate call). + 4) applet private data free, a function used to free the applet private + data when the applet itself is destroyed. + The created applet can be added to the card with vcard_add_applet below. + + void vcard_set_applet_private(VCardApplet *applet, + VCardAppletPrivate *private, + VCardAppletPrivateFree private_free); + This function sets the private data and the corresponding free function. + VCardAppletPrivate is an opaque data structure to the rest of the emulator. + The card type emulator can define it any way it wants by defining + struct VCardAppletPrivateStruct {};. If there is already a private data + structure on the applet, the old one is freed before the new one is set up. + passing two NULL clear any existing private data. + + VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet); + + Add an applet onto the list of applets attached to the card. Once an applet + has been added, it can be selected by it's aid, and then commands will be + routed to it VCardProcessAPDU function. This function adopts the applet the + passed int applet. Note: 2 applets with the same AID should not be added to + the same card. It's permissible to add more than one applet. Multiple applets + may have the same VCardPRocessAPDU entry point. + +The certs and keys should be attached to private data associated with one or +more appropriate applets for that card. Control will come to the card type +emulators once one of its applets are selected through the VCardProcessAPDU +function it specified when it created the applet. + +The signature of VCardResetApplet is: + VCardStatus (*VCardResetApplet) (VCard *card, int channel); + This function will reset the any internal applet state that needs to be + cleared after a select applet call. It should return VCARD_DONE; + +The signature of VCardProcessAPDU is: + VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu, + VCardResponse **response); + This function examines the APDU and determines whether it should process + the apdu directly, reject the apdu as invalid, or pass the apdu on to + the basic 7816 emulator for processing. + If the 7816 emulator should process the apdu, then the VCardProcessAPDU + should return VCARD_NEXT. + If there is an error, then VCardProcessAPDU should return an error + response using vcard_make_response and the appropriate 7816 error code + (see card_7816t.h) or vcard_make_response with a card type specific error + code. It should then return VCARD_DONE. + If the apdu can be processed correctly, VCardProcessAPDU should do so, + set the response value appropriately for that APDU, and return VCARD_DONE. + VCardProcessAPDU should always set the response if it returns VCARD_DONE. + It should always either return VCARD_DONE or VCARD_NEXT. + +Parsing the APDU -- + +Prior to processing calling the card type emulator's VCardProcessAPDU function, the emulator has already decoded the APDU header and set several fields: + + apdu->a_data - The raw apdu data bytes. + apdu->a_len - The len of the raw apdu data. + apdu->a_body - The start of any post header parameter data. + apdu->a_Lc - The parameter length value. + apdu->a_Le - The expected length of any returned data. + apdu->a_cla - The raw apdu class. + apdu->a_channel - The channel (decoded from the class). + apdu->a_secure_messaging_type - The decoded secure messagin type + (from class). + apdu->a_type - The decode class type. + apdu->a_gen_type - the generic class type (7816, PROPRIETARY, RFU, PTS). + apdu->a_ins - The instruction byte. + apdu->a_p1 - Parameter 1. + apdu->a_p2 - Parameter 2. + +Creating a Response -- + +The expected result of any APDU call is a response. The card type emulator must +set *response with an appropriate VCardResponse value if it returns VCARD_DONE. +Reponses could be as simple as returning a 2 byte status word response, to as +complex as returning a block of data along with a 2 byte response. Which is +returned will depend on the semantics of the APDU. The following functions will +create card responses. + + VCardResponse *vcard_make_response(VCard7816Status status); + + This is the most basic function to get a response. This function will + return a response the consists soley one 2 byte status code. If that status + code is defined in card_7816t.h, then this function is guarrenteed to + return a response with that status. If a cart type specific status code + is passed and vcard_make_response fails to allocate the appropriate memory + for that response, then vcard_make_response will return a VCardResponse + of VCARD7816_STATUS_EXC_ERROR_MEMORY. In any case, this function is + guarrenteed to return a valid VCardResponse. + + VCardResponse *vcard_response_new(unsigned char *buf, int len, + VCard7816Status status); + + This function is similar to vcard_make_response except it includes some + returned data with the response. It could also fail to allocate enough + memory, in which case it will return NULL. + + VCardResponse *vcard_response_new_status_bytes(unsigned char sw1, + unsigned char sw2); + + Sometimes in 7816 the response bytes are treated as two separate bytes with + split meanings. This function allows you to create a response based on + two separate bytes. This function could fail, in which case it will return + NULL. + + VCardResponse *vcard_response_new_bytes(unsigned char *buf, int len, + unsigned char sw1, + unsigned char sw2); + + This function is the same as vcard_response_new except you may specify + the status as two separate bytes like vcard_response_new_status_bytes. + + +Implementing functionality --- + +The following helper functions access information about the current card +and applet. + + VCARDAppletPrivate *vcard_get_current_applet_private(VCard *card, + int channel); + + This function returns any private data set by the card type emulator on + the currently selected applet. The card type emulator keeps track of the + current applet state in this data structure. Any certs and keys associated + with a particular applet is also stored here. + + int vcard_emul_get_login_count(VCard *card); + + This function returns the the number of remaing login attempts for this + card. If the card emulator does not know, or the card does not have a + way of giving this information, this function returns -1. + + + VCard7816Status vcard_emul_login(VCard *card, unsigned char *pin, + int pin_len); + + This function logins into the card and return the standard 7816 status + word depending on the success or failure of the call. + + void vcard_emul_delete_key(VCardKey *key); + + This function frees the VCardKey passed in to xxxx_card_init. The card + type emulator is responsible for freeing this key when it no longer needs + it. + + VCard7816Status vcard_emul_rsa_op(VCard *card, VCardKey *key, + unsigned char *buffer, + int buffer_size); + + This function does a raw rsa op on the buffer with the given key. + +The sample card type emulator is found in cac.c. It implements the cac specific +applets. Only those applets needed by the coolkey pkcs#11 driver on the guest +have been implemented. To support the full range CAC middleware, a complete CAC +card according to the CAC specs should be implemented here. + +------------------------------ +Virtual Card Emulator + +This code accesses both real smart cards and simulated smart cards through +services provided on the client. The current implementation uses NSS, which +already knows how to talk to various PKCS #11 modules on the client, and is +portable to most operating systems. A particular emulator can have only one +virtual card implementation at a time. + +The virtual card emulator consists of a series of virtual card services. In +addition to the services describe above (services starting with +vcard_emul_xxxx), the virtual card emulator also provides the following +functions: + + VCardEmulError vcard_emul_init(cont VCardEmulOptions *options); + + The options structure is built by another function in the virtual card + interface where a string of virtual card emulator specific strings are + mapped to the options. The actual structure is defined by the virutal card + emulator and is used to determine the configuration of soft cards, or to + determine which physical cards to present to the guest. + + The vcard_emul_init function will build up sets of readers, create any + threads that are needed to watch for changes in the reader state. If readers + have cards present in them, they are also initialized. + + Readers are created with the function. + + VReader *vreader_new(VReaderEmul *reader_emul, + VReaderEmulFree reader_emul_free); + + The freeFunc is used to free the VReaderEmul * when the reader is + destroyed. The VReaderEmul structure is an opaque structure to the + rest of the code, but defined by the virtual card emulator, which can + use it to store any reader specific state. + + Once the reader has been created, it can be added to the front end with the + call: + + VReaderStatus vreader_add_reader(VReader *reader); + + This function will automatically generate the appropriate new reader + events and add the reader to the list. + + To create a new card, the virtual card emulator will call a similiar + function. + + VCard *vcard_new(VCardEmul *card_emul, + VCardEmulFree card_emul_free); + + Like vreader_new, this function takes a virtual card emulator specific + structure which it uses to keep track of the card state. + + Once the card is created, it is attached to a card type emulator with the + following function: + + VCardStatus vcard_init(VCard *vcard, VCardEmulType type, + const char *flags, + unsigned char *const *certs, + int *cert_len, + VCardKey *key[], + int cert_count); + + The vcard is the value returned from vcard_new. The type is the + card type emulator that this card should presented to the guest as. + The flags are card type emulator specific options. The certs, + cert_len, and keys are all arrays of length cert_count. These are the + the same of the parameters xxxx_card_init() accepts. + + Finally the card is associated with it's reader by the call: + + VReaderStatus vreader_insert_card(VReader *vreader, VCard *vcard); + + This function, like vreader_add_reader, will take care of any event + notification for the card insert. + + + VCardEmulError vcard_emul_force_card_remove(VReader *vreader); + + Force a card that is present to appear to be removed to the guest, even if + that card is a physical card and is present. + + + VCardEmulError vcard_emul_force_card_insert(VReader *reader); + + Force a card that has been removed by vcard_emul_force_card_remove to be + reinserted from the point of view of the guest. This will only work if the + card is physically present (which is always true fro a soft card). + + void vcard_emul_get_atr(Vcard *card, unsigned char *atr, int *atr_len); + + Return the virtual ATR for the card. By convention this should be the value + VCARD_ATR_PREFIX(size) followed by several ascii bytes related to this + particular emulator. For instance the NSS emulator returns + {VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }. Do ot return more data then *atr_len; + + void vcard_emul_reset(VCard *card, VCardPower power) + + Set the state of 'card' to the current power level and reset its internal + state (logout, etc). + +------------------------------------------------------- +List of files and their function: +README - This file +card_7816.c - emulate basic 7816 functionality. Parse APDUs. +card_7816.h - apdu and response services definitions. +card_7816t.h - 7816 specific structures, types and definitions. +event.c - event handling code. +event.h - event handling services definitions. +eventt.h - event handling structures and types +vcard.c - handle common virtual card services like creation, destruction, and + applet management. +vcard.h - common virtual card services function definitions. +vcardt.h - comon virtual card types +vreader.c - common virtual reader services. +vreader.h - common virtual reader services definitions. +vreadert.h - comon virtual reader types. +vcard_emul_type.c - manage the card type emulators. +vcard_emul_type.h - definitions for card type emulators. +cac.c - card type emulator for CAC cards +vcard_emul.h - virtual card emulator service definitions. +vcard_emul_nss.c - virtual card emulator implementation for nss. +vscclient.c - socket connection to guest qemu usb driver. +vscard_common.h - common header with the guest qemu usb driver. +mutex.h - header file for machine independent mutexes. +link_test.c - static test to make sure all the symbols are properly defined. diff -Nru qemu-kvm-0.14.1+noroms/docs/qapi-code-gen.txt qemu-kvm-0.15.0+noroms/docs/qapi-code-gen.txt --- qemu-kvm-0.14.1+noroms/docs/qapi-code-gen.txt 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/docs/qapi-code-gen.txt 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,316 @@ += How to use the QAPI code generator = + +* Note: as of this writing, QMP does not use QAPI. Eventually QMP +commands will be converted to use QAPI internally. The following +information describes QMP/QAPI as it will exist after the +conversion. + +QAPI is a native C API within QEMU which provides management-level +functionality to internal/external users. For external +users/processes, this interface is made available by a JSON-based +QEMU Monitor protocol that is provided by the QMP server. + +To map QMP-defined interfaces to the native C QAPI implementations, +a JSON-based schema is used to define types and function +signatures, and a set of scripts is used to generate types/signatures, +and marshaling/dispatch code. The QEMU Guest Agent also uses these +scripts, paired with a seperate schema, to generate +marshaling/dispatch code for the guest agent server running in the +guest. + +This document will describe how the schemas, scripts, and resulting +code is used. + + +== QMP/Guest agent schema == + +This file defines the types, commands, and events used by QMP. It should +fully describe the interface used by QMP. + +This file is designed to be loosely based on JSON although it's technically +executable Python. While dictionaries are used, they are parsed as +OrderedDicts so that ordering is preserved. + +There are two basic syntaxes used, type definitions and command definitions. + +The first syntax defines a type and is represented by a dictionary. There are +two kinds of types that are supported: complex user-defined types, and enums. + +A complex type is a dictionary containing a single key who's value is a +dictionary. This corresponds to a struct in C or an Object in JSON. An +example of a complex type is: + + { 'type': 'MyType', + 'data' { 'member1': 'str', 'member2': 'int', '*member3': 'str } } + +The use of '*' as a prefix to the name means the member is optional. Optional +members should always be added to the end of the dictionary to preserve +backwards compatibility. + +An enumeration type is a dictionary containing a single key who's value is a +list of strings. An example enumeration is: + + { 'enum': 'MyEnum', 'data': [ 'value1', 'value2', 'value3' ] } + +Generally speaking, complex types and enums should always use CamelCase for +the type names. + +Commands are defined by using a list containing three members. The first +member is the command name, the second member is a dictionary containing +arguments, and the third member is the return type. + +An example command is: + + { 'command': 'my-command', + 'data': { 'arg1': 'str', '*arg2': 'str' }, + 'returns': 'str' ] + +Command names should be all lower case with words separated by a hyphen. + + +== Code generation == + +Schemas are fed into 3 scripts to generate all the code/files that, paired +with the core QAPI libraries, comprise everything required to take JSON +commands read in by a QMP/guest agent server, unmarshal the arguments into +the underlying C types, call into the corresponding C function, and map the +response back to a QMP/guest agent response to be returned to the user. + +As an example, we'll use the following schema, which describes a single +complex user-defined type (which will produce a C struct, along with a list +node structure that can be used to chain together a list of such types in +case we want to accept/return a list of this type with a command), and a +command which takes that type as a parameter and returns the same type: + + mdroth@illuin:~/w/qemu2.git$ cat example-schema.json + { 'type': 'UserDefOne', + 'data': { 'integer': 'int', 'string': 'str' } } + + { 'command': 'my-command', + 'data': {'arg1': 'UserDefOne'}, + 'returns': 'UserDefOne' } + mdroth@illuin:~/w/qemu2.git$ + +=== scripts/qapi-types.py === + +Used to generate the C types defined by a schema. The following files are +created: + +$(prefix)qapi-types.h - C types corresponding to types defined in + the schema you pass in +$(prefix)qapi-types.c - Cleanup functions for the above C types + +The $(prefix) is an optional parameter used as a namespace to keep the +generated code from one schema/code-generation separated from others so code +can be generated/used from multiple schemas without clobbering previously +created code. + +Example: + + mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \ + --output-dir="qapi-generated" --prefix="example-" < example-schema.json + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c + /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #include "qapi/qapi-dealloc-visitor.h" + #include "example-qapi-types.h" + #include "example-qapi-visit.h" + + void qapi_free_UserDefOne(UserDefOne * obj) + { + QapiDeallocVisitor *md; + Visitor *v; + + if (!obj) { + return; + } + + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &obj, NULL, NULL); + qapi_dealloc_visitor_cleanup(md); + } + + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h + /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES + #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES + + #include "qapi/qapi-types-core.h" + + typedef struct UserDefOne UserDefOne; + + typedef struct UserDefOneList + { + UserDefOne *value; + struct UserDefOneList *next; + } UserDefOneList; + + struct UserDefOne + { + int64_t integer; + char * string; + }; + + void qapi_free_UserDefOne(UserDefOne * obj); + + #endif + + +=== scripts/qapi-visit.py === + +Used to generate the visitor functions used to walk through and convert +a QObject (as provided by QMP) to a native C data structure and +vice-versa, as well as the visitor function used to dealloc a complex +schema-defined C type. + +The following files are generated: + +$(prefix)qapi-visit.c: visitor function for a particular C type, used + to automagically convert QObjects into the + corresponding C type and vice-versa, as well + as for deallocating memory for an existing C + type + +$(prefix)qapi-visit.h: declarations for previously mentioned visitor + functions + +Example: + + mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \ + --output-dir="qapi-generated" --prefix="example-" < example-schema.json + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #include "example-qapi-visit.h" + + void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp) + { + visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp); + visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp); + visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp); + visit_end_struct(m, errp); + } + + void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp) + { + GenericList *i; + + visit_start_list(m, name, errp); + + for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) { + UserDefOneList *native_i = (UserDefOneList *)i; + visit_type_UserDefOne(m, &native_i->value, NULL, errp); + } + + visit_end_list(m, errp); + } + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT + #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT + + #include "qapi/qapi-visit-core.h" + #include "example-qapi-types.h" + + void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp); + void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp); + + #endif + mdroth@illuin:~/w/qemu2.git$ + + +=== scripts/qapi-commands.py === + +Used to generate the marshaling/dispatch functions for the commands defined +in the schema. The following files are generated: + +$(prefix)qmp-marshal.c: command marshal/dispatch functions for each + QMP command defined in the schema. Functions + generated by qapi-visit.py are used to + convert QObjects recieved from the wire into + function parameters, and uses the same + visitor functions to convert native C return + values to QObjects from transmission back + over the wire. + +$(prefix)qmp-commands.h: Function prototypes for the QMP commands + specified in the schema. + +Example: + + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #include "qemu-objects.h" + #include "qapi/qmp-core.h" + #include "qapi/qapi-visit-core.h" + #include "qapi/qmp-output-visitor.h" + #include "qapi/qmp-input-visitor.h" + #include "qapi/qapi-dealloc-visitor.h" + #include "example-qapi-types.h" + #include "example-qapi-visit.h" + + #include "example-qmp-commands.h" + static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) + { + QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); + QmpOutputVisitor *mo = qmp_output_visitor_new(); + Visitor *v; + + v = qmp_output_get_visitor(mo); + visit_type_UserDefOne(v, &ret_in, "unused", errp); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &ret_in, "unused", errp); + qapi_dealloc_visitor_cleanup(md); + + + *ret_out = qmp_output_get_qobject(mo); + } + + static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp) + { + UserDefOne * retval = NULL; + QmpInputVisitor *mi; + QapiDeallocVisitor *md; + Visitor *v; + UserDefOne * arg1 = NULL; + + mi = qmp_input_visitor_new(QOBJECT(args)); + v = qmp_input_get_visitor(mi); + visit_type_UserDefOne(v, &arg1, "arg1", errp); + + if (error_is_set(errp)) { + goto out; + } + retval = qmp_my_command(arg1, errp); + qmp_marshal_output_my_command(retval, ret, errp); + + out: + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &arg1, "arg1", errp); + qapi_dealloc_visitor_cleanup(md); + return; + } + + static void qmp_init_marshal(void) + { + qmp_register_command("my-command", qmp_marshal_input_my_command); + } + + qapi_init(qmp_init_marshal); + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS + #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS + + #include "example-qapi-types.h" + #include "error.h" + + UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp); + + #endif + mdroth@illuin:~/w/qemu2.git$ diff -Nru qemu-kvm-0.14.1+noroms/docs/qdev-device-use.txt qemu-kvm-0.15.0+noroms/docs/qdev-device-use.txt --- qemu-kvm-0.14.1+noroms/docs/qdev-device-use.txt 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/docs/qdev-device-use.txt 2011-08-09 07:40:29.000000000 -0500 @@ -8,20 +8,23 @@ 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 +property. Examples: - bus property name value format - PCI addr %x.%x (dev.fn, .fn optional) - I2C address %u - SCSI scsi-id %u + bus property name value format + PCI addr %x.%x (dev.fn, .fn optional) + I2C address %u + SCSI scsi-id %u + IDE unit %u + HDA cad %u + virtio-serial-bus nr %u + ccid-bus slot %u + USB port %d(.%d)* (port.port...) 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. @@ -44,28 +47,43 @@ 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... + -drive if=TYPE,bus=BUS,unit=UNIT,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. + +Instead of bus=BUS,unit=UNIT, you can also say index=IDX. 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: +The old OPTS get split into HOST-OPTS and DEV-OPTS as follows: -* if=ide +* file, format, snapshot, cache, aio, readonly, rerror, werror go into + HOST-OPTS. + +* cyls, head, secs and trans go into HOST-OPTS. Future work: they + should go into DEV-OPTS instead. + +* serial goes into DEV-OPTS, for devices supporting serial numbers. + For other devices, it goes nowhere. - -device ide-drive,drive=DRIVE-ID,bus=IDE-BUS,unit=UNIT +* media is special. In the old way, it selects disk vs. CD-ROM with + if=ide, if=scsi and if=xen. The new way uses DEVNAME for that. + Additionally, readonly=on goes into HOST-OPTS. - where IDE-BUS identifies an IDE bus, normally either ide.0 or ide.1, - and UNIT is either 0 or 1. +* addr is special, see if=virtio below. - 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. +The -device argument differs in detail for each type of drive: + +* if=ide + + -device DEVNAME,drive=DRIVE-ID,bus=IDE-BUS,unit=UNIT + + where DEVNAME is either ide-hd or ide-cd, IDE-BUS identifies an IDE + bus, normally either ide.0 or ide.1, and UNIT is either 0 or 1. * if=scsi @@ -77,27 +95,25 @@ 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: + This SCSI controller provides 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 + -device DEVNAME,drive=DRIVE-ID,bus=ID.0,scsi-id=UNIT - 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". + where DEVNAME is either scsi-hd, scsi-cd or scsi-generic. * if=floppy - -global isa-fdc,driveA=DRIVE-ID,driveB=DRIVE-ID + -global isa-fdc.driveA=DRIVE-ID + -global isa-fdc.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. + Without any -global isa-fdc,... you get an empty driveA and no + driveB. You can use -nodefaults to suppress the default driveA, see + "Default Devices". * if=virtio @@ -105,11 +121,12 @@ 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. + 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. + control the PCI device address. This replaces option addr available + with -drive if=virtio. * if=pflash, if=mtd, if=sd, if=xen are not yet available with -device @@ -117,15 +134,20 @@ -usbdevice disk:format=FMT:FILENAME -Provides much less control than -drive's HOST-OPTS... The new way -fixes that: +Provides much less control than -drive's 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. +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. + +Bug: usb-storage pretends to be a block device, but it's really a SCSI +controller that can serve only a single device, which it creates +automatically. The automatic creation guesses what kind of guest part +to create from the host part, like -drive if=scsi. Host and guest +part are not cleanly separated. === Character Devices === @@ -170,7 +192,9 @@ -device usb-braille,chardev=braille,vendorid=VID,productid=PRID -chardev braille,id=braille -* -virtioconsole is still being worked on +* -virtioconsole becomes + -device virtio-serial-pci,class=C,vectors=V,ioeventfd=IOEVENTFD,max_ports=N + -device virtconsole,is_console=NUM,nr=NR,name=NAME LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows: @@ -219,38 +243,29 @@ === Network Devices === -A QEMU network device (NIC) has a host and a guest part. +Host and guest part of network devices have always been separate. -The old ways to define NICs define host and guest part together. It -looks like this: +The old way to define the guest part looks like this: - -net nic,vlan=VLAN,macaddr=MACADDR,model=MODEL,name=ID,addr=STR,vectors=V + -net nic,netdev=NET-ID,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 + -usbdevice net:netdev=NET-ID,macaddr=MACADDR,name=ID -The new way keeps the parts separate: you create the host part with --netdev, and the guest device with -device, like this: +The new way is -device: - -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. +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 that, which 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). @@ -264,20 +279,25 @@ Some PCI devices aren't available with -net nic, e.g. i82558a. -Bug: usb-net does not work, yet. Patch posted. +To connect to a VLAN instead of an ordinary host part, replace +netdev=NET-ID by vlan=VLAN. === 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 old way to define the guest graphics device is -vga VGA. Not all +machines support all -vga options. -The new way is -device. Map from -vga argument to -device: +The new way is -device. The mapping from -vga argument to -device +depends on the machine type. For machine "pc", it's: std -device VGA cirrus -device cirrus-vga vmware -device vmware-svga - xenfb not yet available with -device + qxl -device qxl-vga + none -nodefaults + disables more than just VGA, see "Default Devices" As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI device address. @@ -285,13 +305,16 @@ -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. +For machine "isapc", it's -Bug: the new way doesn't work for machine type "pc", because it -violates obscure device initialization ordering constraints. + std -device isa-vga + cirrus not yet available with -device + none -nodefaults + disables more than just VGA, see "Default Devices" + +Bug: the new way doesn't work for machine types "pc" and "isapc", +because it violates obscure device initialization ordering +constraints. === Audio Devices === @@ -308,6 +331,7 @@ cs4231a -device cs4231a,iobase=IOADDR,irq=IRQ,dma=DMA es1370 -device ES1370 gus -device gus,iobase=IOADDR,irq=IRQ,dma=DMA,freq=F + hda -device intel-hda,msi=MSI -device hda-duplex 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 @@ -321,9 +345,10 @@ The new way is -device DEVNAME,DEV-OPTS... Details depend on DRIVER: +* ccid -device usb-ccid +* keyboard -device usb-kbd * 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" @@ -353,7 +378,7 @@ -device pci-assign,host=ADDR,iommu=IOMMU,id=ID -The old dma=none becomes iommu=0 with -device. +The old dma=none becomes iommu=off with -device. The old way to assign a host USB device is @@ -365,4 +390,27 @@ -device usb-host,hostbus=BUS,hostaddr=ADDR,vendorid=VID,productid=PRID -where left out or zero BUS, ADDR, VID, PRID serve as wildcard. +Omitted options match anything, just like the old way's wildcard. + +=== Default Devices === + +QEMU creates a number of devices by default, depending on the machine +type. + +-device DEVNAME... and global DEVNAME... suppress default devices for +some DEVNAMEs: + + default device suppressing DEVNAMEs + CD-ROM ide-cd, ide-drive, scsi-cd + isa-fdc's driveA isa-fdc + parallel isa-parallel + serial isa-serial + VGA VGA, cirrus-vga, vmware-svga + virtioconsole virtio-serial-pci, virtio-serial-s390, virtio-serial + +The default NIC is connected to a default part created along with it. +It is *not* suppressed by configuring a NIC with -device (you may call +that a bug). -net and -netdev suppress the default NIC. + +-nodefaults suppresses all the default devices mentioned above, plus a +few other things such as default SD-Card drive and default monitor. diff -Nru qemu-kvm-0.14.1+noroms/docs/specs/qcow2.txt qemu-kvm-0.15.0+noroms/docs/specs/qcow2.txt --- qemu-kvm-0.14.1+noroms/docs/specs/qcow2.txt 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/docs/specs/qcow2.txt 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,260 @@ +== General == + +A qcow2 image file is organized in units of constant size, which are called +(host) clusters. A cluster is the unit in which all allocations are done, +both for actual guest data and for image metadata. + +Likewise, the virtual disk as seen by the guest is divided into (guest) +clusters of the same size. + +All numbers in qcow2 are stored in Big Endian byte order. + + +== Header == + +The first cluster of a qcow2 image contains the file header: + + Byte 0 - 3: magic + QCOW magic string ("QFI\xfb") + + 4 - 7: version + Version number (only valid value is 2) + + 8 - 15: backing_file_offset + Offset into the image file at which the backing file name + is stored (NB: The string is not null terminated). 0 if the + image doesn't have a backing file. + + 16 - 19: backing_file_size + Length of the backing file name in bytes. Must not be + longer than 1023 bytes. Undefined if the image doesn't have + a backing file. + + 20 - 23: cluster_bits + Number of bits that are used for addressing an offset + within a cluster (1 << cluster_bits is the cluster size). + Must not be less than 9 (i.e. 512 byte clusters). + + Note: qemu as of today has an implementation limit of 2 MB + as the maximum cluster size and won't be able to open images + with larger cluster sizes. + + 24 - 31: size + Virtual disk size in bytes + + 32 - 35: crypt_method + 0 for no encryption + 1 for AES encryption + + 36 - 39: l1_size + Number of entries in the active L1 table + + 40 - 47: l1_table_offset + Offset into the image file at which the active L1 table + starts. Must be aligned to a cluster boundary. + + 48 - 55: refcount_table_offset + Offset into the image file at which the refcount table + starts. Must be aligned to a cluster boundary. + + 56 - 59: refcount_table_clusters + Number of clusters that the refcount table occupies + + 60 - 63: nb_snapshots + Number of snapshots contained in the image + + 64 - 71: snapshots_offset + Offset into the image file at which the snapshot table + starts. Must be aligned to a cluster boundary. + +Directly after the image header, optional sections called header extensions can +be stored. Each extension has a structure like the following: + + Byte 0 - 3: Header extension type: + 0x00000000 - End of the header extension area + 0xE2792ACA - Backing file format name + other - Unknown header extension, can be safely + ignored + + 4 - 7: Length of the header extension data + + 8 - n: Header extension data + + n - m: Padding to round up the header extension size to the next + multiple of 8. + +The remaining space between the end of the header extension area and the end of +the first cluster can be used for other data. Usually, the backing file name is +stored there. + + +== Host cluster management == + +qcow2 manages the allocation of host clusters by maintaining a reference count +for each host cluster. A refcount of 0 means that the cluster is free, 1 means +that it is used, and >= 2 means that it is used and any write access must +perform a COW (copy on write) operation. + +The refcounts are managed in a two-level table. The first level is called +refcount table and has a variable size (which is stored in the header). The +refcount table can cover multiple clusters, however it needs to be contiguous +in the image file. + +It contains pointers to the second level structures which are called refcount +blocks and are exactly one cluster in size. + +Given a offset into the image file, the refcount of its cluster can be obtained +as follows: + + refcount_block_entries = (cluster_size / sizeof(uint16_t)) + + refcount_block_index = (offset / cluster_size) % refcount_table_entries + refcount_table_index = (offset / cluster_size) / refcount_table_entries + + refcount_block = load_cluster(refcount_table[refcount_table_index]); + return refcount_block[refcount_block_index]; + +Refcount table entry: + + Bit 0 - 8: Reserved (set to 0) + + 9 - 63: Bits 9-63 of the offset into the image file at which the + refcount block starts. Must be aligned to a cluster + boundary. + + If this is 0, the corresponding refcount block has not yet + been allocated. All refcounts managed by this refcount block + are 0. + +Refcount block entry: + + Bit 0 - 15: Reference count of the cluster + + +== Cluster mapping == + +Just as for refcounts, qcow2 uses a two-level structure for the mapping of +guest clusters to host clusters. They are called L1 and L2 table. + +The L1 table has a variable size (stored in the header) and may use multiple +clusters, however it must be contiguous in the image file. L2 tables are +exactly one cluster in size. + +Given a offset into the virtual disk, the offset into the image file can be +obtained as follows: + + l2_entries = (cluster_size / sizeof(uint64_t)) + + l2_index = (offset / cluster_size) % l2_entries + l1_index = (offset / cluster_size) / l2_entries + + l2_table = load_cluster(l1_table[l1_index]); + cluster_offset = l2_table[l2_index]; + + return cluster_offset + (offset % cluster_size) + +L1 table entry: + + Bit 0 - 8: Reserved (set to 0) + + 9 - 55: Bits 9-55 of the offset into the image file at which the L2 + table starts. Must be aligned to a cluster boundary. If the + offset is 0, the L2 table and all clusters described by this + L2 table are unallocated. + + 56 - 62: Reserved (set to 0) + + 63: 0 for an L2 table that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in the active L1 table. + +L2 table entry (for normal clusters): + + Bit 0 - 8: Reserved (set to 0) + + 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a + cluster boundary. If the offset is 0, the cluster is + unallocated. + + 56 - 61: Reserved (set to 0) + + 62: 0 (this cluster is not compressed) + + 63: 0 for a cluster that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in L2 tables that are reachable from the the active L1 + table. + +L2 table entry (for compressed clusters; x = 62 - (cluster_size - 8)): + + Bit 0 - x: Host cluster offset. This is usually _not_ aligned to a + cluster boundary! + + x+1 - 61: Compressed size of the images in sectors of 512 bytes + + 62: 1 (this cluster is compressed using zlib) + + 63: 0 for a cluster that is unused or requires COW, 1 if its + refcount is exactly one. This information is only accurate + in L2 tables that are reachable from the the active L1 + table. + +If a cluster is unallocated, read requests shall read the data from the backing +file. If there is no backing file or the backing file is smaller than the image, +they shall read zeros for all parts that are not covered by the backing file. + + +== Snapshots == + +qcow2 supports internal snapshots. Their basic principle of operation is to +switch the active L1 table, so that a different set of host clusters are +exposed to the guest. + +When creating a snapshot, the L1 table should be copied and the refcount of all +L2 tables and clusters reachable form this L1 table must be increased, so that +a write causes a COW and isn't visible in other snapshots. + +When loading a snapshot, bit 63 of all entries in the new active L1 table and +all L2 tables referenced by it must be reconstructed from the refcount table +as it doesn't need to be accurate in inactive L1 tables. + +A directory of all snapshots is stored in the snapshot table, a contiguous area +in the image file, whose starting offset and length are given by the header +fields snapshots_offset and nb_snapshots. The entries of the snapshot table +have variable length, depending on the length of ID, name and extra data. + +Snapshot table entry: + + Byte 0 - 7: Offset into the image file at which the L1 table for the + snapshot starts. Must be aligned to a cluster boundary. + + 8 - 11: Number of entries in the L1 table of the snapshots + + 12 - 13: Length of the unique ID string describing the snapshot + + 14 - 15: Length of the name of the snapshot + + 16 - 19: Time at which the snapshot was taken in seconds since the + Epoch + + 20 - 23: Subsecond part of the time at which the snapshot was taken + in nanoseconds + + 24 - 31: Time that the guest was running until the snapshot was + taken in nanoseconds + + 32 - 35: Size of the VM state in bytes. 0 if no VM state is saved. + If there is VM state, it starts at the first cluster + described by first L1 table entry that doesn't describe a + regular guest cluster (i.e. VM state is stored like guest + disk content, except that it is stored at offsets that are + larger than the virtual disk presented to the guest) + + 36 - 39: Size of extra data in the table entry (used for future + extensions of the format) + + variable: Extra data for future extensions. Must be ignored. + + variable: Unique ID string for the snapshot (not null terminated) + + variable: Name of the snapshot (not null terminated) diff -Nru qemu-kvm-0.14.1+noroms/docs/specs/qed_spec.txt qemu-kvm-0.15.0+noroms/docs/specs/qed_spec.txt --- qemu-kvm-0.14.1+noroms/docs/specs/qed_spec.txt 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/docs/specs/qed_spec.txt 2011-08-09 07:40:29.000000000 -0500 @@ -89,6 +89,7 @@ ===Data cluster offsets=== * 0 - unallocated. The data cluster is not yet allocated. +* 1 - zero. The data cluster contents are all zeroes and no cluster is 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. @@ -97,6 +98,13 @@ 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. +===Zero data clusters=== +Zero data clusters are a space-efficient way of storing zeroed regions of the image. + +Reads to a zero data cluster produce zeroes. Note that the difference between an unallocated and a zero data cluster is that zero data clusters stop the reading of contents from the backing file. + +Writes to a zero data cluster cause a new data cluster to be allocated. The new data cluster is populated with zeroes and the data being written. + ===Logical offset translation=== Logical offsets are translated into cluster offsets as follows: diff -Nru qemu-kvm-0.14.1+noroms/docs/tracing.txt qemu-kvm-0.15.0+noroms/docs/tracing.txt --- qemu-kvm-0.14.1+noroms/docs/tracing.txt 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/docs/tracing.txt 2011-08-09 07:40:29.000000000 -0500 @@ -26,14 +26,14 @@ == Trace events == -There is a set of static trace events declared in the trace-events source +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 +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: @@ -52,10 +52,10 @@ === Declaring trace events === -The tracetool script produces the trace.h header file which is included by +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.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: @@ -69,6 +69,11 @@ cannot include all user-defined struct declarations and it is therefore necessary to use void * for pointers to structs. + Pointers (including char *) cannot be dereferenced easily (or at all) in + some trace backends. If pointers are used, ensure they are meaningful by + themselves and do not assume the data they point to will be traced. Do + not pass in string arguments. + * For everything else, use primitive scalar types (char, int, long) with the appropriate signedness. @@ -105,10 +110,10 @@ == Trace backends == -The tracetool script automates tedious trace event code generation and also +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 +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 @@ -126,6 +131,14 @@ can optimize out trace events completely. This is the default and imposes no performance penalty. +=== Stderr === + +The "stderr" backend sends trace events directly to standard error. This +effectively turns trace events into debug printfs. + +This is the simplest backend and can be used together with existing code that +uses DPRINTF(). + === Simpletrace === The "simple" backend supports common use cases and comes as part of the QEMU @@ -133,11 +146,6 @@ 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 @@ -173,12 +181,12 @@ ==== 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 +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, +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. @@ -187,3 +195,17 @@ 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. + +=== SystemTap === + +The "dtrace" backend uses DTrace sdt probes but has only been tested with +SystemTap. When SystemTap support is detected a .stp file with wrapper probes +is generated to make use in scripts more convenient. This step can also be +performed manually after a build in order to change the binary name in the .stp +probes: + + scripts/tracetool --dtrace --stap \ + --binary path/to/qemu-binary \ + --target-type system \ + --target-arch x86_64 \ + qemu.stp diff -Nru qemu-kvm-0.14.1+noroms/docs/usb2.txt qemu-kvm-0.15.0+noroms/docs/usb2.txt --- qemu-kvm-0.14.1+noroms/docs/usb2.txt 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/docs/usb2.txt 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,146 @@ + +USB 2.0 Quick Start +=================== + +The QEMU EHCI Adapter can be used with and without companion +controllers. See below for the companion controller mode. + +When not running in companion controller mode there are two completely +separate USB busses: One USB 1.1 bus driven by the UHCI controller and +one USB 2.0 bus driven by the EHCI controller. Devices must be +attached to the correct controller manually. + +The '-usb' switch will make qemu create the UHCI controller as part of +the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0". + +You can use the standard -device switch to add a EHCI controller to +your virtual machine. It is strongly recommended to specify an ID for +the controller so the USB 2.0 bus gets a individual name, for example +'-device usb-ehci,id=ehci". This will give you a USB 2.0 bus named +"ehci.0". + +I strongly recomment to also use -device to attach usb devices because +you can specify the bus they should be attached to this way. Here is +a complete example: + + qemu -M pc ${otheroptions} \ + -drive if=none,id=usbstick,file=/path/to/image \ + -usb \ + -device usb-ehci,id=ehci \ + -device usb-tablet,bus=usb.0 \ + -device usb-storage,bus=ehci.0,drive=usbstick + +This attaches a usb tablet to the UHCI adapter and a usb mass storage +device to the EHCI adapter. + + +Companion controller support +---------------------------- + +Companion controller support has been added recently. The operational +model described above with two completely separate busses still works +fine. Additionally the UHCI and OHCI controllers got the ability to +attach to a usb bus created by EHCI as companion controllers. This is +done by specifying the masterbus and firstport properties. masterbus +specifies the bus name the controller should attach to. firstport +specifies the first port the controller should attach to, which is +needed as usually one ehci controller with six ports has three uhci +companion controllers with two ports each. + +There is a config file in docs which will do all this for you, just +try ... + + qemu -readconfig docs/ich9-ehci-uhci.cfg + +... then use "bus=ehci.0" to assign your usb devices to that bus. + + +More USB tips & tricks +====================== + +Recently the usb pass through driver (also known as usb-host) and the +qemu usb subsystem gained a few capabilities which are available only +via qdev properties, i,e. when using '-device'. + + +physical port addressing +------------------------ + +First you can (for all usb devices) specify the physical port where +the device will show up in the guest. This can be done using the +"port" property. UHCI has two root ports (1,2). EHCI has four root +ports (1-4), the emulated (1.1) USB hub has eight ports. + +Plugging a tablet into UHCI port 1 works like this: + + -device usb-tablet,bus=usb.0,port=1 + +Plugging a hub into UHCI port 2 works like this: + + -device usb-hub,bus=usb.0,port=2 + +Plugging a virtual usb stick into port 4 of the hub just plugged works +this way: + + -device usb-storage,bus=usb.0,port=2.4,drive=... + +You can do basically the same in the monitor using the device_add +command. If you want to unplug devices too you should specify some +unique id which you can use to refer to the device ... + + (qemu) device_add usb-tablet,bus=usb.0,port=1,id=my-tablet + (qemu) device_del my-tablet + +... when unplugging it with device_del. + + +USB pass through hints +---------------------- + +The usb-host driver has a bunch of properties to specify the device +which should be passed to the guest: + + hostbus= -- Specifies the bus number the device must be attached + to. + + hostaddr= -- Specifies the device address the device got + assigned by the guest os. + + hostport= -- Specifies the physical port the device is attached + to. + + vendorid= -- Specifies the vendor ID of the device. + productid= -- Specifies the product ID of the device. + +In theory you can combine all these properties as you like. In +practice only a few combinations are useful: + + (1) vendorid+productid -- match for a specific device, pass it to + the guest when it shows up somewhere in the host. + + (2) hostbus+hostport -- match for a specific physical port in the + host, any device which is plugged in there gets passed to the + guest. + + (3) hostbus+hostaddr -- most useful for ad-hoc pass through as the + hostaddr isn't stable, the next time you plug in the device it + gets a new one ... + +Note that USB 1.1 devices are handled by UHCI/OHCI and USB 2.0 by +EHCI. That means a device plugged into the very same physical port +may show up on different busses depending on the speed. The port I'm +using for testing is bus 1 + port 1 for 2.0 devices and bus 3 + port 1 +for 1.1 devices. Passing through any device plugged into that port +and also assign them to the correct bus can be done this way: + + qemu -M pc ${otheroptions} \ + -usb \ + -device usb-ehci,id=ehci \ + -device usb-host,bus=usb.0,hostbus=3,hostport=1 \ + -device usb-host,bus=ehci.0,hostbus=1,hostport=1 + +enjoy, + Gerd + +-- +Gerd Hoffmann diff -Nru qemu-kvm-0.14.1+noroms/elf.h qemu-kvm-0.15.0+noroms/elf.h --- qemu-kvm-0.14.1+noroms/elf.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/elf.h 2011-08-09 07:40:29.000000000 -0500 @@ -104,6 +104,9 @@ #define EM_H8_300H 47 /* Hitachi H8/300H */ #define EM_H8S 48 /* Hitachi H8S */ +#define EM_LATTICEMICO32 138 /* LatticeMico32 */ + +#define EM_UNICORE32 110 /* UniCore32 */ /* * This is an interim value that we will use until the committee comes @@ -1191,6 +1194,25 @@ Elf64_Word n_type; /* Content type */ } Elf64_Nhdr; + +/* This data structure represents a PT_LOAD segment. */ +struct elf32_fdpic_loadseg { + /* Core address to which the segment is mapped. */ + Elf32_Addr addr; + /* VMA recorded in the program header. */ + Elf32_Addr p_vaddr; + /* Size of this segment in memory. */ + Elf32_Word p_memsz; +}; +struct elf32_fdpic_loadmap { + /* Protocol version number, must be zero. */ + Elf32_Half version; + /* Number of segments in this map. */ + Elf32_Half nsegs; + /* The actual memory map. */ + struct elf32_fdpic_loadseg segs[/*nsegs*/]; +}; + #ifdef ELF_CLASS #if ELF_CLASS == ELFCLASS32 diff -Nru qemu-kvm-0.14.1+noroms/error.c qemu-kvm-0.15.0+noroms/error.c --- qemu-kvm-0.14.1+noroms/error.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/error.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,141 @@ +/* + * QEMU Error Objects + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2. See + * the COPYING.LIB file in the top-level directory. + */ + +#include "qemu-common.h" +#include "error.h" +#include "error_int.h" +#include "qemu-objects.h" +#include "qerror.h" + +struct Error +{ + QDict *obj; + const char *fmt; + char *msg; +}; + +void error_set(Error **errp, const char *fmt, ...) +{ + Error *err; + va_list ap; + + if (errp == NULL) { + return; + } + + err = qemu_mallocz(sizeof(*err)); + + va_start(ap, fmt); + err->obj = qobject_to_qdict(qobject_from_jsonv(fmt, &ap)); + va_end(ap); + err->fmt = fmt; + + *errp = err; +} + +bool error_is_set(Error **errp) +{ + return (errp && *errp); +} + +const char *error_get_pretty(Error *err) +{ + if (err->msg == NULL) { + QString *str; + str = qerror_format(err->fmt, err->obj); + err->msg = qemu_strdup(qstring_get_str(str)); + QDECREF(str); + } + + return err->msg; +} + +const char *error_get_field(Error *err, const char *field) +{ + if (strcmp(field, "class") == 0) { + return qdict_get_str(err->obj, field); + } else { + QDict *dict = qdict_get_qdict(err->obj, "data"); + return qdict_get_str(dict, field); + } +} + +QDict *error_get_data(Error *err) +{ + QDict *data = qdict_get_qdict(err->obj, "data"); + QINCREF(data); + return data; +} + +void error_set_field(Error *err, const char *field, const char *value) +{ + QDict *dict = qdict_get_qdict(err->obj, "data"); + return qdict_put(dict, field, qstring_from_str(value)); +} + +void error_free(Error *err) +{ + if (err) { + QDECREF(err->obj); + qemu_free(err->msg); + qemu_free(err); + } +} + +bool error_is_type(Error *err, const char *fmt) +{ + const char *error_class; + char *ptr; + char *end; + + ptr = strstr(fmt, "'class': '"); + assert(ptr != NULL); + ptr += strlen("'class': '"); + + end = strchr(ptr, '\''); + assert(end != NULL); + + error_class = error_get_field(err, "class"); + if (strlen(error_class) != end - ptr) { + return false; + } + + return strncmp(ptr, error_class, end - ptr) == 0; +} + +void error_propagate(Error **dst_err, Error *local_err) +{ + if (dst_err) { + *dst_err = local_err; + } else if (local_err) { + error_free(local_err); + } +} + +QObject *error_get_qobject(Error *err) +{ + QINCREF(err->obj); + return QOBJECT(err->obj); +} + +void error_set_qobject(Error **errp, QObject *obj) +{ + Error *err; + if (errp == NULL) { + return; + } + err = qemu_mallocz(sizeof(*err)); + err->obj = qobject_to_qdict(obj); + qobject_incref(obj); + + *errp = err; +} diff -Nru qemu-kvm-0.14.1+noroms/error.h qemu-kvm-0.15.0+noroms/error.h --- qemu-kvm-0.14.1+noroms/error.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/error.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,70 @@ +/* + * QEMU Error Objects + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2. See + * the COPYING.LIB file in the top-level directory. + */ +#ifndef ERROR_H +#define ERROR_H + +#include "compiler.h" +#include + +/** + * A class representing internal errors within QEMU. An error has a string + * typename and optionally a set of named string parameters. + */ +typedef struct Error Error; + +/** + * Set an indirect pointer to an error given a printf-style format parameter. + * Currently, qerror.h defines these error formats. This function is not + * meant to be used outside of QEMU. + */ +void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3); + +/** + * Returns true if an indirect pointer to an error is pointing to a valid + * error object. + */ +bool error_is_set(Error **err); + +/** + * Get a human readable representation of an error object. + */ +const char *error_get_pretty(Error *err); + +/** + * Get an individual named error field. + */ +const char *error_get_field(Error *err, const char *field); + +/** + * Get an individual named error field. + */ +void error_set_field(Error *err, const char *field, const char *value); + +/** + * Propagate an error to an indirect pointer to an error. This function will + * always transfer ownership of the error reference and handles the case where + * dst_err is NULL correctly. + */ +void error_propagate(Error **dst_err, Error *local_err); + +/** + * Free an error object. + */ +void error_free(Error *err); + +/** + * Determine if an error is of a speific type (based on the qerror format). + * Non-QEMU users should get the `class' field to identify the error type. + */ +bool error_is_type(Error *err, const char *fmt); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/error_int.h qemu-kvm-0.15.0+noroms/error_int.h --- qemu-kvm-0.14.1+noroms/error_int.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/error_int.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,29 @@ +/* + * QEMU Error Objects + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2. See + * the COPYING.LIB file in the top-level directory. + */ +#ifndef QEMU_ERROR_INT_H +#define QEMU_ERROR_INT_H + +#include "qemu-common.h" +#include "qobject.h" +#include "qdict.h" +#include "error.h" + +/** + * Internal QEMU functions for working with Error. + * + * These are used to convert QErrors to Errors + */ +QDict *error_get_data(Error *err); +QObject *error_get_qobject(Error *err); +void error_set_qobject(Error **errp, QObject *obj); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/exec-all.h qemu-kvm-0.15.0+noroms/exec-all.h --- qemu-kvm-0.14.1+noroms/exec-all.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/exec-all.h 2011-08-09 07:40:29.000000000 -0500 @@ -40,10 +40,11 @@ #define DISAS_UPDATE 2 /* cpu state was modified dynamically */ #define DISAS_TB_JUMP 3 /* only pc was modified statically */ +struct TranslationBlock; typedef struct TranslationBlock TranslationBlock; /* XXX: make safe guess about sizes */ -#define MAX_OP_PER_INSTR 96 +#define MAX_OP_PER_INSTR 208 #if HOST_LONG_BITS == 32 #define MAX_OPC_PARAM_PER_ARG 2 @@ -77,26 +78,24 @@ void gen_intermediate_code(CPUState *env, struct TranslationBlock *tb); void gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb); -void gen_pc_load(CPUState *env, struct TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc); +void restore_state_to_opc(CPUState *env, struct TranslationBlock *tb, + int pc_pos); 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); + CPUState *env, unsigned long searched_pc); void cpu_resume_from_signal(CPUState *env1, void *puc); void cpu_io_recompile(CPUState *env, void *retaddr); TranslationBlock *tb_gen_code(CPUState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags); void cpu_exec_init(CPUState *env); -void QEMU_NORETURN cpu_loop_exit(void); +void QEMU_NORETURN cpu_loop_exit(CPUState *env1); int page_unprotect(target_ulong address, unsigned long pc, void *puc); 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); #if !defined(CONFIG_USER_ONLY) @@ -180,7 +179,6 @@ 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_page(TranslationBlock *tb, @@ -324,8 +322,8 @@ } pd = env1->tlb_table[mmu_idx][page_index].addr_code & ~TARGET_PAGE_MASK; if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { -#if defined(TARGET_SPARC) || defined(TARGET_MIPS) - do_unassigned_access(addr, 0, 1, 0, 4); +#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC) + cpu_unassigned_access(env1, addr, 0, 1, 0, 4); #else cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr); #endif diff -Nru qemu-kvm-0.14.1+noroms/exec.c qemu-kvm-0.15.0+noroms/exec.c --- qemu-kvm-0.14.1+noroms/exec.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/exec.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,22 +26,20 @@ #include "qemu-common.h" #include "cpu.h" -#include "exec-all.h" #include "cache-utils.h" #if !defined(TARGET_IA64) #include "tcg.h" #endif -#include "qemu-kvm.h" #include "hw/hw.h" #include "hw/qdev.h" #include "osdep.h" #include "kvm.h" +#include "hw/xen.h" #include "qemu-timer.h" #if defined(CONFIG_USER_ONLY) #include -#include #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include #if __FreeBSD_version >= 700104 @@ -57,6 +55,9 @@ #include #endif #endif +#else /* !CONFIG_USER_ONLY */ +#include "xen-mapcache.h" +#include "trace.h" #endif //#define DEBUG_TB_INVALIDATE @@ -562,8 +563,8 @@ #endif #endif /* !USE_STATIC_CODE_GEN_BUFFER */ map_exec(code_gen_prologue, sizeof(code_gen_prologue)); - code_gen_buffer_max_size = code_gen_buffer_size - - (TCG_MAX_OP_SIZE * OPC_MAX_SIZE); + code_gen_buffer_max_size = code_gen_buffer_size - + (TCG_MAX_OP_SIZE * OPC_BUF_SIZE); code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock)); } @@ -647,10 +648,8 @@ env->numa_node = 0; QTAILQ_INIT(&env->breakpoints); QTAILQ_INIT(&env->watchpoints); -#ifdef __WIN32 - env->thread_id = GetCurrentProcessId(); -#else - env->thread_id = getpid(); +#ifndef CONFIG_USER_ONLY + env->thread_id = qemu_get_thread_id(); #endif *penv = env; #if defined(CONFIG_USER_ONLY) @@ -663,6 +662,32 @@ #endif } +/* Allocate a new translation block. Flush the translation buffer if + too many translation blocks or too much generated code. */ +static TranslationBlock *tb_alloc(target_ulong pc) +{ + TranslationBlock *tb; + + if (nb_tbs >= code_gen_max_blocks || + (code_gen_ptr - code_gen_buffer) >= code_gen_buffer_max_size) + return NULL; + tb = &tbs[nb_tbs++]; + tb->pc = pc; + tb->cflags = 0; + return tb; +} + +void tb_free(TranslationBlock *tb) +{ + /* In practice this is mostly used for single use temporary TB + Ignore the hard cases and just back up if this TB happens to + be the last one generated. */ + if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) { + code_gen_ptr = tb->tc_ptr; + nb_tbs--; + } +} + static inline void invalidate_page_bitmap(PageDesc *p) { if (p->code_bitmap) { @@ -1055,8 +1080,7 @@ restore the CPU state */ current_tb_modified = 1; - cpu_restore_state(current_tb, env, - env->mem_io_pc, NULL); + cpu_restore_state(current_tb, env, env->mem_io_pc); cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, ¤t_flags); } @@ -1164,7 +1188,7 @@ restore the CPU state */ current_tb_modified = 1; - cpu_restore_state(current_tb, env, pc, puc); + cpu_restore_state(current_tb, env, pc); cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, ¤t_flags); } @@ -1191,12 +1215,16 @@ unsigned int n, tb_page_addr_t page_addr) { PageDesc *p; - TranslationBlock *last_first_tb; +#ifndef CONFIG_USER_ONLY + bool page_already_protected; +#endif tb->page_addr[n] = page_addr; p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1); tb->page_next[n] = p->first_tb; - last_first_tb = p->first_tb; +#ifndef CONFIG_USER_ONLY + page_already_protected = p->first_tb != NULL; +#endif p->first_tb = (TranslationBlock *)((long)tb | n); invalidate_page_bitmap(p); @@ -1232,7 +1260,7 @@ /* if some code is already present, then the pages are already protected. So we handle the case where only the first TB is allocated in a physical page */ - if (!last_first_tb) { + if (!page_already_protected) { tlb_protect_code(page_addr); } #endif @@ -1240,32 +1268,6 @@ #endif /* TARGET_HAS_SMC */ } -/* Allocate a new translation block. Flush the translation buffer if - too many translation blocks or too much generated code. */ -TranslationBlock *tb_alloc(target_ulong pc) -{ - TranslationBlock *tb; - - if (nb_tbs >= code_gen_max_blocks || - (code_gen_ptr - code_gen_buffer) >= code_gen_buffer_max_size) - return NULL; - tb = &tbs[nb_tbs++]; - tb->pc = pc; - tb->cflags = 0; - return tb; -} - -void tb_free(TranslationBlock *tb) -{ - /* In practice this is mostly used for single use temporary TB - Ignore the hard cases and just back up if this TB happens to - be the last one generated. */ - if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) { - code_gen_ptr = tb->tc_ptr; - nb_tbs--; - } -} - /* 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_page(TranslationBlock *tb, @@ -1641,40 +1643,46 @@ spin_unlock(&interrupt_lock); } +#ifndef CONFIG_USER_ONLY /* mask must never be zero, except for A20 change call */ -void cpu_interrupt(CPUState *env, int mask) +static void tcg_handle_interrupt(CPUState *env, int mask) { int old_mask; old_mask = env->interrupt_request; env->interrupt_request |= mask; - if (kvm_enabled() && !kvm_irqchip_in_kernel()) - kvm_update_interrupt_request(env); -#ifndef CONFIG_USER_ONLY /* * If called from iothread context, wake the target cpu in * case its halted. */ - if (!qemu_cpu_self(env)) { + if (!qemu_cpu_is_self(env)) { qemu_cpu_kick(env); return; } -#endif if (use_icount) { env->icount_decr.u16.high = 0xffff; -#ifndef CONFIG_USER_ONLY if (!can_do_io(env) && (mask & ~old_mask) != 0) { cpu_abort(env, "Raised interrupt while not in I/O function"); } -#endif } else { cpu_unlink_tb(env); } } +CPUInterruptHandler cpu_interrupt_handler = tcg_handle_interrupt; + +#else /* CONFIG_USER_ONLY */ + +void cpu_interrupt(CPUState *env, int mask) +{ + env->interrupt_request |= mask; + cpu_unlink_tb(env); +} +#endif /* CONFIG_USER_ONLY */ + void cpu_reset_interrupt(CPUState *env, int mask) { env->interrupt_request &= ~mask; @@ -1724,11 +1732,12 @@ static void cpu_notify_set_memory(target_phys_addr_t start_addr, ram_addr_t size, - ram_addr_t phys_offset) + ram_addr_t phys_offset, + bool log_dirty) { CPUPhysMemoryClient *client; QLIST_FOREACH(client, &memory_client_list, list) { - client->set_memory(client, start_addr, size, phys_offset); + client->set_memory(client, start_addr, size, phys_offset, log_dirty); } } @@ -1755,8 +1764,21 @@ return 0; } -static void phys_page_for_each_1(CPUPhysMemoryClient *client, - int level, void **lp) +struct last_map { + target_phys_addr_t start_addr; + ram_addr_t size; + ram_addr_t phys_offset; +}; + +/* The l1_phys_map provides the upper P_L1_BITs of the guest physical + * address. Each intermediate table provides the next L2_BITs of guest + * physical address space. The number of levels vary based on host and + * guest configuration, making it efficient to build the final guest + * physical address by seeding the L1 offset and shifting and adding in + * each L2 offset as we recurse through them. */ +static void phys_page_for_each_1(CPUPhysMemoryClient *client, int level, + void **lp, target_phys_addr_t addr, + struct last_map *map) { int i; @@ -1765,16 +1787,32 @@ } if (level == 0) { PhysPageDesc *pd = *lp; + addr <<= L2_BITS + TARGET_PAGE_BITS; 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); + target_phys_addr_t start_addr = addr | i << TARGET_PAGE_BITS; + + if (map->size && + start_addr == map->start_addr + map->size && + pd[i].phys_offset == map->phys_offset + map->size) { + + map->size += TARGET_PAGE_SIZE; + continue; + } else if (map->size) { + client->set_memory(client, map->start_addr, + map->size, map->phys_offset, false); + } + + map->start_addr = start_addr; + map->size = TARGET_PAGE_SIZE; + map->phys_offset = 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); + phys_page_for_each_1(client, level - 1, pp + i, + (addr << L2_BITS) | i, map); } } } @@ -1782,9 +1820,15 @@ static void phys_page_for_each(CPUPhysMemoryClient *client) { int i; + struct last_map map = { }; + for (i = 0; i < P_L1_SIZE; ++i) { phys_page_for_each_1(client, P_L1_SHIFT / L2_BITS - 1, - l1_phys_map + 1); + l1_phys_map + i, i, &map); + } + if (map.size) { + client->set_memory(client, map.start_addr, map.size, map.phys_offset, + false); } } @@ -2055,7 +2099,7 @@ /* we modify the TLB cache so that the dirty bit will be set again when accessing the range */ start1 = (unsigned long)qemu_safe_ram_ptr(start); - /* Chek that we don't span multiple blocks - this breaks the + /* Check that we don't span multiple blocks - this breaks the address comparisons below. */ if ((unsigned long)qemu_safe_ram_ptr(end - 1) - start1 != (end - 1) - start) { @@ -2094,6 +2138,36 @@ return ret; } +int cpu_physical_log_start(target_phys_addr_t start_addr, + ram_addr_t size) +{ + CPUPhysMemoryClient *client; + QLIST_FOREACH(client, &memory_client_list, list) { + if (client->log_start) { + int r = client->log_start(client, start_addr, size); + if (r < 0) { + return r; + } + } + } + return 0; +} + +int cpu_physical_log_stop(target_phys_addr_t start_addr, + ram_addr_t size) +{ + CPUPhysMemoryClient *client; + QLIST_FOREACH(client, &memory_client_list, list) { + if (client->log_stop) { + int r = client->log_stop(client, start_addr, size); + if (r < 0) { + return r; + } + } + } + return 0; +} + static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) { ram_addr_t ram_addr; @@ -2583,10 +2657,11 @@ start_addr and region_offset are rounded down to a page boundary before calculating this offset. This should not be a problem unless the low bits of start_addr and region_offset differ. */ -void cpu_register_physical_memory_offset(target_phys_addr_t start_addr, +void cpu_register_physical_memory_log(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset, - ram_addr_t region_offset) + ram_addr_t region_offset, + bool log_dirty) { target_phys_addr_t addr, end_addr; PhysPageDesc *p; @@ -2594,7 +2669,8 @@ ram_addr_t orig_size = size; subpage_t *subpage; - cpu_notify_set_memory(start_addr, size, phys_offset); + assert(size); + cpu_notify_set_memory(start_addr, size, phys_offset, log_dirty); if (phys_offset == IO_MEM_UNASSIGNED) { region_offset = start_addr; @@ -2602,7 +2678,9 @@ region_offset &= TARGET_PAGE_MASK; size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; end_addr = start_addr + (target_phys_addr_t)size; - for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { + + addr = start_addr; + do { p = phys_page_find(addr >> TARGET_PAGE_BITS); if (p && p->phys_offset != IO_MEM_UNASSIGNED) { ram_addr_t orig_memory = p->phys_offset; @@ -2654,7 +2732,8 @@ } } region_offset += TARGET_PAGE_SIZE; - } + addr += TARGET_PAGE_SIZE; + } while (addr != end_addr); /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ @@ -2851,8 +2930,10 @@ } } + new_block->offset = find_ram_offset(size); if (host) { new_block->host = host; + new_block->flags |= RAM_PREALLOC_MASK; } else { if (mem_path) { #if defined (__linux__) && !defined(TARGET_S390X) @@ -2867,18 +2948,28 @@ #endif } 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, + /* S390 KVM requires the topmost vma of the RAM to be smaller than + an system defined value, which is at least 256GB. Larger systems + have larger values. We put the guest between the end of data + segment (system break) and this value. We use 32GB as a base to + have enough room for the system break to grow. */ + new_block->host = mmap((void*)0x800000000, size, PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); + MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (new_block->host == MAP_FAILED) { + fprintf(stderr, "Allocating RAM failed\n"); + abort(); + } #else - new_block->host = qemu_vmalloc(size); + if (xen_enabled()) { + xen_ram_alloc(new_block->offset, size); + } else { + new_block->host = qemu_vmalloc(size); + } #endif qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE); } } - - new_block->offset = find_ram_offset(size); new_block->length = size; QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next); @@ -2894,7 +2985,12 @@ return new_block->offset; } -void qemu_ram_unmap(ram_addr_t addr) +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_from_ptr(ram_addr_t addr) { RAMBlock *block; @@ -2907,11 +3003,6 @@ } } -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) { RAMBlock *block; @@ -2919,7 +3010,9 @@ QLIST_FOREACH(block, &ram_list.blocks, next) { if (addr == block->offset) { QLIST_REMOVE(block, next); - if (mem_path) { + if (block->flags & RAM_PREALLOC_MASK) { + ; + } else if (mem_path) { #if defined (__linux__) && !defined(TARGET_S390X) if (block->fd) { munmap(block->host, block->length); @@ -2927,12 +3020,18 @@ } else { qemu_vfree(block->host); } +#else + abort(); #endif } else { #if defined(TARGET_S390X) && defined(CONFIG_KVM) munmap(block->host, block->length); #else - qemu_vfree(block->host); + if (xen_enabled()) { + xen_invalidate_map_cache_entry(block->host); + } else { + qemu_vfree(block->host); + } #endif } qemu_free(block); @@ -2942,6 +3041,66 @@ } +#ifndef _WIN32 +void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) +{ + RAMBlock *block; + ram_addr_t offset; + int flags; + void *area, *vaddr; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + offset = addr - block->offset; + if (offset < block->length) { + vaddr = block->host + offset; + if (block->flags & RAM_PREALLOC_MASK) { + ; + } else { + flags = MAP_FIXED; + munmap(vaddr, length); + if (mem_path) { +#if defined(__linux__) && !defined(TARGET_S390X) + if (block->fd) { +#ifdef MAP_POPULATE + flags |= mem_prealloc ? MAP_POPULATE | MAP_SHARED : + MAP_PRIVATE; +#else + flags |= MAP_PRIVATE; +#endif + area = mmap(vaddr, length, PROT_READ | PROT_WRITE, + flags, block->fd, offset); + } else { + flags |= MAP_PRIVATE | MAP_ANONYMOUS; + area = mmap(vaddr, length, PROT_READ | PROT_WRITE, + flags, -1, 0); + } +#else + abort(); +#endif + } else { +#if defined(TARGET_S390X) && defined(CONFIG_KVM) + flags |= MAP_SHARED | MAP_ANONYMOUS; + area = mmap(vaddr, length, PROT_EXEC|PROT_READ|PROT_WRITE, + flags, -1, 0); +#else + flags |= MAP_PRIVATE | MAP_ANONYMOUS; + area = mmap(vaddr, length, PROT_READ | PROT_WRITE, + flags, -1, 0); +#endif + } + if (area != vaddr) { + fprintf(stderr, "Could not remap addr: %lx@%lx\n", + length, addr); + exit(1); + } + qemu_madvise(vaddr, length, QEMU_MADV_MERGEABLE); + } + return; + } + } +} +#endif /* !_WIN32 */ + /* Return a host pointer to ram allocated with qemu_ram_alloc. With the exception of the softmmu code in this file, this should only be used for local memory (e.g. video ram) that the device owns, @@ -2956,8 +3115,23 @@ 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); + /* Move this entry to to start of the list. */ + if (block != QLIST_FIRST(&ram_list.blocks)) { + QLIST_REMOVE(block, next); + QLIST_INSERT_HEAD(&ram_list.blocks, block, next); + } + if (xen_enabled()) { + /* We need to check if the requested address is in the RAM + * because we don't want to map the entire memory in QEMU. + * In that case just map until the end of the page. + */ + if (block->offset == 0) { + return xen_map_cache(addr, 0, 0); + } else if (block->host == NULL) { + block->host = + xen_map_cache(block->offset, block->length, 1); + } + } return block->host + (addr - block->offset); } } @@ -2977,6 +3151,18 @@ QLIST_FOREACH(block, &ram_list.blocks, next) { if (addr - block->offset < block->length) { + if (xen_enabled()) { + /* We need to check if the requested address is in the RAM + * because we don't want to map the entire memory in QEMU. + * In that case just map until the end of the page. + */ + if (block->offset == 0) { + return xen_map_cache(addr, 0, 0); + } else if (block->host == NULL) { + block->host = + xen_map_cache(block->offset, block->length, 1); + } + } return block->host + (addr - block->offset); } } @@ -2987,17 +3173,57 @@ return NULL; } +/* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr + * but takes a size argument */ +void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size) +{ + if (*size == 0) { + return NULL; + } + if (xen_enabled()) { + return xen_map_cache(addr, *size, 1); + } else { + RAMBlock *block; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr - block->offset < block->length) { + if (addr - block->offset + *size > block->length) + *size = block->length - addr + block->offset; + return block->host + (addr - block->offset); + } + } + + fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); + abort(); + } +} + +void qemu_put_ram_ptr(void *addr) +{ + trace_qemu_put_ram_ptr(addr); +} + int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) { RAMBlock *block; uint8_t *host = ptr; + if (xen_enabled()) { + *ram_addr = xen_ram_addr_from_mapcache(ptr); + return 0; + } + QLIST_FOREACH(block, &ram_list.blocks, next) { + /* This case append when the block is not mapped. */ + if (block->host == NULL) { + continue; + } if (host - block->host < block->length) { *ram_addr = block->offset + (host - block->host); return 0; } } + return -1; } @@ -3019,8 +3245,8 @@ #ifdef DEBUG_UNASSIGNED printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif -#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 0, 0, 0, 1); +#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 1); #endif return 0; } @@ -3030,8 +3256,8 @@ #ifdef DEBUG_UNASSIGNED printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif -#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 0, 0, 0, 2); +#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 2); #endif return 0; } @@ -3041,8 +3267,8 @@ #ifdef DEBUG_UNASSIGNED printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif -#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 0, 0, 0, 4); +#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 4); #endif return 0; } @@ -3052,8 +3278,8 @@ #ifdef DEBUG_UNASSIGNED printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); #endif -#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 1, 0, 0, 1); +#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 1); #endif } @@ -3062,8 +3288,8 @@ #ifdef DEBUG_UNASSIGNED printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); #endif -#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 1, 0, 0, 2); +#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 2); #endif } @@ -3072,8 +3298,8 @@ #ifdef DEBUG_UNASSIGNED printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); #endif -#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 1, 0, 0, 4); +#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 4); #endif } @@ -3190,7 +3416,7 @@ cpu_abort(env, "check_watchpoint: could not find TB for " "pc=%p", (void *)env->mem_io_pc); } - cpu_restore_state(tb, env, env->mem_io_pc, NULL); + cpu_restore_state(tb, env, env->mem_io_pc); tb_phys_invalidate(tb, -1); if (wp->flags & BP_STOP_BEFORE_ACCESS) { env->exception_index = EXCP_DEBUG; @@ -3697,6 +3923,7 @@ if (kvm_enabled()) flush_icache_range((unsigned long)ptr, ((unsigned long)ptr)+l); + qemu_put_ram_ptr(ptr); } } else { if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && @@ -3724,9 +3951,9 @@ } } else { /* RAM case */ - ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + - (addr & ~TARGET_PAGE_MASK); - memcpy(buf, ptr, l); + ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK); + memcpy(buf, ptr + (addr & ~TARGET_PAGE_MASK), l); + qemu_put_ram_ptr(ptr); } } len -= l; @@ -3767,6 +3994,7 @@ /* ROM/RAM case */ ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); + qemu_put_ram_ptr(ptr); } len -= l; buf += l; @@ -3832,14 +4060,14 @@ int is_write) { target_phys_addr_t len = *plen; - target_phys_addr_t done = 0; + target_phys_addr_t todo = 0; int l; - uint8_t *ret = NULL; - uint8_t *ptr; target_phys_addr_t page; unsigned long pd; PhysPageDesc *p; - unsigned long addr1; + ram_addr_t raddr = ULONG_MAX; + ram_addr_t rlen; + void *ret; while (len > 0) { page = addr & TARGET_PAGE_MASK; @@ -3854,31 +4082,30 @@ } if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { - if (done || bounce.buffer) { + if (todo || bounce.buffer) { break; } bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); bounce.addr = addr; bounce.len = l; if (!is_write) { - cpu_physical_memory_rw(addr, bounce.buffer, l, 0); + cpu_physical_memory_read(addr, bounce.buffer, l); } - ptr = bounce.buffer; - } else { - addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); - ptr = qemu_get_ram_ptr(addr1); + + *plen = l; + return bounce.buffer; } - if (!done) { - ret = ptr; - } else if (ret + done != ptr) { - break; + if (!todo) { + raddr = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); } len -= l; addr += l; - done += l; + todo += l; } - *plen = done; + rlen = todo; + ret = qemu_ram_ptr_length(raddr, &rlen); + *plen = rlen; return ret; } @@ -3912,6 +4139,9 @@ dma_flush_range((unsigned long)buffer, (unsigned long)buffer + flush_len); } + if (xen_enabled()) { + xen_invalidate_map_cache_entry(buffer); + } return; } if (is_write) { @@ -3923,7 +4153,8 @@ } /* warning: addr must be aligned */ -uint32_t ldl_phys(target_phys_addr_t addr) +static inline uint32_t ldl_phys_internal(target_phys_addr_t addr, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -3945,17 +4176,52 @@ if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap32(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap32(val); + } +#endif } else { /* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); - val = ldl_p(ptr); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = ldl_le_p(ptr); + break; + case DEVICE_BIG_ENDIAN: + val = ldl_be_p(ptr); + break; + default: + val = ldl_p(ptr); + break; + } } return val; } +uint32_t ldl_phys(target_phys_addr_t addr) +{ + return ldl_phys_internal(addr, DEVICE_NATIVE_ENDIAN); +} + +uint32_t ldl_le_phys(target_phys_addr_t addr) +{ + return ldl_phys_internal(addr, DEVICE_LITTLE_ENDIAN); +} + +uint32_t ldl_be_phys(target_phys_addr_t addr) +{ + return ldl_phys_internal(addr, DEVICE_BIG_ENDIAN); +} + /* warning: addr must be aligned */ -uint64_t ldq_phys(target_phys_addr_t addr) +static inline uint64_t ldq_phys_internal(target_phys_addr_t addr, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -3976,6 +4242,9 @@ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; + + /* XXX This is broken when device endian != cpu endian. + Fix and add "endian" variable check */ #ifdef TARGET_WORDS_BIGENDIAN val = (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr) << 32; val |= io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4); @@ -3987,11 +4256,36 @@ /* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); - val = ldq_p(ptr); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = ldq_le_p(ptr); + break; + case DEVICE_BIG_ENDIAN: + val = ldq_be_p(ptr); + break; + default: + val = ldq_p(ptr); + break; + } } return val; } +uint64_t ldq_phys(target_phys_addr_t addr) +{ + return ldq_phys_internal(addr, DEVICE_NATIVE_ENDIAN); +} + +uint64_t ldq_le_phys(target_phys_addr_t addr) +{ + return ldq_phys_internal(addr, DEVICE_LITTLE_ENDIAN); +} + +uint64_t ldq_be_phys(target_phys_addr_t addr) +{ + return ldq_phys_internal(addr, DEVICE_BIG_ENDIAN); +} + /* XXX: optimize */ uint32_t ldub_phys(target_phys_addr_t addr) { @@ -4001,7 +4295,8 @@ } /* warning: addr must be aligned */ -uint32_t lduw_phys(target_phys_addr_t addr) +static inline uint32_t lduw_phys_internal(target_phys_addr_t addr, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4023,15 +4318,49 @@ if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap16(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap16(val); + } +#endif } else { /* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); - val = lduw_p(ptr); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = lduw_le_p(ptr); + break; + case DEVICE_BIG_ENDIAN: + val = lduw_be_p(ptr); + break; + default: + val = lduw_p(ptr); + break; + } } return val; } +uint32_t lduw_phys(target_phys_addr_t addr) +{ + return lduw_phys_internal(addr, DEVICE_NATIVE_ENDIAN); +} + +uint32_t lduw_le_phys(target_phys_addr_t addr) +{ + return lduw_phys_internal(addr, DEVICE_LITTLE_ENDIAN); +} + +uint32_t lduw_be_phys(target_phys_addr_t addr) +{ + return lduw_phys_internal(addr, DEVICE_BIG_ENDIAN); +} + /* warning: addr must be aligned. The ram page is not masked as dirty and the code inside is not invalidated. It is useful if the dirty bits are used to track modified PTEs */ @@ -4104,7 +4433,8 @@ } /* warning: addr must be aligned */ -void stl_phys(target_phys_addr_t addr, uint32_t val) +static inline void stl_phys_internal(target_phys_addr_t addr, uint32_t val, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4122,13 +4452,32 @@ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap32(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap32(val); + } +#endif io_mem_write[io_index][2](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); - stl_p(ptr, val); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + stl_le_p(ptr, val); + break; + case DEVICE_BIG_ENDIAN: + stl_be_p(ptr, val); + break; + default: + stl_p(ptr, val); + break; + } if (!cpu_physical_memory_is_dirty(addr1)) { /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); @@ -4139,6 +4488,21 @@ } } +void stl_phys(target_phys_addr_t addr, uint32_t val) +{ + stl_phys_internal(addr, val, DEVICE_NATIVE_ENDIAN); +} + +void stl_le_phys(target_phys_addr_t addr, uint32_t val) +{ + stl_phys_internal(addr, val, DEVICE_LITTLE_ENDIAN); +} + +void stl_be_phys(target_phys_addr_t addr, uint32_t val) +{ + stl_phys_internal(addr, val, DEVICE_BIG_ENDIAN); +} + /* XXX: optimize */ void stb_phys(target_phys_addr_t addr, uint32_t val) { @@ -4147,7 +4511,8 @@ } /* warning: addr must be aligned */ -void stw_phys(target_phys_addr_t addr, uint32_t val) +static inline void stw_phys_internal(target_phys_addr_t addr, uint32_t val, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4165,13 +4530,32 @@ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap16(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap16(val); + } +#endif 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); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + stw_le_p(ptr, val); + break; + case DEVICE_BIG_ENDIAN: + stw_be_p(ptr, val); + break; + default: + stw_p(ptr, val); + break; + } if (!cpu_physical_memory_is_dirty(addr1)) { /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 2, 0); @@ -4182,11 +4566,38 @@ } } +void stw_phys(target_phys_addr_t addr, uint32_t val) +{ + stw_phys_internal(addr, val, DEVICE_NATIVE_ENDIAN); +} + +void stw_le_phys(target_phys_addr_t addr, uint32_t val) +{ + stw_phys_internal(addr, val, DEVICE_LITTLE_ENDIAN); +} + +void stw_be_phys(target_phys_addr_t addr, uint32_t val) +{ + stw_phys_internal(addr, val, DEVICE_BIG_ENDIAN); +} + /* XXX: optimize */ void stq_phys(target_phys_addr_t addr, uint64_t val) { val = tswap64(val); - cpu_physical_memory_write(addr, (const uint8_t *)&val, 8); + cpu_physical_memory_write(addr, &val, 8); +} + +void stq_le_phys(target_phys_addr_t addr, uint64_t val) +{ + val = cpu_to_le64(val); + cpu_physical_memory_write(addr, &val, 8); +} + +void stq_be_phys(target_phys_addr_t addr, uint64_t val) +{ + val = cpu_to_be64(val); + cpu_physical_memory_write(addr, &val, 8); } /* virtual memory access for debug (includes writing to ROM) */ @@ -4234,7 +4645,7 @@ retaddr); } n = env->icount_decr.u16.low + tb->icount; - cpu_restore_state(tb, env, (unsigned long)retaddr, NULL); + cpu_restore_state(tb, env, (unsigned long)retaddr); /* Calculate how many instructions had been executed before the fault occurred. */ n = n - env->icount_decr.u16.low; diff -Nru qemu-kvm-0.14.1+noroms/EXTERNAL_DEPENDENCIES qemu-kvm-0.15.0+noroms/EXTERNAL_DEPENDENCIES --- qemu-kvm-0.14.1+noroms/EXTERNAL_DEPENDENCIES 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/EXTERNAL_DEPENDENCIES 2011-08-09 07:40:29.000000000 -0500 @@ -1,2 +1,4 @@ +SLOF d1d6b53b713a2b7c2c25685268fa932d28a4b4c0 +ipxe 7aee315f61aaf1be6d2fff26339f28a1137231a5 seabios 06d0bdd9e2e20377b3180e4986b14c8549b393e4 vgabios ca056d8e77a534f4f90548bc8cee166a378c1454 diff -Nru qemu-kvm-0.14.1+noroms/fpu/softfloat.c qemu-kvm-0.15.0+noroms/fpu/softfloat.c --- qemu-kvm-0.14.1+noroms/fpu/softfloat.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/fpu/softfloat.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,3 +1,8 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ /*============================================================================ @@ -59,12 +64,37 @@ STATUS(float_exception_flags) = val; } -#ifdef FLOATX80 void set_floatx80_rounding_precision(int val STATUS_PARAM) { STATUS(floatx80_rounding_precision) = val; } -#endif + +/*---------------------------------------------------------------------------- +| Returns the fraction bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE uint32_t extractFloat16Frac(float16 a) +{ + return float16_val(a) & 0x3ff; +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE int16 extractFloat16Exp(float16 a) +{ + return (float16_val(a) >> 10) & 0x1f; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE flag extractFloat16Sign(float16 a) +{ + return float16_val(a)>>15; +} /*---------------------------------------------------------------------------- | Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 @@ -77,7 +107,7 @@ | positive or negative integer is returned. *----------------------------------------------------------------------------*/ -static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM) +static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM) { int8 roundingMode; flag roundNearestEven; @@ -108,7 +138,7 @@ if ( zSign ) z = - z; if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) { float_raise( float_flag_invalid STATUS_VAR); - return zSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return zSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; return z; @@ -127,7 +157,7 @@ | returned. *----------------------------------------------------------------------------*/ -static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PARAM) +static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATUS_PARAM) { int8 roundingMode; flag roundNearestEven, increment; @@ -135,7 +165,7 @@ roundingMode = STATUS(float_rounding_mode); roundNearestEven = ( roundingMode == float_round_nearest_even ); - increment = ( (sbits64) absZ1 < 0 ); + increment = ( (int64_t) absZ1 < 0 ); if ( ! roundNearestEven ) { if ( roundingMode == float_round_to_zero ) { increment = 0; @@ -152,7 +182,7 @@ if ( increment ) { ++absZ0; if ( absZ0 == 0 ) goto overflow; - absZ0 &= ~ ( ( (bits64) ( absZ1<<1 ) == 0 ) & roundNearestEven ); + absZ0 &= ~ ( ( (uint64_t) ( absZ1<<1 ) == 0 ) & roundNearestEven ); } z = absZ0; if ( zSign ) z = - z; @@ -160,7 +190,7 @@ overflow: float_raise( float_flag_invalid STATUS_VAR); return - zSign ? (sbits64) LIT64( 0x8000000000000000 ) + zSign ? (int64_t) LIT64( 0x8000000000000000 ) : LIT64( 0x7FFFFFFFFFFFFFFF ); } if ( absZ1 ) STATUS(float_exception_flags) |= float_flag_inexact; @@ -172,7 +202,7 @@ | Returns the fraction bits of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits32 extractFloat32Frac( float32 a ) +INLINE uint32_t extractFloat32Frac( float32 a ) { return float32_val(a) & 0x007FFFFF; @@ -224,7 +254,7 @@ *----------------------------------------------------------------------------*/ static void - normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr ) + normalizeFloat32Subnormal( uint32_t aSig, int16 *zExpPtr, uint32_t *zSigPtr ) { int8 shiftCount; @@ -245,11 +275,11 @@ | significand. *----------------------------------------------------------------------------*/ -INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig ) +INLINE float32 packFloat32( flag zSign, int16 zExp, uint32_t zSig ) { return make_float32( - ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig); + ( ( (uint32_t) zSign )<<31 ) + ( ( (uint32_t) zExp )<<23 ) + zSig); } @@ -275,7 +305,7 @@ | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM) +static float32 roundAndPackFloat32( flag zSign, int16 zExp, uint32_t zSig STATUS_PARAM) { int8 roundingMode; flag roundNearestEven; @@ -300,16 +330,19 @@ } } roundBits = zSig & 0x7F; - if ( 0xFD <= (bits16) zExp ) { + if ( 0xFD <= (uint16_t) zExp ) { if ( ( 0xFD < zExp ) || ( ( zExp == 0xFD ) - && ( (sbits32) ( zSig + roundIncrement ) < 0 ) ) + && ( (int32_t) ( zSig + roundIncrement ) < 0 ) ) ) { float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 )); } if ( zExp < 0 ) { - if ( STATUS(flush_to_zero) ) return packFloat32( zSign, 0, 0 ); + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat32(zSign, 0, 0); + } isTiny = ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) || ( zExp < -1 ) @@ -338,7 +371,7 @@ *----------------------------------------------------------------------------*/ static float32 - normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM) + normalizeRoundAndPackFloat32( flag zSign, int16 zExp, uint32_t zSig STATUS_PARAM) { int8 shiftCount; @@ -351,7 +384,7 @@ | Returns the fraction bits of the double-precision floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloat64Frac( float64 a ) +INLINE uint64_t extractFloat64Frac( float64 a ) { return float64_val(a) & LIT64( 0x000FFFFFFFFFFFFF ); @@ -403,7 +436,7 @@ *----------------------------------------------------------------------------*/ static void - normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr ) + normalizeFloat64Subnormal( uint64_t aSig, int16 *zExpPtr, uint64_t *zSigPtr ) { int8 shiftCount; @@ -424,11 +457,11 @@ | significand. *----------------------------------------------------------------------------*/ -INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig ) +INLINE float64 packFloat64( flag zSign, int16 zExp, uint64_t zSig ) { return make_float64( - ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig); + ( ( (uint64_t) zSign )<<63 ) + ( ( (uint64_t) zExp )<<52 ) + zSig); } @@ -454,7 +487,7 @@ | Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM) +static float64 roundAndPackFloat64( flag zSign, int16 zExp, uint64_t zSig STATUS_PARAM) { int8 roundingMode; flag roundNearestEven; @@ -479,16 +512,19 @@ } } roundBits = zSig & 0x3FF; - if ( 0x7FD <= (bits16) zExp ) { + if ( 0x7FD <= (uint16_t) zExp ) { if ( ( 0x7FD < zExp ) || ( ( zExp == 0x7FD ) - && ( (sbits64) ( zSig + roundIncrement ) < 0 ) ) + && ( (int64_t) ( zSig + roundIncrement ) < 0 ) ) ) { float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); return packFloat64( zSign, 0x7FF, - ( roundIncrement == 0 )); } if ( zExp < 0 ) { - if ( STATUS(flush_to_zero) ) return packFloat64( zSign, 0, 0 ); + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat64(zSign, 0, 0); + } isTiny = ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) || ( zExp < -1 ) @@ -517,7 +553,7 @@ *----------------------------------------------------------------------------*/ static float64 - normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM) + normalizeRoundAndPackFloat64( flag zSign, int16 zExp, uint64_t zSig STATUS_PARAM) { int8 shiftCount; @@ -526,14 +562,12 @@ } -#ifdef FLOATX80 - /*---------------------------------------------------------------------------- | Returns the fraction bits of the extended double-precision floating-point | value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloatx80Frac( floatx80 a ) +INLINE uint64_t extractFloatx80Frac( floatx80 a ) { return a.low; @@ -572,7 +606,7 @@ *----------------------------------------------------------------------------*/ static void - normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr ) + normalizeFloatx80Subnormal( uint64_t aSig, int32 *zExpPtr, uint64_t *zSigPtr ) { int8 shiftCount; @@ -587,12 +621,12 @@ | extended double-precision floating-point value, returning the result. *----------------------------------------------------------------------------*/ -INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig ) +INLINE floatx80 packFloatx80( flag zSign, int32 zExp, uint64_t zSig ) { floatx80 z; z.low = zSig; - z.high = ( ( (bits16) zSign )<<15 ) + zExp; + z.high = ( ( (uint16_t) zSign )<<15 ) + zExp; return z; } @@ -623,7 +657,7 @@ static floatx80 roundAndPackFloatx80( - int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 + int8 roundingPrecision, flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) { int8 roundingMode; @@ -660,14 +694,17 @@ } } roundBits = zSig0 & roundMask; - if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) { + if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) { if ( ( 0x7FFE < zExp ) || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) ) ) { goto overflow; } if ( zExp <= 0 ) { - if ( STATUS(flush_to_zero) ) return packFloatx80( zSign, 0, 0 ); + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloatx80(zSign, 0, 0); + } isTiny = ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) || ( zExp < 0 ) @@ -678,7 +715,7 @@ if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR); if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; zSig0 += roundIncrement; - if ( (sbits64) zSig0 < 0 ) zExp = 1; + if ( (int64_t) zSig0 < 0 ) zExp = 1; roundIncrement = roundMask + 1; if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) { roundMask |= roundIncrement; @@ -701,7 +738,7 @@ if ( zSig0 == 0 ) zExp = 0; return packFloatx80( zSign, zExp, zSig0 ); precision80: - increment = ( (sbits64) zSig1 < 0 ); + increment = ( (int64_t) zSig1 < 0 ); if ( ! roundNearestEven ) { if ( roundingMode == float_round_to_zero ) { increment = 0; @@ -715,7 +752,7 @@ } } } - if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) { + if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) { if ( ( 0x7FFE < zExp ) || ( ( zExp == 0x7FFE ) && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) ) @@ -744,7 +781,7 @@ if ( isTiny && zSig1 ) float_raise( float_flag_underflow STATUS_VAR); if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact; if ( roundNearestEven ) { - increment = ( (sbits64) zSig1 < 0 ); + increment = ( (int64_t) zSig1 < 0 ); } else { if ( zSign ) { @@ -757,8 +794,8 @@ if ( increment ) { ++zSig0; zSig0 &= - ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven ); - if ( (sbits64) zSig0 < 0 ) zExp = 1; + ~ ( ( (uint64_t) ( zSig1<<1 ) == 0 ) & roundNearestEven ); + if ( (int64_t) zSig0 < 0 ) zExp = 1; } return packFloatx80( zSign, zExp, zSig0 ); } @@ -771,7 +808,7 @@ zSig0 = LIT64( 0x8000000000000000 ); } else { - zSig0 &= ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven ); + zSig0 &= ~ ( ( (uint64_t) ( zSig1<<1 ) == 0 ) & roundNearestEven ); } } else { @@ -792,7 +829,7 @@ static floatx80 normalizeRoundAndPackFloatx80( - int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 + int8 roundingPrecision, flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) { int8 shiftCount; @@ -810,16 +847,12 @@ } -#endif - -#ifdef FLOAT128 - /*---------------------------------------------------------------------------- | Returns the least-significant 64 fraction bits of the quadruple-precision | floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloat128Frac1( float128 a ) +INLINE uint64_t extractFloat128Frac1( float128 a ) { return a.low; @@ -831,7 +864,7 @@ | floating-point value `a'. *----------------------------------------------------------------------------*/ -INLINE bits64 extractFloat128Frac0( float128 a ) +INLINE uint64_t extractFloat128Frac0( float128 a ) { return a.high & LIT64( 0x0000FFFFFFFFFFFF ); @@ -873,11 +906,11 @@ static void normalizeFloat128Subnormal( - bits64 aSig0, - bits64 aSig1, + uint64_t aSig0, + uint64_t aSig1, int32 *zExpPtr, - bits64 *zSig0Ptr, - bits64 *zSig1Ptr + uint64_t *zSig0Ptr, + uint64_t *zSig1Ptr ) { int8 shiftCount; @@ -916,12 +949,12 @@ *----------------------------------------------------------------------------*/ INLINE float128 - packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 ) + packFloat128( flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 ) { float128 z; z.low = zSig1; - z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0; + z.high = ( ( (uint64_t) zSign )<<63 ) + ( ( (uint64_t) zExp )<<48 ) + zSig0; return z; } @@ -949,14 +982,14 @@ static float128 roundAndPackFloat128( - flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 STATUS_PARAM) + flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1, uint64_t zSig2 STATUS_PARAM) { int8 roundingMode; flag roundNearestEven, increment, isTiny; roundingMode = STATUS(float_rounding_mode); roundNearestEven = ( roundingMode == float_round_nearest_even ); - increment = ( (sbits64) zSig2 < 0 ); + increment = ( (int64_t) zSig2 < 0 ); if ( ! roundNearestEven ) { if ( roundingMode == float_round_to_zero ) { increment = 0; @@ -970,7 +1003,7 @@ } } } - if ( 0x7FFD <= (bits32) zExp ) { + if ( 0x7FFD <= (uint32_t) zExp ) { if ( ( 0x7FFD < zExp ) || ( ( zExp == 0x7FFD ) && eq128( @@ -998,7 +1031,10 @@ return packFloat128( zSign, 0x7FFF, 0, 0 ); } if ( zExp < 0 ) { - if ( STATUS(flush_to_zero) ) return packFloat128( zSign, 0, 0, 0 ); + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat128(zSign, 0, 0, 0); + } isTiny = ( STATUS(float_detect_tininess) == float_tininess_before_rounding ) || ( zExp < -1 ) @@ -1014,7 +1050,7 @@ zExp = 0; if ( isTiny && zSig2 ) float_raise( float_flag_underflow STATUS_VAR); if ( roundNearestEven ) { - increment = ( (sbits64) zSig2 < 0 ); + increment = ( (int64_t) zSig2 < 0 ); } else { if ( zSign ) { @@ -1050,10 +1086,10 @@ static float128 normalizeRoundAndPackFloat128( - flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 STATUS_PARAM) + flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) { int8 shiftCount; - bits64 zSig2; + uint64_t zSig2; if ( zSig0 == 0 ) { zSig0 = zSig1; @@ -1074,8 +1110,6 @@ } -#endif - /*---------------------------------------------------------------------------- | Returns the result of converting the 32-bit two's complement integer `a' | to the single-precision floating-point format. The conversion is performed @@ -1087,7 +1121,7 @@ flag zSign; if ( a == 0 ) return float32_zero; - if ( a == (sbits32) 0x80000000 ) return packFloat32( 1, 0x9E, 0 ); + if ( a == (int32_t) 0x80000000 ) return packFloat32( 1, 0x9E, 0 ); zSign = ( a < 0 ); return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a STATUS_VAR ); @@ -1104,7 +1138,7 @@ flag zSign; uint32 absA; int8 shiftCount; - bits64 zSig; + uint64_t zSig; if ( a == 0 ) return float64_zero; zSign = ( a < 0 ); @@ -1115,8 +1149,6 @@ } -#ifdef FLOATX80 - /*---------------------------------------------------------------------------- | Returns the result of converting the 32-bit two's complement integer `a' | to the extended double-precision floating-point format. The conversion @@ -1129,7 +1161,7 @@ flag zSign; uint32 absA; int8 shiftCount; - bits64 zSig; + uint64_t zSig; if ( a == 0 ) return packFloatx80( 0, 0, 0 ); zSign = ( a < 0 ); @@ -1140,10 +1172,6 @@ } -#endif - -#ifdef FLOAT128 - /*---------------------------------------------------------------------------- | Returns the result of converting the 32-bit two's complement integer `a' to | the quadruple-precision floating-point format. The conversion is performed @@ -1155,7 +1183,7 @@ flag zSign; uint32 absA; int8 shiftCount; - bits64 zSig0; + uint64_t zSig0; if ( a == 0 ) return packFloat128( 0, 0, 0, 0 ); zSign = ( a < 0 ); @@ -1166,8 +1194,6 @@ } -#endif - /*---------------------------------------------------------------------------- | Returns the result of converting the 64-bit two's complement integer `a' | to the single-precision floating-point format. The conversion is performed @@ -1232,7 +1258,7 @@ flag zSign; if ( a == 0 ) return float64_zero; - if ( a == (sbits64) LIT64( 0x8000000000000000 ) ) { + if ( a == (int64_t) LIT64( 0x8000000000000000 ) ) { return packFloat64( 1, 0x43E, 0 ); } zSign = ( a < 0 ); @@ -1247,8 +1273,6 @@ } -#ifdef FLOATX80 - /*---------------------------------------------------------------------------- | Returns the result of converting the 64-bit two's complement integer `a' | to the extended double-precision floating-point format. The conversion @@ -1270,10 +1294,6 @@ } -#endif - -#ifdef FLOAT128 - /*---------------------------------------------------------------------------- | Returns the result of converting the 64-bit two's complement integer `a' to | the quadruple-precision floating-point format. The conversion is performed @@ -1286,7 +1306,7 @@ uint64 absA; int8 shiftCount; int32 zExp; - bits64 zSig0, zSig1; + uint64_t zSig0, zSig1; if ( a == 0 ) return packFloat128( 0, 0, 0, 0 ); zSign = ( a < 0 ); @@ -1307,8 +1327,6 @@ } -#endif - /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value | `a' to the 32-bit two's complement integer format. The conversion is @@ -1323,8 +1341,8 @@ { flag aSign; int16 aExp, shiftCount; - bits32 aSig; - bits64 aSig64; + uint32_t aSig; + uint64_t aSig64; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); @@ -1354,7 +1372,7 @@ { flag aSign; int16 aExp, shiftCount; - bits32 aSig; + uint32_t aSig; int32 z; a = float32_squash_input_denormal(a STATUS_VAR); @@ -1367,7 +1385,7 @@ float_raise( float_flag_invalid STATUS_VAR); if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF; } - return (sbits32) 0x80000000; + return (int32_t) 0x80000000; } else if ( aExp <= 0x7E ) { if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; @@ -1375,7 +1393,7 @@ } aSig = ( aSig | 0x00800000 )<<8; z = aSig>>( - shiftCount ); - if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) { + if ( (uint32_t) ( aSig<<( shiftCount & 31 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } if ( aSign ) z = - z; @@ -1397,7 +1415,7 @@ { flag aSign; int16 aExp, shiftCount; - bits32 aSig; + uint32_t aSig; int32 z; aSig = extractFloat32Frac( a ); @@ -1411,7 +1429,7 @@ return 0x7FFF; } } - return (sbits32) 0xffff8000; + return (int32_t) 0xffff8000; } else if ( aExp <= 0x7E ) { if ( aExp | aSig ) { @@ -1422,7 +1440,7 @@ shiftCount -= 0x10; aSig = ( aSig | 0x00800000 )<<8; z = aSig>>( - shiftCount ); - if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) { + if ( (uint32_t) ( aSig<<( shiftCount & 31 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } if ( aSign ) { @@ -1446,8 +1464,8 @@ { flag aSign; int16 aExp, shiftCount; - bits32 aSig; - bits64 aSig64, aSigExtra; + uint32_t aSig; + uint64_t aSig64, aSigExtra; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); @@ -1459,7 +1477,7 @@ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } if ( aExp ) aSig |= 0x00800000; aSig64 = aSig; @@ -1483,8 +1501,8 @@ { flag aSign; int16 aExp, shiftCount; - bits32 aSig; - bits64 aSig64; + uint32_t aSig; + uint64_t aSig64; int64 z; a = float32_squash_input_denormal(a STATUS_VAR); @@ -1499,7 +1517,7 @@ return LIT64( 0x7FFFFFFFFFFFFFFF ); } } - return (sbits64) LIT64( 0x8000000000000000 ); + return (int64_t) LIT64( 0x8000000000000000 ); } else if ( aExp <= 0x7E ) { if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; @@ -1508,7 +1526,7 @@ aSig64 = aSig | 0x00800000; aSig64 <<= 40; z = aSig64>>( - shiftCount ); - if ( (bits64) ( aSig64<<( shiftCount & 63 ) ) ) { + if ( (uint64_t) ( aSig64<<( shiftCount & 63 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } if ( aSign ) z = - z; @@ -1527,14 +1545,14 @@ { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR )); + if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat64( aSign, 0x7FF, 0 ); } if ( aExp == 0 ) { @@ -1542,12 +1560,10 @@ normalizeFloat32Subnormal( aSig, &aExp, &aSig ); --aExp; } - return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 ); + return packFloat64( aSign, aExp + 0x380, ( (uint64_t) aSig )<<29 ); } -#ifdef FLOATX80 - /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value | `a' to the extended double-precision floating-point format. The conversion @@ -1559,14 +1575,14 @@ { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -1574,14 +1590,10 @@ normalizeFloat32Subnormal( aSig, &aExp, &aSig ); } aSig |= 0x00800000; - return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 ); + return packFloatx80( aSign, aExp + 0x3F80, ( (uint64_t) aSig )<<40 ); } -#endif - -#ifdef FLOAT128 - /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value | `a' to the double-precision floating-point format. The conversion is @@ -1593,14 +1605,14 @@ { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -1608,12 +1620,10 @@ normalizeFloat32Subnormal( aSig, &aExp, &aSig ); --aExp; } - return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 ); + return packFloat128( aSign, aExp + 0x3F80, ( (uint64_t) aSig )<<25, 0 ); } -#endif - /*---------------------------------------------------------------------------- | Rounds the single-precision floating-point value `a' to an integer, and | returns the result as a single-precision floating-point value. The @@ -1625,9 +1635,9 @@ { flag aSign; int16 aExp; - bits32 lastBitMask, roundBitsMask; + uint32_t lastBitMask, roundBitsMask; int8 roundingMode; - bits32 z; + uint32_t z; a = float32_squash_input_denormal(a STATUS_VAR); aExp = extractFloat32Exp( a ); @@ -1638,7 +1648,7 @@ return a; } if ( aExp <= 0x7E ) { - if ( (bits32) ( float32_val(a)<<1 ) == 0 ) return a; + if ( (uint32_t) ( float32_val(a)<<1 ) == 0 ) return a; STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloat32Sign( a ); switch ( STATUS(float_rounding_mode) ) { @@ -1685,7 +1695,7 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) { int16 aExp, bExp, zExp; - bits32 aSig, bSig, zSig; + uint32_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat32Frac( a ); @@ -1729,7 +1739,12 @@ return a; } if ( aExp == 0 ) { - if ( STATUS(flush_to_zero) ) return packFloat32( zSign, 0, 0 ); + if (STATUS(flush_to_zero)) { + if (aSig | bSig) { + float_raise(float_flag_output_denormal STATUS_VAR); + } + return packFloat32(zSign, 0, 0); + } return packFloat32( zSign, 0, ( aSig + bSig )>>6 ); } zSig = 0x40000000 + aSig + bSig; @@ -1739,7 +1754,7 @@ aSig |= 0x20000000; zSig = ( aSig + bSig )<<1; --zExp; - if ( (sbits32) zSig < 0 ) { + if ( (int32_t) zSig < 0 ) { zSig = aSig + bSig; ++zExp; } @@ -1759,7 +1774,7 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) { int16 aExp, bExp, zExp; - bits32 aSig, bSig, zSig; + uint32_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat32Frac( a ); @@ -1879,9 +1894,9 @@ { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits32 aSig, bSig; - bits64 zSig64; - bits32 zSig; + uint32_t aSig, bSig; + uint64_t zSig64; + uint32_t zSig; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); @@ -1922,9 +1937,9 @@ zExp = aExp + bExp - 0x7F; aSig = ( aSig | 0x00800000 )<<7; bSig = ( bSig | 0x00800000 )<<8; - shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 ); + shift64RightJamming( ( (uint64_t) aSig ) * bSig, 32, &zSig64 ); zSig = zSig64; - if ( 0 <= (sbits32) ( zSig<<1 ) ) { + if ( 0 <= (int32_t) ( zSig<<1 ) ) { zSig <<= 1; --zExp; } @@ -1942,7 +1957,7 @@ { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits32 aSig, bSig, zSig; + uint32_t aSig, bSig, zSig; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); @@ -1988,9 +2003,9 @@ aSig >>= 1; ++zExp; } - zSig = ( ( (bits64) aSig )<<32 ) / bSig; + zSig = ( ( (uint64_t) aSig )<<32 ) / bSig; if ( ( zSig & 0x3F ) == 0 ) { - zSig |= ( (bits64) bSig * zSig != ( (bits64) aSig )<<32 ); + zSig |= ( (uint64_t) bSig * zSig != ( (uint64_t) aSig )<<32 ); } return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); @@ -2006,11 +2021,11 @@ { flag aSign, zSign; int16 aExp, bExp, expDiff; - bits32 aSig, bSig; - bits32 q; - bits64 aSig64, bSig64, q64; - bits32 alternateASig; - sbits32 sigMean; + uint32_t aSig, bSig; + uint32_t q; + uint64_t aSig64, bSig64, q64; + uint32_t alternateASig; + int32_t sigMean; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); @@ -2054,7 +2069,7 @@ q = ( bSig <= aSig ); if ( q ) aSig -= bSig; if ( 0 < expDiff ) { - q = ( ( (bits64) aSig )<<32 ) / bSig; + q = ( ( (uint64_t) aSig )<<32 ) / bSig; q >>= 32 - expDiff; bSig >>= 2; aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q; @@ -2066,8 +2081,8 @@ } else { if ( bSig <= aSig ) aSig -= bSig; - aSig64 = ( (bits64) aSig )<<40; - bSig64 = ( (bits64) bSig )<<40; + aSig64 = ( (uint64_t) aSig )<<40; + bSig64 = ( (uint64_t) bSig )<<40; expDiff -= 64; while ( 0 < expDiff ) { q64 = estimateDiv128To64( aSig64, 0, bSig64 ); @@ -2086,12 +2101,12 @@ alternateASig = aSig; ++q; aSig -= bSig; - } while ( 0 <= (sbits32) aSig ); + } while ( 0 <= (int32_t) aSig ); sigMean = aSig + alternateASig; if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { aSig = alternateASig; } - zSign = ( (sbits32) aSig < 0 ); + zSign = ( (int32_t) aSig < 0 ); if ( zSign ) aSig = - aSig; return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig STATUS_VAR ); @@ -2107,8 +2122,8 @@ { flag aSign; int16 aExp, zExp; - bits32 aSig, zSig; - bits64 rem, term; + uint32_t aSig, zSig; + uint64_t rem, term; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); @@ -2138,11 +2153,11 @@ goto roundAndPack; } aSig >>= aExp & 1; - term = ( (bits64) zSig ) * zSig; - rem = ( ( (bits64) aSig )<<32 ) - term; - while ( (sbits64) rem < 0 ) { + term = ( (uint64_t) zSig ) * zSig; + rem = ( ( (uint64_t) aSig )<<32 ) - term; + while ( (int64_t) rem < 0 ) { --zSig; - rem += ( ( (bits64) zSig )<<1 ) | 1; + rem += ( ( (uint64_t) zSig )<<1 ) | 1; } zSig |= ( rem != 0 ); } @@ -2172,28 +2187,28 @@ 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 */ + const_float64( 0x3ff0000000000000ll ), /* 1 */ + const_float64( 0x3fe0000000000000ll ), /* 2 */ + const_float64( 0x3fc5555555555555ll ), /* 3 */ + const_float64( 0x3fa5555555555555ll ), /* 4 */ + const_float64( 0x3f81111111111111ll ), /* 5 */ + const_float64( 0x3f56c16c16c16c17ll ), /* 6 */ + const_float64( 0x3f2a01a01a01a01all ), /* 7 */ + const_float64( 0x3efa01a01a01a01all ), /* 8 */ + const_float64( 0x3ec71de3a556c734ll ), /* 9 */ + const_float64( 0x3e927e4fb7789f5cll ), /* 10 */ + const_float64( 0x3e5ae64567f544e4ll ), /* 11 */ + const_float64( 0x3e21eed8eff8d898ll ), /* 12 */ + const_float64( 0x3de6124613a86d09ll ), /* 13 */ + const_float64( 0x3da93974a8c07c9dll ), /* 14 */ + const_float64( 0x3d6ae7f3e733b81fll ), /* 15 */ }; float32 float32_exp2( float32 a STATUS_PARAM ) { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; float64 r, x, xn; int i; a = float32_squash_input_denormal(a STATUS_VAR); @@ -2241,7 +2256,7 @@ { flag aSign, zSign; int16 aExp; - bits32 aSig, zSig, i; + uint32_t aSig, zSig, i; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); @@ -2267,7 +2282,7 @@ zSig = aExp << 23; for (i = 1 << 22; i > 0; i >>= 1) { - aSig = ( (bits64)aSig * aSig ) >> 23; + aSig = ( (uint64_t)aSig * aSig ) >> 23; if ( aSig & 0x01000000 ) { aSig >>= 1; zSig |= i; @@ -2282,39 +2297,39 @@ /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The comparison is performed +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float32_eq( float32 a, float32 b STATUS_PARAM ) { + uint32_t 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 ) ) ) { - if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } - return ( float32_val(a) == float32_val(b) ) || - ( (bits32) ( ( float32_val(a) | float32_val(b) )<<1 ) == 0 ); - + av = float32_val(a); + bv = float32_val(b); + return ( av == bv ) || ( (uint32_t) ( ( av | bv )<<1 ) == 0 ); } /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is less than -| or equal to the corresponding value `b', and 0 otherwise. The comparison -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| or equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float32_le( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); @@ -2328,21 +2343,22 @@ bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint32_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is less than -| the corresponding value `b', and 0 otherwise. The comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float32_lt( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); @@ -2356,21 +2372,20 @@ bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint32_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- -| Returns 1 if the single-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The invalid exception is -| raised if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| Returns 1 if the single-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float32_eq_signaling( float32 a, float32 b STATUS_PARAM ) +int float32_unordered( 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); @@ -2378,12 +2393,33 @@ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) ) { float_raise( float_flag_invalid STATUS_VAR); - return 0; + return 1; } - av = float32_val(a); - bv = float32_val(b); - return ( av == bv ) || ( (bits32) ( ( av | bv )<<1 ) == 0 ); + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_eq_quiet( 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 ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + return ( float32_val(a) == float32_val(b) ) || + ( (uint32_t) ( ( float32_val(a) | float32_val(b) )<<1 ) == 0 ); } /*---------------------------------------------------------------------------- @@ -2396,7 +2432,7 @@ int float32_le_quiet( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); @@ -2412,7 +2448,7 @@ bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint32_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } @@ -2427,7 +2463,7 @@ int float32_lt_quiet( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; - bits32 av, bv; + uint32_t av, bv; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); @@ -2443,12 +2479,35 @@ bSign = extractFloat32Sign( b ); av = float32_val(a); bv = float32_val(b); - if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint32_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_unordered_quiet( 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 ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value | `a' to the 32-bit two's complement integer format. The conversion is | performed according to the IEC/IEEE Standard for Binary Floating-Point @@ -2462,7 +2521,7 @@ { flag aSign; int16 aExp, shiftCount; - bits64 aSig; + uint64_t aSig; a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); @@ -2490,7 +2549,7 @@ { flag aSign; int16 aExp, shiftCount; - bits64 aSig, savedASig; + uint64_t aSig, savedASig; int32 z; a = float64_squash_input_denormal(a STATUS_VAR); @@ -2514,7 +2573,7 @@ if ( ( z < 0 ) ^ aSign ) { invalid: float_raise( float_flag_invalid STATUS_VAR); - return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig<>( - shiftCount ); - if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) { + if ( (uint64_t) ( aSig<<( shiftCount & 63 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } } @@ -2681,15 +2740,15 @@ { flag aSign; int16 aExp; - bits64 aSig; - bits32 zSig; + uint64_t aSig; + uint32_t zSig; a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat32( aSign, 0xFF, 0 ); } shift64RightJamming( aSig, 22, &aSig ); @@ -2713,29 +2772,28 @@ | than the desired result exponent whenever `zSig' is a complete, normalized | significand. *----------------------------------------------------------------------------*/ -static bits16 packFloat16(flag zSign, int16 zExp, bits16 zSig) +static float16 packFloat16(flag zSign, int16 zExp, uint16_t zSig) { - return (((bits32)zSign) << 15) + (((bits32)zExp) << 10) + zSig; + return make_float16( + (((uint32_t)zSign) << 15) + (((uint32_t)zExp) << 10) + zSig); } /* Half precision floats come in two formats: standard IEEE and "ARM" format. The latter gains extra exponent range by omitting the NaN/Inf encodings. */ - -float32 float16_to_float32( bits16 a, flag ieee STATUS_PARAM ) + +float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) { flag aSign; int16 aExp; - bits32 aSig; + uint32_t aSig; - aSign = a >> 15; - aExp = (a >> 10) & 0x1f; - aSig = a & 0x3ff; + aSign = extractFloat16Sign(a); + aExp = extractFloat16Exp(a); + aSig = extractFloat16Frac(a); if (aExp == 0x1f && ieee) { if (aSig) { - /* Make sure correct exceptions are raised. */ - float32ToCommonNaN(a STATUS_VAR); - aSig |= 0x200; + return commonNaNToFloat32(float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); } return packFloat32(aSign, 0xff, aSig << 13); } @@ -2753,13 +2811,13 @@ return packFloat32( aSign, aExp + 0x70, aSig << 13); } -bits16 float32_to_float16( float32 a, flag ieee STATUS_PARAM) +float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM) { flag aSign; int16 aExp; - bits32 aSig; - bits32 mask; - bits32 increment; + uint32_t aSig; + uint32_t mask; + uint32_t increment; int8 roundingMode; a = float32_squash_input_denormal(a STATUS_VAR); @@ -2768,24 +2826,30 @@ aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { if (aSig) { - /* Make sure correct exceptions are raised. */ - float32ToCommonNaN(a STATUS_VAR); - aSig |= 0x00400000; + /* Input is a NaN */ + float16 r = commonNaNToFloat16( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); + if (!ieee) { + return packFloat16(aSign, 0, 0); + } + return r; + } + /* Infinity */ + if (!ieee) { + float_raise(float_flag_invalid STATUS_VAR); + return packFloat16(aSign, 0x1f, 0x3ff); } - return packFloat16(aSign, 0x1f, aSig >> 13); + return packFloat16(aSign, 0x1f, 0); } - if (aExp == 0 && aSign == 0) { + if (aExp == 0 && aSig == 0) { return packFloat16(aSign, 0, 0); } /* Decimal point between bits 22 and 23. */ aSig |= 0x00800000; aExp -= 0x7f; if (aExp < -14) { - mask = 0x007fffff; - if (aExp < -24) { - aExp = -25; - } else { - mask >>= 24 + aExp; + mask = 0x00ffffff; + if (aExp >= -24) { + mask >>= 25 + aExp; } } else { mask = 0x00001fff; @@ -2827,7 +2891,7 @@ } } else { if (aExp > 16) { - float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR); + float_raise(float_flag_invalid | float_flag_inexact STATUS_VAR); return packFloat16(aSign, 0x1f, 0x3ff); } } @@ -2841,8 +2905,6 @@ return packFloat16(aSign, aExp + 14, aSig >> 13); } -#ifdef FLOATX80 - /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value | `a' to the extended double-precision floating-point format. The conversion @@ -2854,14 +2916,14 @@ { flag aSign; int16 aExp; - bits64 aSig; + uint64_t aSig; a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -2874,10 +2936,6 @@ } -#endif - -#ifdef FLOAT128 - /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value | `a' to the quadruple-precision floating-point format. The conversion is @@ -2889,14 +2947,14 @@ { flag aSign; int16 aExp; - bits64 aSig, zSig0, zSig1; + uint64_t aSig, zSig0, zSig1; a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -2909,8 +2967,6 @@ } -#endif - /*---------------------------------------------------------------------------- | Rounds the double-precision floating-point value `a' to an integer, and | returns the result as a double-precision floating-point value. The @@ -2922,9 +2978,9 @@ { flag aSign; int16 aExp; - bits64 lastBitMask, roundBitsMask; + uint64_t lastBitMask, roundBitsMask; int8 roundingMode; - bits64 z; + uint64_t z; a = float64_squash_input_denormal(a STATUS_VAR); aExp = extractFloat64Exp( a ); @@ -2935,7 +2991,7 @@ return a; } if ( aExp < 0x3FF ) { - if ( (bits64) ( float64_val(a)<<1 ) == 0 ) return a; + if ( (uint64_t) ( float64_val(a)<<1 ) == 0 ) return a; STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloat64Sign( a ); switch ( STATUS(float_rounding_mode) ) { @@ -2995,7 +3051,7 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) { int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig; + uint64_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat64Frac( a ); @@ -3039,7 +3095,12 @@ return a; } if ( aExp == 0 ) { - if ( STATUS(flush_to_zero) ) return packFloat64( zSign, 0, 0 ); + if (STATUS(flush_to_zero)) { + if (aSig | bSig) { + float_raise(float_flag_output_denormal STATUS_VAR); + } + return packFloat64(zSign, 0, 0); + } return packFloat64( zSign, 0, ( aSig + bSig )>>9 ); } zSig = LIT64( 0x4000000000000000 ) + aSig + bSig; @@ -3049,7 +3110,7 @@ aSig |= LIT64( 0x2000000000000000 ); zSig = ( aSig + bSig )<<1; --zExp; - if ( (sbits64) zSig < 0 ) { + if ( (int64_t) zSig < 0 ) { zSig = aSig + bSig; ++zExp; } @@ -3069,7 +3130,7 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) { int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig; + uint64_t aSig, bSig, zSig; int16 expDiff; aSig = extractFloat64Frac( a ); @@ -3189,7 +3250,7 @@ { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3232,7 +3293,7 @@ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; mul64To128( aSig, bSig, &zSig0, &zSig1 ); zSig0 |= ( zSig1 != 0 ); - if ( 0 <= (sbits64) ( zSig0<<1 ) ) { + if ( 0 <= (int64_t) ( zSig0<<1 ) ) { zSig0 <<= 1; --zExp; } @@ -3250,9 +3311,9 @@ { flag aSign, bSign, zSign; int16 aExp, bExp, zExp; - bits64 aSig, bSig, zSig; - bits64 rem0, rem1; - bits64 term0, term1; + uint64_t aSig, bSig, zSig; + uint64_t rem0, rem1; + uint64_t term0, term1; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3302,7 +3363,7 @@ if ( ( zSig & 0x1FF ) <= 2 ) { mul64To128( bSig, zSig, &term0, &term1 ); sub128( aSig, 0, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig; add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); } @@ -3322,9 +3383,9 @@ { flag aSign, zSign; int16 aExp, bExp, expDiff; - bits64 aSig, bSig; - bits64 q, alternateASig; - sbits64 sigMean; + uint64_t aSig, bSig; + uint64_t q, alternateASig; + int64_t sigMean; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3387,12 +3448,12 @@ alternateASig = aSig; ++q; aSig -= bSig; - } while ( 0 <= (sbits64) aSig ); + } while ( 0 <= (int64_t) aSig ); sigMean = aSig + alternateASig; if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { aSig = alternateASig; } - zSign = ( (sbits64) aSig < 0 ); + zSign = ( (int64_t) aSig < 0 ); if ( zSign ) aSig = - aSig; return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig STATUS_VAR ); @@ -3408,8 +3469,8 @@ { flag aSign; int16 aExp, zExp; - bits64 aSig, zSig, doubleZSig; - bits64 rem0, rem1, term0, term1; + uint64_t aSig, zSig, doubleZSig; + uint64_t rem0, rem1, term0, term1; a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); @@ -3439,7 +3500,7 @@ doubleZSig = zSig<<1; mul64To128( zSig, zSig, &term0, &term1 ); sub128( aSig, 0, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig; doubleZSig -= 2; add128( rem0, rem1, zSig>>63, doubleZSig | 1, &rem0, &rem1 ); @@ -3459,7 +3520,7 @@ { flag aSign, zSign; int16 aExp; - bits64 aSig, aSig0, aSig1, zSig, i; + uint64_t aSig, aSig0, aSig1, zSig, i; a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); @@ -3482,7 +3543,7 @@ aExp -= 0x3FF; aSig |= LIT64( 0x0010000000000000 ); zSign = aExp < 0; - zSig = (bits64)aExp << 52; + zSig = (uint64_t)aExp << 52; for (i = 1LL << 51; i > 0; i >>= 1) { mul64To128( aSig, aSig, &aSig0, &aSig1 ); aSig = ( aSig0 << 12 ) | ( aSig1 >> 52 ); @@ -3499,41 +3560,40 @@ /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is equal to the -| corresponding value `b', and 0 otherwise. The comparison is performed +| corresponding value `b', and 0 otherwise. The invalid exception is raised +| if either operand is a NaN. Otherwise, the comparison is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float64_eq( float64 a, float64 b STATUS_PARAM ) { - bits64 av, bv; + uint64_t av, bv; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { - if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } av = float64_val(a); bv = float64_val(b); - return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + return ( av == bv ) || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); } /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is less than or -| equal to the corresponding value `b', and 0 otherwise. The comparison is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float64_le( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3547,21 +3607,22 @@ bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is less than -| the corresponding value `b', and 0 otherwise. The comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float64_lt( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3575,21 +3636,20 @@ bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint64_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } /*---------------------------------------------------------------------------- -| Returns 1 if the double-precision floating-point value `a' is equal to the -| corresponding value `b', and 0 otherwise. The invalid exception is raised -| if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| Returns 1 if the double-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float64_eq_signaling( float64 a, float64 b STATUS_PARAM ) +int float64_unordered( float64 a, float64 b STATUS_PARAM ) { - bits64 av, bv; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3597,11 +3657,35 @@ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) ) { float_raise( float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is equal to the +| corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception.The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_eq_quiet( float64 a, float64 b STATUS_PARAM ) +{ + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } return 0; } av = float64_val(a); bv = float64_val(b); - return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + return ( av == bv ) || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); } @@ -3615,7 +3699,7 @@ int float64_le_quiet( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3631,7 +3715,7 @@ bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 ); + if ( aSign != bSign ) return aSign || ( (uint64_t) ( ( av | bv )<<1 ) == 0 ); return ( av == bv ) || ( aSign ^ ( av < bv ) ); } @@ -3646,7 +3730,7 @@ int float64_lt_quiet( float64 a, float64 b STATUS_PARAM ) { flag aSign, bSign; - bits64 av, bv; + uint64_t av, bv; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); @@ -3662,12 +3746,33 @@ bSign = extractFloat64Sign( b ); av = float64_val(a); bv = float64_val(b); - if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 ); + if ( aSign != bSign ) return aSign && ( (uint64_t) ( ( av | bv )<<1 ) != 0 ); return ( av != bv ) && ( aSign ^ ( av < bv ) ); } -#ifdef FLOATX80 +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_unordered_quiet( float64 a, float64 b STATUS_PARAM ) +{ + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} /*---------------------------------------------------------------------------- | Returns the result of converting the extended double-precision floating- @@ -3683,12 +3788,12 @@ { flag aSign; int32 aExp, shiftCount; - bits64 aSig; + uint64_t aSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); - if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) aSign = 0; shiftCount = 0x4037 - aExp; if ( shiftCount <= 0 ) shiftCount = 1; shift64RightJamming( aSig, shiftCount, &aSig ); @@ -3710,14 +3815,14 @@ { flag aSign; int32 aExp, shiftCount; - bits64 aSig, savedASig; + uint64_t aSig, savedASig; int32 z; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( 0x401E < aExp ) { - if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) aSign = 0; goto invalid; } else if ( aExp < 0x3FFF ) { @@ -3732,7 +3837,7 @@ if ( ( z < 0 ) ^ aSign ) { invalid: float_raise( float_flag_invalid STATUS_VAR); - return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig<>( - shiftCount ); - if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) { + if ( (uint64_t) ( aSig<<( shiftCount & 63 ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } if ( aSign ) z = - z; @@ -3836,14 +3941,14 @@ { flag aSign; int32 aExp; - bits64 aSig; + uint64_t aSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) ); + if ( (uint64_t) ( aSig<<1 ) ) { + return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat32( aSign, 0xFF, 0 ); } @@ -3864,14 +3969,14 @@ { flag aSign; int32 aExp; - bits64 aSig, zSig; + uint64_t aSig, zSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) ); + if ( (uint64_t) ( aSig<<1 ) ) { + return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat64( aSign, 0x7FF, 0 ); } @@ -3881,8 +3986,6 @@ } -#ifdef FLOAT128 - /*---------------------------------------------------------------------------- | Returns the result of converting the extended double-precision floating- | point value `a' to the quadruple-precision floating-point format. The @@ -3894,21 +3997,19 @@ { flag aSign; int16 aExp; - bits64 aSig, zSig0, zSig1; + uint64_t aSig, zSig0, zSig1; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); - if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) ); + if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) { + return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 ); return packFloat128( aSign, aExp, zSig0, zSig1 ); } -#endif - /*---------------------------------------------------------------------------- | Rounds the extended double-precision floating-point value `a' to an integer, | and returns the result as an extended quadruple-precision floating-point @@ -3920,27 +4021,27 @@ { flag aSign; int32 aExp; - bits64 lastBitMask, roundBitsMask; + uint64_t lastBitMask, roundBitsMask; int8 roundingMode; floatx80 z; aExp = extractFloatx80Exp( a ); if ( 0x403E <= aExp ) { - if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) { + if ( ( aExp == 0x7FFF ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) { return propagateFloatx80NaN( a, a STATUS_VAR ); } return a; } if ( aExp < 0x3FFF ) { if ( ( aExp == 0 ) - && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) { + && ( (uint64_t) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) { return a; } STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloatx80Sign( a ); switch ( STATUS(float_rounding_mode) ) { case float_round_nearest_even: - if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 ) + if ( ( aExp == 0x3FFE ) && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) { return packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) ); @@ -3993,7 +4094,7 @@ static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM) { int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; int32 expDiff; aSig = extractFloatx80Frac( a ); @@ -4003,7 +4104,7 @@ expDiff = aExp - bExp; if ( 0 < expDiff ) { if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return a; } if ( bExp == 0 ) --expDiff; @@ -4012,7 +4113,7 @@ } else if ( expDiff < 0 ) { if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) ++expDiff; @@ -4021,7 +4122,7 @@ } else { if ( aExp == 0x7FFF ) { - if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + if ( (uint64_t) ( ( aSig | bSig )<<1 ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } return a; @@ -4036,7 +4137,7 @@ goto shiftRight1; } zSig0 = aSig + bSig; - if ( (sbits64) zSig0 < 0 ) goto roundAndPack; + if ( (int64_t) zSig0 < 0 ) goto roundAndPack; shiftRight1: shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 ); zSig0 |= LIT64( 0x8000000000000000 ); @@ -4059,7 +4160,7 @@ static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM ) { int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; int32 expDiff; floatx80 z; @@ -4071,7 +4172,7 @@ if ( 0 < expDiff ) goto aExpBigger; if ( expDiff < 0 ) goto bExpBigger; if ( aExp == 0x7FFF ) { - if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + if ( (uint64_t) ( ( aSig | bSig )<<1 ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } float_raise( float_flag_invalid STATUS_VAR); @@ -4089,7 +4190,7 @@ return packFloatx80( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); bExpBigger: if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) ++expDiff; @@ -4101,7 +4202,7 @@ goto normalizeRoundAndPack; aExpBigger: if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return a; } if ( bExp == 0 ) --expDiff; @@ -4168,7 +4269,7 @@ { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; + uint64_t aSig, bSig, zSig0, zSig1; floatx80 z; aSig = extractFloatx80Frac( a ); @@ -4179,15 +4280,15 @@ bSign = extractFloatx80Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) - || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + if ( (uint64_t) ( aSig<<1 ) + || ( ( bExp == 0x7FFF ) && (uint64_t) ( bSig<<1 ) ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } if ( ( bExp | bSig ) == 0 ) goto invalid; return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); if ( ( aExp | aSig ) == 0 ) { invalid: float_raise( float_flag_invalid STATUS_VAR); @@ -4207,7 +4308,7 @@ } zExp = aExp + bExp - 0x3FFE; mul64To128( aSig, bSig, &zSig0, &zSig1 ); - if ( 0 < (sbits64) zSig0 ) { + if ( 0 < (int64_t) zSig0 ) { shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 ); --zExp; } @@ -4227,8 +4328,8 @@ { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig, bSig, zSig0, zSig1; - bits64 rem0, rem1, rem2, term0, term1, term2; + uint64_t aSig, bSig, zSig0, zSig1; + uint64_t rem0, rem1, rem2, term0, term1, term2; floatx80 z; aSig = extractFloatx80Frac( a ); @@ -4239,15 +4340,15 @@ bSign = extractFloatx80Sign( b ); zSign = aSign ^ bSign; if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); goto invalid; } return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return packFloatx80( zSign, 0, 0 ); } if ( bExp == 0 ) { @@ -4277,15 +4378,15 @@ zSig0 = estimateDiv128To64( aSig, rem1, bSig ); mul64To128( bSig, zSig0, &term0, &term1 ); sub128( aSig, rem1, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); } zSig1 = estimateDiv128To64( rem1, 0, bSig ); - if ( (bits64) ( zSig1<<1 ) <= 8 ) { + if ( (uint64_t) ( zSig1<<1 ) <= 8 ) { mul64To128( bSig, zSig1, &term1, &term2 ); sub128( rem1, 0, term1, term2, &rem1, &rem2 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; add128( rem1, rem2, 0, bSig, &rem1, &rem2 ); } @@ -4307,8 +4408,8 @@ { flag aSign, zSign; int32 aExp, bExp, expDiff; - bits64 aSig0, aSig1, bSig; - bits64 q, term0, term1, alternateASig0, alternateASig1; + uint64_t aSig0, aSig1, bSig; + uint64_t q, term0, term1, alternateASig0, alternateASig1; floatx80 z; aSig0 = extractFloatx80Frac( a ); @@ -4317,14 +4418,14 @@ bSig = extractFloatx80Frac( b ); bExp = extractFloatx80Exp( b ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig0<<1 ) - || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + if ( (uint64_t) ( aSig0<<1 ) + || ( ( bExp == 0x7FFF ) && (uint64_t) ( bSig<<1 ) ) ) { return propagateFloatx80NaN( a, b STATUS_VAR ); } goto invalid; } if ( bExp == 0x7FFF ) { - if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( (uint64_t) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); return a; } if ( bExp == 0 ) { @@ -4338,7 +4439,7 @@ normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); } if ( aExp == 0 ) { - if ( (bits64) ( aSig0<<1 ) == 0 ) return a; + if ( (uint64_t) ( aSig0<<1 ) == 0 ) return a; normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 ); } bSig |= LIT64( 0x8000000000000000 ); @@ -4403,15 +4504,15 @@ { flag aSign; int32 aExp, zExp; - bits64 aSig0, aSig1, zSig0, zSig1, doubleZSig0; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + uint64_t aSig0, aSig1, zSig0, zSig1, doubleZSig0; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; floatx80 z; aSig0 = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { - if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR ); + if ( (uint64_t) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR ); if ( ! aSign ) return a; goto invalid; } @@ -4434,7 +4535,7 @@ doubleZSig0 = zSig0<<1; mul64To128( zSig0, zSig0, &term0, &term1 ); sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; doubleZSig0 -= 2; add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 ); @@ -4446,7 +4547,7 @@ sub128( rem1, 0, term1, term2, &rem1, &rem2 ); mul64To128( zSig1, zSig1, &term2, &term3 ); sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; shortShift128Left( 0, zSig1, 1, &term2, &term3 ); term3 |= 1; @@ -4464,31 +4565,28 @@ } /*---------------------------------------------------------------------------- -| Returns 1 if the extended double-precision floating-point value `a' is -| equal to the corresponding value `b', and 0 otherwise. The comparison is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| Returns 1 if the extended double-precision floating-point value `a' is equal +| to the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM ) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - if ( floatx80_is_signaling_nan( a ) - || floatx80_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint16_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } @@ -4496,8 +4594,9 @@ /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is | less than or equal to the corresponding value `b', and 0 otherwise. The -| comparison is performed according to the IEC/IEEE Standard for Binary -| Floating-Point Arithmetic. +| invalid exception is raised if either operand is a NaN. The comparison is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. *----------------------------------------------------------------------------*/ int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) @@ -4505,9 +4604,9 @@ flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { float_raise( float_flag_invalid STATUS_VAR); return 0; @@ -4517,7 +4616,7 @@ if ( aSign != bSign ) { return aSign - || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -4528,9 +4627,9 @@ /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is -| less than the corresponding value `b', and 0 otherwise. The comparison -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| less than the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) @@ -4538,9 +4637,9 @@ flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { float_raise( float_flag_invalid STATUS_VAR); return 0; @@ -4550,7 +4649,7 @@ if ( aSign != bSign ) { return aSign - && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -4560,28 +4659,50 @@ } /*---------------------------------------------------------------------------- -| Returns 1 if the extended double-precision floating-point value `a' is equal -| to the corresponding value `b', and 0 otherwise. The invalid exception is -| raised if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| Returns 1 if the extended double-precision floating-point values `a' and `b' +| cannot be compared, and 0 otherwise. The invalid exception is raised if +| either operand is a NaN. The comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ +int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM ) +{ + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM ) +int floatx80_eq_quiet( floatx80 a, floatx80 b STATUS_PARAM ) { if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint16_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } @@ -4598,9 +4719,9 @@ flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { @@ -4613,7 +4734,7 @@ if ( aSign != bSign ) { return aSign - || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -4634,9 +4755,9 @@ flag aSign, bSign; if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) || ( ( extractFloatx80Exp( b ) == 0x7FFF ) - && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) ) { if ( floatx80_is_signaling_nan( a ) || floatx80_is_signaling_nan( b ) ) { @@ -4649,7 +4770,7 @@ if ( aSign != bSign ) { return aSign - && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint16_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -4658,9 +4779,27 @@ } -#endif - -#ifdef FLOAT128 +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point values `a' and `b' +| cannot be compared, and 0 otherwise. Quiet NaNs do not cause an exception. +| The comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ +int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +{ + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (uint64_t) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} /*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point @@ -4676,7 +4815,7 @@ { flag aSign; int32 aExp, shiftCount; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4705,7 +4844,7 @@ { flag aSign; int32 aExp, shiftCount; - bits64 aSig0, aSig1, savedASig; + uint64_t aSig0, aSig1, savedASig; int32 z; aSig1 = extractFloat128Frac1( a ); @@ -4730,7 +4869,7 @@ if ( ( z < 0 ) ^ aSign ) { invalid: float_raise( float_flag_invalid STATUS_VAR); - return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; } if ( ( aSig0<>( ( - shiftCount ) & 63 ) ); - if ( (bits64) ( aSig1<>( - shiftCount ); if ( aSig1 - || ( shiftCount && (bits64) ( aSig0<<( shiftCount & 63 ) ) ) ) { + || ( shiftCount && (uint64_t) ( aSig0<<( shiftCount & 63 ) ) ) ) { STATUS(float_exception_flags) |= float_flag_inexact; } } @@ -4854,8 +4993,8 @@ { flag aSign; int32 aExp; - bits64 aSig0, aSig1; - bits32 zSig; + uint64_t aSig0, aSig1; + uint32_t zSig; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4863,7 +5002,7 @@ aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat32( aSign, 0xFF, 0 ); } @@ -4889,7 +5028,7 @@ { flag aSign; int32 aExp; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4897,7 +5036,7 @@ aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat64( aSign, 0x7FF, 0 ); } @@ -4911,8 +5050,6 @@ } -#ifdef FLOATX80 - /*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point | value `a' to the extended double-precision floating-point format. The @@ -4924,7 +5061,7 @@ { flag aSign; int32 aExp; - bits64 aSig0, aSig1; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); @@ -4932,7 +5069,7 @@ aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } @@ -4948,8 +5085,6 @@ } -#endif - /*---------------------------------------------------------------------------- | Rounds the quadruple-precision floating-point value `a' to an integer, and | returns the result as a quadruple-precision floating-point value. The @@ -4961,7 +5096,7 @@ { flag aSign; int32 aExp; - bits64 lastBitMask, roundBitsMask; + uint64_t lastBitMask, roundBitsMask; int8 roundingMode; float128 z; @@ -4986,9 +5121,9 @@ if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; } else { - if ( (sbits64) z.low < 0 ) { + if ( (int64_t) z.low < 0 ) { ++z.high; - if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1; + if ( (uint64_t) ( z.low<<1 ) == 0 ) z.high &= ~1; } } } @@ -5002,7 +5137,7 @@ } else { if ( aExp < 0x3FFF ) { - if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a; + if ( ( ( (uint64_t) ( a.high<<1 ) ) | a.low ) == 0 ) return a; STATUS(float_exception_flags) |= float_flag_inexact; aSign = extractFloat128Sign( a ); switch ( STATUS(float_rounding_mode) ) { @@ -5064,7 +5199,7 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) { int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; int32 expDiff; aSig1 = extractFloat128Frac1( a ); @@ -5113,7 +5248,12 @@ } add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); if ( aExp == 0 ) { - if ( STATUS(flush_to_zero) ) return packFloat128( zSign, 0, 0, 0 ); + if (STATUS(flush_to_zero)) { + if (zSig0 | zSig1) { + float_raise(float_flag_output_denormal STATUS_VAR); + } + return packFloat128(zSign, 0, 0, 0); + } return packFloat128( zSign, 0, zSig0, zSig1 ); } zSig2 = 0; @@ -5145,7 +5285,7 @@ static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) { int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; int32 expDiff; float128 z; @@ -5270,7 +5410,7 @@ { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5334,8 +5474,8 @@ { flag aSign, bSign, zSign; int32 aExp, bExp, zExp; - bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5389,7 +5529,7 @@ zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 ); mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 ); sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 ); } @@ -5397,7 +5537,7 @@ if ( ( zSig1 & 0x3FFF ) <= 4 ) { mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 ); sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 ); } @@ -5418,9 +5558,9 @@ { flag aSign, zSign; int32 aExp, bExp, expDiff; - bits64 aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2; - bits64 allZero, alternateASig0, alternateASig1, sigMean1; - sbits64 sigMean0; + uint64_t aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2; + uint64_t allZero, alternateASig0, alternateASig1, sigMean1; + int64_t sigMean0; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5502,15 +5642,15 @@ alternateASig1 = aSig1; ++q; sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); - } while ( 0 <= (sbits64) aSig0 ); + } while ( 0 <= (int64_t) aSig0 ); add128( - aSig0, aSig1, alternateASig0, alternateASig1, (bits64 *)&sigMean0, &sigMean1 ); + aSig0, aSig1, alternateASig0, alternateASig1, (uint64_t *)&sigMean0, &sigMean1 ); if ( ( sigMean0 < 0 ) || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) { aSig0 = alternateASig0; aSig1 = alternateASig1; } - zSign = ( (sbits64) aSig0 < 0 ); + zSign = ( (int64_t) aSig0 < 0 ); if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 ); return normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR ); @@ -5527,8 +5667,8 @@ { flag aSign; int32 aExp, zExp; - bits64 aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0; - bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + uint64_t aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; float128 z; aSig1 = extractFloat128Frac1( a ); @@ -5560,7 +5700,7 @@ doubleZSig0 = zSig0<<1; mul64To128( zSig0, zSig0, &term0, &term1 ); sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); - while ( (sbits64) rem0 < 0 ) { + while ( (int64_t) rem0 < 0 ) { --zSig0; doubleZSig0 -= 2; add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 ); @@ -5572,7 +5712,7 @@ sub128( rem1, 0, term1, term2, &rem1, &rem2 ); mul64To128( zSig1, zSig1, &term2, &term3 ); sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); - while ( (sbits64) rem1 < 0 ) { + while ( (int64_t) rem1 < 0 ) { --zSig1; shortShift128Left( 0, zSig1, 1, &term2, &term3 ); term3 |= 1; @@ -5588,7 +5728,8 @@ /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The comparison is performed +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed | according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ @@ -5600,26 +5741,23 @@ || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - if ( float128_is_signaling_nan( a ) - || float128_is_signaling_nan( b ) ) { - float_raise( float_flag_invalid STATUS_VAR); - } + float_raise( float_flag_invalid STATUS_VAR); return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint64_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is less than -| or equal to the corresponding value `b', and 0 otherwise. The comparison -| is performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. +| or equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float128_le( float128 a, float128 b STATUS_PARAM ) @@ -5639,7 +5777,7 @@ if ( aSign != bSign ) { return aSign - || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -5650,8 +5788,9 @@ /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is less than -| the corresponding value `b', and 0 otherwise. The comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ int float128_lt( float128 a, float128 b STATUS_PARAM ) @@ -5671,7 +5810,7 @@ if ( aSign != bSign ) { return aSign - && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -5681,13 +5820,33 @@ } /*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_unordered( float128 a, float128 b STATUS_PARAM ) +{ + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is equal to -| the corresponding value `b', and 0 otherwise. The invalid exception is -| raised if either operand is a NaN. Otherwise, the comparison is performed -| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ -int float128_eq_signaling( float128 a, float128 b STATUS_PARAM ) +int float128_eq_quiet( float128 a, float128 b STATUS_PARAM ) { if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) @@ -5695,14 +5854,17 @@ || ( ( extractFloat128Exp( b ) == 0x7FFF ) && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) ) { - float_raise( float_flag_invalid STATUS_VAR); + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } return 0; } return ( a.low == b.low ) && ( ( a.high == b.high ) || ( ( a.low == 0 ) - && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + && ( (uint64_t) ( ( a.high | b.high )<<1 ) == 0 ) ) ); } @@ -5734,7 +5896,7 @@ if ( aSign != bSign ) { return aSign - || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + || ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) == 0 ); } return @@ -5770,7 +5932,7 @@ if ( aSign != bSign ) { return aSign - && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + && ( ( ( (uint64_t) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) != 0 ); } return @@ -5779,7 +5941,28 @@ } -#endif +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_unordered_quiet( float128 a, float128 b STATUS_PARAM ) +{ + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} /* misc functions */ float32 uint32_to_float32( unsigned int a STATUS_PARAM ) @@ -5928,7 +6111,7 @@ int is_quiet STATUS_PARAM ) \ { \ flag aSign, bSign; \ - bits ## s av, bv; \ + uint ## s ## _t av, bv; \ a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ \ @@ -5948,7 +6131,7 @@ av = float ## s ## _val(a); \ bv = float ## s ## _val(b); \ if ( aSign != bSign ) { \ - if ( (bits ## s) ( ( av | bv )<<1 ) == 0 ) { \ + if ( (uint ## s ## _t) ( ( av | bv )<<1 ) == 0 ) { \ /* zero case */ \ return float_relation_equal; \ } else { \ @@ -5976,6 +6159,52 @@ COMPARE(32, 0xff) COMPARE(64, 0x7ff) +INLINE int floatx80_compare_internal( floatx80 a, floatx80 b, + int is_quiet STATUS_PARAM ) +{ + flag aSign, bSign; + + if (( ( extractFloatx80Exp( a ) == 0x7fff ) && + ( extractFloatx80Frac( a )<<1 ) ) || + ( ( extractFloatx80Exp( b ) == 0x7fff ) && + ( extractFloatx80Frac( b )<<1 ) )) { + if (!is_quiet || + floatx80_is_signaling_nan( a ) || + floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return float_relation_unordered; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + + if ( ( ( (uint16_t) ( ( a.high | b.high ) << 1 ) ) == 0) && + ( ( a.low | b.low ) == 0 ) ) { + /* zero case */ + return float_relation_equal; + } else { + return 1 - (2 * aSign); + } + } else { + if (a.low == b.low && a.high == b.high) { + return float_relation_equal; + } else { + return 1 - 2 * (aSign ^ ( lt128( a.high, a.low, b.high, b.low ) )); + } + } +} + +int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM ) +{ + return floatx80_compare_internal(a, b, 0 STATUS_VAR); +} + +int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +{ + return floatx80_compare_internal(a, b, 1 STATUS_VAR); +} + INLINE int float128_compare_internal( float128 a, float128 b, int is_quiet STATUS_PARAM ) { @@ -6020,12 +6249,61 @@ return float128_compare_internal(a, b, 1 STATUS_VAR); } +/* min() and max() functions. These can't be implemented as + * 'compare and pick one input' because that would mishandle + * NaNs and +0 vs -0. + */ +#define MINMAX(s, nan_exp) \ +INLINE float ## s float ## s ## _minmax(float ## s a, float ## s b, \ + int ismin STATUS_PARAM ) \ +{ \ + flag aSign, bSign; \ + uint ## s ## _t av, bv; \ + a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ + b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ + if (float ## s ## _is_any_nan(a) || \ + float ## s ## _is_any_nan(b)) { \ + return propagateFloat ## s ## NaN(a, b STATUS_VAR); \ + } \ + aSign = extractFloat ## s ## Sign(a); \ + bSign = extractFloat ## s ## Sign(b); \ + av = float ## s ## _val(a); \ + bv = float ## s ## _val(b); \ + if (aSign != bSign) { \ + if (ismin) { \ + return aSign ? a : b; \ + } else { \ + return aSign ? b : a; \ + } \ + } else { \ + if (ismin) { \ + return (aSign ^ (av < bv)) ? a : b; \ + } else { \ + return (aSign ^ (av < bv)) ? b : a; \ + } \ + } \ +} \ + \ +float ## s float ## s ## _min(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 1 STATUS_VAR); \ +} \ + \ +float ## s float ## s ## _max(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 0 STATUS_VAR); \ +} + +MINMAX(32, 0xff) +MINMAX(64, 0x7ff) + + /* Multiply A by 2 raised to the power N. */ float32 float32_scalbn( float32 a, int n STATUS_PARAM ) { flag aSign; - int16 aExp; - bits32 aSig; + int16_t aExp; + uint32_t aSig; a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); @@ -6033,6 +6311,9 @@ aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { + if ( aSig ) { + return propagateFloat32NaN( a, a STATUS_VAR ); + } return a; } if ( aExp != 0 ) @@ -6040,6 +6321,12 @@ else if ( aSig == 0 ) return a; + if (n > 0x200) { + n = 0x200; + } else if (n < -0x200) { + n = -0x200; + } + aExp += n - 1; aSig <<= 7; return normalizeRoundAndPackFloat32( aSign, aExp, aSig STATUS_VAR ); @@ -6048,8 +6335,8 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM ) { flag aSign; - int16 aExp; - bits64 aSig; + int16_t aExp; + uint64_t aSig; a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); @@ -6057,6 +6344,9 @@ aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { + if ( aSig ) { + return propagateFloat64NaN( a, a STATUS_VAR ); + } return a; } if ( aExp != 0 ) @@ -6064,46 +6354,62 @@ else if ( aSig == 0 ) return a; + if (n > 0x1000) { + n = 0x1000; + } else if (n < -0x1000) { + n = -0x1000; + } + aExp += n - 1; aSig <<= 10; return normalizeRoundAndPackFloat64( aSign, aExp, aSig STATUS_VAR ); } -#ifdef FLOATX80 floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM ) { flag aSign; - int16 aExp; - bits64 aSig; + int32_t aExp; + uint64_t aSig; aSig = extractFloatx80Frac( a ); aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); - if ( aExp == 0x7FF ) { + if ( aExp == 0x7FFF ) { + if ( aSig<<1 ) { + return propagateFloatx80NaN( a, a STATUS_VAR ); + } return a; } + if (aExp == 0 && aSig == 0) return a; + if (n > 0x10000) { + n = 0x10000; + } else if (n < -0x10000) { + n = -0x10000; + } + aExp += n; return normalizeRoundAndPackFloatx80( STATUS(floatx80_rounding_precision), aSign, aExp, aSig, 0 STATUS_VAR ); } -#endif -#ifdef FLOAT128 float128 float128_scalbn( float128 a, int n STATUS_PARAM ) { flag aSign; - int32 aExp; - bits64 aSig0, aSig1; + int32_t aExp; + uint64_t aSig0, aSig1; aSig1 = extractFloat128Frac1( a ); aSig0 = extractFloat128Frac0( a ); aExp = extractFloat128Exp( a ); aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) { + return propagateFloat128NaN( a, a STATUS_VAR ); + } return a; } if ( aExp != 0 ) @@ -6111,9 +6417,14 @@ else if ( aSig0 == 0 && aSig1 == 0 ) return a; + if (n > 0x10000) { + n = 0x10000; + } else if (n < -0x10000) { + n = -0x10000; + } + aExp += n - 1; return normalizeRoundAndPackFloat128( aSign, aExp, aSig0, aSig1 STATUS_VAR ); } -#endif diff -Nru qemu-kvm-0.14.1+noroms/fpu/softfloat.h qemu-kvm-0.15.0+noroms/fpu/softfloat.h --- qemu-kvm-0.14.1+noroms/fpu/softfloat.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/fpu/softfloat.h 2011-08-09 07:40:29.000000000 -0500 @@ -1,3 +1,9 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ + /*============================================================================ This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic @@ -59,41 +65,14 @@ typedef uint64_t uint64; typedef int64_t int64; -/*---------------------------------------------------------------------------- -| Each of the following `typedef's defines a type that holds integers -| of _exactly_ the number of bits specified. For instance, for most -| implementation of C, `bits16' and `sbits16' should be `typedef'ed to -| `unsigned short int' and `signed short int' (or `short int'), respectively. -*----------------------------------------------------------------------------*/ -typedef uint8_t bits8; -typedef int8_t sbits8; -typedef uint16_t bits16; -typedef int16_t sbits16; -typedef uint32_t bits32; -typedef int32_t sbits32; -typedef uint64_t bits64; -typedef int64_t sbits64; - #define LIT64( a ) a##LL #define INLINE static inline -/*---------------------------------------------------------------------------- -| The macro `FLOATX80' must be defined to enable the extended double-precision -| floating-point format `floatx80'. If this macro is not defined, the -| `floatx80' type will not be defined, and none of the functions that either -| input or output the `floatx80' type will be defined. The same applies to -| the `FLOAT128' macro and the quadruple-precision format `float128'. -*----------------------------------------------------------------------------*/ -#ifdef CONFIG_SOFTFLOAT -/* bit exact soft float support */ -#define FLOATX80 -#define FLOAT128 +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) +#define SNAN_BIT_IS_ONE 1 #else -/* native float support */ -#if (defined(__i386__) || defined(__x86_64__)) && !defined(CONFIG_BSD) -#define FLOATX80 +#define SNAN_BIT_IS_ONE 0 #endif -#endif /* !CONFIG_SOFTFLOAT */ #define STATUS_PARAM , float_status *status #define STATUS(field) status->field @@ -109,7 +88,6 @@ float_relation_unordered = 2 }; -#ifdef CONFIG_SOFTFLOAT /*---------------------------------------------------------------------------- | Software IEC/IEEE floating-point types. *----------------------------------------------------------------------------*/ @@ -120,31 +98,43 @@ //#define USE_SOFTFLOAT_STRUCT_TYPES #ifdef USE_SOFTFLOAT_STRUCT_TYPES typedef struct { + uint16_t v; +} float16; +#define float16_val(x) (((float16)(x)).v) +#define make_float16(x) __extension__ ({ float16 f16_val = {x}; f16_val; }) +#define const_float16(x) { x } +typedef struct { uint32_t v; } float32; /* The cast ensures an error if the wrong type is passed. */ #define float32_val(x) (((float32)(x)).v) #define make_float32(x) __extension__ ({ float32 f32_val = {x}; f32_val; }) +#define const_float32(x) { x } typedef struct { uint64_t v; } float64; #define float64_val(x) (((float64)(x)).v) #define make_float64(x) __extension__ ({ float64 f64_val = {x}; f64_val; }) +#define const_float64(x) { x } #else +typedef uint16_t float16; typedef uint32_t float32; typedef uint64_t float64; +#define float16_val(x) (x) #define float32_val(x) (x) #define float64_val(x) (x) +#define make_float16(x) (x) #define make_float32(x) (x) #define make_float64(x) (x) +#define const_float16(x) (x) +#define const_float32(x) (x) +#define const_float64(x) (x) #endif -#ifdef FLOATX80 typedef struct { uint64_t low; uint16_t high; } floatx80; -#endif -#ifdef FLOAT128 +#define make_floatx80(exp, mant) ((floatx80) { mant, exp }) typedef struct { #ifdef HOST_WORDS_BIGENDIAN uint64_t high, low; @@ -152,7 +142,6 @@ uint64_t low, high; #endif } float128; -#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE floating-point underflow tininess-detection mode. @@ -181,16 +170,15 @@ float_flag_overflow = 8, float_flag_underflow = 16, float_flag_inexact = 32, - float_flag_input_denormal = 64 + float_flag_input_denormal = 64, + float_flag_output_denormal = 128 }; typedef struct float_status { signed char float_detect_tininess; signed char float_rounding_mode; signed char float_exception_flags; -#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? */ @@ -200,6 +188,10 @@ void set_float_rounding_mode(int val STATUS_PARAM); void set_float_exception_flags(int val STATUS_PARAM); +INLINE void set_float_detect_tininess(int val STATUS_PARAM) +{ + STATUS(float_detect_tininess) = val; +} INLINE void set_flush_to_zero(flag val STATUS_PARAM) { STATUS(flush_to_zero) = val; @@ -216,9 +208,7 @@ { return STATUS(float_exception_flags); } -#ifdef FLOATX80 void set_floatx80_rounding_precision(int val STATUS_PARAM); -#endif /*---------------------------------------------------------------------------- | Routine to raise any or all of the software IEC/IEEE floating-point @@ -229,51 +219,57 @@ /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ -float32 int32_to_float32( int STATUS_PARAM ); -float64 int32_to_float64( int STATUS_PARAM ); +float32 int32_to_float32( int32 STATUS_PARAM ); +float64 int32_to_float64( int32 STATUS_PARAM ); float32 uint32_to_float32( unsigned int STATUS_PARAM ); float64 uint32_to_float64( unsigned int STATUS_PARAM ); -#ifdef FLOATX80 -floatx80 int32_to_floatx80( int STATUS_PARAM ); -#endif -#ifdef FLOAT128 -float128 int32_to_float128( int STATUS_PARAM ); -#endif -float32 int64_to_float32( int64_t STATUS_PARAM ); -float32 uint64_to_float32( uint64_t STATUS_PARAM ); -float64 int64_to_float64( int64_t STATUS_PARAM ); -float64 uint64_to_float64( uint64_t STATUS_PARAM ); -#ifdef FLOATX80 -floatx80 int64_to_floatx80( int64_t STATUS_PARAM ); -#endif -#ifdef FLOAT128 -float128 int64_to_float128( int64_t STATUS_PARAM ); -#endif +floatx80 int32_to_floatx80( int32 STATUS_PARAM ); +float128 int32_to_float128( int32 STATUS_PARAM ); +float32 int64_to_float32( int64 STATUS_PARAM ); +float32 uint64_to_float32( uint64 STATUS_PARAM ); +float64 int64_to_float64( int64 STATUS_PARAM ); +float64 uint64_to_float64( uint64 STATUS_PARAM ); +floatx80 int64_to_floatx80( int64 STATUS_PARAM ); +float128 int64_to_float128( int64 STATUS_PARAM ); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ -bits16 float32_to_float16( float32, flag STATUS_PARAM ); -float32 float16_to_float32( bits16, flag STATUS_PARAM ); +float16 float32_to_float16( float32, flag STATUS_PARAM ); +float32 float16_to_float32( float16, flag STATUS_PARAM ); + +/*---------------------------------------------------------------------------- +| Software half-precision operations. +*----------------------------------------------------------------------------*/ +int float16_is_quiet_nan( float16 ); +int float16_is_signaling_nan( float16 ); +float16 float16_maybe_silence_nan( float16 ); + +/*---------------------------------------------------------------------------- +| The pattern for a default generated half-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_ARM) +#define float16_default_nan make_float16(0x7E00) +#elif SNAN_BIT_IS_ONE +#define float16_default_nan make_float16(0x7DFF) +#else +#define float16_default_nan make_float16(0xFE00) +#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ -int float32_to_int16_round_to_zero( float32 STATUS_PARAM ); +int16 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 ); -unsigned int float32_to_uint32_round_to_zero( float32 STATUS_PARAM ); -int64_t float32_to_int64( float32 STATUS_PARAM ); -int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM ); +int32 float32_to_int32( float32 STATUS_PARAM ); +int32 float32_to_int32_round_to_zero( float32 STATUS_PARAM ); +uint32 float32_to_uint32( float32 STATUS_PARAM ); +uint32 float32_to_uint32_round_to_zero( float32 STATUS_PARAM ); +int64 float32_to_int64( float32 STATUS_PARAM ); +int64 float32_to_int64_round_to_zero( float32 STATUS_PARAM ); float64 float32_to_float64( float32 STATUS_PARAM ); -#ifdef FLOATX80 floatx80 float32_to_floatx80( float32 STATUS_PARAM ); -#endif -#ifdef FLOAT128 float128 float32_to_float128( float32 STATUS_PARAM ); -#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision operations. @@ -290,11 +286,15 @@ int float32_eq( float32, float32 STATUS_PARAM ); int float32_le( float32, float32 STATUS_PARAM ); int float32_lt( float32, float32 STATUS_PARAM ); -int float32_eq_signaling( float32, float32 STATUS_PARAM ); +int float32_unordered( float32, float32 STATUS_PARAM ); +int float32_eq_quiet( float32, float32 STATUS_PARAM ); int float32_le_quiet( float32, float32 STATUS_PARAM ); int float32_lt_quiet( float32, float32 STATUS_PARAM ); +int float32_unordered_quiet( float32, float32 STATUS_PARAM ); int float32_compare( float32, float32 STATUS_PARAM ); int float32_compare_quiet( float32, float32 STATUS_PARAM ); +float32 float32_min(float32, float32 STATUS_PARAM); +float32 float32_max(float32, float32 STATUS_PARAM); int float32_is_quiet_nan( float32 ); int float32_is_signaling_nan( float32 ); float32 float32_maybe_silence_nan( float32 ); @@ -341,30 +341,48 @@ return (float32_val(a) & 0x7f800000) == 0; } +INLINE float32 float32_set_sign(float32 a, int sign) +{ + return make_float32((float32_val(a) & 0x7fffffff) | (sign << 31)); +} + #define float32_zero make_float32(0) #define float32_one make_float32(0x3f800000) #define float32_ln2 make_float32(0x3f317218) +#define float32_pi make_float32(0x40490fdb) +#define float32_half make_float32(0x3f000000) +#define float32_infinity make_float32(0x7f800000) + + +/*---------------------------------------------------------------------------- +| The pattern for a default generated single-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +#define float32_default_nan make_float32(0x7FFFFFFF) +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +#define float32_default_nan make_float32(0x7FC00000) +#elif SNAN_BIT_IS_ONE +#define float32_default_nan make_float32(0x7FBFFFFF) +#else +#define float32_default_nan make_float32(0xFFC00000) +#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ -int float64_to_int16_round_to_zero( float64 STATUS_PARAM ); +int16 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 ); -unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM ); -int64_t float64_to_int64( float64 STATUS_PARAM ); -int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM ); -uint64_t float64_to_uint64 (float64 a STATUS_PARAM); -uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM); +int32 float64_to_int32( float64 STATUS_PARAM ); +int32 float64_to_int32_round_to_zero( float64 STATUS_PARAM ); +uint32 float64_to_uint32( float64 STATUS_PARAM ); +uint32 float64_to_uint32_round_to_zero( float64 STATUS_PARAM ); +int64 float64_to_int64( float64 STATUS_PARAM ); +int64 float64_to_int64_round_to_zero( float64 STATUS_PARAM ); +uint64 float64_to_uint64 (float64 a STATUS_PARAM); +uint64 float64_to_uint64_round_to_zero (float64 a STATUS_PARAM); float32 float64_to_float32( float64 STATUS_PARAM ); -#ifdef FLOATX80 floatx80 float64_to_floatx80( float64 STATUS_PARAM ); -#endif -#ifdef FLOAT128 float128 float64_to_float128( float64 STATUS_PARAM ); -#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision operations. @@ -381,11 +399,15 @@ int float64_eq( float64, float64 STATUS_PARAM ); int float64_le( float64, float64 STATUS_PARAM ); int float64_lt( float64, float64 STATUS_PARAM ); -int float64_eq_signaling( float64, float64 STATUS_PARAM ); +int float64_unordered( float64, float64 STATUS_PARAM ); +int float64_eq_quiet( float64, float64 STATUS_PARAM ); int float64_le_quiet( float64, float64 STATUS_PARAM ); int float64_lt_quiet( float64, float64 STATUS_PARAM ); +int float64_unordered_quiet( float64, float64 STATUS_PARAM ); int float64_compare( float64, float64 STATUS_PARAM ); int float64_compare_quiet( float64, float64 STATUS_PARAM ); +float64 float64_min(float64, float64 STATUS_PARAM); +float64 float64_max(float64, float64 STATUS_PARAM); int float64_is_quiet_nan( float64 a ); int float64_is_signaling_nan( float64 ); float64 float64_maybe_silence_nan( float64 ); @@ -427,24 +449,47 @@ return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL); } +INLINE int float64_is_zero_or_denormal(float64 a) +{ + return (float64_val(a) & 0x7ff0000000000000LL) == 0; +} + +INLINE float64 float64_set_sign(float64 a, int sign) +{ + return make_float64((float64_val(a) & 0x7fffffffffffffffULL) + | ((int64_t)sign << 63)); +} + #define float64_zero make_float64(0) #define float64_one make_float64(0x3ff0000000000000LL) #define float64_ln2 make_float64(0x3fe62e42fefa39efLL) +#define float64_pi make_float64(0x400921fb54442d18LL) +#define float64_half make_float64(0x3fe0000000000000LL) +#define float64_infinity make_float64(0x7ff0000000000000LL) -#ifdef FLOATX80 +/*---------------------------------------------------------------------------- +| The pattern for a default generated double-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF )) +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 )) +#elif SNAN_BIT_IS_ONE +#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF )) +#else +#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 )) +#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision conversion routines. *----------------------------------------------------------------------------*/ -int floatx80_to_int32( floatx80 STATUS_PARAM ); -int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); -int64_t floatx80_to_int64( floatx80 STATUS_PARAM ); -int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM ); +int32 floatx80_to_int32( floatx80 STATUS_PARAM ); +int32 floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); +int64 floatx80_to_int64( floatx80 STATUS_PARAM ); +int64 floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM ); float32 floatx80_to_float32( floatx80 STATUS_PARAM ); float64 floatx80_to_float64( floatx80 STATUS_PARAM ); -#ifdef FLOAT128 float128 floatx80_to_float128( floatx80 STATUS_PARAM ); -#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision operations. @@ -459,9 +504,13 @@ int floatx80_eq( floatx80, floatx80 STATUS_PARAM ); int floatx80_le( floatx80, floatx80 STATUS_PARAM ); int floatx80_lt( floatx80, floatx80 STATUS_PARAM ); -int floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM ); +int floatx80_unordered( floatx80, floatx80 STATUS_PARAM ); +int floatx80_eq_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); +int floatx80_unordered_quiet( floatx80, floatx80 STATUS_PARAM ); +int floatx80_compare( floatx80, floatx80 STATUS_PARAM ); +int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_is_quiet_nan( floatx80 ); int floatx80_is_signaling_nan( floatx80 ); floatx80 floatx80_maybe_silence_nan( floatx80 ); @@ -481,7 +530,7 @@ INLINE int floatx80_is_infinity(floatx80 a) { - return (a.high & 0x7fff) == 0x7fff && a.low == 0; + return (a.high & 0x7fff) == 0x7fff && a.low == 0x8000000000000000LL; } INLINE int floatx80_is_neg(floatx80 a) @@ -494,27 +543,46 @@ return (a.high & 0x7fff) == 0 && a.low == 0; } +INLINE int floatx80_is_zero_or_denormal(floatx80 a) +{ + return (a.high & 0x7fff) == 0; +} + INLINE int floatx80_is_any_nan(floatx80 a) { return ((a.high & 0x7fff) == 0x7fff) && (a.low<<1); } -#endif +#define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL) +#define floatx80_one make_floatx80(0x3fff, 0x8000000000000000LL) +#define floatx80_ln2 make_floatx80(0x3ffe, 0xb17217f7d1cf79acLL) +#define floatx80_pi make_floatx80(0x4000, 0xc90fdaa22168c235LL) +#define floatx80_half make_floatx80(0x3ffe, 0x8000000000000000LL) +#define floatx80_infinity make_floatx80(0x7fff, 0x8000000000000000LL) -#ifdef FLOAT128 +/*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision NaN. The +| `high' and `low' values hold the most- and least-significant bits, +| respectively. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define floatx80_default_nan_high 0x7FFF +#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF ) +#else +#define floatx80_default_nan_high 0xFFFF +#define floatx80_default_nan_low LIT64( 0xC000000000000000 ) +#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE quadruple-precision conversion routines. *----------------------------------------------------------------------------*/ -int float128_to_int32( float128 STATUS_PARAM ); -int float128_to_int32_round_to_zero( float128 STATUS_PARAM ); -int64_t float128_to_int64( float128 STATUS_PARAM ); -int64_t float128_to_int64_round_to_zero( float128 STATUS_PARAM ); +int32 float128_to_int32( float128 STATUS_PARAM ); +int32 float128_to_int32_round_to_zero( float128 STATUS_PARAM ); +int64 float128_to_int64( float128 STATUS_PARAM ); +int64 float128_to_int64_round_to_zero( float128 STATUS_PARAM ); float32 float128_to_float32( float128 STATUS_PARAM ); float64 float128_to_float64( float128 STATUS_PARAM ); -#ifdef FLOATX80 floatx80 float128_to_floatx80( float128 STATUS_PARAM ); -#endif /*---------------------------------------------------------------------------- | Software IEC/IEEE quadruple-precision operations. @@ -529,9 +597,11 @@ int float128_eq( float128, float128 STATUS_PARAM ); int float128_le( float128, float128 STATUS_PARAM ); int float128_lt( float128, float128 STATUS_PARAM ); -int float128_eq_signaling( float128, float128 STATUS_PARAM ); +int float128_unordered( float128, float128 STATUS_PARAM ); +int float128_eq_quiet( float128, float128 STATUS_PARAM ); int float128_le_quiet( float128, float128 STATUS_PARAM ); int float128_lt_quiet( float128, float128 STATUS_PARAM ); +int float128_unordered_quiet( float128, float128 STATUS_PARAM ); int float128_compare( float128, float128 STATUS_PARAM ); int float128_compare_quiet( float128, float128 STATUS_PARAM ); int float128_is_quiet_nan( float128 ); @@ -566,18 +636,27 @@ return (a.high & 0x7fffffffffffffffLL) == 0 && a.low == 0; } +INLINE int float128_is_zero_or_denormal(float128 a) +{ + return (a.high & 0x7fff000000000000LL) == 0; +} + INLINE int float128_is_any_nan(float128 a) { return ((a.high >> 48) & 0x7fff) == 0x7fff && ((a.low != 0) || ((a.high & 0xffffffffffffLL) != 0)); } +/*---------------------------------------------------------------------------- +| The pattern for a default generated quadruple-precision NaN. The `high' and +| `low' values hold the most- and least-significant bits, respectively. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF ) +#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) +#else +#define float128_default_nan_high LIT64( 0xFFFF800000000000 ) +#define float128_default_nan_low LIT64( 0x0000000000000000 ) #endif -#else /* CONFIG_SOFTFLOAT */ - -#include "softfloat-native.h" - -#endif /* !CONFIG_SOFTFLOAT */ - #endif /* !SOFTFLOAT_H */ diff -Nru qemu-kvm-0.14.1+noroms/fpu/softfloat-macros.h qemu-kvm-0.15.0+noroms/fpu/softfloat-macros.h --- qemu-kvm-0.14.1+noroms/fpu/softfloat-macros.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/fpu/softfloat-macros.h 2011-08-09 07:40:29.000000000 -0500 @@ -1,3 +1,8 @@ +/* + * QEMU float support macros + * + * Derived from SoftFloat. + */ /*============================================================================ @@ -31,6 +36,17 @@ =============================================================================*/ /*---------------------------------------------------------------------------- +| This macro tests for minimum version of the GNU C compiler. +*----------------------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define SOFTFLOAT_GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define SOFTFLOAT_GNUC_PREREQ(maj, min) 0 +#endif + + +/*---------------------------------------------------------------------------- | Shifts `a' right by the number of bits given in `count'. If any nonzero | bits are shifted off, they are ``jammed'' into the least significant bit of | the result by setting the least significant bit to 1. The value of `count' @@ -39,9 +55,9 @@ | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr ) +INLINE void shift32RightJamming( uint32_t a, int16 count, uint32_t *zPtr ) { - bits32 z; + uint32_t z; if ( count == 0 ) { z = a; @@ -65,9 +81,9 @@ | The result is stored in the location pointed to by `zPtr'. *----------------------------------------------------------------------------*/ -INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr ) +INLINE void shift64RightJamming( uint64_t a, int16 count, uint64_t *zPtr ) { - bits64 z; + uint64_t z; if ( count == 0 ) { z = a; @@ -101,9 +117,9 @@ INLINE void shift64ExtraRightJamming( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits64 z0, z1; + uint64_t z0, z1; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -138,9 +154,9 @@ INLINE void shift128Right( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits64 z0, z1; + uint64_t z0, z1; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -173,9 +189,9 @@ INLINE void shift128RightJamming( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { - bits64 z0, z1; + uint64_t z0, z1; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -224,16 +240,16 @@ INLINE void shift128ExtraRightJamming( - bits64 a0, - bits64 a1, - bits64 a2, + uint64_t a0, + uint64_t a1, + uint64_t a2, int16 count, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr ) { - bits64 z0, z1, z2; + uint64_t z0, z1, z2; int8 negCount = ( - count ) & 63; if ( count == 0 ) { @@ -282,7 +298,7 @@ INLINE void shortShift128Left( - bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) + uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr ) { *z1Ptr = a1<>32; bLow = b; bHigh = b>>32; - z1 = ( (bits64) aLow ) * bLow; - zMiddleA = ( (bits64) aLow ) * bHigh; - zMiddleB = ( (bits64) aHigh ) * bLow; - z0 = ( (bits64) aHigh ) * bHigh; + z1 = ( (uint64_t) aLow ) * bLow; + zMiddleA = ( (uint64_t) aLow ) * bHigh; + zMiddleB = ( (uint64_t) aHigh ) * bLow; + z0 = ( (uint64_t) aHigh ) * bHigh; zMiddleA += zMiddleB; - z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 ); + z0 += ( ( (uint64_t) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 ); zMiddleA <<= 32; z1 += zMiddleA; z0 += ( z1 < zMiddleA ); @@ -478,15 +494,15 @@ INLINE void mul128By64To192( - bits64 a0, - bits64 a1, - bits64 b, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr + uint64_t a0, + uint64_t a1, + uint64_t b, + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr ) { - bits64 z0, z1, z2, more1; + uint64_t z0, z1, z2, more1; mul64To128( a1, b, &z1, &z2 ); mul64To128( a0, b, &z0, &more1 ); @@ -506,18 +522,18 @@ INLINE void mul128To256( - bits64 a0, - bits64 a1, - bits64 b0, - bits64 b1, - bits64 *z0Ptr, - bits64 *z1Ptr, - bits64 *z2Ptr, - bits64 *z3Ptr + uint64_t a0, + uint64_t a1, + uint64_t b0, + uint64_t b1, + uint64_t *z0Ptr, + uint64_t *z1Ptr, + uint64_t *z2Ptr, + uint64_t *z3Ptr ) { - bits64 z0, z1, z2, z3; - bits64 more1, more2; + uint64_t z0, z1, z2, z3; + uint64_t more1, more2; mul64To128( a1, b1, &z2, &z3 ); mul64To128( a1, b0, &z1, &more2 ); @@ -543,18 +559,18 @@ | unsigned integer is returned. *----------------------------------------------------------------------------*/ -static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b ) +static uint64_t estimateDiv128To64( uint64_t a0, uint64_t a1, uint64_t b ) { - bits64 b0, b1; - bits64 rem0, rem1, term0, term1; - bits64 z; + uint64_t b0, b1; + uint64_t rem0, rem1, term0, term1; + uint64_t z; if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF ); b0 = b>>32; z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32; mul64To128( b, z, &term0, &term1 ); sub128( a0, a1, term0, term1, &rem0, &rem1 ); - while ( ( (sbits64) rem0 ) < 0 ) { + while ( ( (int64_t) rem0 ) < 0 ) { z -= LIT64( 0x100000000 ); b1 = b<<32; add128( rem0, rem1, b0, b1, &rem0, &rem1 ); @@ -575,18 +591,18 @@ | value. *----------------------------------------------------------------------------*/ -static bits32 estimateSqrt32( int16 aExp, bits32 a ) +static uint32_t estimateSqrt32( int16 aExp, uint32_t a ) { - static const bits16 sqrtOddAdjustments[] = { + static const uint16_t sqrtOddAdjustments[] = { 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0, 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67 }; - static const bits16 sqrtEvenAdjustments[] = { + static const uint16_t sqrtEvenAdjustments[] = { 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E, 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002 }; int8 index; - bits32 z; + uint32_t z; index = ( a>>27 ) & 15; if ( aExp & 1 ) { @@ -598,9 +614,9 @@ z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ (int)index ]; z = a / z + z; z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 ); - if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 ); + if ( z <= a ) return (uint32_t) ( ( (int32_t) a )>>1 ); } - return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 ); + return ( (uint32_t) ( ( ( (uint64_t) a )<<31 ) / z ) ) + ( z>>1 ); } @@ -609,8 +625,15 @@ | `a'. If `a' is zero, 32 is returned. *----------------------------------------------------------------------------*/ -static int8 countLeadingZeros32( bits32 a ) +static int8 countLeadingZeros32( uint32_t a ) { +#if SOFTFLOAT_GNUC_PREREQ(3, 4) + if (a) { + return __builtin_clz(a); + } else { + return 32; + } +#else static const int8 countLeadingZerosHigh[] = { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -642,7 +665,7 @@ } shiftCount += countLeadingZerosHigh[ a>>24 ]; return shiftCount; - +#endif } /*---------------------------------------------------------------------------- @@ -650,12 +673,19 @@ | `a'. If `a' is zero, 64 is returned. *----------------------------------------------------------------------------*/ -static int8 countLeadingZeros64( bits64 a ) +static int8 countLeadingZeros64( uint64_t a ) { +#if SOFTFLOAT_GNUC_PREREQ(3, 4) + if (a) { + return __builtin_clzll(a); + } else { + return 64; + } +#else int8 shiftCount; shiftCount = 0; - if ( a < ( (bits64) 1 )<<32 ) { + if ( a < ( (uint64_t) 1 )<<32 ) { shiftCount += 32; } else { @@ -663,7 +693,7 @@ } shiftCount += countLeadingZeros32( a ); return shiftCount; - +#endif } /*---------------------------------------------------------------------------- @@ -672,7 +702,7 @@ | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag eq128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 == b0 ) && ( a1 == b1 ); @@ -685,7 +715,7 @@ | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag le128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) ); @@ -698,7 +728,7 @@ | returns 0. *----------------------------------------------------------------------------*/ -INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag lt128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) ); @@ -711,7 +741,7 @@ | Otherwise, returns 0. *----------------------------------------------------------------------------*/ -INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +INLINE flag ne128( uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1 ) { return ( a0 != b0 ) || ( a1 != b1 ); diff -Nru qemu-kvm-0.14.1+noroms/fpu/softfloat-native.c qemu-kvm-0.15.0+noroms/fpu/softfloat-native.c --- qemu-kvm-0.14.1+noroms/fpu/softfloat-native.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/fpu/softfloat-native.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,515 +0,0 @@ -/* Native implementation of soft float functions. Only a single status - context is supported */ -#include "softfloat.h" -#include -#if defined(CONFIG_SOLARIS) -#include -#endif -#include "config-host.h" - -void set_float_rounding_mode(int val STATUS_PARAM) -{ - STATUS(float_rounding_mode) = val; -#if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) || \ - (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10) - fpsetround(val); -#else - fesetround(val); -#endif -} - -#ifdef FLOATX80 -void set_floatx80_rounding_precision(int val STATUS_PARAM) -{ - STATUS(floatx80_rounding_precision) = val; -} -#endif - -#if defined(CONFIG_BSD) || \ - (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10) -#define lrint(d) ((int32_t)rint(d)) -#define llrint(d) ((int64_t)rint(d)) -#define lrintf(f) ((int32_t)rint(f)) -#define llrintf(f) ((int64_t)rint(f)) -#define sqrtf(f) ((float)sqrt(f)) -#define remainderf(fa, fb) ((float)remainder(fa, fb)) -#define rintf(f) ((float)rint(f)) -#if !defined(__sparc__) && \ - (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10) -extern long double rintl(long double); -extern long double scalbnl(long double, int); - -long long -llrintl(long double x) { - return ((long long) rintl(x)); -} - -long -lrintl(long double x) { - return ((long) rintl(x)); -} - -long double -ldexpl(long double x, int n) { - return (scalbnl(x, n)); -} -#endif -#endif - -#if defined(_ARCH_PPC) - -/* correct (but slow) PowerPC rint() (glibc version is incorrect) */ -static double qemu_rint(double x) -{ - double y = 4503599627370496.0; - if (fabs(x) >= y) - return x; - if (x < 0) - y = -y; - y = (x + y) - y; - if (y == 0.0) - y = copysign(y, x); - return y; -} - -#define rint qemu_rint -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE integer-to-floating-point conversion routines. -*----------------------------------------------------------------------------*/ -float32 int32_to_float32(int v STATUS_PARAM) -{ - return (float32)v; -} - -float32 uint32_to_float32(unsigned int v STATUS_PARAM) -{ - return (float32)v; -} - -float64 int32_to_float64(int v STATUS_PARAM) -{ - return (float64)v; -} - -float64 uint32_to_float64(unsigned int v STATUS_PARAM) -{ - return (float64)v; -} - -#ifdef FLOATX80 -floatx80 int32_to_floatx80(int v STATUS_PARAM) -{ - return (floatx80)v; -} -#endif -float32 int64_to_float32( int64_t v STATUS_PARAM) -{ - return (float32)v; -} -float32 uint64_to_float32( uint64_t v STATUS_PARAM) -{ - return (float32)v; -} -float64 int64_to_float64( int64_t v STATUS_PARAM) -{ - return (float64)v; -} -float64 uint64_to_float64( uint64_t v STATUS_PARAM) -{ - return (float64)v; -} -#ifdef FLOATX80 -floatx80 int64_to_floatx80( int64_t v STATUS_PARAM) -{ - return (floatx80)v; -} -#endif - -/* XXX: this code implements the x86 behaviour, not the IEEE one. */ -#if HOST_LONG_BITS == 32 -static inline int long_to_int32(long a) -{ - return a; -} -#else -static inline int long_to_int32(long a) -{ - if (a != (int32_t)a) - a = 0x80000000; - return a; -} -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE single-precision conversion routines. -*----------------------------------------------------------------------------*/ -int float32_to_int32( float32 a STATUS_PARAM) -{ - return long_to_int32(lrintf(a)); -} -int float32_to_int32_round_to_zero( float32 a STATUS_PARAM) -{ - return (int)a; -} -int64_t float32_to_int64( float32 a STATUS_PARAM) -{ - return llrintf(a); -} - -int64_t float32_to_int64_round_to_zero( float32 a STATUS_PARAM) -{ - return (int64_t)a; -} - -float64 float32_to_float64( float32 a STATUS_PARAM) -{ - return a; -} -#ifdef FLOATX80 -floatx80 float32_to_floatx80( float32 a STATUS_PARAM) -{ - return a; -} -#endif - -unsigned int float32_to_uint32( float32 a STATUS_PARAM) -{ - int64_t v; - unsigned int res; - - v = llrintf(a); - if (v < 0) { - res = 0; - } else if (v > 0xffffffff) { - res = 0xffffffff; - } else { - res = v; - } - return res; -} -unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM) -{ - int64_t v; - unsigned int res; - - v = (int64_t)a; - if (v < 0) { - res = 0; - } else if (v > 0xffffffff) { - res = 0xffffffff; - } else { - res = v; - } - return res; -} - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE single-precision operations. -*----------------------------------------------------------------------------*/ -float32 float32_round_to_int( float32 a STATUS_PARAM) -{ - return rintf(a); -} - -float32 float32_rem( float32 a, float32 b STATUS_PARAM) -{ - return remainderf(a, b); -} - -float32 float32_sqrt( float32 a STATUS_PARAM) -{ - return sqrtf(a); -} -int float32_compare( float32 a, float32 b STATUS_PARAM ) -{ - if (a < b) { - return float_relation_less; - } else if (a == b) { - return float_relation_equal; - } else if (a > b) { - return float_relation_greater; - } else { - return float_relation_unordered; - } -} -int float32_compare_quiet( float32 a, float32 b STATUS_PARAM ) -{ - if (isless(a, b)) { - return float_relation_less; - } else if (a == b) { - return float_relation_equal; - } else if (isgreater(a, b)) { - return float_relation_greater; - } else { - return float_relation_unordered; - } -} -int float32_is_signaling_nan( float32 a1) -{ - float32u u; - uint32_t a; - u.f = a1; - a = u.i; - return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); -} - -int float32_is_quiet_nan( float32 a1 ) -{ - float32u u; - uint64_t a; - u.f = a1; - a = u.i; - return ( 0xFF800000 < ( a<<1 ) ); -} - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE double-precision conversion routines. -*----------------------------------------------------------------------------*/ -int float64_to_int32( float64 a STATUS_PARAM) -{ - return long_to_int32(lrint(a)); -} -int float64_to_int32_round_to_zero( float64 a STATUS_PARAM) -{ - return (int)a; -} -int64_t float64_to_int64( float64 a STATUS_PARAM) -{ - return llrint(a); -} -int64_t float64_to_int64_round_to_zero( float64 a STATUS_PARAM) -{ - return (int64_t)a; -} -float32 float64_to_float32( float64 a STATUS_PARAM) -{ - return a; -} -#ifdef FLOATX80 -floatx80 float64_to_floatx80( float64 a STATUS_PARAM) -{ - return a; -} -#endif -#ifdef FLOAT128 -float128 float64_to_float128( float64 a STATUS_PARAM) -{ - return a; -} -#endif - -unsigned int float64_to_uint32( float64 a STATUS_PARAM) -{ - int64_t v; - unsigned int res; - - v = llrint(a); - if (v < 0) { - res = 0; - } else if (v > 0xffffffff) { - res = 0xffffffff; - } else { - res = v; - } - return res; -} -unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM) -{ - int64_t v; - unsigned int res; - - v = (int64_t)a; - if (v < 0) { - res = 0; - } else if (v > 0xffffffff) { - res = 0xffffffff; - } else { - res = v; - } - return res; -} -uint64_t float64_to_uint64 (float64 a STATUS_PARAM) -{ - int64_t v; - - v = llrint(a + (float64)INT64_MIN); - - return v - INT64_MIN; -} -uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM) -{ - int64_t v; - - v = (int64_t)(a + (float64)INT64_MIN); - - return v - INT64_MIN; -} - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE double-precision operations. -*----------------------------------------------------------------------------*/ -#if defined(__sun__) && \ - (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10) -static inline float64 trunc(float64 x) -{ - return x < 0 ? -floor(-x) : floor(x); -} -#endif -float64 float64_trunc_to_int( float64 a STATUS_PARAM ) -{ - return trunc(a); -} - -float64 float64_round_to_int( float64 a STATUS_PARAM ) -{ - return rint(a); -} - -float64 float64_rem( float64 a, float64 b STATUS_PARAM) -{ - return remainder(a, b); -} - -float64 float64_sqrt( float64 a STATUS_PARAM) -{ - return sqrt(a); -} -int float64_compare( float64 a, float64 b STATUS_PARAM ) -{ - if (a < b) { - return float_relation_less; - } else if (a == b) { - return float_relation_equal; - } else if (a > b) { - return float_relation_greater; - } else { - return float_relation_unordered; - } -} -int float64_compare_quiet( float64 a, float64 b STATUS_PARAM ) -{ - if (isless(a, b)) { - return float_relation_less; - } else if (a == b) { - return float_relation_equal; - } else if (isgreater(a, b)) { - return float_relation_greater; - } else { - return float_relation_unordered; - } -} -int float64_is_signaling_nan( float64 a1) -{ - float64u u; - uint64_t a; - u.f = a1; - a = u.i; - return - ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) - && ( a & LIT64( 0x0007FFFFFFFFFFFF ) ); - -} - -int float64_is_quiet_nan( float64 a1 ) -{ - float64u u; - uint64_t a; - u.f = a1; - a = u.i; - - return ( LIT64( 0xFFF0000000000000 ) < (bits64) ( a<<1 ) ); - -} - -#ifdef FLOATX80 - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE extended double-precision conversion routines. -*----------------------------------------------------------------------------*/ -int floatx80_to_int32( floatx80 a STATUS_PARAM) -{ - return long_to_int32(lrintl(a)); -} -int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM) -{ - return (int)a; -} -int64_t floatx80_to_int64( floatx80 a STATUS_PARAM) -{ - return llrintl(a); -} -int64_t floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM) -{ - return (int64_t)a; -} -float32 floatx80_to_float32( floatx80 a STATUS_PARAM) -{ - return a; -} -float64 floatx80_to_float64( floatx80 a STATUS_PARAM) -{ - return a; -} - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE extended double-precision operations. -*----------------------------------------------------------------------------*/ -floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM) -{ - return rintl(a); -} -floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM) -{ - return remainderl(a, b); -} -floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM) -{ - return sqrtl(a); -} -int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM ) -{ - if (a < b) { - return float_relation_less; - } else if (a == b) { - return float_relation_equal; - } else if (a > b) { - return float_relation_greater; - } else { - return float_relation_unordered; - } -} -int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM ) -{ - if (isless(a, b)) { - return float_relation_less; - } else if (a == b) { - return float_relation_equal; - } else if (isgreater(a, b)) { - return float_relation_greater; - } else { - return float_relation_unordered; - } -} -int floatx80_is_signaling_nan( floatx80 a1) -{ - floatx80u u; - uint64_t aLow; - u.f = a1; - - aLow = u.i.low & ~ LIT64( 0x4000000000000000 ); - return - ( ( u.i.high & 0x7FFF ) == 0x7FFF ) - && (bits64) ( aLow<<1 ) - && ( u.i.low == aLow ); -} - -int floatx80_is_quiet_nan( floatx80 a1 ) -{ - floatx80u u; - u.f = a1; - return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 ); -} - -#endif diff -Nru qemu-kvm-0.14.1+noroms/fpu/softfloat-native.h qemu-kvm-0.15.0+noroms/fpu/softfloat-native.h --- qemu-kvm-0.14.1+noroms/fpu/softfloat-native.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/fpu/softfloat-native.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,492 +0,0 @@ -/* Native implementation of soft float functions */ -#include - -#if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) \ - || defined(CONFIG_SOLARIS) -#include -#define fabsf(f) ((float)fabs(f)) -#else -#include -#endif - -#if defined(__OpenBSD__) || defined(__NetBSD__) -#include -#endif - -/* - * Define some C99-7.12.3 classification macros and - * some C99-.12.4 for Solaris systems OS less than 10, - * or Solaris 10 systems running GCC 3.x or less. - * Solaris 10 with GCC4 does not need these macros as they - * are defined in with a compiler directive - */ -#if defined(CONFIG_SOLARIS) && \ - ((CONFIG_SOLARIS_VERSION <= 9 ) || \ - ((CONFIG_SOLARIS_VERSION == 10) && (__GNUC__ < 4))) \ - || (defined(__OpenBSD__) && (OpenBSD < 200811)) -/* - * C99 7.12.3 classification macros - * and - * C99 7.12.14 comparison macros - * - * ... do not work on Solaris 10 using GNU CC 3.4.x. - * Try to workaround the missing / broken C99 math macros. - */ -#if defined(__OpenBSD__) -#define unordered(x, y) (isnan(x) || isnan(y)) -#endif - -#ifdef __NetBSD__ -#ifndef isgreater -#define isgreater(x, y) __builtin_isgreater(x, y) -#endif -#ifndef isgreaterequal -#define isgreaterequal(x, y) __builtin_isgreaterequal(x, y) -#endif -#ifndef isless -#define isless(x, y) __builtin_isless(x, y) -#endif -#ifndef islessequal -#define islessequal(x, y) __builtin_islessequal(x, y) -#endif -#ifndef isunordered -#define isunordered(x, y) __builtin_isunordered(x, y) -#endif -#endif - - -#define isnormal(x) (fpclass(x) >= FP_NZERO) -#define isgreater(x, y) ((!unordered(x, y)) && ((x) > (y))) -#define isgreaterequal(x, y) ((!unordered(x, y)) && ((x) >= (y))) -#define isless(x, y) ((!unordered(x, y)) && ((x) < (y))) -#define islessequal(x, y) ((!unordered(x, y)) && ((x) <= (y))) -#define isunordered(x,y) unordered(x, y) -#endif - -#if defined(__sun__) && !defined(CONFIG_NEEDS_LIBSUNMATH) - -#ifndef isnan -# define isnan(x) \ - (sizeof (x) == sizeof (long double) ? isnan_ld (x) \ - : sizeof (x) == sizeof (double) ? isnan_d (x) \ - : isnan_f (x)) -static inline int isnan_f (float x) { return x != x; } -static inline int isnan_d (double x) { return x != x; } -static inline int isnan_ld (long double x) { return x != x; } -#endif - -#ifndef isinf -# define isinf(x) \ - (sizeof (x) == sizeof (long double) ? isinf_ld (x) \ - : sizeof (x) == sizeof (double) ? isinf_d (x) \ - : isinf_f (x)) -static inline int isinf_f (float x) { return isnan (x - x); } -static inline int isinf_d (double x) { return isnan (x - x); } -static inline int isinf_ld (long double x) { return isnan (x - x); } -#endif -#endif - -typedef float float32; -typedef double float64; -#ifdef FLOATX80 -typedef long double floatx80; -#endif - -typedef union { - float32 f; - uint32_t i; -} float32u; -typedef union { - float64 f; - uint64_t i; -} float64u; -#ifdef FLOATX80 -typedef union { - floatx80 f; - struct { - uint64_t low; - uint16_t high; - } i; -} floatx80u; -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE floating-point rounding mode. -*----------------------------------------------------------------------------*/ -#if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) \ - || defined(CONFIG_SOLARIS) -#if defined(__OpenBSD__) -#define FE_RM FP_RM -#define FE_RP FP_RP -#define FE_RZ FP_RZ -#endif -enum { - float_round_nearest_even = FP_RN, - float_round_down = FP_RM, - float_round_up = FP_RP, - float_round_to_zero = FP_RZ -}; -#else -enum { - float_round_nearest_even = FE_TONEAREST, - float_round_down = FE_DOWNWARD, - float_round_up = FE_UPWARD, - float_round_to_zero = FE_TOWARDZERO -}; -#endif - -typedef struct float_status { - int float_rounding_mode; -#ifdef FLOATX80 - int floatx80_rounding_precision; -#endif -} float_status; - -void set_float_rounding_mode(int val STATUS_PARAM); -#ifdef FLOATX80 -void set_floatx80_rounding_precision(int val STATUS_PARAM); -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE integer-to-floating-point conversion routines. -*----------------------------------------------------------------------------*/ -float32 int32_to_float32( int STATUS_PARAM); -float32 uint32_to_float32( unsigned int STATUS_PARAM); -float64 int32_to_float64( int STATUS_PARAM); -float64 uint32_to_float64( unsigned int STATUS_PARAM); -#ifdef FLOATX80 -floatx80 int32_to_floatx80( int STATUS_PARAM); -#endif -#ifdef FLOAT128 -float128 int32_to_float128( int STATUS_PARAM); -#endif -float32 int64_to_float32( int64_t STATUS_PARAM); -float32 uint64_to_float32( uint64_t STATUS_PARAM); -float64 int64_to_float64( int64_t STATUS_PARAM); -float64 uint64_to_float64( uint64_t v STATUS_PARAM); -#ifdef FLOATX80 -floatx80 int64_to_floatx80( int64_t STATUS_PARAM); -#endif -#ifdef FLOAT128 -float128 int64_to_float128( int64_t STATUS_PARAM); -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE single-precision conversion routines. -*----------------------------------------------------------------------------*/ -int float32_to_int32( float32 STATUS_PARAM); -int float32_to_int32_round_to_zero( float32 STATUS_PARAM); -unsigned int float32_to_uint32( float32 a STATUS_PARAM); -unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM); -int64_t float32_to_int64( float32 STATUS_PARAM); -int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM); -float64 float32_to_float64( float32 STATUS_PARAM); -#ifdef FLOATX80 -floatx80 float32_to_floatx80( float32 STATUS_PARAM); -#endif -#ifdef FLOAT128 -float128 float32_to_float128( float32 STATUS_PARAM); -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE single-precision operations. -*----------------------------------------------------------------------------*/ -float32 float32_round_to_int( float32 STATUS_PARAM); -INLINE float32 float32_add( float32 a, float32 b STATUS_PARAM) -{ - return a + b; -} -INLINE float32 float32_sub( float32 a, float32 b STATUS_PARAM) -{ - return a - b; -} -INLINE float32 float32_mul( float32 a, float32 b STATUS_PARAM) -{ - return a * b; -} -INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM) -{ - return a / b; -} -float32 float32_rem( float32, float32 STATUS_PARAM); -float32 float32_sqrt( float32 STATUS_PARAM); -INLINE int float32_eq( float32 a, float32 b STATUS_PARAM) -{ - return a == b; -} -INLINE int float32_le( float32 a, float32 b STATUS_PARAM) -{ - return a <= b; -} -INLINE int float32_lt( float32 a, float32 b STATUS_PARAM) -{ - return a < b; -} -INLINE int float32_eq_signaling( float32 a, float32 b STATUS_PARAM) -{ - return a <= b && a >= b; -} -INLINE int float32_le_quiet( float32 a, float32 b STATUS_PARAM) -{ - return islessequal(a, b); -} -INLINE int float32_lt_quiet( float32 a, float32 b STATUS_PARAM) -{ - return isless(a, b); -} -INLINE int float32_unordered( float32 a, float32 b STATUS_PARAM) -{ - return isunordered(a, b); - -} -int float32_compare( float32, float32 STATUS_PARAM ); -int float32_compare_quiet( float32, float32 STATUS_PARAM ); -int float32_is_signaling_nan( float32 ); -int float32_is_quiet_nan( float32 ); - -INLINE float32 float32_abs(float32 a) -{ - return fabsf(a); -} - -INLINE float32 float32_chs(float32 a) -{ - return -a; -} - -INLINE float32 float32_is_infinity(float32 a) -{ - return fpclassify(a) == FP_INFINITE; -} - -INLINE float32 float32_is_neg(float32 a) -{ - float32u u; - u.f = a; - return u.i >> 31; -} - -INLINE float32 float32_is_zero(float32 a) -{ - return fpclassify(a) == FP_ZERO; -} - -INLINE float32 float32_scalbn(float32 a, int n) -{ - return scalbnf(a, n); -} - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE double-precision conversion routines. -*----------------------------------------------------------------------------*/ -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 ); -unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM ); -int64_t float64_to_int64( float64 STATUS_PARAM ); -int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM ); -uint64_t float64_to_uint64( float64 STATUS_PARAM ); -uint64_t float64_to_uint64_round_to_zero( float64 STATUS_PARAM ); -float32 float64_to_float32( float64 STATUS_PARAM ); -#ifdef FLOATX80 -floatx80 float64_to_floatx80( float64 STATUS_PARAM ); -#endif -#ifdef FLOAT128 -float128 float64_to_float128( float64 STATUS_PARAM ); -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE double-precision operations. -*----------------------------------------------------------------------------*/ -float64 float64_round_to_int( float64 STATUS_PARAM ); -float64 float64_trunc_to_int( float64 STATUS_PARAM ); -INLINE float64 float64_add( float64 a, float64 b STATUS_PARAM) -{ - return a + b; -} -INLINE float64 float64_sub( float64 a, float64 b STATUS_PARAM) -{ - return a - b; -} -INLINE float64 float64_mul( float64 a, float64 b STATUS_PARAM) -{ - return a * b; -} -INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM) -{ - return a / b; -} -float64 float64_rem( float64, float64 STATUS_PARAM ); -float64 float64_sqrt( float64 STATUS_PARAM ); -INLINE int float64_eq( float64 a, float64 b STATUS_PARAM) -{ - return a == b; -} -INLINE int float64_le( float64 a, float64 b STATUS_PARAM) -{ - return a <= b; -} -INLINE int float64_lt( float64 a, float64 b STATUS_PARAM) -{ - return a < b; -} -INLINE int float64_eq_signaling( float64 a, float64 b STATUS_PARAM) -{ - return a <= b && a >= b; -} -INLINE int float64_le_quiet( float64 a, float64 b STATUS_PARAM) -{ - return islessequal(a, b); -} -INLINE int float64_lt_quiet( float64 a, float64 b STATUS_PARAM) -{ - return isless(a, b); - -} -INLINE int float64_unordered( float64 a, float64 b STATUS_PARAM) -{ - return isunordered(a, b); - -} -int float64_compare( float64, float64 STATUS_PARAM ); -int float64_compare_quiet( float64, float64 STATUS_PARAM ); -int float64_is_signaling_nan( float64 ); -int float64_is_quiet_nan( float64 ); - -INLINE float64 float64_abs(float64 a) -{ - return fabs(a); -} - -INLINE float64 float64_chs(float64 a) -{ - return -a; -} - -INLINE float64 float64_is_infinity(float64 a) -{ - return fpclassify(a) == FP_INFINITE; -} - -INLINE float64 float64_is_neg(float64 a) -{ - float64u u; - u.f = a; - return u.i >> 63; -} - -INLINE float64 float64_is_zero(float64 a) -{ - return fpclassify(a) == FP_ZERO; -} - -INLINE float64 float64_scalbn(float64 a, int n) -{ - return scalbn(a, n); -} - -#ifdef FLOATX80 - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE extended double-precision conversion routines. -*----------------------------------------------------------------------------*/ -int floatx80_to_int32( floatx80 STATUS_PARAM ); -int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); -int64_t floatx80_to_int64( floatx80 STATUS_PARAM); -int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM); -float32 floatx80_to_float32( floatx80 STATUS_PARAM ); -float64 floatx80_to_float64( floatx80 STATUS_PARAM ); -#ifdef FLOAT128 -float128 floatx80_to_float128( floatx80 STATUS_PARAM ); -#endif - -/*---------------------------------------------------------------------------- -| Software IEC/IEEE extended double-precision operations. -*----------------------------------------------------------------------------*/ -floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM ); -INLINE floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a + b; -} -INLINE floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a - b; -} -INLINE floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a * b; -} -INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a / b; -} -floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM ); -floatx80 floatx80_sqrt( floatx80 STATUS_PARAM ); -INLINE int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a == b; -} -INLINE int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a <= b; -} -INLINE int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a < b; -} -INLINE int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM) -{ - return a <= b && a >= b; -} -INLINE int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM) -{ - return islessequal(a, b); -} -INLINE int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM) -{ - return isless(a, b); - -} -INLINE int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM) -{ - return isunordered(a, b); - -} -int floatx80_compare( floatx80, floatx80 STATUS_PARAM ); -int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM ); -int floatx80_is_signaling_nan( floatx80 ); -int floatx80_is_quiet_nan( floatx80 ); - -INLINE floatx80 floatx80_abs(floatx80 a) -{ - return fabsl(a); -} - -INLINE floatx80 floatx80_chs(floatx80 a) -{ - return -a; -} - -INLINE floatx80 floatx80_is_infinity(floatx80 a) -{ - return fpclassify(a) == FP_INFINITE; -} - -INLINE floatx80 floatx80_is_neg(floatx80 a) -{ - floatx80u u; - u.f = a; - return u.i.high >> 15; -} - -INLINE floatx80 floatx80_is_zero(floatx80 a) -{ - return fpclassify(a) == FP_ZERO; -} - -INLINE floatx80 floatx80_scalbn(floatx80 a, int n) -{ - return scalbnl(a, n); -} - -#endif diff -Nru qemu-kvm-0.14.1+noroms/fpu/softfloat-specialize.h qemu-kvm-0.15.0+noroms/fpu/softfloat-specialize.h --- qemu-kvm-0.14.1+noroms/fpu/softfloat-specialize.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/fpu/softfloat-specialize.h 2011-08-09 07:40:29.000000000 -0500 @@ -1,3 +1,8 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ /*============================================================================ @@ -30,12 +35,6 @@ =============================================================================*/ -#if defined(TARGET_MIPS) || defined(TARGET_SH4) -#define SNAN_BIT_IS_ONE 1 -#else -#define SNAN_BIT_IS_ONE 0 -#endif - /*---------------------------------------------------------------------------- | Raises the exceptions specified by `flags'. Floating-point traps can be | defined here if desired. It is currently not possible for such a trap @@ -53,21 +52,98 @@ *----------------------------------------------------------------------------*/ typedef struct { flag sign; - bits64 high, low; + uint64_t high, low; } commonNaNT; /*---------------------------------------------------------------------------- -| The pattern for a default generated single-precision NaN. +| Returns 1 if the half-precision floating-point value `a' is a quiet +| NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -#if defined(TARGET_SPARC) -#define float32_default_nan make_float32(0x7FFFFFFF) -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) -#define float32_default_nan make_float32(0x7FC00000) -#elif SNAN_BIT_IS_ONE -#define float32_default_nan make_float32(0x7FBFFFFF) + +int float16_is_quiet_nan(float16 a_) +{ + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); +#else + return ((a & ~0x8000) >= 0x7c80); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the half-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float16_is_signaling_nan(float16 a_) +{ + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return ((a & ~0x8000) >= 0x7c80); +#else + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the half-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ +float16 float16_maybe_silence_nan(float16 a_) +{ + if (float16_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + return float16_default_nan; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif #else -#define float32_default_nan make_float32(0xFFC00000) + uint16_t a = float16_val(a_); + a |= (1 << 9); + return make_float16(a); #endif + } + return a_; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the half-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float16ToCommonNaN( float16 a STATUS_PARAM ) +{ + commonNaNT z; + + if ( float16_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); + z.sign = float16_val(a) >> 15; + z.low = 0; + z.high = ((uint64_t) float16_val(a))<<54; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the half- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float16 commonNaNToFloat16(commonNaNT a STATUS_PARAM) +{ + uint16_t mantissa = a.high>>54; + + if (STATUS(default_nan_mode)) { + return float16_default_nan; + } + + if (mantissa) { + return make_float16(((((uint16_t) a.sign) << 15) + | (0x1F << 10) | mantissa)); + } else { + return float16_default_nan; + } +} /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is a quiet @@ -80,7 +156,7 @@ #if SNAN_BIT_IS_ONE return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); #else - return ( 0xFF800000 <= (bits32) ( a<<1 ) ); + return ( 0xFF800000 <= (uint32_t) ( a<<1 ) ); #endif } @@ -93,7 +169,7 @@ { uint32_t a = float32_val(a_); #if SNAN_BIT_IS_ONE - return ( 0xFF800000 <= (bits32) ( a<<1 ) ); + return ( 0xFF800000 <= (uint32_t) ( a<<1 ) ); #else return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); #endif @@ -108,13 +184,13 @@ { if (float32_is_signaling_nan(a_)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) || defined(TARGET_SH4) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) return float32_default_nan; # else # error Rules for silencing a signaling NaN are target-specific # endif #else - bits32 a = float32_val(a_); + uint32_t a = float32_val(a_); a |= (1 << 22); return make_float32(a); #endif @@ -135,7 +211,7 @@ if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); z.sign = float32_val(a)>>31; z.low = 0; - z.high = ( (bits64) float32_val(a) )<<41; + z.high = ( (uint64_t) float32_val(a) )<<41; return z; } @@ -144,12 +220,17 @@ | precision floating-point format. *----------------------------------------------------------------------------*/ -static float32 commonNaNToFloat32( commonNaNT a ) +static float32 commonNaNToFloat32( commonNaNT a STATUS_PARAM) { - bits32 mantissa = a.high>>41; + uint32_t mantissa = a.high>>41; + + if ( STATUS(default_nan_mode) ) { + return float32_default_nan; + } + if ( mantissa ) return make_float32( - ( ( (bits32) a.sign )<<31 ) | 0x7F800000 | ( a.high>>41 ) ); + ( ( (uint32_t) a.sign )<<31 ) | 0x7F800000 | ( a.high>>41 ) ); else return float32_default_nan; } @@ -276,7 +357,7 @@ { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; - bits32 av, bv; + uint32_t av, bv; aIsQuietNaN = float32_is_quiet_nan( a ); aIsSignalingNaN = float32_is_signaling_nan( a ); @@ -290,9 +371,9 @@ if ( STATUS(default_nan_mode) ) return float32_default_nan; - if ((bits32)(av<<1) < (bits32)(bv<<1)) { + if ((uint32_t)(av<<1) < (uint32_t)(bv<<1)) { aIsLargerSignificand = 0; - } else if ((bits32)(bv<<1) < (bits32)(av<<1)) { + } else if ((uint32_t)(bv<<1) < (uint32_t)(av<<1)) { aIsLargerSignificand = 1; } else { aIsLargerSignificand = (av < bv) ? 1 : 0; @@ -307,32 +388,19 @@ } /*---------------------------------------------------------------------------- -| The pattern for a default generated double-precision NaN. -*----------------------------------------------------------------------------*/ -#if defined(TARGET_SPARC) -#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF )) -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) -#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 )) -#elif SNAN_BIT_IS_ONE -#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF )) -#else -#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 )) -#endif - -/*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ int float64_is_quiet_nan( float64 a_ ) { - bits64 a = float64_val(a_); + uint64_t a = float64_val(a_); #if SNAN_BIT_IS_ONE return ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) && ( a & LIT64( 0x0007FFFFFFFFFFFF ) ); #else - return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) ); + return ( LIT64( 0xFFF0000000000000 ) <= (uint64_t) ( a<<1 ) ); #endif } @@ -343,9 +411,9 @@ int float64_is_signaling_nan( float64 a_ ) { - bits64 a = float64_val(a_); + uint64_t a = float64_val(a_); #if SNAN_BIT_IS_ONE - return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) ); + return ( LIT64( 0xFFF0000000000000 ) <= (uint64_t) ( a<<1 ) ); #else return ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) @@ -362,13 +430,13 @@ { if (float64_is_signaling_nan(a_)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) || defined(TARGET_SH4) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) return float64_default_nan; # else # error Rules for silencing a signaling NaN are target-specific # endif #else - bits64 a = float64_val(a_); + uint64_t a = float64_val(a_); a |= LIT64( 0x0008000000000000 ); return make_float64(a); #endif @@ -398,13 +466,17 @@ | precision floating-point format. *----------------------------------------------------------------------------*/ -static float64 commonNaNToFloat64( commonNaNT a ) +static float64 commonNaNToFloat64( commonNaNT a STATUS_PARAM) { - bits64 mantissa = a.high>>12; + uint64_t mantissa = a.high>>12; + + if ( STATUS(default_nan_mode) ) { + return float64_default_nan; + } if ( mantissa ) return make_float64( - ( ( (bits64) a.sign )<<63 ) + ( ( (uint64_t) a.sign )<<63 ) | LIT64( 0x7FF0000000000000 ) | ( a.high>>12 )); else @@ -421,7 +493,7 @@ { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; - bits64 av, bv; + uint64_t av, bv; aIsQuietNaN = float64_is_quiet_nan( a ); aIsSignalingNaN = float64_is_signaling_nan( a ); @@ -435,9 +507,9 @@ if ( STATUS(default_nan_mode) ) return float64_default_nan; - if ((bits64)(av<<1) < (bits64)(bv<<1)) { + if ((uint64_t)(av<<1) < (uint64_t)(bv<<1)) { aIsLargerSignificand = 0; - } else if ((bits64)(bv<<1) < (bits64)(av<<1)) { + } else if ((uint64_t)(bv<<1) < (uint64_t)(av<<1)) { aIsLargerSignificand = 1; } else { aIsLargerSignificand = (av < bv) ? 1 : 0; @@ -451,21 +523,6 @@ } } -#ifdef FLOATX80 - -/*---------------------------------------------------------------------------- -| The pattern for a default generated extended double-precision NaN. The -| `high' and `low' values hold the most- and least-significant bits, -| respectively. -*----------------------------------------------------------------------------*/ -#if SNAN_BIT_IS_ONE -#define floatx80_default_nan_high 0x7FFF -#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF ) -#else -#define floatx80_default_nan_high 0xFFFF -#define floatx80_default_nan_low LIT64( 0xC000000000000000 ) -#endif - /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a | quiet NaN; otherwise returns 0. This slightly differs from the same @@ -475,16 +532,16 @@ int floatx80_is_quiet_nan( floatx80 a ) { #if SNAN_BIT_IS_ONE - bits64 aLow; + uint64_t aLow; aLow = a.low & ~ LIT64( 0x4000000000000000 ); return ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (bits64) ( aLow<<1 ) + && (uint64_t) ( aLow<<1 ) && ( a.low == aLow ); #else return ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 ))); + && (LIT64( 0x8000000000000000 ) <= ((uint64_t) ( a.low<<1 ))); #endif } @@ -498,14 +555,14 @@ { #if SNAN_BIT_IS_ONE return ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 ))); + && (LIT64( 0x8000000000000000 ) <= ((uint64_t) ( a.low<<1 ))); #else - bits64 aLow; + uint64_t aLow; aLow = a.low & ~ LIT64( 0x4000000000000000 ); return ( ( a.high & 0x7FFF ) == 0x7FFF ) - && (bits64) ( aLow<<1 ) + && (uint64_t) ( aLow<<1 ) && ( a.low == aLow ); #endif } @@ -519,7 +576,7 @@ { if (floatx80_is_signaling_nan(a)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) || defined(TARGET_SH4) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) a.low = floatx80_default_nan_low; a.high = floatx80_default_nan_high; # else @@ -544,9 +601,15 @@ commonNaNT z; if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); - z.sign = a.high>>15; - z.low = 0; - z.high = a.low; + if ( a.low >> 63 ) { + z.sign = a.high >> 15; + z.low = 0; + z.high = a.low << 1; + } else { + z.sign = floatx80_default_nan_high >> 15; + z.low = 0; + z.high = floatx80_default_nan_low << 1; + } return z; } @@ -555,15 +618,24 @@ | double-precision floating-point format. *----------------------------------------------------------------------------*/ -static floatx80 commonNaNToFloatx80( commonNaNT a ) +static floatx80 commonNaNToFloatx80( commonNaNT a STATUS_PARAM) { floatx80 z; - if (a.high) - z.low = a.high; - else + if ( STATUS(default_nan_mode) ) { z.low = floatx80_default_nan_low; - z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF; + z.high = floatx80_default_nan_high; + return z; + } + + if (a.high >> 1) { + z.low = LIT64( 0x8000000000000000 ) | a.high >> 1; + z.high = ( ( (uint16_t) a.sign )<<15 ) | 0x7FFF; + } else { + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + } + return z; } @@ -607,22 +679,6 @@ } } -#endif - -#ifdef FLOAT128 - -/*---------------------------------------------------------------------------- -| The pattern for a default generated quadruple-precision NaN. The `high' and -| `low' values hold the most- and least-significant bits, respectively. -*----------------------------------------------------------------------------*/ -#if SNAN_BIT_IS_ONE -#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF ) -#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) -#else -#define float128_default_nan_high LIT64( 0xFFFF800000000000 ) -#define float128_default_nan_low LIT64( 0x0000000000000000 ) -#endif - /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -636,7 +692,7 @@ && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) ); #else return - ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) ) + ( LIT64( 0xFFFE000000000000 ) <= (uint64_t) ( a.high<<1 ) ) && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); #endif } @@ -650,7 +706,7 @@ { #if SNAN_BIT_IS_ONE return - ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) ) + ( LIT64( 0xFFFE000000000000 ) <= (uint64_t) ( a.high<<1 ) ) && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); #else return @@ -668,7 +724,7 @@ { if (float128_is_signaling_nan(a)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) || defined(TARGET_SH4) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) a.low = float128_default_nan_low; a.high = float128_default_nan_high; # else @@ -703,12 +759,18 @@ | precision floating-point format. *----------------------------------------------------------------------------*/ -static float128 commonNaNToFloat128( commonNaNT a ) +static float128 commonNaNToFloat128( commonNaNT a STATUS_PARAM) { float128 z; + if ( STATUS(default_nan_mode) ) { + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + shift128Right( a.high, a.low, 16, &z.high, &z.low ); - z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF000000000000 ); + z.high |= ( ( (uint64_t) a.sign )<<63 ) | LIT64( 0x7FFF000000000000 ); return z; } @@ -752,4 +814,3 @@ } } -#endif diff -Nru qemu-kvm-0.14.1+noroms/fsdev/file-op-9p.h qemu-kvm-0.15.0+noroms/fsdev/file-op-9p.h --- qemu-kvm-0.14.1+noroms/fsdev/file-op-9p.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/fsdev/file-op-9p.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,100 @@ +/* + * 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; + +#endif diff -Nru qemu-kvm-0.14.1+noroms/fsdev/qemu-fsdev-dummy.c qemu-kvm-0.15.0+noroms/fsdev/qemu-fsdev-dummy.c --- qemu-kvm-0.14.1+noroms/fsdev/qemu-fsdev-dummy.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/fsdev/qemu-fsdev-dummy.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,28 @@ +/* + * 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-config.h" + +int qemu_fsdev_add(QemuOpts *opts) +{ + return 0; +} + +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.14.1+noroms/fsdev/qemu-fsdev.h qemu-kvm-0.15.0+noroms/fsdev/qemu-fsdev.h --- qemu-kvm-0.14.1+noroms/fsdev/qemu-fsdev.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/fsdev/qemu-fsdev.h 2011-08-09 07:40:29.000000000 -0500 @@ -13,7 +13,7 @@ #ifndef QEMU_FSDEV_H #define QEMU_FSDEV_H #include "qemu-option.h" -#include "hw/file-op-9p.h" +#include "file-op-9p.h" /* diff -Nru qemu-kvm-0.14.1+noroms/gdbstub.c qemu-kvm-0.15.0+noroms/gdbstub.c --- qemu-kvm-0.14.1+noroms/gdbstub.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/gdbstub.c 2011-08-09 07:40:29.000000000 -0500 @@ -34,11 +34,10 @@ #include "sysemu.h" #include "gdbstub.h" #endif -#include "qemu-kvm.h" #define MAX_PACKET_LENGTH 4096 -#include "exec-all.h" +#include "cpu.h" #include "qemu_socket.h" #include "kvm.h" @@ -46,7 +45,12 @@ enum { GDB_SIGNAL_0 = 0, GDB_SIGNAL_INT = 2, + GDB_SIGNAL_QUIT = 3, GDB_SIGNAL_TRAP = 5, + GDB_SIGNAL_ABRT = 6, + GDB_SIGNAL_ALRM = 14, + GDB_SIGNAL_IO = 23, + GDB_SIGNAL_XCPU = 24, GDB_SIGNAL_UNKNOWN = 143 }; @@ -315,7 +319,7 @@ int ret; for(;;) { - ret = recv(s->fd, &ch, 1, 0); + ret = qemu_recv(s->fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) s->fd = -1; @@ -1101,10 +1105,6 @@ env->active_fpu.fcr31 = tmp & 0xFF83FFFF; /* set rounding mode */ RESTORE_ROUNDING_MODE; -#ifndef CONFIG_SOFTFLOAT - /* no floating point exception for native float */ - SET_FP_ENABLE(env->active_fpu.fcr31, 0); -#endif break; case 71: env->active_fpu.fcr0 = tmp; break; } @@ -1432,7 +1432,11 @@ /* XXX */ break; case S390_PC_REGNUM: GET_REGL(env->psw.addr); break; - case S390_CC_REGNUM: GET_REG32(env->cc); break; + case S390_CC_REGNUM: + env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, + env->cc_vr); + GET_REG32(env->cc_op); + break; } return 0; @@ -1458,11 +1462,85 @@ /* XXX */ break; case S390_PC_REGNUM: env->psw.addr = tmpl; break; - case S390_CC_REGNUM: env->cc = tmp32; r=4; break; + case S390_CC_REGNUM: env->cc_op = tmp32; r=4; break; } return r; } +#elif defined (TARGET_LM32) + +#include "hw/lm32_pic.h" +#define NUM_CORE_REGS (32 + 7) + +static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + GET_REG32(env->regs[n]); + } else { + switch (n) { + case 32: + GET_REG32(env->pc); + break; + /* FIXME: put in right exception ID */ + case 33: + GET_REG32(0); + break; + case 34: + GET_REG32(env->eba); + break; + case 35: + GET_REG32(env->deba); + break; + case 36: + GET_REG32(env->ie); + break; + case 37: + GET_REG32(lm32_pic_get_im(env->pic_state)); + break; + case 38: + GET_REG32(lm32_pic_get_ip(env->pic_state)); + break; + } + } + return 0; +} + +static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n) +{ + uint32_t tmp; + + if (n > NUM_CORE_REGS) { + return 0; + } + + tmp = ldl_p(mem_buf); + + if (n < 32) { + env->regs[n] = tmp; + } else { + switch (n) { + case 32: + env->pc = tmp; + break; + case 34: + env->eba = tmp; + break; + case 35: + env->deba = tmp; + break; + case 36: + env->ie = tmp; + break; + case 37: + lm32_pic_set_im(env->pic_state, tmp); + break; + case 38: + lm32_pic_set_ip(env->pic_state, tmp); + break; + } + } + return 4; +} #else #define NUM_CORE_REGS 0 @@ -1738,6 +1816,8 @@ #elif defined (TARGET_S390X) cpu_synchronize_state(s->c_cpu); s->c_cpu->psw.addr = pc; +#elif defined (TARGET_LM32) + s->c_cpu->pc = pc; #endif } @@ -2195,14 +2275,11 @@ const char *type; int ret; - if (running || (reason != EXCP_DEBUG && reason != EXCP_INTERRUPT) || - s->state == RS_INACTIVE || s->state == RS_SYSCALL) + if (running || s->state == RS_INACTIVE || s->state == RS_SYSCALL) { return; - - /* disable single step if it was enable */ - cpu_single_step(env, 0); - - if (reason == EXCP_DEBUG) { + } + switch (reason) { + case VMSTOP_DEBUG: if (env->watchpoint_hit) { switch (env->watchpoint_hit->flags & BP_MEM_ACCESS) { case BP_MEM_READ: @@ -2219,17 +2296,44 @@ "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", GDB_SIGNAL_TRAP, gdb_id(env), type, env->watchpoint_hit->vaddr); - put_packet(s, buf); env->watchpoint_hit = NULL; - return; + goto send_packet; } - tb_flush(env); + tb_flush(env); ret = GDB_SIGNAL_TRAP; - } else { + break; + case VMSTOP_USER: ret = GDB_SIGNAL_INT; + break; + case VMSTOP_SHUTDOWN: + ret = GDB_SIGNAL_QUIT; + break; + case VMSTOP_DISKFULL: + ret = GDB_SIGNAL_IO; + break; + case VMSTOP_WATCHDOG: + ret = GDB_SIGNAL_ALRM; + break; + case VMSTOP_PANIC: + ret = GDB_SIGNAL_ABRT; + break; + case VMSTOP_SAVEVM: + case VMSTOP_LOADVM: + return; + case VMSTOP_MIGRATE: + ret = GDB_SIGNAL_XCPU; + break; + default: + ret = GDB_SIGNAL_UNKNOWN; + break; } snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env)); + +send_packet: put_packet(s, buf); + + /* disable single step if it was enabled */ + cpu_single_step(env, 0); } #endif @@ -2253,7 +2357,7 @@ gdb_current_syscall_cb = cb; s->state = RS_SYSCALL; #ifndef CONFIG_USER_ONLY - vm_stop(EXCP_DEBUG); + vm_stop(VMSTOP_DEBUG); #endif s->state = RS_IDLE; va_start(va, fmt); @@ -2327,7 +2431,7 @@ if (vm_running) { /* when the CPU is running, we cannot do anything except stop it when receiving a char */ - vm_stop(EXCP_INTERRUPT); + vm_stop(VMSTOP_USER); } else #endif { @@ -2589,7 +2693,7 @@ { switch (event) { case CHR_EVENT_OPENED: - vm_stop(EXCP_INTERRUPT); + vm_stop(VMSTOP_USER); gdb_has_xml = 0; break; default: @@ -2629,8 +2733,9 @@ #ifndef _WIN32 static void gdb_sigterm_handler(int signal) { - if (vm_running) - vm_stop(EXCP_INTERRUPT); + if (vm_running) { + vm_stop(VMSTOP_USER); + } } #endif diff -Nru qemu-kvm-0.14.1+noroms/gen-icount.h qemu-kvm-0.15.0+noroms/gen-icount.h --- qemu-kvm-0.14.1+noroms/gen-icount.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/gen-icount.h 2011-08-09 07:40:29.000000000 -0500 @@ -29,7 +29,7 @@ if (use_icount) { *icount_arg = num_insns; gen_set_label(icount_label); - tcg_gen_exit_tb((long)tb + 2); + tcg_gen_exit_tb((tcg_target_long)tb + 2); } } diff -Nru qemu-kvm-0.14.1+noroms/.gitignore qemu-kvm-0.15.0+noroms/.gitignore --- qemu-kvm-0.14.1+noroms/.gitignore 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/.gitignore 2011-08-09 07:40:29.000000000 -0500 @@ -15,6 +15,7 @@ libhw32 libhw64 libuser +qapi-generated qemu-doc.html qemu-tech.html qemu-doc.info @@ -32,6 +33,7 @@ qemu-img-cmds.texi qemu-img-cmds.h qemu-io +qemu-ga qemu-monitor.texi QMP/qmp-commands.txt .gdbinit @@ -44,6 +46,9 @@ *.ky *.log *.pdf +*.cps +*.fns +*.kys *.pg *.pyc *.toc @@ -51,6 +56,8 @@ *.vr *.d *.o +*.swp +*.orig .pc patches pc-bios/bios-pq/status @@ -62,3 +69,6 @@ pc-bios/optionrom/vapic.bin .stgit-* cscope.* +tags +TAGS +*~ diff -Nru qemu-kvm-0.14.1+noroms/.gitmodules qemu-kvm-0.15.0+noroms/.gitmodules --- qemu-kvm-0.14.1+noroms/.gitmodules 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/.gitmodules 2011-08-09 07:40:29.000000000 -0500 @@ -4,3 +4,9 @@ [submodule "roms/seabios"] path = roms/seabios url = git://git.qemu.org/seabios.git/ +[submodule "roms/SLOF"] + path = roms/SLOF + url = git://git.qemu.org/SLOF.git +[submodule "roms/ipxe"] + path = roms/ipxe + url = git://git.qemu.org/ipxe.git diff -Nru qemu-kvm-0.14.1+noroms/HACKING qemu-kvm-0.15.0+noroms/HACKING --- qemu-kvm-0.14.1+noroms/HACKING 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/HACKING 2011-08-09 07:40:29.000000000 -0500 @@ -120,6 +120,3 @@ 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.14.1+noroms/hmp-commands.hx qemu-kvm-0.15.0+noroms/hmp-commands.hx --- qemu-kvm-0.14.1+noroms/hmp-commands.hx 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hmp-commands.hx 2011-08-09 07:40:29.000000000 -0500 @@ -740,10 +740,11 @@ #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, + .args_type = "", + .params = "", + .help = "inject an NMI on all guest's CPUs", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_inject_nmi, }, #endif STEXI @@ -821,16 +822,12 @@ 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, + .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 @@ -842,12 +839,16 @@ 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, + .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 diff -Nru qemu-kvm-0.14.1+noroms/hppa-dis.c qemu-kvm-0.15.0+noroms/hppa-dis.c --- qemu-kvm-0.14.1+noroms/hppa-dis.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hppa-dis.c 2011-08-09 07:40:29.000000000 -0500 @@ -1645,7 +1645,7 @@ typedef unsigned int CORE_ADDR; -/* Get at various relevent fields of an instruction word. */ +/* Get at various relevant fields of an instruction word. */ #define MASK_5 0x1f #define MASK_10 0x3ff @@ -1771,13 +1771,13 @@ static void fput_reg (unsigned reg, disassemble_info *info) { - (*info->fprintf_func) (info->stream, reg ? reg_names[reg] : "r0"); + (*info->fprintf_func) (info->stream, "%s", reg ? reg_names[reg] : "r0"); } static void fput_fp_reg (unsigned reg, disassemble_info *info) { - (*info->fprintf_func) (info->stream, reg ? fp_reg_names[reg] : "fr0"); + (*info->fprintf_func) (info->stream, "%s", reg ? fp_reg_names[reg] : "fr0"); } static void @@ -1794,7 +1794,7 @@ static void fput_creg (unsigned reg, disassemble_info *info) { - (*info->fprintf_func) (info->stream, control_reg[reg]); + (*info->fprintf_func) (info->stream, "%s", control_reg[reg]); } /* Print constants with sign. */ diff -Nru qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p.c qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p.c --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,3652 @@ +/* + * 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 "hw/virtio.h" +#include "hw/pc.h" +#include "qemu_socket.h" +#include "hw/virtio-pci.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; + + 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); +} + +/* + * Return TRUE if s1 is an ancestor of s2. + * + * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d". + * As a special case, We treat s1 as ancestor of s2 if they are same! + */ +static int v9fs_path_is_ancestor(V9fsString *s1, V9fsString *s2) +{ + if (!strncmp(s1->data, s2->data, s1->size)) { + if (s2->data[s1->size] == '\0' || s2->data[s1->size] == '/') { + return 1; + } + } + return 0; +} + +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) { + if (debug_9p_pdu) { + pprint_pdu(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 *); + offset += pdu_unpack(&val, pdu, offset, sizeof(val)); + *valp = le16_to_cpu(val); + break; + } + case 'd': { + uint32_t val, *valp; + valp = va_arg(ap, uint32_t *); + offset += pdu_unpack(&val, pdu, offset, sizeof(val)); + *valp = le32_to_cpu(val); + break; + } + case 'q': { + uint64_t val, *valp; + valp = va_arg(ap, uint64_t *); + offset += pdu_unpack(&val, pdu, offset, sizeof(val)); + *valp = le64_to_cpu(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 && vs->nwnames <= P9_MAXWELEM) { + 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->nwnames <= P9_MAXWELEM) { + 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]); + } + } else if (vs->nwnames > P9_MAXWELEM) { + err = -EINVAL; + goto out; + } + + 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 v9fs_path_is_ancestor check will + * work + */ + continue; + } + if (v9fs_path_is_ancestor(&vs->fidp->path, &fidp->path)) { + /* 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 v9fs_op_not_supp(V9fsState *s, V9fsPDU *pdu) +{ + complete_pdu(s, pdu, -EOPNOTSUPP); +} + +static void submit_pdu(V9fsState *s, V9fsPDU *pdu) +{ + pdu_handler_t *handler; + + if (debug_9p_pdu) { + pprint_pdu(pdu); + } + if (pdu->id >= ARRAY_SIZE(pdu_handlers) || + (pdu_handlers[pdu->id] == NULL)) { + handler = v9fs_op_not_supp; + } else { + handler = pdu_handlers[pdu->id]; + } + handler(s, pdu); +} + +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); +} diff -Nru qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-debug.c qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-debug.c --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-debug.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-debug.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,646 @@ +/* + * 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 "hw/virtio.h" +#include "hw/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.14.1+noroms/hw/9pfs/virtio-9p-debug.h qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-debug.h --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-debug.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-debug.h 2011-08-09 07:40:29.000000000 -0500 @@ -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.14.1+noroms/hw/9pfs/virtio-9p-device.c qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-device.c --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-device.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-device.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,173 @@ +/* + * 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 "hw/virtio.h" +#include "hw/pc.h" +#include "qemu_socket.h" +#include "hw/virtio-pci.h" +#include "virtio-9p.h" +#include "fsdev/qemu-fsdev.h" +#include "virtio-9p-xattr.h" + +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; +} + +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); + /* make the actual value visible */ + proxy->nvectors = vdev->nvectors; + return 0; +} + +static PCIDeviceInfo virtio_9p_info = { + .qdev.name = "virtio-9p-pci", + .qdev.size = sizeof(VirtIOPCIProxy), + .init = virtio_9p_init_pci, + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = 0x1009, + .revision = VIRTIO_PCI_ABI_VERSION, + .class_id = 0x2, + .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(), + } +}; + +static void virtio_9p_register_devices(void) +{ + pci_qdev_register(&virtio_9p_info); +} + +device_init(virtio_9p_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p.h qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p.h --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,514 @@ +#ifndef _QEMU_VIRTIO_9P_H +#define _QEMU_VIRTIO_9P_H + +#include +#include +#include +#include + +#include "fsdev/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 +static inline const char *rpath(FsContext *ctx, const char *path, char *buffer) +{ + snprintf(buffer, PATH_MAX, "%s/%s", ctx->fs_root, path); + return buffer; +} + +/* + * 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; + uint16_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); +} + +extern void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-local.c qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-local.c --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-local.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-local.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,557 @@ +/* + * 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 "hw/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; + char buffer[PATH_MAX]; + err = lstat(rpath(fs_ctx, path, buffer), 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, buffer), "user.virtfs.uid", &tmp_uid, + sizeof(uid_t)) > 0) { + stbuf->st_uid = tmp_uid; + } + if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.gid", &tmp_gid, + sizeof(gid_t)) > 0) { + stbuf->st_gid = tmp_gid; + } + if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.mode", + &tmp_mode, sizeof(mode_t)) > 0) { + stbuf->st_mode = tmp_mode; + } + if (getxattr(rpath(fs_ctx, path, buffer), "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) +{ + char buffer[PATH_MAX]; + if (chmod(rpath(fs_ctx, path, buffer), credp->fc_mode & 07777) < 0) { + return -1; + } + if (lchown(rpath(fs_ctx, path, buffer), 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; + char buffer[PATH_MAX]; + if (fs_ctx->fs_sm == SM_MAPPED) { + int fd; + fd = open(rpath(fs_ctx, path, buffer), 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, buffer), 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) +{ + char buffer[PATH_MAX]; + return open(rpath(ctx, path, buffer), flags); +} + +static DIR *local_opendir(FsContext *ctx, const char *path) +{ + char buffer[PATH_MAX]; + return opendir(rpath(ctx, path, buffer)); +} + +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) +{ + char buffer[PATH_MAX]; + if (fs_ctx->fs_sm == SM_MAPPED) { + return local_set_xattr(rpath(fs_ctx, path, buffer), credp); + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode); + } + return -1; +} + +static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp) +{ + int err = -1; + int serrno = 0; + char buffer[PATH_MAX]; + + /* Determine the security model */ + if (fs_ctx->fs_sm == SM_MAPPED) { + err = mknod(rpath(fs_ctx, path, buffer), + SM_LOCAL_MODE_BITS|S_IFREG, 0); + if (err == -1) { + return err; + } + local_set_xattr(rpath(fs_ctx, path, buffer), 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, buffer), 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, buffer)); + errno = serrno; + return err; +} + +static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp) +{ + int err = -1; + int serrno = 0; + char buffer[PATH_MAX]; + + /* Determine the security model */ + if (fs_ctx->fs_sm == SM_MAPPED) { + err = mkdir(rpath(fs_ctx, path, buffer), 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, buffer), 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, buffer), 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, buffer)); + 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; + char buffer[PATH_MAX]; + + /* Determine the security model */ + if (fs_ctx->fs_sm == SM_MAPPED) { + fd = open(rpath(fs_ctx, path, buffer), 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, buffer), 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, buffer), 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, buffer)); + 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; + char buffer[PATH_MAX]; + + /* 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, buffer), 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); + 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, buffer), 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, buffer)); + if (err) { + return err; + } + err = lchown(rpath(fs_ctx, newpath, buffer), 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, buffer)); + errno = serrno; + return err; +} + +static int local_link(FsContext *ctx, const char *oldpath, const char *newpath) +{ + char buffer[PATH_MAX], buffer1[PATH_MAX]; + + return link(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1)); +} + +static int local_truncate(FsContext *ctx, const char *path, off_t size) +{ + char buffer[PATH_MAX]; + return truncate(rpath(ctx, path, buffer), size); +} + +static int local_rename(FsContext *ctx, const char *oldpath, + const char *newpath) +{ + char buffer[PATH_MAX], buffer1[PATH_MAX]; + + return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1)); +} + +static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp) +{ + char buffer[PATH_MAX]; + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || + (fs_ctx->fs_sm == SM_PASSTHROUGH)) { + return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, + credp->fc_gid); + } else if (fs_ctx->fs_sm == SM_MAPPED) { + return local_set_xattr(rpath(fs_ctx, path, buffer), credp); + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, + credp->fc_gid); + } + return -1; +} + +static int local_utimensat(FsContext *s, const char *path, + const struct timespec *buf) +{ + char buffer[PATH_MAX]; + return qemu_utimensat(AT_FDCWD, rpath(s, path, buffer), buf, + AT_SYMLINK_NOFOLLOW); +} + +static int local_remove(FsContext *ctx, const char *path) +{ + char buffer[PATH_MAX]; + return remove(rpath(ctx, path, buffer)); +} + +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) +{ + char buffer[PATH_MAX]; + return statfs(rpath(s, path, buffer), 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.14.1+noroms/hw/9pfs/virtio-9p-posix-acl.c qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-posix-acl.c --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-posix-acl.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-posix-acl.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,159 @@ +/* + * 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 "hw/virtio.h" +#include "virtio-9p.h" +#include "fsdev/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) +{ + char buffer[PATH_MAX]; + return lgetxattr(rpath(ctx, path, buffer), 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) +{ + char buffer[PATH_MAX]; + return lsetxattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS, value, + size, flags); +} + +static int mp_pacl_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + int ret; + char buffer[PATH_MAX]; + ret = lremovexattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS); + if (ret == -1 && errno == ENODATA) { + /* + * We don't get ENODATA error when trying to remove 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) +{ + char buffer[PATH_MAX]; + return lgetxattr(rpath(ctx, path, buffer), 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) +{ + char buffer[PATH_MAX]; + return lsetxattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT, value, + size, flags); +} + +static int mp_dacl_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + int ret; + char buffer[PATH_MAX]; + ret = lremovexattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT); + if (ret == -1 && errno == ENODATA) { + /* + * We don't get ENODATA error when trying to remove 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; +} + + +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.14.1+noroms/hw/9pfs/virtio-9p-xattr.c qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-xattr.c --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-xattr.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-xattr.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,160 @@ +/* + * 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 "hw/virtio.h" +#include "virtio-9p.h" +#include "fsdev/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; + char buffer[PATH_MAX]; + 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, buffer), 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, buffer), 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.14.1+noroms/hw/9pfs/virtio-9p-xattr.h qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-xattr.h --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-xattr.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-xattr.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,105 @@ +/* + * 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) +{ + char buffer[PATH_MAX]; + return lgetxattr(rpath(ctx, path, buffer), name, value, size); +} + +static inline int pt_setxattr(FsContext *ctx, const char *path, + const char *name, void *value, + size_t size, int flags) +{ + char buffer[PATH_MAX]; + return lsetxattr(rpath(ctx, path, buffer), name, value, size, flags); +} + +static inline int pt_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + char buffer[PATH_MAX]; + return lremovexattr(rpath(ctx, path, buffer), 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.14.1+noroms/hw/9pfs/virtio-9p-xattr-user.c qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-xattr-user.c --- qemu-kvm-0.14.1+noroms/hw/9pfs/virtio-9p-xattr-user.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/9pfs/virtio-9p-xattr-user.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,112 @@ +/* + * 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 "hw/virtio.h" +#include "virtio-9p.h" +#include "fsdev/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) +{ + char buffer[PATH_MAX]; + 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, buffer), 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) +{ + char buffer[PATH_MAX]; + 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, buffer), name, value, size, flags); +} + +static int mp_user_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + char buffer[PATH_MAX]; + 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, buffer), 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.14.1+noroms/hw/a9mpcore.c qemu-kvm-0.15.0+noroms/hw/a9mpcore.c --- qemu-kvm-0.14.1+noroms/hw/a9mpcore.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/a9mpcore.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* 64 external IRQ lines. */ diff -Nru qemu-kvm-0.14.1+noroms/hw/ac97.c qemu-kvm-0.15.0+noroms/hw/ac97.c --- qemu-kvm-0.14.1+noroms/hw/ac97.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ac97.c 2011-08-09 07:40:29.000000000 -0500 @@ -1001,8 +1001,6 @@ static void write_bup (AC97LinkState *s, int elapsed) { - int written = 0; - dolog ("write_bup\n"); if (!(s->bup_flag & BUP_SET)) { if (s->bup_flag & BUP_LAST) { @@ -1026,7 +1024,6 @@ return; temp -= copied; elapsed -= copied; - written += copied; } } } @@ -1069,7 +1066,7 @@ static void transfer_audio (AC97LinkState *s, int index, int elapsed) { AC97BusMasterRegs *r = &s->bm_regs[index]; - int written = 0, stop = 0; + int stop = 0; if (s->invalid_freq[index]) { AUD_log ("ac97", "attempt to use voice %d with invalid frequency %d\n", @@ -1114,7 +1111,6 @@ switch (index) { case PO_INDEX: temp = write_audio (s, r, elapsed, &stop); - written += temp; elapsed -= temp; r->picb -= (temp >> 1); break; @@ -1281,9 +1277,6 @@ AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev); uint8_t *c = s->dev.config; - pci_config_set_vendor_id (c, PCI_VENDOR_ID_INTEL); /* ro */ - pci_config_set_device_id (c, PCI_DEVICE_ID_INTEL_82801AA_5); /* ro */ - /* TODO: no need to override */ c[PCI_COMMAND] = 0x00; /* pcicmd pci command rw, ro */ c[PCI_COMMAND + 1] = 0x00; @@ -1292,9 +1285,7 @@ c[PCI_STATUS] = PCI_STATUS_FAST_BACK; /* pcists pci status rwc, ro */ c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8; - 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 */ /* TODO set when bar is registered. no need to override. */ /* nabmar native audio mixer base address rw */ @@ -1341,6 +1332,10 @@ .qdev.size = sizeof (AC97LinkState), .qdev.vmsd = &vmstate_ac97, .init = ac97_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801AA_5, + .revision = 0x01, + .class_id = PCI_CLASS_MULTIMEDIA_AUDIO, }; static void ac97_register (void) diff -Nru qemu-kvm-0.14.1+noroms/hw/acpi.c qemu-kvm-0.15.0+noroms/hw/acpi.c --- qemu-kvm-0.14.1+noroms/hw/acpi.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/acpi.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,12 +15,10 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see */ +#include "sysemu.h" #include "hw.h" #include "pc.h" #include "acpi.h" -#include "kvm.h" -#include "qemu-kvm.h" -#include "string.h" struct acpi_table_header { @@ -200,3 +198,199 @@ } return -1; } + +/* ACPI PM1a EVT */ +uint16_t acpi_pm1_evt_get_sts(ACPIPM1EVT *pm1, int64_t overflow_time) +{ + int64_t d = acpi_pm_tmr_get_clock(); + if (d >= overflow_time) { + pm1->sts |= ACPI_BITMASK_TIMER_STATUS; + } + return pm1->sts; +} + +void acpi_pm1_evt_write_sts(ACPIPM1EVT *pm1, ACPIPMTimer *tmr, uint16_t val) +{ + uint16_t pm1_sts = acpi_pm1_evt_get_sts(pm1, tmr->overflow_time); + if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) { + /* if TMRSTS is reset, then compute the new overflow time */ + acpi_pm_tmr_calc_overflow_time(tmr); + } + pm1->sts &= ~val; +} + +void acpi_pm1_evt_power_down(ACPIPM1EVT *pm1, ACPIPMTimer *tmr) +{ + if (!pm1) { + qemu_system_shutdown_request(); + } else if (pm1->en & ACPI_BITMASK_POWER_BUTTON_ENABLE) { + pm1->sts |= ACPI_BITMASK_POWER_BUTTON_STATUS; + tmr->update_sci(tmr); + } +} + +void acpi_pm1_evt_reset(ACPIPM1EVT *pm1) +{ + pm1->sts = 0; + pm1->en = 0; +} + +/* ACPI PM_TMR */ +void acpi_pm_tmr_update(ACPIPMTimer *tmr, bool enable) +{ + int64_t expire_time; + + /* schedule a timer interruption if needed */ + if (enable) { + expire_time = muldiv64(tmr->overflow_time, get_ticks_per_sec(), + PM_TIMER_FREQUENCY); + qemu_mod_timer(tmr->timer, expire_time); + } else { + qemu_del_timer(tmr->timer); + } +} + +void acpi_pm_tmr_calc_overflow_time(ACPIPMTimer *tmr) +{ + int64_t d = acpi_pm_tmr_get_clock(); + tmr->overflow_time = (d + 0x800000LL) & ~0x7fffffLL; +} + +uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr) +{ + uint32_t d = acpi_pm_tmr_get_clock();; + return d & 0xffffff; +} + +static void acpi_pm_tmr_timer(void *opaque) +{ + ACPIPMTimer *tmr = opaque; + tmr->update_sci(tmr); +} + +void acpi_pm_tmr_init(ACPIPMTimer *tmr, acpi_update_sci_fn update_sci) +{ + tmr->update_sci = update_sci; + tmr->timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, tmr); +} + +void acpi_pm_tmr_reset(ACPIPMTimer *tmr) +{ + tmr->overflow_time = 0; + qemu_del_timer(tmr->timer); +} + +/* ACPI PM1aCNT */ +void acpi_pm1_cnt_init(ACPIPM1CNT *pm1_cnt, qemu_irq cmos_s3) +{ + pm1_cnt->cmos_s3 = cmos_s3; +} + +void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val) +{ + pm1_cnt->cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE); + + if (val & ACPI_BITMASK_SLEEP_ENABLE) { + /* change suspend type */ + uint16_t 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 */ + pm1a->sts |= + (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS); + qemu_system_reset_request(); + qemu_irq_raise(pm1_cnt->cmos_s3); + default: + break; + } + } +} + +void acpi_pm1_cnt_update(ACPIPM1CNT *pm1_cnt, + bool sci_enable, bool sci_disable) +{ + /* ACPI specs 3.0, 4.7.2.5 */ + if (sci_enable) { + pm1_cnt->cnt |= ACPI_BITMASK_SCI_ENABLE; + } else if (sci_disable) { + pm1_cnt->cnt &= ~ACPI_BITMASK_SCI_ENABLE; + } +} + +void acpi_pm1_cnt_reset(ACPIPM1CNT *pm1_cnt) +{ + pm1_cnt->cnt = 0; + if (pm1_cnt->cmos_s3) { + qemu_irq_lower(pm1_cnt->cmos_s3); + } +} + +/* ACPI GPE */ +void acpi_gpe_init(ACPIGPE *gpe, uint8_t len) +{ + gpe->len = len; + gpe->sts = qemu_mallocz(len / 2); + gpe->en = qemu_mallocz(len / 2); +} + +void acpi_gpe_blk(ACPIGPE *gpe, uint32_t blk) +{ + gpe->blk = blk; +} + +void acpi_gpe_reset(ACPIGPE *gpe) +{ + memset(gpe->sts, 0, gpe->len / 2); + memset(gpe->en, 0, gpe->len / 2); +} + +static uint8_t *acpi_gpe_ioport_get_ptr(ACPIGPE *gpe, uint32_t addr) +{ + uint8_t *cur = NULL; + + if (addr < gpe->len / 2) { + cur = gpe->sts + addr; + } else if (addr < gpe->len) { + cur = gpe->en + addr - gpe->len / 2; + } else { + abort(); + } + + return cur; +} + +void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val) +{ + uint8_t *cur; + + addr -= gpe->blk; + cur = acpi_gpe_ioport_get_ptr(gpe, addr); + if (addr < gpe->len / 2) { + /* GPE_STS */ + *cur = (*cur) & ~val; + } else if (addr < gpe->len) { + /* GPE_EN */ + *cur = val; + } else { + abort(); + } +} + +uint32_t acpi_gpe_ioport_readb(ACPIGPE *gpe, uint32_t addr) +{ + uint8_t *cur; + uint32_t val; + + addr -= gpe->blk; + cur = acpi_gpe_ioport_get_ptr(gpe, addr); + val = 0; + if (cur != NULL) { + val = *cur; + } + + return val; +} diff -Nru qemu-kvm-0.14.1+noroms/hw/acpi.h qemu-kvm-0.15.0+noroms/hw/acpi.h --- qemu-kvm-0.14.1+noroms/hw/acpi.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/acpi.h 2011-08-09 07:40:29.000000000 -0500 @@ -74,5 +74,73 @@ #define ACPI_BITMASK_ARB_DISABLE 0x0001 /* PM_TMR */ +struct ACPIPMTimer; +typedef struct ACPIPMTimer ACPIPMTimer; + +typedef void (*acpi_update_sci_fn)(ACPIPMTimer *tmr); + +struct ACPIPMTimer { + QEMUTimer *timer; + int64_t overflow_time; + + acpi_update_sci_fn update_sci; +}; + +void acpi_pm_tmr_update(ACPIPMTimer *tmr, bool enable); +void acpi_pm_tmr_calc_overflow_time(ACPIPMTimer *tmr); +uint32_t acpi_pm_tmr_get(ACPIPMTimer *tmr); +void acpi_pm_tmr_init(ACPIPMTimer *tmr, acpi_update_sci_fn update_sci); +void acpi_pm_tmr_reset(ACPIPMTimer *tmr); + +#include "qemu-timer.h" +static inline int64_t acpi_pm_tmr_get_clock(void) +{ + return muldiv64(qemu_get_clock_ns(vm_clock), PM_TIMER_FREQUENCY, + get_ticks_per_sec()); +} + +/* PM1a_EVT: piix and ich9 don't implement PM1b. */ +struct ACPIPM1EVT +{ + uint16_t sts; + uint16_t en; +}; +typedef struct ACPIPM1EVT ACPIPM1EVT; + +uint16_t acpi_pm1_evt_get_sts(ACPIPM1EVT *pm1, int64_t overflow_time); +void acpi_pm1_evt_write_sts(ACPIPM1EVT *pm1, ACPIPMTimer *tmr, uint16_t val); +void acpi_pm1_evt_power_down(ACPIPM1EVT *pm1, ACPIPMTimer *tmr); +void acpi_pm1_evt_reset(ACPIPM1EVT *pm1); + +/* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */ +struct ACPIPM1CNT { + uint16_t cnt; + + qemu_irq cmos_s3; +}; +typedef struct ACPIPM1CNT ACPIPM1CNT; + +void acpi_pm1_cnt_init(ACPIPM1CNT *pm1_cnt, qemu_irq cmos_s3); +void acpi_pm1_cnt_write(ACPIPM1EVT *pm1a, ACPIPM1CNT *pm1_cnt, uint16_t val); +void acpi_pm1_cnt_update(ACPIPM1CNT *pm1_cnt, + bool sci_enable, bool sci_disable); +void acpi_pm1_cnt_reset(ACPIPM1CNT *pm1_cnt); + +/* GPE0 */ +struct ACPIGPE { + uint32_t blk; + uint8_t len; + + uint8_t *sts; + uint8_t *en; +}; +typedef struct ACPIGPE ACPIGPE; + +void acpi_gpe_init(ACPIGPE *gpe, uint8_t len); +void acpi_gpe_blk(ACPIGPE *gpe, uint32_t blk); +void acpi_gpe_reset(ACPIGPE *gpe); + +void acpi_gpe_ioport_writeb(ACPIGPE *gpe, uint32_t addr, uint32_t val); +uint32_t acpi_gpe_ioport_readb(ACPIGPE *gpe, uint32_t addr); #endif /* !QEMU_HW_ACPI_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/acpi_piix4.c qemu-kvm-0.15.0+noroms/hw/acpi_piix4.c --- qemu-kvm-0.14.1+noroms/hw/acpi_piix4.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/acpi_piix4.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,6 +23,7 @@ #include "acpi.h" #include "sysemu.h" #include "range.h" +#include "ioport.h" //#define DEBUG @@ -36,6 +37,7 @@ #define GPE_BASE 0xafe0 #define PROC_BASE 0xaf00 +#define GPE_LEN 4 #define PCI_BASE 0xae00 #define PCI_EJ_BASE 0xae08 #define PCI_RMV_BASE 0xae0c @@ -44,8 +46,6 @@ #define PIIX4_PCI_HOTPLUG_STATUS 2 struct gpe_regs { - uint16_t sts; /* status */ - uint16_t en; /* enabled */ uint8_t cpus_sts[32]; }; @@ -57,25 +57,24 @@ typedef struct PIIX4PMState { PCIDevice dev; IORange ioport; - uint16_t pmsts; - uint16_t pmen; - uint16_t pmcntrl; + ACPIPM1EVT pm1a; + ACPIPM1CNT pm1_cnt; APMState apm; - QEMUTimer *tmr_timer; - int64_t tmr_overflow_time; + ACPIPMTimer tmr; PMSMBus smb; uint32_t smb_io_base; qemu_irq irq; - qemu_irq cmos_s3; qemu_irq smi_irq; int kvm_enabled; + Notifier machine_ready; /* for pci hotplug */ - struct gpe_regs gpe; + ACPIGPE gpe; + struct gpe_regs gpe_cpu; struct pci_status pci0_status; uint32_t pci0_hotplug_enable; } PIIX4PMState; @@ -85,53 +84,27 @@ #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) & + pmsts = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time); + sci_level = (((pmsts & s->pm1a.en) & (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); + (((s->gpe.sts[0] & s->gpe.en[0]) & 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); - } + acpi_pm_tmr_update(&s->tmr, (s->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) && + !(pmsts & ACPI_BITMASK_TIMER_STATUS)); } -static void pm_tmr_timer(void *opaque) +static void pm_tmr_timer(ACPIPMTimer *tmr) { - PIIX4PMState *s = opaque; + PIIX4PMState *s = container_of(tmr, PIIX4PMState, tmr); pm_update_sci(s); } @@ -147,54 +120,21 @@ 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); - } + acpi_pm1_evt_write_sts(&s->pm1a, &s->tmr, val); + pm_update_sci(s); break; case 0x02: - s->pmen = val; + s->pm1a.en = 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; - } - } - } + acpi_pm1_cnt_write(&s->pm1a, &s->pm1_cnt, val); break; default: break; } - PIIX4_DPRINTF("PM writew port=0x%04x val=0x%04x\n", addr, val); + PIIX4_DPRINTF("PM writew port=0x%04x val=0x%04x\n", (unsigned int)addr, + (unsigned int)val); } static void pm_ioport_read(IORange *ioport, uint64_t addr, unsigned width, @@ -205,22 +145,22 @@ switch(addr) { case 0x00: - val = get_pmsts(s); + val = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time); break; case 0x02: - val = s->pmen; + val = s->pm1a.en; break; case 0x04: - val = s->pmcntrl; + val = s->pm1_cnt.cnt; break; case 0x08: - val = get_pmtmr(s); + val = acpi_pm_tmr_get(&s->tmr); break; default: val = 0; break; } - PIIX4_DPRINTF("PM readw port=0x%04x val=0x%04x\n", addr, val); + PIIX4_DPRINTF("PM readw port=0x%04x val=0x%04x\n", (unsigned int)addr, val); *data = val; } @@ -234,11 +174,7 @@ 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; - } + acpi_pm1_cnt_update(&s->pm1_cnt, val == ACPI_ENABLE, val == ACPI_DISABLE); if (s->dev.config[0x5b] & (1 << 1)) { if (s->smi_irq) { @@ -283,14 +219,24 @@ return 0; } +#define VMSTATE_GPE_ARRAY(_field, _state) \ + { \ + .name = (stringify(_field)), \ + .version_id = 0, \ + .info = &vmstate_info_uint16, \ + .size = sizeof(uint16_t), \ + .flags = VMS_SINGLE | VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ + } + 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_GPE_ARRAY(sts, ACPIGPE), + VMSTATE_GPE_ARRAY(en, ACPIGPE), VMSTATE_END_OF_LIST() } }; @@ -315,13 +261,13 @@ .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_UINT16(pm1a.sts, PIIX4PMState), + VMSTATE_UINT16(pm1a.en, PIIX4PMState), + VMSTATE_UINT16(pm1_cnt.cnt, 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_TIMER(tmr.timer, PIIX4PMState), + VMSTATE_INT64(tmr.overflow_time, PIIX4PMState), + VMSTATE_STRUCT(gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status, struct pci_status), VMSTATE_END_OF_LIST() @@ -367,13 +313,23 @@ static void piix4_powerdown(void *opaque, int irq, int power_failing) { PIIX4PMState *s = opaque; + ACPIPM1EVT *pm1a = s? &s->pm1a: NULL; + ACPIPMTimer *tmr = s? &s->tmr: NULL; + + acpi_pm1_evt_power_down(pm1a, tmr); +} + +static void piix4_pm_machine_ready(Notifier *n, void *opaque) +{ + PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); + uint8_t *pci_conf; + + pci_conf = s->dev.config; + pci_conf[0x5f] = (isa_is_ioport_assigned(0x378) ? 0x80 : 0) | 0x10; + pci_conf[0x63] = 0x60; + pci_conf[0x67] = (isa_is_ioport_assigned(0x3f8) ? 0x08 : 0) | + (isa_is_ioport_assigned(0x2f8) ? 0x90 : 0); - 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 */ @@ -387,13 +343,9 @@ 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 */ @@ -418,22 +370,20 @@ /* 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); + acpi_pm_tmr_init(&s->tmr, pm_tmr_timer); + acpi_gpe_init(&s->gpe, GPE_LEN); qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); pm_smbus_init(&s->dev.qdev, &s->smb); + s->machine_ready.notify = piix4_pm_machine_ready; + qemu_add_machine_init_done_notifier(&s->machine_ready); qemu_register_reset(piix4_reset, s); piix4_acpi_system_hot_add_init(dev->bus, s); @@ -452,7 +402,7 @@ s = DO_UPCAST(PIIX4PMState, dev, dev); s->irq = sci_irq; - s->cmos_s3 = cmos_s3; + acpi_pm1_cnt_init(&s->pm1_cnt, cmos_s3); s->smi_irq = smi_irq; s->kvm_enabled = kvm_enabled; @@ -470,6 +420,10 @@ .no_hotplug = 1, .init = piix4_pm_initfn, .config_write = pm_write_config, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371AB_3, + .revision = 0x03, + .class_id = PCI_CLASS_BRIDGE_OTHER, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), DEFINE_PROP_END_OF_LIST(), @@ -483,78 +437,29 @@ 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; + uint32_t val = 0; + struct gpe_regs *g = &s->gpe_cpu; 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; + val = acpi_gpe_ioport_readb(&s->gpe, addr); } 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; - } + acpi_gpe_ioport_writeb(&s->gpe, addr, val); pm_update_sci(s); PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val); @@ -605,11 +510,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); } } @@ -641,12 +548,13 @@ int i = 0, cpus = smp_cpus; while (cpus > 0) { - s->gpe.cpus_sts[i++] = (cpus < 8) ? (1 << cpus) - 1 : 0xff; + s->gpe_cpu.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(GPE_BASE, GPE_LEN, 1, gpe_writeb, s); + register_ioport_read(GPE_BASE, GPE_LEN, 1, gpe_readb, s); + acpi_gpe_blk(&s->gpe, GPE_BASE); register_ioport_write(PROC_BASE, 32, 1, gpe_writeb, s); register_ioport_read(PROC_BASE, 32, 1, gpe_readb, s); @@ -664,15 +572,21 @@ } #if defined(TARGET_I386) -static void enable_processor(struct gpe_regs *g, int cpu) +static void enable_processor(PIIX4PMState *s, int cpu) { - g->sts |= PIIX4_CPU_HOTPLUG_STATUS; + struct gpe_regs *g = &s->gpe_cpu; + ACPIGPE *gpe = &s->gpe; + + *gpe->sts = *gpe->sts | PIIX4_CPU_HOTPLUG_STATUS; g->cpus_sts[cpu/8] |= (1 << (cpu%8)); } -static void disable_processor(struct gpe_regs *g, int cpu) +static void disable_processor(PIIX4PMState *s, int cpu) { - g->sts |= PIIX4_CPU_HOTPLUG_STATUS; + struct gpe_regs *g = &s->gpe_cpu; + ACPIGPE *gpe = &s->gpe; + + *gpe->sts = *gpe->sts | PIIX4_CPU_HOTPLUG_STATUS; g->cpus_sts[cpu/8] &= ~(1 << (cpu%8)); } @@ -691,9 +605,9 @@ } if (state) - enable_processor(&s->gpe, cpu); + enable_processor(s, cpu); else - disable_processor(&s->gpe, cpu); + disable_processor(s, cpu); pm_update_sci(s); } @@ -701,13 +615,13 @@ static void enable_device(PIIX4PMState *s, int slot) { - s->gpe.sts |= PIIX4_PCI_HOTPLUG_STATUS; + s->gpe.sts[0] |= 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->gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS; s->pci0_status.down |= (1 << slot); } diff -Nru qemu-kvm-0.14.1+noroms/hw/adb.c qemu-kvm-0.15.0+noroms/hw/adb.c --- qemu-kvm-0.14.1+noroms/hw/adb.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/adb.c 2011-08-09 07:40:29.000000000 -0500 @@ -261,30 +261,19 @@ return olen; } -static void adb_kbd_save(QEMUFile *f, void *opaque) -{ - KBDState *s = (KBDState *)opaque; - - qemu_put_buffer(f, s->data, sizeof(s->data)); - qemu_put_sbe32s(f, &s->rptr); - qemu_put_sbe32s(f, &s->wptr); - qemu_put_sbe32s(f, &s->count); -} - -static int adb_kbd_load(QEMUFile *f, void *opaque, int version_id) -{ - KBDState *s = (KBDState *)opaque; - - if (version_id != 1) - return -EINVAL; - - qemu_get_buffer(f, s->data, sizeof(s->data)); - qemu_get_sbe32s(f, &s->rptr); - qemu_get_sbe32s(f, &s->wptr); - qemu_get_sbe32s(f, &s->count); - - return 0; -} +static const VMStateDescription vmstate_adb_kbd = { + .name = "adb_kbd", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(data, KBDState), + VMSTATE_INT32(rptr, KBDState), + VMSTATE_INT32(wptr, KBDState), + VMSTATE_INT32(count, KBDState), + VMSTATE_END_OF_LIST() + } +}; static int adb_kbd_reset(ADBDevice *d) { @@ -305,8 +294,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(NULL, "adb_kbd", -1, 1, adb_kbd_save, - adb_kbd_load, s); + vmstate_register(NULL, -1, &vmstate_adb_kbd, s); } /***************************************************************/ @@ -439,32 +427,20 @@ return 0; } -static void adb_mouse_save(QEMUFile *f, void *opaque) -{ - MouseState *s = (MouseState *)opaque; - - qemu_put_sbe32s(f, &s->buttons_state); - qemu_put_sbe32s(f, &s->last_buttons_state); - qemu_put_sbe32s(f, &s->dx); - qemu_put_sbe32s(f, &s->dy); - qemu_put_sbe32s(f, &s->dz); -} - -static int adb_mouse_load(QEMUFile *f, void *opaque, int version_id) -{ - MouseState *s = (MouseState *)opaque; - - if (version_id != 1) - return -EINVAL; - - qemu_get_sbe32s(f, &s->buttons_state); - qemu_get_sbe32s(f, &s->last_buttons_state); - qemu_get_sbe32s(f, &s->dx); - qemu_get_sbe32s(f, &s->dy); - qemu_get_sbe32s(f, &s->dz); - - return 0; -} +static const VMStateDescription vmstate_adb_mouse = { + .name = "adb_mouse", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(buttons_state, MouseState), + VMSTATE_INT32(last_buttons_state, MouseState), + VMSTATE_INT32(dx, MouseState), + VMSTATE_INT32(dy, MouseState), + VMSTATE_INT32(dz, MouseState), + VMSTATE_END_OF_LIST() + } +}; void adb_mouse_init(ADBBusState *bus) { @@ -475,6 +451,5 @@ 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(NULL, "adb_mouse", -1, 1, adb_mouse_save, - adb_mouse_load, s); + vmstate_register(NULL, -1, &vmstate_adb_mouse, s); } diff -Nru qemu-kvm-0.14.1+noroms/hw/adlib.c qemu-kvm-0.15.0+noroms/hw/adlib.c --- qemu-kvm-0.14.1+noroms/hw/adlib.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/adlib.c 2011-08-09 07:40:29.000000000 -0500 @@ -166,7 +166,7 @@ s->ticking[n] = 1; #ifdef DEBUG interval = get_ticks_per_sec() * interval_Sec; - exp = qemu_get_clock (vm_clock) + interval; + exp = qemu_get_clock_ns (vm_clock) + interval; s->exp[n] = exp; #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/ads7846.c qemu-kvm-0.15.0+noroms/hw/ads7846.c --- qemu-kvm-0.14.1+noroms/hw/ads7846.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ads7846.c 2011-08-09 07:40:29.000000000 -0500 @@ -105,35 +105,30 @@ } } -static void ads7846_save(QEMUFile *f, void *opaque) +static int ads7856_post_load(void *opaque, int version_id) { - ADS7846State *s = (ADS7846State *) opaque; - int i; - - for (i = 0; i < 8; i ++) - qemu_put_be32(f, s->input[i]); - qemu_put_be32(f, s->noise); - qemu_put_be32(f, s->cycle); - qemu_put_be32(f, s->output); -} - -static int ads7846_load(QEMUFile *f, void *opaque, int version_id) -{ - ADS7846State *s = (ADS7846State *) opaque; - int i; - - for (i = 0; i < 8; i ++) - s->input[i] = qemu_get_be32(f); - s->noise = qemu_get_be32(f); - s->cycle = qemu_get_be32(f); - s->output = qemu_get_be32(f); + ADS7846State *s = opaque; s->pressure = 0; ads7846_int_update(s); - return 0; } +static const VMStateDescription vmstate_ads7846 = { + .name = "ads7846", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = ads7856_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32_ARRAY(input, ADS7846State, 8), + VMSTATE_INT32(noise, ADS7846State), + VMSTATE_INT32(cycle, ADS7846State), + VMSTATE_INT32(output, ADS7846State), + VMSTATE_END_OF_LIST() + } +}; + static int ads7846_init(SSISlave *dev) { ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev); @@ -151,7 +146,7 @@ ads7846_int_update(s); - register_savevm(NULL, "ads7846", -1, 0, ads7846_save, ads7846_load, s); + vmstate_register(NULL, -1, &vmstate_ads7846, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/alpha_palcode.c qemu-kvm-0.15.0+noroms/hw/alpha_palcode.c --- qemu-kvm-0.14.1+noroms/hw/alpha_palcode.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/alpha_palcode.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,1048 +0,0 @@ -/* - * Alpha emulation - PALcode emulation for qemu. - * - * Copyright (c) 2007 Jocelyn Mayer - * - * 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 "cpu.h" -#include "exec-all.h" - -/* Shared handlers */ -static void pal_reset (CPUState *env); -/* Console handlers */ -static void pal_console_call (CPUState *env, uint32_t palcode); -/* OpenVMS handlers */ -static void pal_openvms_call (CPUState *env, uint32_t palcode); -/* UNIX / Linux handlers */ -static void pal_unix_call (CPUState *env, uint32_t palcode); - -pal_handler_t pal_handlers[] = { - /* Console handler */ - { - .reset = &pal_reset, - .call_pal = &pal_console_call, - }, - /* OpenVMS handler */ - { - .reset = &pal_reset, - .call_pal = &pal_openvms_call, - }, - /* UNIX / Linux handler */ - { - .reset = &pal_reset, - .call_pal = &pal_unix_call, - }, -}; - -#if 0 -/* One must explicitly check that the TB is valid and the FOE bit is reset */ -static void update_itb (void) -{ - /* This writes into a temp register, not the actual one */ - mtpr(TB_TAG); - mtpr(TB_CTL); - /* This commits the TB update */ - mtpr(ITB_PTE); -} - -static void update_dtb (void); -{ - mtpr(TB_CTL); - /* This write into a temp register, not the actual one */ - mtpr(TB_TAG); - /* This commits the TB update */ - mtpr(DTB_PTE); -} -#endif - -static void pal_reset (CPUState *env) -{ -} - -static void do_swappal (CPUState *env, uint64_t palid) -{ - pal_handler_t *pal_handler; - - switch (palid) { - case 0 ... 2: - pal_handler = &pal_handlers[palid]; - env->pal_handler = pal_handler; - env->ipr[IPR_PAL_BASE] = -1ULL; - (*pal_handler->reset)(env); - break; - case 3 ... 255: - /* Unknown identifier */ - env->ir[0] = 1; - return; - default: - /* We were given the entry point address */ - env->pal_handler = NULL; - env->ipr[IPR_PAL_BASE] = palid; - env->pc = env->ipr[IPR_PAL_BASE]; - cpu_loop_exit(); - } -} - -static void pal_console_call (CPUState *env, uint32_t palcode) -{ - uint64_t palid; - - if (palcode < 0x00000080) { - /* Privileged palcodes */ - if (!(env->ps >> 3)) { - /* TODO: generate privilege exception */ - } - } - switch (palcode) { - case 0x00000000: - /* HALT */ - /* REQUIRED */ - break; - case 0x00000001: - /* CFLUSH */ - break; - case 0x00000002: - /* DRAINA */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000009: - /* CSERVE */ - /* REQUIRED */ - break; - case 0x0000000A: - /* SWPPAL */ - /* REQUIRED */ - palid = env->ir[16]; - do_swappal(env, palid); - break; - case 0x00000080: - /* BPT */ - /* REQUIRED */ - break; - case 0x00000081: - /* BUGCHK */ - /* REQUIRED */ - break; - case 0x00000086: - /* IMB */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x0000009E: - /* RDUNIQUE */ - /* REQUIRED */ - break; - case 0x0000009F: - /* WRUNIQUE */ - /* REQUIRED */ - break; - case 0x000000AA: - /* GENTRAP */ - /* REQUIRED */ - break; - default: - break; - } -} - -static void pal_openvms_call (CPUState *env, uint32_t palcode) -{ - uint64_t palid, val, oldval; - - if (palcode < 0x00000080) { - /* Privileged palcodes */ - if (!(env->ps >> 3)) { - /* TODO: generate privilege exception */ - } - } - switch (palcode) { - case 0x00000000: - /* HALT */ - /* REQUIRED */ - break; - case 0x00000001: - /* CFLUSH */ - break; - case 0x00000002: - /* DRAINA */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000003: - /* LDQP */ - break; - case 0x00000004: - /* STQP */ - break; - case 0x00000005: - /* SWPCTX */ - break; - case 0x00000006: - /* MFPR_ASN */ - if (cpu_alpha_mfpr(env, IPR_ASN, &val) == 0) - env->ir[0] = val; - break; - case 0x00000007: - /* MTPR_ASTEN */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_ASTEN, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000008: - /* MTPR_ASTSR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_ASTSR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000009: - /* CSERVE */ - /* REQUIRED */ - break; - case 0x0000000A: - /* SWPPAL */ - /* REQUIRED */ - palid = env->ir[16]; - do_swappal(env, palid); - break; - case 0x0000000B: - /* MFPR_FEN */ - if (cpu_alpha_mfpr(env, IPR_FEN, &val) == 0) - env->ir[0] = val; - break; - case 0x0000000C: - /* MTPR_FEN */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_FEN, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000000D: - /* MTPR_IPIR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000000E: - /* MFPR_IPL */ - if (cpu_alpha_mfpr(env, IPR_IPL, &val) == 0) - env->ir[0] = val; - break; - case 0x0000000F: - /* MTPR_IPL */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_IPL, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000010: - /* MFPR_MCES */ - if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) - env->ir[0] = val; - break; - case 0x00000011: - /* MTPR_MCES */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000012: - /* MFPR_PCBB */ - if (cpu_alpha_mfpr(env, IPR_PCBB, &val) == 0) - env->ir[0] = val; - break; - case 0x00000013: - /* MFPR_PRBR */ - if (cpu_alpha_mfpr(env, IPR_PRBR, &val) == 0) - env->ir[0] = val; - break; - case 0x00000014: - /* MTPR_PRBR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PRBR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000015: - /* MFPR_PTBR */ - if (cpu_alpha_mfpr(env, IPR_PTBR, &val) == 0) - env->ir[0] = val; - break; - case 0x00000016: - /* MFPR_SCBB */ - if (cpu_alpha_mfpr(env, IPR_SCBB, &val) == 0) - env->ir[0] = val; - break; - case 0x00000017: - /* MTPR_SCBB */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_SCBB, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000018: - /* MTPR_SIRR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_SIRR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000019: - /* MFPR_SISR */ - if (cpu_alpha_mfpr(env, IPR_SISR, &val) == 0) - env->ir[0] = val; - break; - case 0x0000001A: - /* MFPR_TBCHK */ - if (cpu_alpha_mfpr(env, IPR_TBCHK, &val) == 0) - env->ir[0] = val; - break; - case 0x0000001B: - /* MTPR_TBIA */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIA, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000001C: - /* MTPR_TBIAP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIAP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000001D: - /* MTPR_TBIS */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000001E: - /* MFPR_ESP */ - if (cpu_alpha_mfpr(env, IPR_ESP, &val) == 0) - env->ir[0] = val; - break; - case 0x0000001F: - /* MTPR_ESP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_ESP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000020: - /* MFPR_SSP */ - if (cpu_alpha_mfpr(env, IPR_SSP, &val) == 0) - env->ir[0] = val; - break; - case 0x00000021: - /* MTPR_SSP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_SSP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000022: - /* MFPR_USP */ - if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) - env->ir[0] = val; - break; - case 0x00000023: - /* MTPR_USP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000024: - /* MTPR_TBISD */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBISD, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000025: - /* MTPR_TBISI */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBISI, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000026: - /* MFPR_ASTEN */ - if (cpu_alpha_mfpr(env, IPR_ASTEN, &val) == 0) - env->ir[0] = val; - break; - case 0x00000027: - /* MFPR_ASTSR */ - if (cpu_alpha_mfpr(env, IPR_ASTSR, &val) == 0) - env->ir[0] = val; - break; - case 0x00000029: - /* MFPR_VPTB */ - if (cpu_alpha_mfpr(env, IPR_VPTB, &val) == 0) - env->ir[0] = val; - break; - case 0x0000002A: - /* MTPR_VPTB */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_VPTB, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002B: - /* MTPR_PERFMON */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002E: - /* MTPR_DATFX */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_DATFX, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000003E: - /* WTINT */ - break; - case 0x0000003F: - /* MFPR_WHAMI */ - if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) - env->ir[0] = val; - break; - case 0x00000080: - /* BPT */ - /* REQUIRED */ - break; - case 0x00000081: - /* BUGCHK */ - /* REQUIRED */ - break; - case 0x00000082: - /* CHME */ - break; - case 0x00000083: - /* CHMK */ - break; - case 0x00000084: - /* CHMS */ - break; - case 0x00000085: - /* CHMU */ - break; - case 0x00000086: - /* IMB */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000087: - /* INSQHIL */ - break; - case 0x00000088: - /* INSQTIL */ - break; - case 0x00000089: - /* INSQHIQ */ - break; - case 0x0000008A: - /* INSQTIQ */ - break; - case 0x0000008B: - /* INSQUEL */ - break; - case 0x0000008C: - /* INSQUEQ */ - break; - case 0x0000008D: - /* INSQUEL/D */ - break; - case 0x0000008E: - /* INSQUEQ/D */ - break; - case 0x0000008F: - /* PROBER */ - break; - case 0x00000090: - /* PROBEW */ - break; - case 0x00000091: - /* RD_PS */ - break; - case 0x00000092: - /* REI */ - break; - case 0x00000093: - /* REMQHIL */ - break; - case 0x00000094: - /* REMQTIL */ - break; - case 0x00000095: - /* REMQHIQ */ - break; - case 0x00000096: - /* REMQTIQ */ - break; - case 0x00000097: - /* REMQUEL */ - break; - case 0x00000098: - /* REMQUEQ */ - break; - case 0x00000099: - /* REMQUEL/D */ - break; - case 0x0000009A: - /* REMQUEQ/D */ - break; - case 0x0000009B: - /* SWASTEN */ - break; - case 0x0000009C: - /* WR_PS_SW */ - break; - case 0x0000009D: - /* RSCC */ - break; - case 0x0000009E: - /* READ_UNQ */ - /* REQUIRED */ - break; - case 0x0000009F: - /* WRITE_UNQ */ - /* REQUIRED */ - break; - case 0x000000A0: - /* AMOVRR */ - break; - case 0x000000A1: - /* AMOVRM */ - break; - case 0x000000A2: - /* INSQHILR */ - break; - case 0x000000A3: - /* INSQTILR */ - break; - case 0x000000A4: - /* INSQHIQR */ - break; - case 0x000000A5: - /* INSQTIQR */ - break; - case 0x000000A6: - /* REMQHILR */ - break; - case 0x000000A7: - /* REMQTILR */ - break; - case 0x000000A8: - /* REMQHIQR */ - break; - case 0x000000A9: - /* REMQTIQR */ - break; - case 0x000000AA: - /* GENTRAP */ - /* REQUIRED */ - break; - case 0x000000AE: - /* CLRFEN */ - break; - default: - break; - } -} - -static void pal_unix_call (CPUState *env, uint32_t palcode) -{ - uint64_t palid, val, oldval; - - if (palcode < 0x00000080) { - /* Privileged palcodes */ - if (!(env->ps >> 3)) { - /* TODO: generate privilege exception */ - } - } - switch (palcode) { - case 0x00000000: - /* HALT */ - /* REQUIRED */ - break; - case 0x00000001: - /* CFLUSH */ - break; - case 0x00000002: - /* DRAINA */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000009: - /* CSERVE */ - /* REQUIRED */ - break; - case 0x0000000A: - /* SWPPAL */ - /* REQUIRED */ - palid = env->ir[16]; - do_swappal(env, palid); - break; - case 0x0000000D: - /* WRIPIR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000010: - /* RDMCES */ - if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) - env->ir[0] = val; - break; - case 0x00000011: - /* WRMCES */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002B: - /* WRFEN */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002D: - /* WRVPTPTR */ - break; - case 0x00000030: - /* SWPCTX */ - break; - case 0x00000031: - /* WRVAL */ - break; - case 0x00000032: - /* RDVAL */ - break; - case 0x00000033: - /* TBI */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000034: - /* WRENT */ - break; - case 0x00000035: - /* SWPIPL */ - break; - case 0x00000036: - /* RDPS */ - break; - case 0x00000037: - /* WRKGP */ - break; - case 0x00000038: - /* WRUSP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000039: - /* WRPERFMON */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000003A: - /* RDUSP */ - if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) - env->ir[0] = val; - break; - case 0x0000003C: - /* WHAMI */ - if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) - env->ir[0] = val; - break; - case 0x0000003D: - /* RETSYS */ - break; - case 0x0000003E: - /* WTINT */ - break; - case 0x0000003F: - /* RTI */ - if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) - env->ir[0] = val; - break; - case 0x00000080: - /* BPT */ - /* REQUIRED */ - break; - case 0x00000081: - /* BUGCHK */ - /* REQUIRED */ - break; - case 0x00000083: - /* CALLSYS */ - break; - case 0x00000086: - /* IMB */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000092: - /* URTI */ - break; - case 0x0000009E: - /* RDUNIQUE */ - /* REQUIRED */ - break; - case 0x0000009F: - /* WRUNIQUE */ - /* REQUIRED */ - break; - case 0x000000AA: - /* GENTRAP */ - /* REQUIRED */ - break; - case 0x000000AE: - /* CLRFEN */ - break; - default: - break; - } -} - -void call_pal (CPUState *env) -{ - pal_handler_t *pal_handler = env->pal_handler; - - switch (env->exception_index) { - case EXCP_RESET: - (*pal_handler->reset)(env); - break; - case EXCP_MCHK: - (*pal_handler->machine_check)(env); - break; - case EXCP_ARITH: - (*pal_handler->arithmetic)(env); - break; - case EXCP_INTERRUPT: - (*pal_handler->interrupt)(env); - break; - case EXCP_DFAULT: - (*pal_handler->dfault)(env); - break; - case EXCP_DTB_MISS_PAL: - (*pal_handler->dtb_miss_pal)(env); - break; - case EXCP_DTB_MISS_NATIVE: - (*pal_handler->dtb_miss_native)(env); - break; - case EXCP_UNALIGN: - (*pal_handler->unalign)(env); - break; - case EXCP_ITB_MISS: - (*pal_handler->itb_miss)(env); - break; - case EXCP_ITB_ACV: - (*pal_handler->itb_acv)(env); - break; - case EXCP_OPCDEC: - (*pal_handler->opcdec)(env); - break; - case EXCP_FEN: - (*pal_handler->fen)(env); - break; - default: - if (env->exception_index >= EXCP_CALL_PAL && - env->exception_index < EXCP_CALL_PALP) { - /* Unprivileged PAL call */ - (*pal_handler->call_pal) - (env, (env->exception_index - EXCP_CALL_PAL) >> 6); - } else if (env->exception_index >= EXCP_CALL_PALP && - env->exception_index < EXCP_CALL_PALE) { - /* Privileged PAL call */ - (*pal_handler->call_pal) - (env, ((env->exception_index - EXCP_CALL_PALP) >> 6) + 0x80); - } else { - /* Should never happen */ - } - break; - } - env->ipr[IPR_EXC_ADDR] &= ~1; -} - -void pal_init (CPUState *env) -{ - do_swappal(env, 0); -} - -#if 0 -static uint64_t get_ptebase (CPUState *env, uint64_t vaddr) -{ - uint64_t virbnd, ptbr; - - if ((env->features & FEATURE_VIRBND)) { - cpu_alpha_mfpr(env, IPR_VIRBND, &virbnd); - if (vaddr >= virbnd) - cpu_alpha_mfpr(env, IPR_SYSPTBR, &ptbr); - else - cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); - } else { - cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); - } - - return ptbr; -} - -static int get_page_bits (CPUState *env) -{ - /* XXX */ - return 13; -} - -static int get_pte (uint64_t *pfnp, int *zbitsp, int *protp, - uint64_t ptebase, int page_bits, uint64_t level, - int mmu_idx, int rw) -{ - uint64_t pteaddr, pte, pfn; - uint8_t gh; - int ure, uwe, kre, kwe, foE, foR, foW, v, ret, ar, is_user; - - /* XXX: TOFIX */ - is_user = mmu_idx == MMU_USER_IDX; - pteaddr = (ptebase << page_bits) + (8 * level); - pte = ldq_raw(pteaddr); - /* Decode all interresting PTE fields */ - pfn = pte >> 32; - uwe = (pte >> 13) & 1; - kwe = (pte >> 12) & 1; - ure = (pte >> 9) & 1; - kre = (pte >> 8) & 1; - gh = (pte >> 5) & 3; - foE = (pte >> 3) & 1; - foW = (pte >> 2) & 1; - foR = (pte >> 1) & 1; - v = pte & 1; - ret = 0; - if (!v) - ret = 0x1; - /* Check access rights */ - ar = 0; - if (is_user) { - if (ure) - ar |= PAGE_READ; - if (uwe) - ar |= PAGE_WRITE; - if (rw == 1 && !uwe) - ret |= 0x2; - if (rw != 1 && !ure) - ret |= 0x2; - } else { - if (kre) - ar |= PAGE_READ; - if (kwe) - ar |= PAGE_WRITE; - if (rw == 1 && !kwe) - ret |= 0x2; - if (rw != 1 && !kre) - ret |= 0x2; - } - if (rw == 0 && foR) - ret |= 0x4; - if (rw == 2 && foE) - ret |= 0x8; - if (rw == 1 && foW) - ret |= 0xC; - *pfnp = pfn; - if (zbitsp != NULL) - *zbitsp = page_bits + (3 * gh); - if (protp != NULL) - *protp = ar; - - return ret; -} - -static int paddr_from_pte (uint64_t *paddr, int *zbitsp, int *prot, - uint64_t ptebase, int page_bits, - uint64_t vaddr, int mmu_idx, int rw) -{ - uint64_t pfn, page_mask, lvl_mask, level1, level2, level3; - int lvl_bits, ret; - - page_mask = (1ULL << page_bits) - 1ULL; - lvl_bits = page_bits - 3; - lvl_mask = (1ULL << lvl_bits) - 1ULL; - level3 = (vaddr >> page_bits) & lvl_mask; - level2 = (vaddr >> (page_bits + lvl_bits)) & lvl_mask; - level1 = (vaddr >> (page_bits + (2 * lvl_bits))) & lvl_mask; - /* Level 1 PTE */ - ret = get_pte(&pfn, NULL, NULL, ptebase, page_bits, level1, 0, 0); - switch (ret) { - case 3: - /* Access violation */ - return 2; - case 2: - /* translation not valid */ - return 1; - default: - /* OK */ - break; - } - /* Level 2 PTE */ - ret = get_pte(&pfn, NULL, NULL, pfn, page_bits, level2, 0, 0); - switch (ret) { - case 3: - /* Access violation */ - return 2; - case 2: - /* translation not valid */ - return 1; - default: - /* OK */ - break; - } - /* Level 3 PTE */ - ret = get_pte(&pfn, zbitsp, prot, pfn, page_bits, level3, mmu_idx, rw); - if (ret & 0x1) { - /* Translation not valid */ - ret = 1; - } else if (ret & 2) { - /* Access violation */ - ret = 2; - } else { - switch (ret & 0xC) { - case 0: - /* OK */ - ret = 0; - break; - case 0x4: - /* Fault on read */ - ret = 3; - break; - case 0x8: - /* Fault on execute */ - ret = 4; - break; - case 0xC: - /* Fault on write */ - ret = 5; - break; - } - } - *paddr = (pfn << page_bits) | (vaddr & page_mask); - - return 0; -} - -static int virtual_to_physical (CPUState *env, uint64_t *physp, - int *zbitsp, int *protp, - uint64_t virtual, int mmu_idx, int rw) -{ - uint64_t sva, ptebase; - int seg, page_bits, ret; - - sva = ((int64_t)(virtual << (64 - VA_BITS))) >> (64 - VA_BITS); - if (sva != virtual) - seg = -1; - else - seg = sva >> (VA_BITS - 2); - virtual &= ~(0xFFFFFC0000000000ULL << (VA_BITS - 43)); - ptebase = get_ptebase(env, virtual); - page_bits = get_page_bits(env); - ret = 0; - switch (seg) { - case 0: - /* seg1: 3 levels of PTE */ - ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, - virtual, mmu_idx, rw); - break; - case 1: - /* seg1: 2 levels of PTE */ - ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, - virtual, mmu_idx, rw); - break; - case 2: - /* kernel segment */ - if (mmu_idx != 0) { - ret = 2; - } else { - *physp = virtual; - } - break; - case 3: - /* seg1: TB mapped */ - ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, - virtual, mmu_idx, rw); - break; - default: - ret = 1; - break; - } - - return ret; -} - -/* XXX: code provision */ -int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, - int mmu_idx, int is_softmmu) -{ - uint64_t physical, page_size, end; - int prot, zbits, ret; - - 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) { - tlb_set_page(env, address, physical, prot, mmu_idx, - TARGET_PAGE_SIZE); - address += 0x1000; - } - ret = 0; - break; -#if 0 - case 1: - env->exception_index = EXCP_DFAULT; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - case 2: - env->exception_index = EXCP_ACCESS_VIOLATION; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - case 3: - env->exception_index = EXCP_FAULT_ON_READ; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - case 4: - env->exception_index = EXCP_FAULT_ON_EXECUTE; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - case 5: - env->exception_index = EXCP_FAULT_ON_WRITE; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; -#endif - default: - /* Should never happen */ - env->exception_index = EXCP_MCHK; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - } - - return ret; -} -#endif diff -Nru qemu-kvm-0.14.1+noroms/hw/an5206.c qemu-kvm-0.15.0+noroms/hw/an5206.c --- qemu-kvm-0.14.1+noroms/hw/an5206.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/an5206.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,13 +3,12 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "pc.h" #include "mcf.h" -#include "sysemu.h" #include "boards.h" #include "loader.h" #include "elf.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/apb_pci.c qemu-kvm-0.15.0+noroms/hw/apb_pci.c --- qemu-kvm-0.14.1+noroms/hw/apb_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/apb_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -304,9 +304,6 @@ 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: * According to PCI bridge spec, after reset @@ -321,7 +318,6 @@ 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; } @@ -436,14 +432,11 @@ static int pbm_pci_host_init(PCIDevice *d) { - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_SUN); - pci_config_set_device_id(d->config, PCI_DEVICE_ID_SUN_SABRE); 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); return 0; } @@ -451,6 +444,9 @@ .qdev.name = "pbm", .qdev.size = sizeof(PCIDevice), .init = pbm_pci_host_init, + .vendor_id = PCI_VENDOR_ID_SUN, + .device_id = PCI_DEVICE_ID_SUN_SABRE, + .class_id = PCI_CLASS_BRIDGE_HOST, .is_bridge = 1, }; @@ -468,6 +464,9 @@ .qdev.reset = pci_bridge_reset, .init = apb_pci_bridge_initfn, .exit = pci_bridge_exitfn, + .vendor_id = PCI_VENDOR_ID_SUN, + .device_id = PCI_DEVICE_ID_SUN_SIMBA, + .revision = 0x11, .config_write = pci_bridge_write_config, .is_bridge = 1, }; diff -Nru qemu-kvm-0.14.1+noroms/hw/apic.c qemu-kvm-0.15.0+noroms/hw/apic.c --- qemu-kvm-0.14.1+noroms/hw/apic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/apic.c 2011-08-09 07:40:29.000000000 -0500 @@ -650,7 +650,7 @@ { int64_t d; uint32_t val; - d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> + d = (qemu_get_clock_ns(vm_clock) - s->initial_count_load_time) >> s->count_shift; if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { /* periodic */ @@ -874,12 +874,12 @@ int n = index - 0x32; s->lvt[n] = val; if (n == APIC_LVT_TIMER) - apic_timer_update(s, qemu_get_clock(vm_clock)); + apic_timer_update(s, qemu_get_clock_ns(vm_clock)); } break; case 0x38: s->initial_count = val; - s->initial_count_load_time = qemu_get_clock(vm_clock); + s->initial_count_load_time = qemu_get_clock_ns(vm_clock); apic_timer_update(s, s->initial_count_load_time); break; case 0x39: @@ -941,7 +941,7 @@ v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); s->count_shift = (v + 1) & 7; - s->initial_count_load_time = qemu_get_clock(vm_clock); + s->initial_count_load_time = qemu_get_clock_ns(vm_clock); apic_timer_update(s, s->initial_count_load_time); } @@ -1121,7 +1121,7 @@ DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MSI_ADDR_SIZE, apic_io_memory); - s->timer = qemu_new_timer(vm_clock, apic_timer, s); + s->timer = qemu_new_timer_ns(vm_clock, apic_timer, s); s->idx = last_apic_idx++; local_apics[s->idx] = s; return 0; diff -Nru qemu-kvm-0.14.1+noroms/hw/arm11mpcore.c qemu-kvm-0.15.0+noroms/hw/arm11mpcore.c --- qemu-kvm-0.14.1+noroms/hw/arm11mpcore.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/arm11mpcore.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines diff -Nru qemu-kvm-0.14.1+noroms/hw/arm_boot.c qemu-kvm-0.15.0+noroms/hw/arm_boot.c --- qemu-kvm-0.14.1+noroms/hw/arm_boot.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/arm_boot.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" @@ -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[] = { @@ -49,7 +49,7 @@ p += 4; \ } while (0) -static void set_kernel_args(struct arm_boot_info *info, +static void set_kernel_args(const struct arm_boot_info *info, int initrd_size, target_phys_addr_t base) { target_phys_addr_t p; @@ -102,7 +102,7 @@ WRITE_WORD(p, 0); } -static void set_kernel_args_old(struct arm_boot_info *info, +static void set_kernel_args_old(const struct arm_boot_info *info, int initrd_size, target_phys_addr_t base) { target_phys_addr_t p; @@ -175,10 +175,10 @@ } } -static void main_cpu_reset(void *opaque) +static void do_cpu_reset(void *opaque) { CPUState *env = opaque; - struct arm_boot_info *info = env->boot_info; + const struct arm_boot_info *info = env->boot_info; cpu_reset(env); if (info) { @@ -187,16 +187,20 @@ 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, + if (env == first_cpu) { + env->regs[15] = info->loader_start; + if (old_param) { + set_kernel_args_old(info, info->initrd_size, + info->loader_start); + } else { + set_kernel_args(info, info->initrd_size, info->loader_start); + } } else { - set_kernel_args(info, info->initrd_size, info->loader_start); + env->regs[15] = info->smp_loader_start; } } } - /* TODO: Reset secondary CPUs. */ } void arm_load_kernel(CPUState *env, struct arm_boot_info *info) @@ -217,7 +221,6 @@ if (info->nb_cpus == 0) info->nb_cpus = 1; - env->boot_info = info; #ifdef TARGET_WORDS_BIGENDIAN big_endian = 1; @@ -279,5 +282,9 @@ info->initrd_size = initrd_size; } info->is_linux = is_linux; - qemu_register_reset(main_cpu_reset, env); + + for (; env; env = env->next_cpu) { + env->boot_info = info; + qemu_register_reset(do_cpu_reset, env); + } } diff -Nru qemu-kvm-0.14.1+noroms/hw/arm_gic.c qemu-kvm-0.15.0+noroms/hw/arm_gic.c --- qemu-kvm-0.14.1+noroms/hw/arm_gic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/arm_gic.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* This file contains implementation code for the RealView EB interrupt @@ -549,10 +549,10 @@ mask = (value >> 16) & ALL_CPU_MASK; break; case 1: - mask = 1 << cpu; + mask = ALL_CPU_MASK ^ (1 << cpu); break; case 2: - mask = ALL_CPU_MASK ^ (1 << cpu); + mask = 1 << cpu; break; default: DPRINTF("Bad Soft Int target filter\n"); diff -Nru qemu-kvm-0.14.1+noroms/hw/arm-misc.h qemu-kvm-0.15.0+noroms/hw/arm-misc.h --- qemu-kvm-0.14.1+noroms/hw/arm-misc.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/arm-misc.h 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ @@ -31,7 +31,7 @@ target_phys_addr_t smp_priv_base; int nb_cpus; int board_id; - int (*atag_board)(struct arm_boot_info *info, void *p); + int (*atag_board)(const struct arm_boot_info *info, void *p); /* Used internally by arm_boot.c */ int is_linux; target_phys_addr_t initrd_size; diff -Nru qemu-kvm-0.14.1+noroms/hw/arm_pic.c qemu-kvm-0.15.0+noroms/hw/arm_pic.c --- qemu-kvm-0.14.1+noroms/hw/arm_pic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/arm_pic.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL + * This code is licensed under the LGPL */ #include "hw.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/arm_sysctl.c qemu-kvm-0.15.0+noroms/hw/arm_sysctl.c --- qemu-kvm-0.14.1+noroms/hw/arm_sysctl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/arm_sysctl.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" @@ -26,11 +26,15 @@ uint32_t nvflags; uint32_t resetlevel; uint32_t proc_id; + uint32_t sys_mci; + uint32_t sys_cfgdata; + uint32_t sys_cfgctrl; + uint32_t sys_cfgstat; } arm_sysctl_state; static const VMStateDescription vmstate_arm_sysctl = { .name = "realview_sysctl", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(leds, arm_sysctl_state), @@ -40,10 +44,30 @@ VMSTATE_UINT32(flags, arm_sysctl_state), VMSTATE_UINT32(nvflags, arm_sysctl_state), VMSTATE_UINT32(resetlevel, arm_sysctl_state), + VMSTATE_UINT32_V(sys_mci, arm_sysctl_state, 2), + VMSTATE_UINT32_V(sys_cfgdata, arm_sysctl_state, 2), + VMSTATE_UINT32_V(sys_cfgctrl, arm_sysctl_state, 2), + VMSTATE_UINT32_V(sys_cfgstat, arm_sysctl_state, 2), VMSTATE_END_OF_LIST() } }; +/* The PB926 actually uses a different format for + * its SYS_ID register. Fortunately the bits which are + * board type on later boards are distinct. + */ +#define BOARD_ID_PB926 0x100 +#define BOARD_ID_EB 0x140 +#define BOARD_ID_PBA8 0x178 +#define BOARD_ID_PBX 0x182 +#define BOARD_ID_VEXPRESS 0x190 + +static int board_id(arm_sysctl_state *s) +{ + /* Extract the board ID field from the SYS_ID register value */ + return (s->sys_id >> 16) & 0xfff; +} + static void arm_sysctl_reset(DeviceState *d) { arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, sysbus_from_qdev(d)); @@ -88,11 +112,15 @@ case 0x38: /* NVFLAGS */ return s->nvflags; case 0x40: /* RESETCTL */ + if (board_id(s) == BOARD_ID_VEXPRESS) { + /* reserved: RAZ/WI */ + return 0; + } return s->resetlevel; case 0x44: /* PCICTL */ return 1; case 0x48: /* MCI */ - return 0; + return s->sys_mci; case 0x4c: /* FLASH */ return 0; case 0x50: /* CLCD */ @@ -102,7 +130,7 @@ case 0x58: /* BOOTCS */ return 0; case 0x5c: /* 24MHz */ - return muldiv64(qemu_get_clock(vm_clock), 24000000, get_ticks_per_sec()); + return muldiv64(qemu_get_clock_ns(vm_clock), 24000000, get_ticks_per_sec()); case 0x60: /* MISC */ return 0; case 0x84: /* PROCID0 */ @@ -126,7 +154,23 @@ case 0xcc: /* SYS_TEST_OSC3 */ case 0xd0: /* SYS_TEST_OSC4 */ return 0; + case 0xa0: /* SYS_CFGDATA */ + if (board_id(s) != BOARD_ID_VEXPRESS) { + goto bad_reg; + } + return s->sys_cfgdata; + case 0xa4: /* SYS_CFGCTRL */ + if (board_id(s) != BOARD_ID_VEXPRESS) { + goto bad_reg; + } + return s->sys_cfgctrl; + case 0xa8: /* SYS_CFGSTAT */ + if (board_id(s) != BOARD_ID_VEXPRESS) { + goto bad_reg; + } + return s->sys_cfgstat; default: + bad_reg: printf ("arm_sysctl_read: Bad register offset 0x%x\n", (int)offset); return 0; } @@ -174,6 +218,10 @@ s->nvflags &= ~val; break; case 0x40: /* RESETCTL */ + if (board_id(s) == BOARD_ID_VEXPRESS) { + /* reserved: RAZ/WI */ + break; + } if (s->lockval == LOCK_VALUE) { s->resetlevel = val; if (val & 0x100) @@ -200,7 +248,37 @@ case 0x98: /* OSCRESET3 */ case 0x9c: /* OSCRESET4 */ break; + case 0xa0: /* SYS_CFGDATA */ + if (board_id(s) != BOARD_ID_VEXPRESS) { + goto bad_reg; + } + s->sys_cfgdata = val; + return; + case 0xa4: /* SYS_CFGCTRL */ + if (board_id(s) != BOARD_ID_VEXPRESS) { + goto bad_reg; + } + s->sys_cfgctrl = val & ~(3 << 18); + s->sys_cfgstat = 1; /* complete */ + switch (s->sys_cfgctrl) { + case 0xc0800000: /* SYS_CFG_SHUTDOWN to motherboard */ + qemu_system_shutdown_request(); + break; + case 0xc0900000: /* SYS_CFG_REBOOT to motherboard */ + qemu_system_reset_request(); + break; + default: + s->sys_cfgstat |= 2; /* error */ + } + return; + case 0xa8: /* SYS_CFGSTAT */ + if (board_id(s) != BOARD_ID_VEXPRESS) { + goto bad_reg; + } + s->sys_cfgstat = val & 3; + return; default: + bad_reg: printf ("arm_sysctl_write: Bad register offset 0x%x\n", (int)offset); return; } @@ -218,6 +296,34 @@ arm_sysctl_write }; +static void arm_sysctl_gpio_set(void *opaque, int line, int level) +{ + arm_sysctl_state *s = (arm_sysctl_state *)opaque; + switch (line) { + case ARM_SYSCTL_GPIO_MMC_WPROT: + { + /* For PB926 and EB write-protect is bit 2 of SYS_MCI; + * for all later boards it is bit 1. + */ + int bit = 2; + if ((board_id(s) == BOARD_ID_PB926) || (board_id(s) == BOARD_ID_EB)) { + bit = 4; + } + s->sys_mci &= ~bit; + if (level) { + s->sys_mci |= bit; + } + break; + } + case ARM_SYSCTL_GPIO_MMC_CARDIN: + s->sys_mci &= ~1; + if (level) { + s->sys_mci |= 1; + } + break; + } +} + static int arm_sysctl_init1(SysBusDevice *dev) { arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev); @@ -227,6 +333,7 @@ arm_sysctl_writefn, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); + qdev_init_gpio_in(&s->busdev.qdev, arm_sysctl_gpio_set, 2); /* ??? Save/restore. */ return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/arm_timer.c qemu-kvm-0.15.0+noroms/hw/arm_timer.c --- qemu-kvm-0.14.1+noroms/hw/arm_timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/arm_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2005-2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -140,28 +140,19 @@ arm_timer_update(s); } -static void arm_timer_save(QEMUFile *f, void *opaque) -{ - arm_timer_state *s = (arm_timer_state *)opaque; - qemu_put_be32(f, s->control); - qemu_put_be32(f, s->limit); - qemu_put_be32(f, s->int_level); - qemu_put_ptimer(f, s->timer); -} - -static int arm_timer_load(QEMUFile *f, void *opaque, int version_id) -{ - arm_timer_state *s = (arm_timer_state *)opaque; - - if (version_id != 1) - return -EINVAL; - - s->control = qemu_get_be32(f); - s->limit = qemu_get_be32(f); - s->int_level = qemu_get_be32(f); - qemu_get_ptimer(f, s->timer); - return 0; -} +static const VMStateDescription vmstate_arm_timer = { + .name = "arm_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, arm_timer_state), + VMSTATE_UINT32(limit, arm_timer_state), + VMSTATE_INT32(int_level, arm_timer_state), + VMSTATE_PTIMER(timer, arm_timer_state), + VMSTATE_END_OF_LIST() + } +}; static arm_timer_state *arm_timer_init(uint32_t freq) { @@ -174,7 +165,7 @@ bh = qemu_bh_new(arm_timer_tick, s); s->timer = ptimer_init(bh); - register_savevm(NULL, "arm_timer", -1, 1, arm_timer_save, arm_timer_load, s); + vmstate_register(NULL, -1, &vmstate_arm_timer, s); return s; } @@ -235,24 +226,17 @@ sp804_write }; -static void sp804_save(QEMUFile *f, void *opaque) -{ - sp804_state *s = (sp804_state *)opaque; - qemu_put_be32(f, s->level[0]); - qemu_put_be32(f, s->level[1]); -} - -static int sp804_load(QEMUFile *f, void *opaque, int version_id) -{ - sp804_state *s = (sp804_state *)opaque; - if (version_id != 1) - return -EINVAL; - - s->level[0] = qemu_get_be32(f); - s->level[1] = qemu_get_be32(f); - return 0; -} +static const VMStateDescription vmstate_sp804 = { + .name = "sp804", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32_ARRAY(level, sp804_state, 2), + VMSTATE_END_OF_LIST() + } +}; static int sp804_init(SysBusDevice *dev) { @@ -271,7 +255,7 @@ iomemtype = cpu_register_io_memory(sp804_readfn, sp804_writefn, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); - register_savevm(&dev->qdev, "sp804", -1, 1, sp804_save, sp804_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_sp804, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/armv7m.c qemu-kvm-0.15.0+noroms/hw/armv7m.c --- qemu-kvm-0.14.1+noroms/hw/armv7m.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/armv7m.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,18 +4,17 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" #include "arm-misc.h" -#include "sysemu.h" #include "loader.h" #include "elf.h" /* Bitbanded IO. Each word corresponds to a single bit. */ -/* Get the byte address of the real memory for a bitband acess. */ +/* Get the byte address of the real memory for a bitband access. */ static inline uint32_t bitband_addr(void * opaque, uint32_t addr) { uint32_t res; diff -Nru qemu-kvm-0.14.1+noroms/hw/armv7m_nvic.c qemu-kvm-0.15.0+noroms/hw/armv7m_nvic.c --- qemu-kvm-0.14.1+noroms/hw/armv7m_nvic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/armv7m_nvic.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. * * The ARMv7M System controller is fairly tightly tied in with the * NVIC. Much of that is also implemented here. @@ -64,7 +64,7 @@ static void systick_reload(nvic_state *s, int reset) { if (reset) - s->systick.tick = qemu_get_clock(vm_clock); + s->systick.tick = qemu_get_clock_ns(vm_clock); s->systick.tick += (s->systick.reload + 1) * systick_scale(s); qemu_mod_timer(s->systick.timer, s->systick.tick); } @@ -136,7 +136,7 @@ int64_t t; if ((s->systick.control & SYSTICK_ENABLE) == 0) return 0; - t = qemu_get_clock(vm_clock); + t = qemu_get_clock_ns(vm_clock); if (t >= s->systick.tick) return 0; val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1; @@ -273,7 +273,7 @@ s->systick.control &= 0xfffffff8; s->systick.control |= value & 7; if ((oldval ^ value) & SYSTICK_ENABLE) { - int64_t now = qemu_get_clock(vm_clock); + int64_t now = qemu_get_clock_ns(vm_clock); if (value & SYSTICK_ENABLE) { if (s->systick.tick) { s->systick.tick += now; @@ -365,30 +365,19 @@ } } -static void nvic_save(QEMUFile *f, void *opaque) -{ - nvic_state *s = (nvic_state *)opaque; - - qemu_put_be32(f, s->systick.control); - qemu_put_be32(f, s->systick.reload); - qemu_put_be64(f, s->systick.tick); - qemu_put_timer(f, s->systick.timer); -} - -static int nvic_load(QEMUFile *f, void *opaque, int version_id) -{ - nvic_state *s = (nvic_state *)opaque; - - if (version_id != 1) - return -EINVAL; - - s->systick.control = qemu_get_be32(f); - s->systick.reload = qemu_get_be32(f); - s->systick.tick = qemu_get_be64(f); - qemu_get_timer(f, s->systick.timer); - - return 0; -} +static const VMStateDescription vmstate_nvic = { + .name = "armv7m_nvic", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(systick.control, nvic_state), + VMSTATE_UINT32(systick.reload, nvic_state), + VMSTATE_INT64(systick.tick, nvic_state), + VMSTATE_TIMER(systick.timer, nvic_state), + VMSTATE_END_OF_LIST() + } +}; static int armv7m_nvic_init(SysBusDevice *dev) { @@ -396,8 +385,8 @@ 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(&dev->qdev, "armv7m_nvic", -1, 1, nvic_save, nvic_load, s); + s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); + vmstate_register(&dev->qdev, -1, &vmstate_nvic, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/axis_dev88.c qemu-kvm-0.15.0+noroms/hw/axis_dev88.c --- qemu-kvm-0.14.1+noroms/hw/axis_dev88.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/axis_dev88.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,7 +26,6 @@ #include "net.h" #include "flash.h" #include "boards.h" -#include "sysemu.h" #include "etraxfs.h" #include "loader.h" #include "elf.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/baum.c qemu-kvm-0.15.0+noroms/hw/baum.c --- qemu-kvm-0.14.1+noroms/hw/baum.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/baum.c 2011-08-09 07:40:29.000000000 -0500 @@ -335,7 +335,7 @@ int i; /* Allow 100ms to complete the DisplayData packet */ - qemu_mod_timer(baum->cellCount_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(baum->cellCount_timer, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10); for (i = 0; i < baum->x * baum->y ; i++) { EAT(c); @@ -576,7 +576,7 @@ qemu_free(baum); } -CharDriverState *chr_baum_init(QemuOpts *opts) +int chr_baum_init(QemuOpts *opts, CharDriverState **_chr) { BaumDriverState *baum; CharDriverState *chr; @@ -604,7 +604,7 @@ goto fail_handle; } - baum->cellCount_timer = qemu_new_timer(vm_clock, baum_cellCount_timer_cb, baum); + baum->cellCount_timer = qemu_new_timer_ns(vm_clock, baum_cellCount_timer_cb, baum); if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) { brlapi_perror("baum_init: brlapi_getDisplaySize"); @@ -629,7 +629,8 @@ qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; fail: qemu_free_timer(baum->cellCount_timer); @@ -638,5 +639,5 @@ qemu_free(handle); qemu_free(chr); qemu_free(baum); - return NULL; + return -EIO; } diff -Nru qemu-kvm-0.14.1+noroms/hw/baum.h qemu-kvm-0.15.0+noroms/hw/baum.h --- qemu-kvm-0.14.1+noroms/hw/baum.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/baum.h 2011-08-09 07:40:29.000000000 -0500 @@ -23,4 +23,4 @@ */ /* char device */ -CharDriverState *chr_baum_init(QemuOpts *opts); +int chr_baum_init(QemuOpts *opts, CharDriverState **_chr); diff -Nru qemu-kvm-0.14.1+noroms/hw/bitbang_i2c.c qemu-kvm-0.15.0+noroms/hw/bitbang_i2c.c --- qemu-kvm-0.14.1+noroms/hw/bitbang_i2c.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/bitbang_i2c.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "hw.h" #include "bitbang_i2c.h" @@ -38,7 +38,8 @@ RECEIVING_BIT2, RECEIVING_BIT1, RECEIVING_BIT0, - SENDING_ACK + SENDING_ACK, + SENT_NACK } bitbang_i2c_state; struct bitbang_i2c_interface { @@ -115,6 +116,7 @@ } switch (i2c->state) { case STOPPED: + case SENT_NACK: return bitbang_i2c_ret(i2c, 1); case SENDING_BIT7 ... SENDING_BIT0: @@ -155,6 +157,7 @@ i2c->state = RECEIVING_BIT7; if (data != 0) { DPRINTF("NACKED\n"); + i2c->state = SENT_NACK; i2c_nack(i2c->bus); } else { DPRINTF("ACKED\n"); diff -Nru qemu-kvm-0.14.1+noroms/hw/blizzard.c qemu-kvm-0.15.0+noroms/hw/blizzard.c --- qemu-kvm-0.14.1+noroms/hw/blizzard.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/blizzard.c 2011-08-09 07:40:29.000000000 -0500 @@ -19,7 +19,6 @@ */ #include "qemu-common.h" -#include "sysemu.h" #include "console.h" #include "devices.h" #include "vga_int.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/boards.h qemu-kvm-0.15.0+noroms/hw/boards.h --- qemu-kvm-0.14.1+noroms/hw/boards.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/boards.h 2011-08-09 07:40:29.000000000 -0500 @@ -27,6 +27,7 @@ no_cdrom:1, no_sdcard:1; int is_default; + const char *default_machine_opts; GlobalProperty *compat_props; struct QEMUMachine *next; } QEMUMachine; diff -Nru qemu-kvm-0.14.1+noroms/hw/bonito.c qemu-kvm-0.15.0+noroms/hw/bonito.c --- qemu-kvm-0.14.1+noroms/hw/bonito.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/bonito.c 2011-08-09 07:40:29.000000000 -0500 @@ -691,11 +691,7 @@ 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, @@ -796,6 +792,11 @@ .qdev.vmsd = &vmstate_bonito, .qdev.no_user = 1, .init = bonito_initfn, + /*Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined"*/ + .vendor_id = 0xdf53, + .device_id = 0x00d5, + .revision = 0x01, + .class_id = PCI_CLASS_BRIDGE_HOST, }; static SysBusDeviceInfo bonito_pcihost_info = { diff -Nru qemu-kvm-0.14.1+noroms/hw/bt.h qemu-kvm-0.15.0+noroms/hw/bt.h --- qemu-kvm-0.14.1+noroms/hw/bt.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/bt.h 2011-08-09 07:40:29.000000000 -0500 @@ -1441,7 +1441,7 @@ #define EVT_FLUSH_OCCURRED 0x11 typedef struct { uint16_t handle; -} __attribute__ ((packed)) evt_flush_occured; +} __attribute__ ((packed)) evt_flush_occurred; #define EVT_FLUSH_OCCURRED_SIZE 2 #define EVT_ROLE_CHANGE 0x12 diff -Nru qemu-kvm-0.14.1+noroms/hw/bt-hci.c qemu-kvm-0.15.0+noroms/hw/bt-hci.c --- qemu-kvm-0.14.1+noroms/hw/bt-hci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/bt-hci.c 2011-08-09 07:40:29.000000000 -0500 @@ -576,7 +576,7 @@ static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period) { - qemu_mod_timer(timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(timer, qemu_get_clock_ns(vm_clock) + muldiv64(period << 7, get_ticks_per_sec(), 100)); } @@ -657,7 +657,7 @@ if (master) { link->acl_mode = acl_active; hci->lm.handle[hci->lm.last_handle].acl_mode_timer = - qemu_new_timer(vm_clock, bt_hci_mode_tick, link); + qemu_new_timer_ns(vm_clock, bt_hci_mode_tick, link); } } @@ -1084,7 +1084,7 @@ bt_hci_event_status(hci, HCI_SUCCESS); - qemu_mod_timer(link->acl_mode_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(link->acl_mode_timer, qemu_get_clock_ns(vm_clock) + muldiv64(interval * 625, get_ticks_per_sec(), 1000000)); bt_hci_lmp_mode_change_master(hci, link->link, mode, interval); @@ -2145,10 +2145,10 @@ { struct bt_hci_s *s = qemu_mallocz(sizeof(struct bt_hci_s)); - s->lm.inquiry_done = qemu_new_timer(vm_clock, bt_hci_inquiry_done, s); - s->lm.inquiry_next = qemu_new_timer(vm_clock, bt_hci_inquiry_next, s); + s->lm.inquiry_done = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_done, s); + s->lm.inquiry_next = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_next, s); s->conn_accept_timer = - qemu_new_timer(vm_clock, bt_hci_conn_accept_timeout, s); + qemu_new_timer_ns(vm_clock, bt_hci_conn_accept_timeout, s); s->evt_packet = bt_hci_evt_packet; s->evt_submit = bt_hci_evt_submit; diff -Nru qemu-kvm-0.14.1+noroms/hw/bt-hci-csr.c qemu-kvm-0.15.0+noroms/hw/bt-hci-csr.c --- qemu-kvm-0.14.1+noroms/hw/bt-hci-csr.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/bt-hci-csr.c 2011-08-09 07:40:29.000000000 -0500 @@ -22,7 +22,6 @@ #include "qemu-char.h" #include "qemu-timer.h" #include "irq.h" -#include "sysemu.h" #include "net.h" #include "bt.h" @@ -88,7 +87,7 @@ } if (s->out_len) - qemu_mod_timer(s->out_tm, qemu_get_clock(vm_clock) + s->baud_delay); + qemu_mod_timer(s->out_tm, qemu_get_clock_ns(vm_clock) + s->baud_delay); } #define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len) @@ -446,7 +445,7 @@ s->hci->evt_recv = csrhci_out_hci_packet_event; s->hci->acl_recv = csrhci_out_hci_packet_acl; - s->out_tm = qemu_new_timer(vm_clock, csrhci_out_tick, s); + s->out_tm = qemu_new_timer_ns(vm_clock, csrhci_out_tick, s); s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins); csrhci_reset(s); diff -Nru qemu-kvm-0.14.1+noroms/hw/bt-hid.c qemu-kvm-0.15.0+noroms/hw/bt-hid.c --- qemu-kvm-0.14.1+noroms/hw/bt-hid.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/bt-hid.c 2011-08-09 07:40:29.000000000 -0500 @@ -323,7 +323,7 @@ break; } s->proto = parameter; - s->usbdev->info->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0, + s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0, NULL); ret = BT_HS_SUCCESSFUL; break; @@ -333,7 +333,7 @@ ret = BT_HS_ERR_INVALID_PARAMETER; break; } - s->usbdev->info->handle_control(s->usbdev, GET_IDLE, 0, 0, 1, + s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1, s->control->sdu_out(s->control, 1)); s->control->sdu_submit(s->control); break; @@ -346,7 +346,7 @@ /* We don't need to know about the Idle Rate here really, * so just pass it on to the device. */ - ret = s->usbdev->info->handle_control(s->usbdev, + ret = s->usbdev->info->handle_control(s->usbdev, NULL, SET_IDLE, data[1], 0, 0, NULL) ? BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; /* XXX: Does this generate a handshake? */ diff -Nru qemu-kvm-0.14.1+noroms/hw/ccid-card-emulated.c qemu-kvm-0.15.0+noroms/hw/ccid-card-emulated.c --- qemu-kvm-0.14.1+noroms/hw/ccid-card-emulated.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/ccid-card-emulated.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,595 @@ +/* + * CCID Card Device. Emulated card. + * + * Copyright (c) 2011 Red Hat. + * Written by Alon Levy. + * + * This code is licensed under the GNU LGPL, version 2 or later. + */ + +/* + * It can be used to provide access to the local hardware in a non exclusive + * way, or it can use certificates. It requires the usb-ccid bus. + * + * Usage 1: standard, mirror hardware reader+card: + * qemu .. -usb -device usb-ccid -device ccid-card-emulated + * + * Usage 2: use certificates, no hardware required + * one time: create the certificates: + * for i in 1 2 3; do + * certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i + * done + * qemu .. -usb -device usb-ccid \ + * -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3 + * + * If you use a non default db for the certificates you can specify it using + * the db parameter. + */ + +#include +#include +#include +#include + +#include "qemu-thread.h" +#include "qemu-char.h" +#include "monitor.h" +#include "hw/ccid.h" + +#define DPRINTF(card, lvl, fmt, ...) \ +do {\ + if (lvl <= card->debug) {\ + printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ + } \ +} while (0) + +#define EMULATED_DEV_NAME "ccid-card-emulated" + +#define BACKEND_NSS_EMULATED_NAME "nss-emulated" +#define BACKEND_CERTIFICATES_NAME "certificates" + +enum { + BACKEND_NSS_EMULATED = 1, + BACKEND_CERTIFICATES +}; + +#define DEFAULT_BACKEND BACKEND_NSS_EMULATED + +typedef struct EmulatedState EmulatedState; + +enum { + EMUL_READER_INSERT = 0, + EMUL_READER_REMOVE, + EMUL_CARD_INSERT, + EMUL_CARD_REMOVE, + EMUL_GUEST_APDU, + EMUL_RESPONSE_APDU, + EMUL_ERROR, +}; + +static const char *emul_event_to_string(uint32_t emul_event) +{ + switch (emul_event) { + case EMUL_READER_INSERT: + return "EMUL_READER_INSERT"; + case EMUL_READER_REMOVE: + return "EMUL_READER_REMOVE"; + case EMUL_CARD_INSERT: + return "EMUL_CARD_INSERT"; + case EMUL_CARD_REMOVE: + return "EMUL_CARD_REMOVE"; + case EMUL_GUEST_APDU: + return "EMUL_GUEST_APDU"; + case EMUL_RESPONSE_APDU: + return "EMUL_RESPONSE_APDU"; + case EMUL_ERROR: + return "EMUL_ERROR"; + } + return "UNKNOWN"; +} + +typedef struct EmulEvent { + QSIMPLEQ_ENTRY(EmulEvent) entry; + union { + struct { + uint32_t type; + } gen; + struct { + uint32_t type; + uint64_t code; + } error; + struct { + uint32_t type; + uint32_t len; + uint8_t data[]; + } data; + } p; +} EmulEvent; + +#define MAX_ATR_SIZE 40 +struct EmulatedState { + CCIDCardState base; + uint8_t debug; + char *backend_str; + uint32_t backend; + char *cert1; + char *cert2; + char *cert3; + char *db; + uint8_t atr[MAX_ATR_SIZE]; + uint8_t atr_length; + QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; + QemuMutex event_list_mutex; + VReader *reader; + QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; + QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ + QemuMutex handle_apdu_mutex; + QemuCond handle_apdu_cond; + int pipe[2]; + int quit_apdu_thread; + QemuMutex apdu_thread_quit_mutex; + QemuCond apdu_thread_quit_cond; +}; + +static void emulated_apdu_from_guest(CCIDCardState *base, + const uint8_t *apdu, uint32_t len) +{ + EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent) + len); + + assert(event); + event->p.data.type = EMUL_GUEST_APDU; + event->p.data.len = len; + memcpy(event->p.data.data, apdu, len); + qemu_mutex_lock(&card->vreader_mutex); + QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); + qemu_mutex_unlock(&card->vreader_mutex); + qemu_mutex_lock(&card->handle_apdu_mutex); + qemu_cond_signal(&card->handle_apdu_cond); + qemu_mutex_unlock(&card->handle_apdu_mutex); +} + +static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) +{ + EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + + *len = card->atr_length; + return card->atr; +} + +static void emulated_push_event(EmulatedState *card, EmulEvent *event) +{ + qemu_mutex_lock(&card->event_list_mutex); + QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); + qemu_mutex_unlock(&card->event_list_mutex); + if (write(card->pipe[1], card, 1) != 1) { + DPRINTF(card, 1, "write to pipe failed\n"); + } +} + +static void emulated_push_type(EmulatedState *card, uint32_t type) +{ + EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent)); + + assert(event); + event->p.gen.type = type; + emulated_push_event(card, event); +} + +static void emulated_push_error(EmulatedState *card, uint64_t code) +{ + EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent)); + + assert(event); + event->p.error.type = EMUL_ERROR; + event->p.error.code = code; + emulated_push_event(card, event); +} + +static void emulated_push_data_type(EmulatedState *card, uint32_t type, + const uint8_t *data, uint32_t len) +{ + EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent) + len); + + assert(event); + event->p.data.type = type; + event->p.data.len = len; + memcpy(event->p.data.data, data, len); + emulated_push_event(card, event); +} + +static void emulated_push_reader_insert(EmulatedState *card) +{ + emulated_push_type(card, EMUL_READER_INSERT); +} + +static void emulated_push_reader_remove(EmulatedState *card) +{ + emulated_push_type(card, EMUL_READER_REMOVE); +} + +static void emulated_push_card_insert(EmulatedState *card, + const uint8_t *atr, uint32_t len) +{ + emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); +} + +static void emulated_push_card_remove(EmulatedState *card) +{ + emulated_push_type(card, EMUL_CARD_REMOVE); +} + +static void emulated_push_response_apdu(EmulatedState *card, + const uint8_t *apdu, uint32_t len) +{ + emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); +} + +#define APDU_BUF_SIZE 270 +static void *handle_apdu_thread(void* arg) +{ + EmulatedState *card = arg; + uint8_t recv_data[APDU_BUF_SIZE]; + int recv_len; + VReaderStatus reader_status; + EmulEvent *event; + + while (1) { + qemu_mutex_lock(&card->handle_apdu_mutex); + qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); + qemu_mutex_unlock(&card->handle_apdu_mutex); + if (card->quit_apdu_thread) { + card->quit_apdu_thread = 0; /* debugging */ + break; + } + qemu_mutex_lock(&card->vreader_mutex); + while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) { + event = QSIMPLEQ_FIRST(&card->guest_apdu_list); + assert((unsigned long)event > 1000); + QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); + if (event->p.data.type != EMUL_GUEST_APDU) { + DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); + qemu_free(event); + continue; + } + if (card->reader == NULL) { + DPRINTF(card, 1, "reader is NULL\n"); + qemu_free(event); + continue; + } + recv_len = sizeof(recv_data); + reader_status = vreader_xfr_bytes(card->reader, + event->p.data.data, event->p.data.len, + recv_data, &recv_len); + DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); + if (reader_status == VREADER_OK) { + emulated_push_response_apdu(card, recv_data, recv_len); + } else { + emulated_push_error(card, reader_status); + } + qemu_free(event); + } + qemu_mutex_unlock(&card->vreader_mutex); + } + qemu_mutex_lock(&card->apdu_thread_quit_mutex); + qemu_cond_signal(&card->apdu_thread_quit_cond); + qemu_mutex_unlock(&card->apdu_thread_quit_mutex); + return NULL; +} + +static void *event_thread(void *arg) +{ + int atr_len = MAX_ATR_SIZE; + uint8_t atr[MAX_ATR_SIZE]; + VEvent *event = NULL; + EmulatedState *card = arg; + + while (1) { + const char *reader_name; + + event = vevent_wait_next_vevent(); + if (event == NULL || event->type == VEVENT_LAST) { + break; + } + if (event->type != VEVENT_READER_INSERT) { + if (card->reader == NULL && event->reader != NULL) { + /* Happens after device_add followed by card remove or insert. + * XXX: create synthetic add_reader events if vcard_emul_init + * already called, which happens if device_del and device_add + * are called */ + card->reader = vreader_reference(event->reader); + } else { + if (event->reader != card->reader) { + fprintf(stderr, + "ERROR: wrong reader: quiting event_thread\n"); + break; + } + } + } + switch (event->type) { + case VEVENT_READER_INSERT: + /* TODO: take a specific reader. i.e. track which reader + * we are seeing here, check it is the one we want (the first, + * or by a particular name), and ignore if we don't want it. + */ + reader_name = vreader_get_name(event->reader); + if (card->reader != NULL) { + DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", + vreader_get_name(card->reader), reader_name); + qemu_mutex_lock(&card->vreader_mutex); + vreader_free(card->reader); + qemu_mutex_unlock(&card->vreader_mutex); + emulated_push_reader_remove(card); + } + qemu_mutex_lock(&card->vreader_mutex); + DPRINTF(card, 2, "READER INSERT %s\n", reader_name); + card->reader = vreader_reference(event->reader); + qemu_mutex_unlock(&card->vreader_mutex); + emulated_push_reader_insert(card); + break; + case VEVENT_READER_REMOVE: + DPRINTF(card, 2, " READER REMOVE: %s\n", + vreader_get_name(event->reader)); + qemu_mutex_lock(&card->vreader_mutex); + vreader_free(card->reader); + card->reader = NULL; + qemu_mutex_unlock(&card->vreader_mutex); + emulated_push_reader_remove(card); + break; + case VEVENT_CARD_INSERT: + /* get the ATR (intended as a response to a power on from the + * reader */ + atr_len = MAX_ATR_SIZE; + vreader_power_on(event->reader, atr, &atr_len); + card->atr_length = (uint8_t)atr_len; + DPRINTF(card, 2, " CARD INSERT\n"); + emulated_push_card_insert(card, atr, atr_len); + break; + case VEVENT_CARD_REMOVE: + DPRINTF(card, 2, " CARD REMOVE\n"); + emulated_push_card_remove(card); + break; + case VEVENT_LAST: /* quit */ + vevent_delete(event); + return NULL; + break; + default: + break; + } + vevent_delete(event); + } + return NULL; +} + +static void pipe_read(void *opaque) +{ + EmulatedState *card = opaque; + EmulEvent *event, *next; + char dummy; + int len; + + do { + len = read(card->pipe[0], &dummy, sizeof(dummy)); + } while (len == sizeof(dummy)); + qemu_mutex_lock(&card->event_list_mutex); + QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { + DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); + switch (event->p.gen.type) { + case EMUL_RESPONSE_APDU: + ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, + event->p.data.len); + break; + case EMUL_READER_INSERT: + ccid_card_ccid_attach(&card->base); + break; + case EMUL_READER_REMOVE: + ccid_card_ccid_detach(&card->base); + break; + case EMUL_CARD_INSERT: + assert(event->p.data.len <= MAX_ATR_SIZE); + card->atr_length = event->p.data.len; + memcpy(card->atr, event->p.data.data, card->atr_length); + ccid_card_card_inserted(&card->base); + break; + case EMUL_CARD_REMOVE: + ccid_card_card_removed(&card->base); + break; + case EMUL_ERROR: + ccid_card_card_error(&card->base, event->p.error.code); + break; + default: + DPRINTF(card, 2, "unexpected event\n"); + break; + } + qemu_free(event); + } + QSIMPLEQ_INIT(&card->event_list); + qemu_mutex_unlock(&card->event_list_mutex); +} + +static int init_pipe_signaling(EmulatedState *card) +{ + if (pipe(card->pipe) < 0) { + DPRINTF(card, 2, "pipe creation failed\n"); + return -1; + } + fcntl(card->pipe[0], F_SETFL, O_NONBLOCK); + fcntl(card->pipe[1], F_SETFL, O_NONBLOCK); + fcntl(card->pipe[0], F_SETOWN, getpid()); + qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card); + return 0; +} + +#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" +#define CERTIFICATES_ARGS_TEMPLATE\ + "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)" + +static int wrap_vcard_emul_init(VCardEmulOptions *options) +{ + static int called; + static int options_was_null; + + if (called) { + if ((options == NULL) != options_was_null) { + printf("%s: warning: running emulated with certificates" + " and emulated side by side is not supported\n", + __func__); + return VCARD_EMUL_FAIL; + } + vcard_emul_replay_insertion_events(); + return VCARD_EMUL_OK; + } + options_was_null = (options == NULL); + called = 1; + return vcard_emul_init(options); +} + +static int emulated_initialize_vcard_from_certificates(EmulatedState *card) +{ + char emul_args[200]; + VCardEmulOptions *options = NULL; + + snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, + card->db ? card->db : CERTIFICATES_DEFAULT_DB, + card->cert1, card->cert2, card->cert3); + options = vcard_emul_options(emul_args); + if (options == NULL) { + printf("%s: warning: not using certificates due to" + " initialization error\n", __func__); + } + return wrap_vcard_emul_init(options); +} + +typedef struct EnumTable { + const char *name; + uint32_t value; +} EnumTable; + +EnumTable backend_enum_table[] = { + {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, + {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, + {NULL, 0}, +}; + +static uint32_t parse_enumeration(char *str, + EnumTable *table, uint32_t not_found_value) +{ + uint32_t ret = not_found_value; + + while (table->name != NULL) { + if (strcmp(table->name, str) == 0) { + ret = table->value; + break; + } + table++; + } + return ret; +} + +static int emulated_initfn(CCIDCardState *base) +{ + EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + QemuThread thread_id; + VCardEmulError ret; + EnumTable *ptable; + + QSIMPLEQ_INIT(&card->event_list); + QSIMPLEQ_INIT(&card->guest_apdu_list); + qemu_mutex_init(&card->event_list_mutex); + qemu_mutex_init(&card->vreader_mutex); + qemu_mutex_init(&card->handle_apdu_mutex); + qemu_cond_init(&card->handle_apdu_cond); + card->reader = NULL; + card->quit_apdu_thread = 0; + if (init_pipe_signaling(card) < 0) { + return -1; + } + card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0); + if (card->backend == 0) { + printf("unknown backend, must be one of:\n"); + for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { + printf("%s\n", ptable->name); + } + return -1; + } + + /* TODO: a passthru backened that works on local machine. third card type?*/ + if (card->backend == BACKEND_CERTIFICATES) { + if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { + ret = emulated_initialize_vcard_from_certificates(card); + } else { + printf("%s: you must provide all three certs for" + " certificates backend\n", EMULATED_DEV_NAME); + return -1; + } + } else { + if (card->backend != BACKEND_NSS_EMULATED) { + printf("%s: bad backend specified. The options are:\n%s (default)," + " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME, + BACKEND_CERTIFICATES_NAME); + return -1; + } + if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { + printf("%s: unexpected cert parameters to nss emulated backend\n", + EMULATED_DEV_NAME); + return -1; + } + /* default to mirroring the local hardware readers */ + ret = wrap_vcard_emul_init(NULL); + } + if (ret != VCARD_EMUL_OK) { + printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME); + return -1; + } + qemu_thread_create(&thread_id, event_thread, card); + qemu_thread_create(&thread_id, handle_apdu_thread, card); + return 0; +} + +static int emulated_exitfn(CCIDCardState *base) +{ + EmulatedState *card = DO_UPCAST(EmulatedState, base, base); + VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); + + vevent_queue_vevent(vevent); /* stop vevent thread */ + qemu_mutex_lock(&card->apdu_thread_quit_mutex); + card->quit_apdu_thread = 1; /* stop handle_apdu thread */ + qemu_cond_signal(&card->handle_apdu_cond); + qemu_cond_wait(&card->apdu_thread_quit_cond, + &card->apdu_thread_quit_mutex); + /* handle_apdu thread stopped, can destroy all of it's mutexes */ + qemu_cond_destroy(&card->handle_apdu_cond); + qemu_cond_destroy(&card->apdu_thread_quit_cond); + qemu_mutex_destroy(&card->apdu_thread_quit_mutex); + qemu_mutex_destroy(&card->handle_apdu_mutex); + qemu_mutex_destroy(&card->vreader_mutex); + qemu_mutex_destroy(&card->event_list_mutex); + return 0; +} + +static CCIDCardInfo emulated_card_info = { + .qdev.name = EMULATED_DEV_NAME, + .qdev.desc = "emulated smartcard", + .qdev.size = sizeof(EmulatedState), + .initfn = emulated_initfn, + .exitfn = emulated_exitfn, + .get_atr = emulated_get_atr, + .apdu_from_guest = emulated_apdu_from_guest, + .qdev.unplug = qdev_simple_unplug_cb, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("backend", EmulatedState, backend_str), + DEFINE_PROP_STRING("cert1", EmulatedState, cert1), + DEFINE_PROP_STRING("cert2", EmulatedState, cert2), + DEFINE_PROP_STRING("cert3", EmulatedState, cert3), + DEFINE_PROP_STRING("db", EmulatedState, db), + DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void ccid_card_emulated_register_devices(void) +{ + ccid_card_qdev_register(&emulated_card_info); +} + +device_init(ccid_card_emulated_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/ccid-card-passthru.c qemu-kvm-0.15.0+noroms/hw/ccid-card-passthru.c --- qemu-kvm-0.14.1+noroms/hw/ccid-card-passthru.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/ccid-card-passthru.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,339 @@ +/* + * CCID Passthru Card Device emulation + * + * Copyright (c) 2011 Red Hat. + * Written by Alon Levy. + * + * This work is licensed under the terms of the GNU GPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu-char.h" +#include "qemu_socket.h" +#include "monitor.h" +#include "hw/ccid.h" +#include "libcacard/vscard_common.h" + +#define DPRINTF(card, lvl, fmt, ...) \ +do { \ + if (lvl <= card->debug) { \ + printf("ccid-card-passthru: " fmt , ## __VA_ARGS__); \ + } \ +} while (0) + +#define D_WARN 1 +#define D_INFO 2 +#define D_MORE_INFO 3 +#define D_VERBOSE 4 + +/* TODO: do we still need this? */ +uint8_t DEFAULT_ATR[] = { +/* + * From some example somewhere + * 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28 + */ + +/* From an Athena smart card */ + 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21, + 0x13, 0x08 +}; + + +#define PASSTHRU_DEV_NAME "ccid-card-passthru" +#define VSCARD_IN_SIZE 65536 + +/* maximum size of ATR - from 7816-3 */ +#define MAX_ATR_SIZE 40 + +typedef struct PassthruState PassthruState; + +struct PassthruState { + CCIDCardState base; + CharDriverState *cs; + uint8_t vscard_in_data[VSCARD_IN_SIZE]; + uint32_t vscard_in_pos; + uint32_t vscard_in_hdr; + uint8_t atr[MAX_ATR_SIZE]; + uint8_t atr_length; + uint8_t debug; +}; + +/* + * VSCard protocol over chardev + * This code should not depend on the card type. + */ + +static void ccid_card_vscard_send_msg(PassthruState *s, + VSCMsgType type, uint32_t reader_id, + const uint8_t *payload, uint32_t length) +{ + VSCMsgHeader scr_msg_header; + + scr_msg_header.type = htonl(type); + scr_msg_header.reader_id = htonl(reader_id); + scr_msg_header.length = htonl(length); + qemu_chr_write(s->cs, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader)); + qemu_chr_write(s->cs, payload, length); +} + +static void ccid_card_vscard_send_apdu(PassthruState *s, + const uint8_t *apdu, uint32_t length) +{ + ccid_card_vscard_send_msg( + s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length); +} + +static void ccid_card_vscard_send_error(PassthruState *s, + uint32_t reader_id, VSCErrorCode code) +{ + VSCMsgError msg = {.code = htonl(code)}; + + ccid_card_vscard_send_msg( + s, VSC_Error, reader_id, (uint8_t *)&msg, sizeof(msg)); +} + +static void ccid_card_vscard_send_init(PassthruState *s) +{ + VSCMsgInit msg = { + .version = htonl(VSCARD_VERSION), + .magic = VSCARD_MAGIC, + .capabilities = {0} + }; + + ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID, + (uint8_t *)&msg, sizeof(msg)); +} + +static int ccid_card_vscard_can_read(void *opaque) +{ + PassthruState *card = opaque; + + return VSCARD_IN_SIZE >= card->vscard_in_pos ? + VSCARD_IN_SIZE - card->vscard_in_pos : 0; +} + +static void ccid_card_vscard_handle_init( + PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init) +{ + uint32_t *capabilities; + int num_capabilities; + int i; + + capabilities = init->capabilities; + num_capabilities = + 1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); + init->version = ntohl(init->version); + for (i = 0 ; i < num_capabilities; ++i) { + capabilities[i] = ntohl(capabilities[i]); + } + if (init->magic != VSCARD_MAGIC) { + error_report("wrong magic"); + /* we can't disconnect the chardev */ + } + if (init->version != VSCARD_VERSION) { + DPRINTF(card, D_WARN, + "got version %d, have %d", init->version, VSCARD_VERSION); + } + /* future handling of capabilities, none exist atm */ + ccid_card_vscard_send_init(card); +} + +static void ccid_card_vscard_handle_message(PassthruState *card, + VSCMsgHeader *scr_msg_header) +{ + uint8_t *data = (uint8_t *)&scr_msg_header[1]; + + switch (scr_msg_header->type) { + case VSC_ATR: + DPRINTF(card, D_INFO, "VSC_ATR %d\n", scr_msg_header->length); + if (scr_msg_header->length > MAX_ATR_SIZE) { + error_report("ATR size exceeds spec, ignoring"); + ccid_card_vscard_send_error(card, scr_msg_header->reader_id, + VSC_GENERAL_ERROR); + } + memcpy(card->atr, data, scr_msg_header->length); + card->atr_length = scr_msg_header->length; + ccid_card_card_inserted(&card->base); + ccid_card_vscard_send_error(card, scr_msg_header->reader_id, + VSC_SUCCESS); + break; + case VSC_APDU: + ccid_card_send_apdu_to_guest( + &card->base, data, scr_msg_header->length); + break; + case VSC_CardRemove: + DPRINTF(card, D_INFO, "VSC_CardRemove\n"); + ccid_card_card_removed(&card->base); + ccid_card_vscard_send_error(card, + scr_msg_header->reader_id, VSC_SUCCESS); + break; + case VSC_Init: + ccid_card_vscard_handle_init( + card, scr_msg_header, (VSCMsgInit *)data); + break; + case VSC_Error: + ccid_card_card_error(&card->base, *(uint32_t *)data); + break; + case VSC_ReaderAdd: + if (ccid_card_ccid_attach(&card->base) < 0) { + ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID, + VSC_CANNOT_ADD_MORE_READERS); + } else { + ccid_card_vscard_send_error(card, VSCARD_MINIMAL_READER_ID, + VSC_SUCCESS); + } + break; + case VSC_ReaderRemove: + ccid_card_ccid_detach(&card->base); + ccid_card_vscard_send_error(card, + scr_msg_header->reader_id, VSC_SUCCESS); + break; + default: + printf("usb-ccid: chardev: unexpected message of type %X\n", + scr_msg_header->type); + ccid_card_vscard_send_error(card, scr_msg_header->reader_id, + VSC_GENERAL_ERROR); + } +} + +static void ccid_card_vscard_drop_connection(PassthruState *card) +{ + qemu_chr_close(card->cs); + card->vscard_in_pos = card->vscard_in_hdr = 0; +} + +static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size) +{ + PassthruState *card = opaque; + VSCMsgHeader *hdr; + + if (card->vscard_in_pos + size > VSCARD_IN_SIZE) { + error_report( + "no room for data: pos %d + size %d > %d. dropping connection.", + card->vscard_in_pos, size, VSCARD_IN_SIZE); + ccid_card_vscard_drop_connection(card); + return; + } + assert(card->vscard_in_pos < VSCARD_IN_SIZE); + assert(card->vscard_in_hdr < VSCARD_IN_SIZE); + memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size); + card->vscard_in_pos += size; + hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr); + + while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader)) + &&(card->vscard_in_pos - card->vscard_in_hdr >= + sizeof(VSCMsgHeader) + ntohl(hdr->length))) { + hdr->reader_id = ntohl(hdr->reader_id); + hdr->length = ntohl(hdr->length); + hdr->type = ntohl(hdr->type); + ccid_card_vscard_handle_message(card, hdr); + card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader); + hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr); + } + if (card->vscard_in_hdr == card->vscard_in_pos) { + card->vscard_in_pos = card->vscard_in_hdr = 0; + } +} + +static void ccid_card_vscard_event(void *opaque, int event) +{ + PassthruState *card = opaque; + + switch (event) { + case CHR_EVENT_BREAK: + card->vscard_in_pos = card->vscard_in_hdr = 0; + break; + case CHR_EVENT_FOCUS: + break; + case CHR_EVENT_OPENED: + DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__); + break; + } +} + +/* End VSCard handling */ + +static void passthru_apdu_from_guest( + CCIDCardState *base, const uint8_t *apdu, uint32_t len) +{ + PassthruState *card = DO_UPCAST(PassthruState, base, base); + + if (!card->cs) { + printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); + return; + } + ccid_card_vscard_send_apdu(card, apdu, len); +} + +static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) +{ + PassthruState *card = DO_UPCAST(PassthruState, base, base); + + *len = card->atr_length; + return card->atr; +} + +static int passthru_initfn(CCIDCardState *base) +{ + PassthruState *card = DO_UPCAST(PassthruState, base, base); + + card->vscard_in_pos = 0; + card->vscard_in_hdr = 0; + if (card->cs) { + DPRINTF(card, D_INFO, "initing chardev\n"); + qemu_chr_add_handlers(card->cs, + ccid_card_vscard_can_read, + ccid_card_vscard_read, + ccid_card_vscard_event, card); + ccid_card_vscard_send_init(card); + } else { + error_report("missing chardev"); + return -1; + } + assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE); + memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR)); + card->atr_length = sizeof(DEFAULT_ATR); + return 0; +} + +static int passthru_exitfn(CCIDCardState *base) +{ + return 0; +} + +static VMStateDescription passthru_vmstate = { + .name = PASSTHRU_DEV_NAME, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(vscard_in_data, PassthruState), + VMSTATE_UINT32(vscard_in_pos, PassthruState), + VMSTATE_UINT32(vscard_in_hdr, PassthruState), + VMSTATE_BUFFER(atr, PassthruState), + VMSTATE_UINT8(atr_length, PassthruState), + VMSTATE_END_OF_LIST() + } +}; + +static CCIDCardInfo passthru_card_info = { + .qdev.name = PASSTHRU_DEV_NAME, + .qdev.desc = "passthrough smartcard", + .qdev.size = sizeof(PassthruState), + .qdev.vmsd = &passthru_vmstate, + .initfn = passthru_initfn, + .exitfn = passthru_exitfn, + .get_atr = passthru_get_atr, + .apdu_from_guest = passthru_apdu_from_guest, + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", PassthruState, cs), + DEFINE_PROP_UINT8("debug", PassthruState, debug, 0), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void ccid_card_passthru_register_devices(void) +{ + ccid_card_qdev_register(&passthru_card_info); +} + +device_init(ccid_card_passthru_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/ccid.h qemu-kvm-0.15.0+noroms/hw/ccid.h --- qemu-kvm-0.14.1+noroms/hw/ccid.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/ccid.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,58 @@ +/* + * CCID Passthru Card Device emulation + * + * Copyright (c) 2011 Red Hat. + * Written by Alon Levy. + * + * This code is licensed under the GNU LGPL, version 2 or later. + */ + +#ifndef CCID_H +#define CCID_H + +#include "qdev.h" + +typedef struct CCIDCardState CCIDCardState; +typedef struct CCIDCardInfo CCIDCardInfo; + +/* + * state of the CCID Card device (i.e. hw/ccid-card-*.c) + */ +struct CCIDCardState { + DeviceState qdev; + uint32_t slot; /* For future use with multiple slot reader. */ +}; + +/* + * callbacks to be used by the CCID device (hw/usb-ccid.c) to call + * into the smartcard device (hw/ccid-card-*.c) + */ +struct CCIDCardInfo { + DeviceInfo qdev; + const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len); + void (*apdu_from_guest)(CCIDCardState *card, + const uint8_t *apdu, + uint32_t len); + int (*exitfn)(CCIDCardState *card); + int (*initfn)(CCIDCardState *card); +}; + +/* + * API for smartcard calling the CCID device (used by hw/ccid-card-*.c) + */ +void ccid_card_send_apdu_to_guest(CCIDCardState *card, + uint8_t *apdu, + uint32_t len); +void ccid_card_card_removed(CCIDCardState *card); +void ccid_card_card_inserted(CCIDCardState *card); +void ccid_card_card_error(CCIDCardState *card, uint64_t error); +void ccid_card_qdev_register(CCIDCardInfo *card); + +/* + * support guest visible insertion/removal of ccid devices based on actual + * devices connected/removed. Called by card implementation (passthru, local) + */ +int ccid_card_ccid_attach(CCIDCardState *card); +void ccid_card_ccid_detach(CCIDCardState *card); + +#endif /* CCID_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/cirrus_vga.c qemu-kvm-0.15.0+noroms/hw/cirrus_vga.c --- qemu-kvm-0.14.1+noroms/hw/cirrus_vga.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/cirrus_vga.c 2011-08-09 07:40:29.000000000 -0500 @@ -31,8 +31,6 @@ #include "pci.h" #include "console.h" #include "vga_int.h" -#include "kvm.h" -#include "qemu-kvm.h" #include "loader.h" /* @@ -2488,11 +2486,12 @@ static void map_linear_vram(CirrusVGAState *s) { - vga_dirty_log_stop(&s->vga); if (!s->vga.map_addr && s->vga.lfb_addr && s->vga.lfb_end) { s->vga.map_addr = s->vga.lfb_addr; s->vga.map_end = s->vga.lfb_end; - cpu_register_physical_memory(s->vga.map_addr, s->vga.map_end - s->vga.map_addr, s->vga.vram_offset); + cpu_register_physical_memory_log(s->vga.map_addr, + s->vga.map_end - s->vga.map_addr, + s->vga.vram_offset, 0, true); } if (!s->vga.map_addr) @@ -2501,20 +2500,19 @@ #ifndef TARGET_IA64 s->vga.lfb_vram_mapped = 0; - cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000, - (s->vga.vram_offset + s->cirrus_bank_base[0]) | IO_MEM_UNASSIGNED); - cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, - (s->vga.vram_offset + s->cirrus_bank_base[1]) | IO_MEM_UNASSIGNED); if (!(s->cirrus_srcptr != s->cirrus_srcptr_end) && !((s->vga.sr[0x07] & 0x01) == 0) && !((s->vga.gr[0x0B] & 0x14) == 0x14) && !(s->vga.gr[0x0B] & 0x02)) { - vga_dirty_log_stop(&s->vga); - cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000, - (s->vga.vram_offset + s->cirrus_bank_base[0]) | IO_MEM_RAM); - cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, - (s->vga.vram_offset + s->cirrus_bank_base[1]) | IO_MEM_RAM); + cpu_register_physical_memory_log(isa_mem_base + 0xa0000, 0x8000, + (s->vga.vram_offset + + s->cirrus_bank_base[0]) | + IO_MEM_RAM, 0, true); + cpu_register_physical_memory_log(isa_mem_base + 0xa8000, 0x8000, + (s->vga.vram_offset + + s->cirrus_bank_base[1]) | + IO_MEM_RAM, 0, true); s->vga.lfb_vram_mapped = 1; } @@ -2529,7 +2527,6 @@ static void unmap_linear_vram(CirrusVGAState *s) { - vga_dirty_log_stop(&s->vga); if (s->vga.map_addr && s->vga.lfb_addr && s->vga.lfb_end) { s->vga.map_addr = s->vga.map_end = 0; cpu_register_physical_memory(s->vga.lfb_addr, s->vga.vram_size, @@ -2537,8 +2534,6 @@ } cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000, s->vga.vga_io_memory); - - vga_dirty_log_start(&s->vga); } /* Compute the memory access functions */ @@ -3037,7 +3032,6 @@ s->vga.cursor_draw_line = cirrus_cursor_draw_line; qemu_register_reset(cirrus_reset, s); - cirrus_reset(s); } /*************************************** @@ -3073,8 +3067,6 @@ { CirrusVGAState *s = &DO_UPCAST(PCICirrusVGAState, dev, d)->cirrus_vga; - vga_dirty_log_stop(&s->vga); - /* XXX: add byte swapping apertures */ cpu_register_physical_memory(addr, s->vga.vram_size, s->cirrus_linear_io_addr); @@ -3091,37 +3083,27 @@ vga_dirty_log_start(&s->vga); } -static void cirrus_pci_mmio_map(PCIDevice *d, int region_num, - pcibus_t addr, pcibus_t size, int type) -{ - CirrusVGAState *s = &DO_UPCAST(PCICirrusVGAState, dev, d)->cirrus_vga; - - cpu_register_physical_memory(addr, CIRRUS_PNPMMIO_SIZE, - s->cirrus_mmio_io_addr); -} - static void pci_cirrus_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { PCICirrusVGAState *pvs = DO_UPCAST(PCICirrusVGAState, dev, d); CirrusVGAState *s = &pvs->cirrus_vga; - vga_dirty_log_stop(&s->vga); - pci_default_write_config(d, address, val, len); - if (s->vga.map_addr && d->io_regions[0].addr == PCI_BAR_UNMAPPED) + if (s->vga.map_addr && d->io_regions[0].addr == PCI_BAR_UNMAPPED) { s->vga.map_addr = 0; + s->vga.lfb_addr = 0; + s->vga.lfb_end = 0; + } cirrus_update_memory_access(s); - - vga_dirty_log_start(&s->vga); } static int pci_cirrus_vga_initfn(PCIDevice *dev) { PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev); CirrusVGAState *s = &d->cirrus_vga; - uint8_t *pci_conf = d->dev.config; - int device_id = CIRRUS_ID_CLGD5446; + PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, dev->qdev.info); + int16_t device_id = info->device_id; /* setup VGA */ vga_common_init(&s->vga, VGA_RAM_SIZE); @@ -3131,9 +3113,6 @@ &s->vga); /* setup PCI */ - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_CIRRUS); - pci_config_set_device_id(pci_conf, device_id); - pci_config_set_class(pci_conf, PCI_CLASS_DISPLAY_VGA); /* setup memory space */ /* memory #0 LFB */ @@ -3142,8 +3121,8 @@ 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(&d->dev, 1, CIRRUS_PNPMMIO_SIZE, - PCI_BASE_ADDRESS_SPACE_MEMORY, cirrus_pci_mmio_map); + pci_register_bar_simple(&d->dev, 1, CIRRUS_PNPMMIO_SIZE, 0, + s->cirrus_mmio_io_addr); } return 0; } @@ -3162,6 +3141,9 @@ .init = pci_cirrus_vga_initfn, .romfile = VGABIOS_CIRRUS_FILENAME, .config_write = pci_cirrus_write_config, + .vendor_id = PCI_VENDOR_ID_CIRRUS, + .device_id = CIRRUS_ID_CLGD5446, + .class_id = PCI_CLASS_DISPLAY_VGA, }; static void cirrus_vga_register(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/collie.c qemu-kvm-0.15.0+noroms/hw/collie.c --- qemu-kvm-0.14.1+noroms/hw/collie.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/collie.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,69 @@ +/* + * SA-1110-based Sharp Zaurus SL-5500 platform. + * + * Copyright (C) 2011 Dmitry Eremin-Solenikov + * + * This code is licensed under GNU GPL v2. + */ +#include "hw.h" +#include "sysbus.h" +#include "boards.h" +#include "devices.h" +#include "strongarm.h" +#include "arm-misc.h" +#include "flash.h" +#include "blockdev.h" + +static struct arm_boot_info collie_binfo = { + .loader_start = SA_SDCS0, + .ram_size = 0x20000000, +}; + +static void collie_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) +{ + StrongARMState *s; + DriveInfo *dinfo; + ram_addr_t phys_flash; + + if (!cpu_model) { + cpu_model = "sa1110"; + } + + s = sa1110_init(collie_binfo.ram_size, cpu_model); + + phys_flash = qemu_ram_alloc(NULL, "collie.fl1", 0x02000000); + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi01_register(SA_CS0, phys_flash, + dinfo ? dinfo->bdrv : NULL, (64 * 1024), + 512, 4, 0x00, 0x00, 0x00, 0x00, 0); + + phys_flash = qemu_ram_alloc(NULL, "collie.fl2", 0x02000000); + dinfo = drive_get(IF_PFLASH, 0, 1); + pflash_cfi01_register(SA_CS1, phys_flash, + dinfo ? dinfo->bdrv : NULL, (64 * 1024), + 512, 4, 0x00, 0x00, 0x00, 0x00, 0); + + sysbus_create_simple("scoop", 0x40800000, NULL); + + collie_binfo.kernel_filename = kernel_filename; + collie_binfo.kernel_cmdline = kernel_cmdline; + collie_binfo.initrd_filename = initrd_filename; + collie_binfo.board_id = 0x208; + arm_load_kernel(s->env, &collie_binfo); +} + +static QEMUMachine collie_machine = { + .name = "collie", + .desc = "Collie PDA (SA-1110)", + .init = collie_init, +}; + +static void collie_machine_init(void) +{ + qemu_register_machine(&collie_machine); +} + +machine_init(collie_machine_init) diff -Nru qemu-kvm-0.14.1+noroms/hw/cris-boot.c qemu-kvm-0.15.0+noroms/hw/cris-boot.c --- qemu-kvm-0.14.1+noroms/hw/cris-boot.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/cris-boot.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,7 +23,6 @@ */ #include "hw.h" -#include "sysemu.h" #include "loader.h" #include "elf.h" #include "cris-boot.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/cuda.c qemu-kvm-0.15.0+noroms/hw/cuda.c --- qemu-kvm-0.14.1+noroms/hw/cuda.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/cuda.c 2011-08-09 07:40:29.000000000 -0500 @@ -170,7 +170,7 @@ int64_t d; unsigned int counter; - d = muldiv64(qemu_get_clock(vm_clock) - s->load_time, + d = muldiv64(qemu_get_clock_ns(vm_clock) - s->load_time, CUDA_TIMER_FREQ, get_ticks_per_sec()); if (s->index == 0) { /* the timer goes down from latch to -1 (period of latch + 2) */ @@ -189,7 +189,7 @@ static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val) { CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val); - ti->load_time = qemu_get_clock(vm_clock); + ti->load_time = qemu_get_clock_ns(vm_clock); ti->counter_value = val; cuda_timer_update(s, ti, ti->load_time); } @@ -346,7 +346,7 @@ break; case 4: s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; - cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock)); break; case 5: s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); @@ -355,12 +355,12 @@ break; case 6: s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; - cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock)); break; case 7: s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); s->ifr &= ~T1_INT; - cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock)); break; case 8: s->timers[1].latch = val; @@ -374,7 +374,7 @@ break; case 11: s->acr = val; - cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock)); cuda_update(s); break; case 12: @@ -506,7 +506,7 @@ cuda_send_packet_to_host(s, obuf, olen + 2); } qemu_mod_timer(s->adb_poll_timer, - qemu_get_clock(vm_clock) + + qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); } @@ -524,7 +524,7 @@ s->autopoll = autopoll; if (autopoll) { qemu_mod_timer(s->adb_poll_timer, - qemu_get_clock(vm_clock) + + qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); } else { qemu_del_timer(s->adb_poll_timer); @@ -536,14 +536,14 @@ break; case CUDA_SET_TIME: ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4]; - s->tick_offset = ti - (qemu_get_clock(vm_clock) / get_ticks_per_sec()); + s->tick_offset = ti - (qemu_get_clock_ns(vm_clock) / get_ticks_per_sec()); obuf[0] = CUDA_PACKET; obuf[1] = 0; obuf[2] = 0; cuda_send_packet_to_host(s, obuf, 3); break; case CUDA_GET_TIME: - ti = s->tick_offset + (qemu_get_clock(vm_clock) / get_ticks_per_sec()); + ti = s->tick_offset + (qemu_get_clock_ns(vm_clock) / get_ticks_per_sec()); obuf[0] = CUDA_PACKET; obuf[1] = 0; obuf[2] = 0; @@ -644,81 +644,57 @@ &cuda_readl, }; -static void cuda_save_timer(QEMUFile *f, CUDATimer *s) +static bool cuda_timer_exist(void *opaque, int version_id) { - qemu_put_be16s(f, &s->latch); - qemu_put_be16s(f, &s->counter_value); - qemu_put_sbe64s(f, &s->load_time); - qemu_put_sbe64s(f, &s->next_irq_time); - if (s->timer) - qemu_put_timer(f, s->timer); -} - -static void cuda_save(QEMUFile *f, void *opaque) -{ - CUDAState *s = (CUDAState *)opaque; - - qemu_put_ubyte(f, s->b); - qemu_put_ubyte(f, s->a); - qemu_put_ubyte(f, s->dirb); - qemu_put_ubyte(f, s->dira); - qemu_put_ubyte(f, s->sr); - qemu_put_ubyte(f, s->acr); - qemu_put_ubyte(f, s->pcr); - qemu_put_ubyte(f, s->ifr); - qemu_put_ubyte(f, s->ier); - qemu_put_ubyte(f, s->anh); - qemu_put_sbe32s(f, &s->data_in_size); - qemu_put_sbe32s(f, &s->data_in_index); - qemu_put_sbe32s(f, &s->data_out_index); - qemu_put_ubyte(f, s->autopoll); - qemu_put_buffer(f, s->data_in, sizeof(s->data_in)); - qemu_put_buffer(f, s->data_out, sizeof(s->data_out)); - qemu_put_be32s(f, &s->tick_offset); - cuda_save_timer(f, &s->timers[0]); - cuda_save_timer(f, &s->timers[1]); -} - -static void cuda_load_timer(QEMUFile *f, CUDATimer *s) -{ - qemu_get_be16s(f, &s->latch); - qemu_get_be16s(f, &s->counter_value); - qemu_get_sbe64s(f, &s->load_time); - qemu_get_sbe64s(f, &s->next_irq_time); - if (s->timer) - qemu_get_timer(f, s->timer); -} - -static int cuda_load(QEMUFile *f, void *opaque, int version_id) -{ - CUDAState *s = (CUDAState *)opaque; - - if (version_id != 1) - return -EINVAL; - - s->b = qemu_get_ubyte(f); - s->a = qemu_get_ubyte(f); - s->dirb = qemu_get_ubyte(f); - s->dira = qemu_get_ubyte(f); - s->sr = qemu_get_ubyte(f); - s->acr = qemu_get_ubyte(f); - s->pcr = qemu_get_ubyte(f); - s->ifr = qemu_get_ubyte(f); - s->ier = qemu_get_ubyte(f); - s->anh = qemu_get_ubyte(f); - qemu_get_sbe32s(f, &s->data_in_size); - qemu_get_sbe32s(f, &s->data_in_index); - qemu_get_sbe32s(f, &s->data_out_index); - s->autopoll = qemu_get_ubyte(f); - qemu_get_buffer(f, s->data_in, sizeof(s->data_in)); - qemu_get_buffer(f, s->data_out, sizeof(s->data_out)); - qemu_get_be32s(f, &s->tick_offset); - cuda_load_timer(f, &s->timers[0]); - cuda_load_timer(f, &s->timers[1]); + CUDATimer *s = opaque; - return 0; + return s->timer != NULL; } +static const VMStateDescription vmstate_cuda_timer = { + .name = "cuda_timer", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT16(latch, CUDATimer), + VMSTATE_UINT16(counter_value, CUDATimer), + VMSTATE_INT64(load_time, CUDATimer), + VMSTATE_INT64(next_irq_time, CUDATimer), + VMSTATE_TIMER_TEST(timer, CUDATimer, cuda_timer_exist), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_cuda = { + .name = "cuda", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(a, CUDAState), + VMSTATE_UINT8(b, CUDAState), + VMSTATE_UINT8(dira, CUDAState), + VMSTATE_UINT8(dirb, CUDAState), + VMSTATE_UINT8(sr, CUDAState), + VMSTATE_UINT8(acr, CUDAState), + VMSTATE_UINT8(pcr, CUDAState), + VMSTATE_UINT8(ifr, CUDAState), + VMSTATE_UINT8(ier, CUDAState), + VMSTATE_UINT8(anh, CUDAState), + VMSTATE_INT32(data_in_size, CUDAState), + VMSTATE_INT32(data_in_index, CUDAState), + VMSTATE_INT32(data_out_index, CUDAState), + VMSTATE_UINT8(autopoll, CUDAState), + VMSTATE_BUFFER(data_in, CUDAState), + VMSTATE_BUFFER(data_out, CUDAState), + VMSTATE_UINT32(tick_offset, CUDAState), + VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1, + vmstate_cuda_timer, CUDATimer), + VMSTATE_END_OF_LIST() + } +}; + static void cuda_reset(void *opaque) { CUDAState *s = opaque; @@ -754,16 +730,16 @@ s->irq = irq; s->timers[0].index = 0; - s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s); + s->timers[0].timer = qemu_new_timer_ns(vm_clock, cuda_timer1, s); s->timers[1].index = 1; qemu_get_timedate(&tm, 0); s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; - s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s); + s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, 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); + vmstate_register(NULL, -1, &vmstate_cuda, s); qemu_register_reset(cuda_reset, s); } diff -Nru qemu-kvm-0.14.1+noroms/hw/dec_pci.c qemu-kvm-0.15.0+noroms/hw/dec_pci.c --- qemu-kvm-0.14.1+noroms/hw/dec_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/dec_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -50,28 +50,16 @@ 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, + .init = pci_bridge_initfn, .exit = pci_bridge_exitfn, + .vendor_id = PCI_VENDOR_ID_DEC, + .device_id = PCI_DEVICE_ID_DEC_21154, .config_write = pci_bridge_write_config, .is_bridge = 1, }; @@ -108,10 +96,6 @@ 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; } @@ -119,6 +103,10 @@ .qdev.name = "dec-21154", .qdev.size = sizeof(PCIDevice), .init = dec_21154_pci_host_init, + .vendor_id = PCI_VENDOR_ID_DEC, + .device_id = PCI_DEVICE_ID_DEC_21154, + .revision = 0x02, + .class_id = PCI_CLASS_BRIDGE_PCI, .is_bridge = 1, }; diff -Nru qemu-kvm-0.14.1+noroms/hw/device-assignment.c qemu-kvm-0.15.0+noroms/hw/device-assignment.c --- qemu-kvm-0.14.1+noroms/hw/device-assignment.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/device-assignment.c 2011-08-09 07:40:29.000000000 -0500 @@ -47,7 +47,7 @@ #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800 -#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ +#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ /* #define DEVICE_ASSIGNMENT_DEBUG 1 */ @@ -71,6 +71,28 @@ static uint32_t assigned_device_pci_cap_read_config(PCIDevice *pci_dev, uint32_t address, int len); +/* 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_dev_ioport_rw(AssignedDevRegion *dev_region, uint32_t addr, int len, uint32_t *val) { @@ -242,38 +264,12 @@ &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, pcibus_t e_phys, pcibus_t e_size, int type) { - AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); AssignedDevRegion *region = &r_dev->v_addrs[region_num]; PCIRegion *real_region = &r_dev->real_device.regions[region_num]; - int ret = 0; 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); @@ -294,20 +290,15 @@ TARGET_PAGE_SIZE, r_dev->mmio_index); } } - - if (ret != 0) { - fprintf(stderr, "%s: Error: create new mapping failed\n", __func__); - exit(1); - } } static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type) { - AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); AssignedDevRegion *region = &r_dev->v_addrs[region_num]; int first_map = (region->e_size == 0); - CPUState *env; + int r; region->e_physbase = addr; region->e_size = size; @@ -316,22 +307,11 @@ addr, region->u.r_baseport, type, size, region_num); if (first_map && region->region->resource_fd < 0) { - struct ioperm_data *data; - - data = qemu_mallocz(sizeof(struct ioperm_data)); - if (data == NULL) { - fprintf(stderr, "%s: Out of memory\n", __func__); - exit(1); - } - - data->start_port = region->u.r_baseport; - data->num = region->r_size; - data->turn_on = 1; - - kvm_add_ioperm_data(data); - - for (env = first_cpu; env; env = env->next_cpu) - kvm_ioperm(env, data); + r = kvm_add_ioport_region(region->u.r_baseport, region->r_size); + if (r < 0) { + fprintf(stderr, "%s: failed to enable ioport access (%m)\n", + __func__); + } } register_ioport_read(addr, size, 1, assigned_dev_ioport_readb, @@ -350,7 +330,7 @@ static uint32_t assigned_dev_pci_read(PCIDevice *d, int pos, int len) { - AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d); uint32_t val; ssize_t ret; int fd = pci_dev->real_device.config_fd; @@ -377,7 +357,7 @@ static void assigned_dev_pci_write(PCIDevice *d, int pos, uint32_t val, int len) { - AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d); ssize_t ret; int fd = pci_dev->real_device.config_fd; @@ -430,7 +410,7 @@ { int fd; ssize_t ret; - AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d); DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7), @@ -440,16 +420,34 @@ return assigned_device_pci_cap_write_config(d, address, val, len); } - if (address == 0x4) { + if (ranges_overlap(address, len, PCI_COMMAND, 2)) { 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) { - /* used for update-mappings (BAR emulation) */ + /* + * Catch access to + * - base address registers + * - ROM base address & capability pointer + * - interrupt line & pin + */ + if (ranges_overlap(address, len, PCI_BASE_ADDRESS_0, 24) || + ranges_overlap(address, len, PCI_ROM_ADDRESS, 4)) { pci_default_write_config(d, address, val, len); return; + } else if (ranges_overlap(address, len, PCI_CAPABILITY_LIST, 1) || + ranges_overlap(address, len, PCI_INTERRUPT_LINE, 2)) { + uint32_t real_val; + + pci_default_write_config(d, address, val, len); + + /* Ensure that writes to overlapping areas we don't virtualize still + * hit the device. */ + real_val = assigned_dev_pci_read(d, address, len); + val = merge_bits(val, real_val, address, len, + PCI_CAPABILITY_LIST, 0xff); + val = merge_bits(val, real_val, address, len, + PCI_INTERRUPT_LINE, 0xffff); } DEBUG("NON BAR (%x.%x): address=%04x val=0x%08x len=%d\n", @@ -474,10 +472,10 @@ static uint32_t assigned_dev_pci_read_config(PCIDevice *d, uint32_t address, int len) { - uint32_t val = 0; + uint32_t val = 0, virt_val; int fd; ssize_t ret; - AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d); if (address >= PCI_CONFIG_HEADER_SIZE && d->config_map[address]) { val = assigned_device_pci_cap_read_config(d, address, len); @@ -486,19 +484,21 @@ return val; } - if (address < 0x4 || (pci_dev->need_emulate_cmd && address == 0x4) || - (address >= 0x10 && address <= 0x24) || address == 0x30 || - address == 0x34 || address == 0x3c || address == 0x3d) { + /* + * Catch access to + * - vendor & device ID + * - base address registers + * - ROM base address + */ + if (ranges_overlap(address, len, PCI_VENDOR_ID, 4) || + ranges_overlap(address, len, PCI_BASE_ADDRESS_0, 24) || + ranges_overlap(address, len, PCI_ROM_ADDRESS, 4)) { 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); return val; } - /* vga specific, remove later */ - if (address == 0xFC) - goto do_log; - fd = pci_dev->real_device.config_fd; again: @@ -513,16 +513,30 @@ exit(1); } -do_log: DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len); + if (pci_dev->emulate_cmd_mask) { + val = merge_bits(val, pci_default_read_config(d, address, len), + address, len, PCI_COMMAND, pci_dev->emulate_cmd_mask); + } + + /* + * Merge bits from virtualized + * - capability pointer + * - interrupt line & pin + */ + virt_val = pci_default_read_config(d, address, len); + val = merge_bits(val, virt_val, address, len, PCI_CAPABILITY_LIST, 0xff); + val = merge_bits(val, virt_val, address, len, PCI_INTERRUPT_LINE, 0xffff); + if (!pci_dev->cap.available) { /* kill the special capabilities */ - if (address == 4 && len == 4) - val &= ~0x100000; - else if (address == 6) - val &= ~0x10; + if (address == PCI_COMMAND && len == 4) { + val &= ~(PCI_STATUS_CAP_LIST << 16); + } else if (address == PCI_STATUS) { + val &= ~PCI_STATUS_CAP_LIST; + } } return val; @@ -542,21 +556,10 @@ /* 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, "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); - slow_map = 1; - } - /* map physical memory */ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; pci_dev->v_addrs[i].u.r_virtbase = mmap(NULL, cur_region->size, @@ -580,8 +583,18 @@ pci_dev->v_addrs[i].u.r_virtbase += (cur_region->base_addr & 0xFFF); - - if (!slow_map) { + if (cur_region->size & 0xFFF) { + 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); + pci_dev->v_addrs[i].memory_index = + cpu_register_io_memory(slow_bar_read, slow_bar_write, + &pci_dev->v_addrs[i], + DEVICE_NATIVE_ENDIAN); + } else { void *virtbase = pci_dev->v_addrs[i].u.r_virtbase; char name[32]; snprintf(name, sizeof(name), "%s.bar%d", @@ -591,13 +604,10 @@ &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, - slow_map ? assigned_dev_iomem_map_slow - : assigned_dev_iomem_map); + pci_register_bar((PCIDevice *) pci_dev, i, cur_region->size, t, + assigned_dev_iomem_map); continue; } else { /* handle port io regions */ @@ -780,10 +790,9 @@ /* dealing with virtual function device */ snprintf(name, sizeof(name), "%sphysfn/", dir); - if (!stat(name, &statbuf)) - pci_dev->need_emulate_cmd = 1; - else - pci_dev->need_emulate_cmd = 0; + if (!stat(name, &statbuf)) { + pci_dev->emulate_cmd_mask = 0xffff; + } dev->region_number = r; return 0; @@ -791,7 +800,6 @@ static QLIST_HEAD(, AssignedDevice) devs = QLIST_HEAD_INITIALIZER(devs); -#ifdef KVM_CAP_IRQ_ROUTING static void free_dev_irq_entries(AssignedDevice *dev) { int i; @@ -802,56 +810,55 @@ dev->entry = NULL; dev->irq_entries_nr = 0; } -#endif static void free_assigned_device(AssignedDevice *dev) { - if (dev) { - int i; + int i; + + for (i = 0; i < dev->real_device.region_number; i++) { + PCIRegion *pci_region = &dev->real_device.regions[i]; + AssignedDevRegion *region = &dev->v_addrs[i]; - for (i = 0; i < dev->real_device.region_number; i++) { - PCIRegion *pci_region = &dev->real_device.regions[i]; - AssignedDevRegion *region = &dev->v_addrs[i]; - - if (!pci_region->valid) - continue; - - if (pci_region->type & IORESOURCE_IO) { - if (pci_region->resource_fd < 0) { - kvm_remove_ioperm_data(region->u.r_baseport, - region->r_size); + if (!pci_region->valid) { + continue; + } + if (pci_region->type & IORESOURCE_IO) { + if (pci_region->resource_fd < 0) { + kvm_remove_ioport_region(region->u.r_baseport, region->r_size); + } + } else if (pci_region->type & IORESOURCE_MEM) { + if (region->u.r_virtbase) { + if (region->e_size > 0) { + cpu_register_physical_memory(region->e_physbase, + region->e_size, + IO_MEM_UNASSIGNED); } - } else if (pci_region->type & IORESOURCE_MEM) { - if (region->u.r_virtbase) { - 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 (region->r_size & 0xFFF) { + cpu_unregister_io_memory(region->memory_index); + } else { + qemu_ram_free_from_ptr(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->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); + if (pci_region->resource_fd >= 0) { + close(pci_region->resource_fd); } + } -#ifdef KVM_CAP_IRQ_ROUTING - free_dev_irq_entries(dev); -#endif + 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); + } + + free_dev_irq_entries(dev); } static uint32_t calc_assigned_dev_id(uint16_t seg, uint8_t bus, uint8_t devfn) @@ -911,7 +918,6 @@ 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) { @@ -919,18 +925,14 @@ "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_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->features & ASSIGNED_DEVICE_USE_IOMMU_MASK) { if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) { @@ -940,9 +942,6 @@ } assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU; } -#else - 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 " @@ -950,7 +949,7 @@ "requests!\n"); } - r = kvm_assign_pci_device(kvm_context, &assigned_dev_data); + r = kvm_assign_pci_device(kvm_state, &assigned_dev_data); if (r < 0) { fprintf(stderr, "Failed to assign device \"%s\" : %s\n", dev->dev.qdev.id, strerror(-r)); @@ -990,10 +989,9 @@ 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 if (dev->irq_requested_type) { assigned_irq_data.flags = dev->irq_requested_type; - r = kvm_deassign_irq(kvm_context, &assigned_irq_data); + r = kvm_deassign_irq(kvm_state, &assigned_irq_data); /* -ENXIO means no assigned irq */ if (r && r != -ENXIO) perror("assign_irq: deassign"); @@ -1005,9 +1003,8 @@ assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_MSI; else assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_INTX; -#endif - r = kvm_assign_irq(kvm_context, &assigned_irq_data); + r = kvm_assign_irq(kvm_state, &assigned_irq_data); if (r < 0) { fprintf(stderr, "Failed to assign irq for \"%s\": %s\n", dev->dev.qdev.id, strerror(-r)); @@ -1023,7 +1020,6 @@ static void deassign_device(AssignedDevice *dev) { -#ifdef KVM_CAP_DEVICE_DEASSIGNMENT struct kvm_assigned_pci_dev assigned_dev_data; int r; @@ -1031,11 +1027,10 @@ assigned_dev_data.assigned_dev_id = calc_assigned_dev_id(dev->h_segnr, dev->h_busnr, dev->h_devfn); - r = kvm_deassign_pci_device(kvm_context, &assigned_dev_data); + r = kvm_deassign_pci_device(kvm_state, &assigned_dev_data); if (r < 0) fprintf(stderr, "Failed to deassign device \"%s\" : %s\n", dev->dev.qdev.id, strerror(-r)); -#endif } #if 0 @@ -1073,13 +1068,10 @@ } } -#ifdef KVM_CAP_IRQ_ROUTING - -#ifdef KVM_CAP_DEVICE_MSI static void assigned_dev_update_msi(PCIDevice *pci_dev, unsigned int ctrl_pos) { struct kvm_assigned_irq assigned_irq_data; - AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); uint8_t ctrl_byte = pci_dev->config[ctrl_pos]; int r; @@ -1096,7 +1088,7 @@ assigned_irq_data.flags = assigned_dev->irq_requested_type; free_dev_irq_entries(assigned_dev); - r = kvm_deassign_irq(kvm_context, &assigned_irq_data); + r = kvm_deassign_irq(kvm_state, &assigned_irq_data); /* -ENXIO means no assigned irq */ if (r && r != -ENXIO) perror("assigned_dev_update_msi: deassign irq"); @@ -1106,11 +1098,7 @@ 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 = qemu_mallocz(sizeof(*(assigned_dev->entry))); assigned_dev->entry->u.msi.address_lo = pci_get_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO); assigned_dev->entry->u.msi.address_hi = 0; @@ -1134,8 +1122,9 @@ assigned_irq_data.guest_irq = assigned_dev->entry->gsi; assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSI | KVM_DEV_IRQ_GUEST_MSI; - if (kvm_assign_irq(kvm_context, &assigned_irq_data) < 0) + if (kvm_assign_irq(kvm_state, &assigned_irq_data) < 0) { perror("assigned_dev_enable_msi: assign irq"); + } assigned_dev->girq = -1; assigned_dev->irq_requested_type = assigned_irq_data.flags; @@ -1143,12 +1132,10 @@ assign_irq(assigned_dev); } } -#endif -#ifdef KVM_CAP_DEVICE_MSIX static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) { - AssignedDevice *adev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevice *adev = DO_UPCAST(AssignedDevice, dev, pci_dev); uint16_t entries_nr = 0, entries_max_nr; int pos = 0, i, r = 0; uint32_t msg_addr, msg_upper_addr, msg_data, msg_ctrl; @@ -1179,7 +1166,7 @@ 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); + r = kvm_assign_set_msix_nr(kvm_state, &msix_nr); if (r != 0) { fprintf(stderr, "fail to set MSI-X entry number for MSIX! %s\n", strerror(-r)); @@ -1188,11 +1175,7 @@ free_dev_irq_entries(adev); adev->irq_entries_nr = entries_nr; - adev->entry = calloc(entries_nr, sizeof(struct kvm_irq_routing_entry)); - if (!adev->entry) { - perror("assigned_dev_update_msix_mmio: "); - return -errno; - } + adev->entry = qemu_mallocz(entries_nr * sizeof(*(adev->entry))); msix_entry.assigned_dev_id = msix_nr.assigned_dev_id; entries_nr = 0; @@ -1222,7 +1205,7 @@ msix_entry.gsi = adev->entry[entries_nr].gsi; msix_entry.entry = i; - r = kvm_assign_set_msix_entry(kvm_context, &msix_entry); + r = kvm_assign_set_msix_entry(kvm_state, &msix_entry); if (r) { fprintf(stderr, "fail to set MSI-X entry! %s\n", strerror(-r)); break; @@ -1243,7 +1226,7 @@ static void assigned_dev_update_msix(PCIDevice *pci_dev, unsigned int ctrl_pos) { struct kvm_assigned_irq assigned_irq_data; - AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev); uint16_t *ctrl_word = (uint16_t *)(pci_dev->config + ctrl_pos); int r; @@ -1260,7 +1243,7 @@ assigned_irq_data.flags = assigned_dev->irq_requested_type; free_dev_irq_entries(assigned_dev); - r = kvm_deassign_irq(kvm_context, &assigned_irq_data); + r = kvm_deassign_irq(kvm_state, &assigned_irq_data); /* -ENXIO means no assigned irq */ if (r && r != -ENXIO) perror("assigned_dev_update_msix: deassign irq"); @@ -1276,7 +1259,7 @@ perror("assigned_dev_update_msix_mmio"); return; } - if (kvm_assign_irq(kvm_context, &assigned_irq_data) < 0) { + if (kvm_assign_irq(kvm_state, &assigned_irq_data) < 0) { perror("assigned_dev_enable_msix: assign irq"); return; } @@ -1286,8 +1269,6 @@ assign_irq(assigned_dev); } } -#endif -#endif /* There can be multiple VNDR capabilities per device, we need to find the * one that starts closet to the given address without going over. */ @@ -1305,28 +1286,6 @@ 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) { @@ -1356,32 +1315,23 @@ uint32_t val, int len) { uint8_t cap_id = pci_dev->config_map[address]; + uint8_t cap; 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 - { - 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); - } + 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 - { - 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); - } + 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 case PCI_CAP_ID_VPD: case PCI_CAP_ID_VNDR: @@ -1392,7 +1342,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev) { - AssignedDevice *dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); PCIRegion *pci_region = dev->real_device.regions; int ret, pos; @@ -1402,8 +1352,6 @@ pci_get_word(pci_dev->config + PCI_STATUS) & ~PCI_STATUS_CAP_LIST); -#ifdef KVM_CAP_IRQ_ROUTING -#ifdef KVM_CAP_DEVICE_MSI /* Expose MSI capability * MSI capability is the 1st capability in capability config */ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0))) { @@ -1425,8 +1373,6 @@ 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 ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSIX, 0))) { int bar_nr; @@ -1450,8 +1396,6 @@ msix_table_entry &= ~PCI_MSIX_BIR; dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry; } -#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))) { @@ -1475,18 +1419,37 @@ } if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_EXP, 0))) { - uint8_t version; + uint8_t version, size; 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); + } else if (version == 2) { + /* + * Check for non-std size, accept reduced size to 0x34, + * which is what bcm5761 implemented, violating the + * PCIe v3.0 spec that regs should exist and be read as 0, + * not optionally provided and shorten the struct size. + */ + size = MIN(0x3c, PCI_CONFIG_SPACE_SIZE - pos); + if (size < 0x34) { + fprintf(stderr, + "%s: Invalid size PCIe cap-id 0x%x \n", + __func__, PCI_CAP_ID_EXP); + return -EINVAL; + } else if (size != 0x3c) { + fprintf(stderr, + "WARNING, %s: PCIe cap-id 0x%x has " + "non-standard size 0x%x; std size should be 0x3c \n", + __func__, PCI_CAP_ID_EXP, size); + } + } else { + fprintf(stderr, + "%s: Unsupported PCI express capability version %d\n", + __func__, version); return -EINVAL; } @@ -1706,19 +1669,40 @@ static void reset_assigned_device(DeviceState *dev) { - PCIDevice *d = DO_UPCAST(PCIDevice, qdev, dev); + PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev); + AssignedDevice *adev = DO_UPCAST(AssignedDevice, dev, pci_dev); + char reset_file[64]; + const char reset[] = "1"; + int fd, ret; + + snprintf(reset_file, sizeof(reset_file), + "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/reset", + adev->host.seg, adev->host.bus, adev->host.dev, adev->host.func); + + /* + * Issue a device reset via pci-sysfs. Note that we use write(2) here + * and ignore the return value because some kernels have a bug that + * returns 0 rather than bytes written on success, sending us into an + * infinite retry loop using other write mechanisms. + */ + fd = open(reset_file, O_WRONLY); + if (fd != -1) { + ret = write(fd, reset, strlen(reset)); + (void)ret; + close(fd); + } /* * 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); + assigned_dev_pci_write_config(pci_dev, PCI_COMMAND, 0, 2); } static int assigned_initfn(struct PCIDevice *pci_dev) { AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - uint8_t e_device, e_intx; + uint8_t e_intx; int r; if (!kvm_enabled()) { @@ -1745,7 +1729,6 @@ goto out; /* handle interrupt routing */ - e_device = (dev->dev.devfn >> 3) & 0x1f; e_intx = dev->dev.config[0x3d] - 1; dev->intpin = e_intx; dev->run = 0; diff -Nru qemu-kvm-0.14.1+noroms/hw/device-assignment.h qemu-kvm-0.15.0+noroms/hw/device-assignment.h --- qemu-kvm-0.14.1+noroms/hw/device-assignment.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/device-assignment.h 2011-08-09 07:40:29.000000000 -0500 @@ -109,7 +109,7 @@ void *msix_table_page; target_phys_addr_t msix_table_addr; int mmio_index; - int need_emulate_cmd; + uint32_t emulate_cmd_mask; char *configfd_name; int32_t bootindex; QLIST_ENTRY(AssignedDevice) next; diff -Nru qemu-kvm-0.14.1+noroms/hw/dp8393x.c qemu-kvm-0.15.0+noroms/hw/dp8393x.c --- qemu-kvm-0.14.1+noroms/hw/dp8393x.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/dp8393x.c 2011-08-09 07:40:29.000000000 -0500 @@ -290,7 +290,7 @@ } ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; - s->wt_last_update = qemu_get_clock(vm_clock); + s->wt_last_update = qemu_get_clock_ns(vm_clock); delay = get_ticks_per_sec() * ticks / 5000000; qemu_mod_timer(s->watchdog, s->wt_last_update + delay); } @@ -305,7 +305,7 @@ return; } - elapsed = s->wt_last_update - qemu_get_clock(vm_clock); + elapsed = s->wt_last_update - qemu_get_clock_ns(vm_clock); val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; val -= elapsed / 5000000; s->regs[SONIC_WT1] = (val >> 16) & 0xffff; @@ -895,10 +895,10 @@ s->memory_rw = memory_rw; s->it_shift = it_shift; s->irq = irq; - s->watchdog = qemu_new_timer(vm_clock, dp8393x_watchdog, s); + s->watchdog = qemu_new_timer_ns(vm_clock, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(s->conf.macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff -Nru qemu-kvm-0.14.1+noroms/hw/ds1225y.c qemu-kvm-0.15.0+noroms/hw/ds1225y.c --- qemu-kvm-0.14.1+noroms/hw/ds1225y.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ds1225y.c 2011-08-09 07:40:29.000000000 -0500 @@ -22,31 +22,24 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" -#include "nvram.h" +#include "sysbus.h" +#include "trace.h" -//#define DEBUG_NVRAM - -typedef struct ds1225y_t -{ +typedef struct { + DeviceState qdev; uint32_t chip_size; + char *filename; QEMUFile *file; uint8_t *contents; - uint8_t protection; -} ds1225y_t; - +} NvRamState; static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) { - ds1225y_t *s = opaque; + NvRamState *s = opaque; uint32_t val; val = s->contents[addr]; - -#ifdef DEBUG_NVRAM - printf("nvram: read 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif + trace_nvram_read(addr, val); return val; } @@ -70,13 +63,12 @@ static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) { - ds1225y_t *s = opaque; + NvRamState *s = opaque; -#ifdef DEBUG_NVRAM - printf("nvram: write 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif + val &= 0xff; + trace_nvram_write(addr, s->contents[addr], val); - s->contents[addr] = val & 0xff; + s->contents[addr] = val; if (s->file) { qemu_fseek(s->file, addr, SEEK_SET); qemu_put_byte(s->file, (int)val); @@ -98,34 +90,6 @@ nvram_writeb(opaque, addr + 3, (val >> 24) & 0xff); } -static void nvram_writeb_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - ds1225y_t *s = opaque; - - if (s->protection != 7) { -#ifdef DEBUG_NVRAM - printf("nvram: prevent write of 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif - return; - } - - nvram_writeb(opaque, addr, val); -} - -static void nvram_writew_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - nvram_writeb_protected(opaque, addr, val & 0xff); - nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff); -} - -static void nvram_writel_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - nvram_writeb_protected(opaque, addr, val & 0xff); - nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff); - nvram_writeb_protected(opaque, addr + 2, (val >> 16) & 0xff); - nvram_writeb_protected(opaque, addr + 3, (val >> 24) & 0xff); -} - static CPUReadMemoryFunc * const nvram_read[] = { &nvram_readb, &nvram_readw, @@ -138,45 +102,83 @@ &nvram_writel, }; -static CPUWriteMemoryFunc * const nvram_write_protected[] = { - &nvram_writeb_protected, - &nvram_writew_protected, - &nvram_writel_protected, +static int nvram_post_load(void *opaque, int version_id) +{ + NvRamState *s = opaque; + + /* Close file, as filename may has changed in load/store process */ + if (s->file) { + qemu_fclose(s->file); + } + + /* Write back nvram contents */ + s->file = qemu_fopen(s->filename, "wb"); + if (s->file) { + /* Write back contents, as 'wb' mode cleaned the file */ + qemu_put_buffer(s->file, s->contents, s->chip_size); + qemu_fflush(s->file); + } + + return 0; +} + +static const VMStateDescription vmstate_nvram = { + .name = "nvram", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = nvram_post_load, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0, + vmstate_info_uint8, uint8_t), + VMSTATE_END_OF_LIST() + } }; -/* Initialisation routine */ -void *ds1225y_init(target_phys_addr_t mem_base, const char *filename) +typedef struct { + SysBusDevice busdev; + NvRamState nvram; +} SysBusNvRamState; + +static int nvram_sysbus_initfn(SysBusDevice *dev) { - ds1225y_t *s; - int mem_indexRW, mem_indexRP; + NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram; QEMUFile *file; + int s_io; - s = qemu_mallocz(sizeof(ds1225y_t)); - s->chip_size = 0x2000; /* Fixed for ds1225y chip: 8 KiB */ s->contents = qemu_mallocz(s->chip_size); - s->protection = 7; + + s_io = cpu_register_io_memory(nvram_read, nvram_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, s->chip_size, s_io); /* Read current file */ - file = qemu_fopen(filename, "rb"); + file = qemu_fopen(s->filename, "rb"); if (file) { /* Read nvram contents */ qemu_get_buffer(file, s->contents, s->chip_size); qemu_fclose(file); } - s->file = qemu_fopen(filename, "wb"); - if (s->file) { - /* Write back contents, as 'wb' mode cleaned the file */ - qemu_put_buffer(s->file, s->contents, s->chip_size); - qemu_fflush(s->file); - } + nvram_post_load(s, 0); + + return 0; +} + +static SysBusDeviceInfo nvram_sysbus_info = { + .qdev.name = "ds1225y", + .qdev.size = sizeof(SysBusNvRamState), + .qdev.vmsd = &vmstate_nvram, + .init = nvram_sysbus_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000), + DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), + DEFINE_PROP_END_OF_LIST(), + }, +}; - /* Read/write memory */ - 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, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(mem_base + s->chip_size, s->chip_size, mem_indexRP); - return s; +static void nvram_register(void) +{ + sysbus_register_withprop(&nvram_sysbus_info); } + +device_init(nvram_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/ds1338.c qemu-kvm-0.15.0+noroms/hw/ds1338.c --- qemu-kvm-0.14.1+noroms/hw/ds1338.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ds1338.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "i2c.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/dummy_m68k.c qemu-kvm-0.15.0+noroms/hw/dummy_m68k.c --- qemu-kvm-0.14.1+noroms/hw/dummy_m68k.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/dummy_m68k.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,11 +3,10 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" -#include "sysemu.h" #include "boards.h" #include "loader.h" #include "elf.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/e1000.c qemu-kvm-0.15.0+noroms/hw/e1000.c --- qemu-kvm-0.14.1+noroms/hw/e1000.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/e1000.c 2011-08-09 07:40:29.000000000 -0500 @@ -446,7 +446,9 @@ return; } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) { // data descriptor - tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8; + if (tp->size == 0) { + tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8; + } tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0; } else { // legacy descriptor @@ -515,6 +517,14 @@ return E1000_ICR_TXDW; } +static uint64_t tx_desc_base(E1000State *s) +{ + uint64_t bah = s->mac_reg[TDBAH]; + uint64_t bal = s->mac_reg[TDBAL] & ~0xf; + + return (bah << 32) + bal; +} + static void start_xmit(E1000State *s) { @@ -528,7 +538,7 @@ } while (s->mac_reg[TDH] != s->mac_reg[TDT]) { - base = ((uint64_t)s->mac_reg[TDBAH] << 32) + s->mac_reg[TDBAL] + + base = tx_desc_base(s) + sizeof(struct e1000_tx_desc) * s->mac_reg[TDH]; cpu_physical_memory_read(base, (void *)&desc, sizeof(desc)); @@ -623,12 +633,38 @@ set_ics(s, 0, E1000_ICR_LSC); } +static bool e1000_has_rxbufs(E1000State *s, size_t total_size) +{ + int bufs; + /* Fast-path short packets */ + if (total_size <= s->rxbuf_size) { + return s->mac_reg[RDH] != s->mac_reg[RDT] || !s->check_rxov; + } + if (s->mac_reg[RDH] < s->mac_reg[RDT]) { + bufs = s->mac_reg[RDT] - s->mac_reg[RDH]; + } else if (s->mac_reg[RDH] > s->mac_reg[RDT] || !s->check_rxov) { + bufs = s->mac_reg[RDLEN] / sizeof(struct e1000_rx_desc) + + s->mac_reg[RDT] - s->mac_reg[RDH]; + } else { + return false; + } + return total_size <= bufs * s->rxbuf_size; +} + static int e1000_can_receive(VLANClientState *nc) { E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - return (s->mac_reg[RCTL] & E1000_RCTL_EN); + return (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1); +} + +static uint64_t rx_desc_base(E1000State *s) +{ + uint64_t bah = s->mac_reg[RDBAH]; + uint64_t bal = s->mac_reg[RDBAL] & ~0xf; + + return (bah << 32) + bal; } static ssize_t @@ -642,6 +678,9 @@ uint16_t vlan_special = 0; uint8_t vlan_status = 0, vlan_offset = 0; uint8_t min_buf[MIN_BUF_SIZE]; + size_t desc_offset; + size_t desc_size; + size_t total_size; if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) return -1; @@ -654,12 +693,6 @@ 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); - return -1; - } - if (!receive_filter(s, buf, size)) return size; @@ -672,21 +705,40 @@ } rdh_start = s->mac_reg[RDH]; - do { - if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) { + desc_offset = 0; + total_size = size + fcs_len(s); + if (!e1000_has_rxbufs(s, total_size)) { set_ics(s, 0, E1000_ICS_RXO); return -1; + } + do { + desc_size = total_size - desc_offset; + if (desc_size > s->rxbuf_size) { + desc_size = s->rxbuf_size; } - base = ((uint64_t)s->mac_reg[RDBAH] << 32) + s->mac_reg[RDBAL] + - sizeof(desc) * s->mac_reg[RDH]; + base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH]; cpu_physical_memory_read(base, (void *)&desc, sizeof(desc)); desc.special = vlan_special; desc.status |= (vlan_status | E1000_RXD_STAT_DD); 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 + fcs_len(s)); - desc.status |= E1000_RXD_STAT_EOP|E1000_RXD_STAT_IXSM; + if (desc_offset < size) { + size_t copy_size = size - desc_offset; + if (copy_size > s->rxbuf_size) { + copy_size = s->rxbuf_size; + } + cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr), + (void *)(buf + desc_offset + vlan_offset), + copy_size); + } + desc_offset += desc_size; + desc.length = cpu_to_le16(desc_size); + if (desc_offset >= total_size) { + desc.status |= E1000_RXD_STAT_EOP | E1000_RXD_STAT_IXSM; + } else { + /* Guest zeroing out status is not a hardware requirement. + Clear EOP in case guest didn't do it. */ + desc.status &= ~E1000_RXD_STAT_EOP; + } } else { // as per intel docs; skip descriptors with null buf addr DBGOUT(RX, "Null RX descriptor!!\n"); } @@ -702,7 +754,7 @@ set_ics(s, 0, E1000_ICS_RXO); return -1; } - } while (desc.buffer_addr == 0); + } while (desc_offset < total_size); s->mac_reg[GPRC]++; s->mac_reg[TPR]++; @@ -1112,12 +1164,8 @@ pci_conf = d->dev.config; - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); - pci_config_set_device_id(pci_conf, E1000_DEVID); /* 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); /* TODO: RST# value should be 0, PCI spec 6.2.4 */ pci_conf[PCI_CACHE_LINE_SIZE] = 0x10; @@ -1168,7 +1216,11 @@ .qdev.vmsd = &vmstate_e1000, .init = pci_e1000_init, .exit = pci_e1000_uninit, - .romfile = "pxe-e1000.bin", + .romfile = "pxe-e1000.rom", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = E1000_DEVID, + .revision = 0x03, + .class_id = PCI_CLASS_NETWORK_ETHERNET, .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(E1000State, conf), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/eepro100.c qemu-kvm-0.15.0+noroms/hw/eepro100.c --- qemu-kvm-0.14.1+noroms/hw/eepro100.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/eepro100.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,7 +1,7 @@ /* * QEMU i8255x (PRO100) emulation * - * Copyright (C) 2006-2010 Stefan Weil + * Copyright (C) 2006-2011 Stefan Weil * * Portions of the code are copies from grub / etherboot eepro100.c * and linux e100.c. @@ -20,11 +20,10 @@ * along with this program. If not, see . * * Tested features (i82559): - * PXE boot (i386) ok + * PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok * Linux networking (i386) ok * * Untested: - * non-i386 platforms * Windows networking * * References: @@ -48,6 +47,15 @@ #include "eeprom93xx.h" #include "sysemu.h" +/* QEMU sends frames smaller than 60 bytes to ethernet nics. + * Such frames are rejected by real nics and their emulations. + * To avoid this behaviour, other nic emulations pad received + * frames. The following definition enables this padding for + * eepro100, too. We keep the define around in case it might + * become useful the future if the core networking is ever + * changed to pad short packets itself. */ +#define CONFIG_PAD_RECEIVED_FRAMES + #define KiB 1024 /* Debug EEPRO100 card. */ @@ -121,8 +129,6 @@ 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; @@ -130,7 +136,7 @@ /* Offsets to the various registers. All accesses need not be longword aligned. */ -enum speedo_offsets { +typedef enum { SCBStatus = 0, /* Status Word. */ SCBAck = 1, SCBCmd = 2, /* Rx/Command Unit command and status. */ @@ -145,7 +151,7 @@ SCBpmdr = 27, /* Power Management Driver. */ SCBgctrl = 28, /* General Control. */ SCBgstat = 29, /* General Status. */ -}; +} E100RegisterOffset; /* A speedo3 transmit buffer descriptor with two buffers... */ typedef struct { @@ -173,7 +179,7 @@ uint32_t rx_buf_addr; /* void * */ uint16_t count; uint16_t size; - char packet[MAX_ETH_FRAME_SIZE + 4]; + /* Ethernet frame data follows. */ } eepro100_rx_t; typedef enum { @@ -228,11 +234,10 @@ uint8_t scb_stat; /* SCB stat/ack byte */ uint8_t int_stat; /* PCI interrupt status */ /* region must not be saved by nic_save. */ - uint32_t region[3]; /* PCI region addresses */ + uint32_t region1; /* PCI region 1 address */ uint16_t mdimem[32]; eeprom_t *eeprom; uint32_t device; /* device variant */ - uint32_t pointer; /* (cu_base + cu_offset) address the next command block in the command block list. */ uint32_t cu_base; /* CU base address */ uint32_t cu_offset; /* CU address offset */ @@ -249,11 +254,13 @@ /* Statistical counters. Also used for wake-up packet (i82559). */ eepro100_stats_t statistics; + /* Data in mem is always in the byte order of the controller (le). + * It must be dword aligned to allow direct access to 32 bit values. */ + uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));; + /* Configuration bytes. */ uint8_t configuration[22]; - /* Data in mem is always in the byte order of the controller (le). */ - uint8_t mem[PCI_MEM_SIZE]; /* vmstate for each particular nic */ VMStateDescription *vmstate; @@ -307,11 +314,36 @@ 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; -/* XXX: optimize */ -static void stl_le_phys(target_phys_addr_t addr, uint32_t val) +/* Read a 16 bit little endian value from physical memory. */ +static uint16_t e100_ldw_le_phys(target_phys_addr_t addr) +{ + /* Load 16 bit (little endian) word from emulated hardware. */ + uint16_t val; + cpu_physical_memory_read(addr, &val, sizeof(val)); + return le16_to_cpu(val); +} + +/* Read a 32 bit little endian value from physical memory. */ +static uint32_t e100_ldl_le_phys(target_phys_addr_t addr) +{ + /* Load 32 bit (little endian) word from emulated hardware. */ + uint32_t val; + cpu_physical_memory_read(addr, &val, sizeof(val)); + return le32_to_cpu(val); +} + +/* Write a 16 bit little endian value to physical memory. */ +static void e100_stw_le_phys(target_phys_addr_t addr, uint16_t val) +{ + val = cpu_to_le16(val); + cpu_physical_memory_write(addr, &val, sizeof(val)); +} + +/* Write a 32 bit little endian value to physical memory. */ +static void e100_stl_le_phys(target_phys_addr_t addr, uint32_t val) { val = cpu_to_le32(val); - cpu_physical_memory_write(addr, (const uint8_t *)&val, sizeof(val)); + cpu_physical_memory_write(addr, &val, sizeof(val)); } #define POLYNOMIAL 0x04c11db6 @@ -339,6 +371,36 @@ return (crc & BITS(7, 2)) >> 2; } +/* Read a 16 bit control/status (CSR) register. */ +static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr) +{ + assert(!((uintptr_t)&s->mem[addr] & 1)); + return le16_to_cpup((uint16_t *)&s->mem[addr]); +} + +/* Read a 32 bit control/status (CSR) register. */ +static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr) +{ + assert(!((uintptr_t)&s->mem[addr] & 3)); + return le32_to_cpup((uint32_t *)&s->mem[addr]); +} + +/* Write a 16 bit control/status (CSR) register. */ +static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr, + uint16_t val) +{ + assert(!((uintptr_t)&s->mem[addr] & 1)); + cpu_to_le16w((uint16_t *)&s->mem[addr], val); +} + +/* Read a 32 bit control/status (CSR) register. */ +static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr, + uint32_t val) +{ + assert(!((uintptr_t)&s->mem[addr] & 3)); + cpu_to_le32w((uint32_t *)&s->mem[addr], val); +} + #if defined(DEBUG_EEPRO100) static const char *nic_dump(const uint8_t * buf, unsigned size) { @@ -462,16 +524,9 @@ TRACE(OTHER, logout("%p\n", s)); - /* PCI Vendor ID */ - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); - /* PCI Device ID */ - pci_config_set_device_id(pci_conf, e100_device->device_id); /* PCI Status */ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_FAST_BACK); - /* PCI Revision ID */ - pci_config_set_revision(pci_conf, e100_device->revision); - pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); /* PCI Latency Timer */ pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */ /* Capability Pointer is set by PCI framework. */ @@ -499,12 +554,7 @@ case i82559ER: case i82562: case i82801: - break; case i82559C: -#if EEPROM_SIZE > 0 - pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_INTEL); - pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0040); -#endif break; default: logout("Device %X is undefined!\n", device); @@ -590,8 +640,7 @@ TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1])); memset(s->mem, 0, sizeof(s->mem)); - uint32_t val = BIT(21); - memcpy(&s->mem[SCBCtrlMDI], &val, sizeof(val)); + e100_write_reg4(s, SCBCtrlMDI, BIT(21)); assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default)); memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem)); @@ -694,22 +743,21 @@ * values which really matter. * Number of data should check configuration!!! */ - cpu_physical_memory_write(s->statsaddr, - (uint8_t *) & s->statistics, s->stats_size); - stl_le_phys(s->statsaddr + 0, s->statistics.tx_good_frames); - 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); + cpu_physical_memory_write(s->statsaddr, &s->statistics, s->stats_size); + e100_stl_le_phys(s->statsaddr + 0, s->statistics.tx_good_frames); + e100_stl_le_phys(s->statsaddr + 36, s->statistics.rx_good_frames); + e100_stl_le_phys(s->statsaddr + 48, s->statistics.rx_resource_errors); + e100_stl_le_phys(s->statsaddr + 60, s->statistics.rx_short_frame_errors); #if 0 - stw_le_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames); - stw_le_phys(s->statsaddr + 78, s->statistics.rcv_tco_frames); + e100_stw_le_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames); + e100_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)); + cpu_physical_memory_read(s->cb_address, &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); @@ -739,10 +787,10 @@ } 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); + uint32_t tx_buffer_address = e100_ldl_le_phys(tbd_address); + uint16_t tx_buffer_size = e100_ldw_le_phys(tbd_address + 4); #if 0 - uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); + uint16_t tx_buffer_el = e100_ldw_le_phys(tbd_address + 6); #endif tbd_address += 8; TRACE(RXTX, logout @@ -761,9 +809,9 @@ 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); + uint32_t tx_buffer_address = e100_ldl_le_phys(tbd_address); + uint16_t tx_buffer_size = e100_ldw_le_phys(tbd_address + 4); + uint16_t tx_buffer_el = e100_ldw_le_phys(tbd_address + 6); tbd_address += 8; TRACE(RXTX, logout ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", @@ -779,9 +827,9 @@ } 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); + uint32_t tx_buffer_address = e100_ldl_le_phys(tbd_address); + uint16_t tx_buffer_size = e100_ldw_le_phys(tbd_address + 4); + uint16_t tx_buffer_el = e100_ldw_le_phys(tbd_address + 6); tbd_address += 8; TRACE(RXTX, logout ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", @@ -889,7 +937,7 @@ break; } /* Write new status. */ - stw_phys(s->cb_address, s->tx.status | ok_status | STATUS_C); + e100_stw_le_phys(s->cb_address, s->tx.status | ok_status | STATUS_C); if (bit_i) { /* CU completed action. */ eepro100_cx_interrupt(s); @@ -928,7 +976,7 @@ logout("unexpected CU state is %u\n", cu_state); } set_cu_state(s, cu_active); - s->cu_offset = s->pointer; + s->cu_offset = e100_read_reg4(s, SCBPointer); action_command(s); break; case CU_RESUME: @@ -949,25 +997,25 @@ break; case CU_STATSADDR: /* Load dump counters address. */ - s->statsaddr = s->pointer; + s->statsaddr = e100_read_reg4(s, SCBPointer); TRACE(OTHER, logout("val=0x%02x (status address)\n", val)); break; case CU_SHOWSTATS: /* Dump statistical counters. */ TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val)); dump_statistics(s); - stl_le_phys(s->statsaddr + s->stats_size, 0xa005); + e100_stl_le_phys(s->statsaddr + s->stats_size, 0xa005); break; case CU_CMD_BASE: /* Load CU base. */ TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val)); - s->cu_base = s->pointer; + s->cu_base = e100_read_reg4(s, SCBPointer); break; case CU_DUMPSTATS: /* Dump and reset statistical counters. */ TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val)); dump_statistics(s); - stl_le_phys(s->statsaddr + s->stats_size, 0xa007); + e100_stl_le_phys(s->statsaddr + s->stats_size, 0xa007); memset(&s->statistics, 0, sizeof(s->statistics)); break; case CU_SRESUME: @@ -994,7 +1042,7 @@ #endif } set_ru_state(s, ru_ready); - s->ru_offset = s->pointer; + s->ru_offset = e100_read_reg4(s, SCBPointer); TRACE(OTHER, logout("val=0x%02x (rx start)\n", val)); break; case RX_RESUME: @@ -1018,7 +1066,7 @@ case RX_ADDR_LOAD: /* Load RU base. */ TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val)); - s->ru_base = s->pointer; + s->ru_base = e100_read_reg4(s, SCBPointer); break; default: logout("val=0x%02x (undefined RU command)\n", val); @@ -1050,8 +1098,7 @@ static uint16_t eepro100_read_eeprom(EEPRO100State * s) { - uint16_t val; - memcpy(&val, &s->mem[SCBeeprom], sizeof(val)); + uint16_t val = e100_read_reg2(s, SCBeeprom); if (eeprom93xx_read(s->eeprom)) { val |= EEPROM_DO; } else { @@ -1065,7 +1112,7 @@ { TRACE(EEPROM, logout("val=0x%02x\n", val)); - /* mask unwriteable bits */ + /* mask unwritable bits */ #if 0 val = SET_MASKED(val, 0x31, eeprom->value); #endif @@ -1076,12 +1123,6 @@ eeprom93xx_write(eeprom, eecs, eesk, eedi); } -static void eepro100_write_pointer(EEPRO100State * s, uint32_t val) -{ - s->pointer = le32_to_cpu(val); - TRACE(OTHER, logout("val=0x%08x\n", val)); -} - /***************************************************************************** * * MDI emulation. @@ -1121,8 +1162,7 @@ static uint32_t eepro100_read_mdi(EEPRO100State * s) { - uint32_t val; - memcpy(&val, &s->mem[0x10], sizeof(val)); + uint32_t val = e100_read_reg4(s, SCBCtrlMDI); #ifdef DEBUG_EEPRO100 uint8_t raiseint = (val & BIT(29)) >> 29; @@ -1139,8 +1179,9 @@ return val; } -static void eepro100_write_mdi(EEPRO100State * s, uint32_t val) +static void eepro100_write_mdi(EEPRO100State *s) { + uint32_t val = e100_read_reg4(s, SCBCtrlMDI); uint8_t raiseint = (val & BIT(29)) >> 29; uint8_t opcode = (val & BITS(27, 26)) >> 26; uint8_t phy = (val & BITS(25, 21)) >> 21; @@ -1231,7 +1272,7 @@ } } val = (val & 0xffff0000) + data; - memcpy(&s->mem[0x10], &val, sizeof(val)); + e100_write_reg4(s, SCBCtrlMDI, val); } /***************************************************************************** @@ -1256,9 +1297,9 @@ return 0; } -static void eepro100_write_port(EEPRO100State * s, uint32_t val) +static void eepro100_write_port(EEPRO100State *s) { - val = le32_to_cpu(val); + uint32_t val = e100_read_reg4(s, SCBPort); uint32_t address = (val & ~PORT_SELECTION_MASK); uint8_t selection = (val & PORT_SELECTION_MASK); switch (selection) { @@ -1268,10 +1309,10 @@ case PORT_SELFTEST: TRACE(OTHER, logout("selftest address=0x%08x\n", address)); eepro100_selftest_t data; - cpu_physical_memory_read(address, (uint8_t *) & data, sizeof(data)); + cpu_physical_memory_read(address, &data, sizeof(data)); data.st_sign = 0xffffffff; data.st_result = 0; - cpu_physical_memory_write(address, (uint8_t *) & data, sizeof(data)); + cpu_physical_memory_write(address, &data, sizeof(data)); break; case PORT_SELECTIVE_RESET: TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address)); @@ -1293,7 +1334,7 @@ { uint8_t val = 0; if (addr <= sizeof(s->mem) - sizeof(val)) { - memcpy(&val, &s->mem[addr], sizeof(val)); + val = s->mem[addr]; } switch (addr) { @@ -1316,10 +1357,20 @@ case SCBeeprom: val = eepro100_read_eeprom(s); break; + case SCBCtrlMDI: + case SCBCtrlMDI + 1: + case SCBCtrlMDI + 2: + case SCBCtrlMDI + 3: + val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); + break; case SCBpmdr: /* Power Management Driver Register */ val = 0; TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); break; + case SCBgctrl: /* General Control Register */ + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); + break; case SCBgstat: /* General Status Register */ /* 100 Mbps full duplex, valid link */ val = 0x07; @@ -1336,7 +1387,7 @@ { uint16_t val = 0; if (addr <= sizeof(s->mem) - sizeof(val)) { - memcpy(&val, &s->mem[addr], sizeof(val)); + val = e100_read_reg2(s, addr); } switch (addr) { @@ -1348,6 +1399,11 @@ val = eepro100_read_eeprom(s); TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); break; + case SCBCtrlMDI: + case SCBCtrlMDI + 2: + val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); + break; default: logout("addr=%s val=0x%04x\n", regname(addr), val); missing("unknown word read"); @@ -1359,7 +1415,7 @@ { uint32_t val = 0; if (addr <= sizeof(s->mem) - sizeof(val)) { - memcpy(&val, &s->mem[addr], sizeof(val)); + val = e100_read_reg4(s, addr); } switch (addr) { @@ -1367,15 +1423,16 @@ TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); break; case SCBPointer: -#if 0 - val = eepro100_read_pointer(s); -#endif TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); break; case SCBPort: val = eepro100_read_port(s); TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); break; + case SCBflash: + val = eepro100_read_eeprom(s); + TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); + break; case SCBCtrlMDI: val = eepro100_read_mdi(s); break; @@ -1390,27 +1447,43 @@ { /* SCBStatus is readonly. */ if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { - memcpy(&s->mem[addr], &val, sizeof(val)); + s->mem[addr] = val; } - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - switch (addr) { case SCBStatus: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); break; case SCBAck: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); eepro100_acknowledge(s); break; case SCBCmd: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); eepro100_write_command(s, val); break; case SCBIntmask: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); if (val & BIT(1)) { eepro100_swi_interrupt(s); } eepro100_interrupt(s, 0); break; + case SCBPointer: + case SCBPointer + 1: + case SCBPointer + 2: + case SCBPointer + 3: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); + break; + case SCBPort: + case SCBPort + 1: + case SCBPort + 2: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); + break; case SCBPort + 3: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); + eepro100_write_port(s); + break; case SCBFlow: /* does not exist on 82557 */ case SCBFlow + 1: case SCBFlow + 2: @@ -1418,8 +1491,18 @@ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); break; case SCBeeprom: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); eepro100_write_eeprom(s->eeprom, val); break; + case SCBCtrlMDI: + case SCBCtrlMDI + 1: + case SCBCtrlMDI + 2: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); + break; + case SCBCtrlMDI + 3: + TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); + eepro100_write_mdi(s); + break; default: logout("addr=%s val=0x%02x\n", regname(addr), val); missing("unknown byte write"); @@ -1430,23 +1513,42 @@ { /* SCBStatus is readonly. */ if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { - memcpy(&s->mem[addr], &val, sizeof(val)); + e100_write_reg2(s, addr, val); } - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - switch (addr) { case SCBStatus: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); s->mem[SCBAck] = (val >> 8); eepro100_acknowledge(s); break; case SCBCmd: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); eepro100_write_command(s, val); eepro100_write1(s, SCBIntmask, val >> 8); break; + case SCBPointer: + case SCBPointer + 2: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); + break; + case SCBPort: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); + break; + case SCBPort + 2: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); + eepro100_write_port(s); + break; case SCBeeprom: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); eepro100_write_eeprom(s->eeprom, val); break; + case SCBCtrlMDI: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); + break; + case SCBCtrlMDI + 2: + TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); + eepro100_write_mdi(s); + break; default: logout("addr=%s val=0x%04x\n", regname(addr), val); missing("unknown word write"); @@ -1456,19 +1558,25 @@ static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val) { if (addr <= sizeof(s->mem) - sizeof(val)) { - memcpy(&s->mem[addr], &val, sizeof(val)); + e100_write_reg4(s, addr, val); } switch (addr) { case SCBPointer: - eepro100_write_pointer(s, val); + TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); break; case SCBPort: TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - eepro100_write_port(s, val); + eepro100_write_port(s); + break; + case SCBflash: + TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); + val = val >> 16; + eepro100_write_eeprom(s->eeprom, val); break; case SCBCtrlMDI: - eepro100_write_mdi(s, val); + TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); + eepro100_write_mdi(s); break; default: logout("addr=%s val=0x%08x\n", regname(addr), val); @@ -1488,19 +1596,19 @@ #if 0 logout("addr=%s\n", regname(addr)); #endif - return eepro100_read1(s, addr - s->region[1]); + return eepro100_read1(s, addr - s->region1); } static uint32_t ioport_read2(void *opaque, uint32_t addr) { EEPRO100State *s = opaque; - return eepro100_read2(s, addr - s->region[1]); + return eepro100_read2(s, addr - s->region1); } static uint32_t ioport_read4(void *opaque, uint32_t addr) { EEPRO100State *s = opaque; - return eepro100_read4(s, addr - s->region[1]); + return eepro100_read4(s, addr - s->region1); } static void ioport_write1(void *opaque, uint32_t addr, uint32_t val) @@ -1509,19 +1617,19 @@ #if 0 logout("addr=%s val=0x%02x\n", regname(addr), val); #endif - eepro100_write1(s, addr - s->region[1], val); + eepro100_write1(s, addr - s->region1, val); } static void ioport_write2(void *opaque, uint32_t addr, uint32_t val) { EEPRO100State *s = opaque; - eepro100_write2(s, addr - s->region[1], val); + eepro100_write2(s, addr - s->region1, val); } static void ioport_write4(void *opaque, uint32_t addr, uint32_t val) { EEPRO100State *s = opaque; - eepro100_write4(s, addr - s->region[1], val); + eepro100_write4(s, addr - s->region1, val); } /***********************************************************/ @@ -1544,7 +1652,7 @@ register_ioport_write(addr, size, 4, ioport_write4, s); register_ioport_read(addr, size, 4, ioport_read4, s); - s->region[region_num] = addr; + s->region1 = addr; } /***************************************************************************** @@ -1619,22 +1727,6 @@ pci_mmio_readl }; -static void pci_mmio_map(PCIDevice * pci_dev, int region_num, - pcibus_t addr, pcibus_t size, int type) -{ - EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); - - TRACE(OTHER, logout("region %d, addr=0x%08"FMT_PCIBUS", " - "size=0x%08"FMT_PCIBUS", type=%d\n", - region_num, addr, size, type)); - - 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) { EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; @@ -1653,19 +1745,32 @@ */ EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; uint16_t rfd_status = 0xa000; +#if defined(CONFIG_PAD_RECEIVED_FRAMES) + uint8_t min_buf[60]; +#endif static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +#if defined(CONFIG_PAD_RECEIVED_FRAMES) + /* 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); + } +#endif + if (s->configuration[8] & 0x80) { /* CSMA is disabled. */ logout("%p received while CSMA is disabled\n", s); return -1; +#if !defined(CONFIG_PAD_RECEIVED_FRAMES) } 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++; -#if 0 return -1; #endif } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) { @@ -1734,8 +1839,8 @@ } /* !!! */ eepro100_rx_t rx; - cpu_physical_memory_read(s->ru_base + s->ru_offset, (uint8_t *) & rx, - offsetof(eepro100_rx_t, packet)); + cpu_physical_memory_read(s->ru_base + s->ru_offset, &rx, + sizeof(eepro100_rx_t)); uint16_t rfd_command = le16_to_cpu(rx.command); uint16_t rfd_size = le16_to_cpu(rx.size); @@ -1744,14 +1849,17 @@ "(%zu bytes); data truncated\n", rfd_size, size); size = rfd_size; } +#if !defined(CONFIG_PAD_RECEIVED_FRAMES) if (size < 64) { rfd_status |= 0x0080; } +#endif TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n", rfd_command, rx.link, rx.rx_buf_addr, rfd_size)); - stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, status), - rfd_status); - stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, count), size); + e100_stw_le_phys(s->ru_base + s->ru_offset + + offsetof(eepro100_rx_t, status), rfd_status); + e100_stw_le_phys(s->ru_base + s->ru_offset + + offsetof(eepro100_rx_t, count), size); /* Early receive interrupt not supported. */ #if 0 eepro100_er_interrupt(s); @@ -1766,7 +1874,7 @@ assert(!(s->configuration[17] & BIT(0))); #endif cpu_physical_memory_write(s->ru_base + s->ru_offset + - offsetof(eepro100_rx_t, packet), buf, size); + sizeof(eepro100_rx_t), buf, size); s->statistics.rx_good_frames++; eepro100_fr_interrupt(s); s->ru_offset = le32_to_cpu(rx.link); @@ -1801,7 +1909,6 @@ /* The eeprom should be saved and restored by its own routines. */ VMSTATE_UINT32(device, EEPRO100State), /* TODO check device. */ - VMSTATE_UINT32(pointer, EEPRO100State), VMSTATE_UINT32(cu_base, EEPRO100State), VMSTATE_UINT32(cu_offset, EEPRO100State), VMSTATE_UINT32(ru_base, EEPRO100State), @@ -1880,19 +1987,18 @@ /* Handler for memory-mapped I/O */ s->mmio_index = cpu_register_io_memory(pci_mmio_read, pci_mmio_write, s, - DEVICE_NATIVE_ENDIAN); + DEVICE_LITTLE_ENDIAN); + + pci_register_bar_simple(&s->dev, 0, PCI_MEM_SIZE, + PCI_BASE_ADDRESS_MEM_PREFETCH, s->mmio_index); - pci_register_bar(&s->dev, 0, PCI_MEM_SIZE, - PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH, pci_mmio_map); pci_register_bar(&s->dev, 1, PCI_IO_SIZE, PCI_BASE_ADDRESS_SPACE_IO, pci_map); - pci_register_bar(&s->dev, 2, PCI_FLASH_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY, - pci_mmio_map); + pci_register_bar_simple(&s->dev, 2, PCI_FLASH_SIZE, 0, s->mmio_index); qemu_macaddr_default_if_unset(&s->conf.macaddr); logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)); - assert(s->region[1] == 0); + assert(s->region1 == 0); nic_reset(s); @@ -1920,9 +2026,9 @@ .pci.qdev.desc = "Intel i82550 Ethernet", .device = i82550, /* TODO: check device id. */ - .device_id = PCI_DEVICE_ID_INTEL_82551IT, + .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT, /* Revision ID: 0x0c, 0x0d, 0x0e. */ - .revision = 0x0e, + .pci.revision = 0x0e, /* TODO: check size of statistical counters. */ .stats_size = 80, /* TODO: check extended tcb support. */ @@ -1932,9 +2038,9 @@ .pci.qdev.name = "i82551", .pci.qdev.desc = "Intel i82551 Ethernet", .device = i82551, - .device_id = PCI_DEVICE_ID_INTEL_82551IT, + .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT, /* Revision ID: 0x0f, 0x10. */ - .revision = 0x0f, + .pci.revision = 0x0f, /* TODO: check size of statistical counters. */ .stats_size = 80, .has_extended_tcb_support = true, @@ -1943,29 +2049,29 @@ .pci.qdev.name = "i82557a", .pci.qdev.desc = "Intel i82557A Ethernet", .device = i82557A, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x01, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.revision = 0x01, .power_management = false, },{ .pci.qdev.name = "i82557b", .pci.qdev.desc = "Intel i82557B Ethernet", .device = i82557B, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x02, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.revision = 0x02, .power_management = false, },{ .pci.qdev.name = "i82557c", .pci.qdev.desc = "Intel i82557C Ethernet", .device = i82557C, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x03, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.revision = 0x03, .power_management = false, },{ .pci.qdev.name = "i82558a", .pci.qdev.desc = "Intel i82558A Ethernet", .device = i82558A, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x04, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.revision = 0x04, .stats_size = 76, .has_extended_tcb_support = true, .power_management = true, @@ -1973,8 +2079,8 @@ .pci.qdev.name = "i82558b", .pci.qdev.desc = "Intel i82558B Ethernet", .device = i82558B, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x05, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.revision = 0x05, .stats_size = 76, .has_extended_tcb_support = true, .power_management = true, @@ -1982,8 +2088,8 @@ .pci.qdev.name = "i82559a", .pci.qdev.desc = "Intel i82559A Ethernet", .device = i82559A, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x06, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.revision = 0x06, .stats_size = 80, .has_extended_tcb_support = true, .power_management = true, @@ -1991,8 +2097,8 @@ .pci.qdev.name = "i82559b", .pci.qdev.desc = "Intel i82559B Ethernet", .device = i82559B, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x07, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.revision = 0x07, .stats_size = 80, .has_extended_tcb_support = true, .power_management = true, @@ -2000,12 +2106,16 @@ .pci.qdev.name = "i82559c", .pci.qdev.desc = "Intel i82559C Ethernet", .device = i82559C, - .device_id = PCI_DEVICE_ID_INTEL_82557, + .pci.device_id = PCI_DEVICE_ID_INTEL_82557, #if 0 - .revision = 0x08, + .pci.revision = 0x08, #endif /* TODO: Windows wants revision id 0x0c. */ - .revision = 0x0c, + .pci.revision = 0x0c, +#if EEPROM_SIZE > 0 + .pci.subsystem_vendor_id = PCI_VENDOR_ID_INTEL, + .pci.subsystem_id = 0x0040, +#endif .stats_size = 80, .has_extended_tcb_support = true, .power_management = true, @@ -2013,8 +2123,8 @@ .pci.qdev.name = "i82559er", .pci.qdev.desc = "Intel i82559ER Ethernet", .device = i82559ER, - .device_id = PCI_DEVICE_ID_INTEL_82551IT, - .revision = 0x09, + .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT, + .pci.revision = 0x09, .stats_size = 80, .has_extended_tcb_support = true, .power_management = true, @@ -2023,9 +2133,9 @@ .pci.qdev.desc = "Intel i82562 Ethernet", .device = i82562, /* TODO: check device id. */ - .device_id = PCI_DEVICE_ID_INTEL_82551IT, + .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT, /* TODO: wrong revision id. */ - .revision = 0x0e, + .pci.revision = 0x0e, .stats_size = 80, .has_extended_tcb_support = true, .power_management = true, @@ -2034,8 +2144,8 @@ .pci.qdev.name = "i82801", .pci.qdev.desc = "Intel i82801 Ethernet", .device = i82801, - .device_id = 0x2449, - .revision = 0x03, + .pci.device_id = 0x2449, + .pci.revision = 0x03, .stats_size = 80, .has_extended_tcb_support = true, .power_management = true, @@ -2054,7 +2164,9 @@ 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->vendor_id = PCI_VENDOR_ID_INTEL; + pci_dev->class_id = PCI_CLASS_NETWORK_ETHERNET; + pci_dev->romfile = "pxe-eepro100.rom"; pci_dev->init = e100_nic_init; pci_dev->exit = pci_nic_uninit; pci_dev->qdev.props = e100_properties; diff -Nru qemu-kvm-0.14.1+noroms/hw/eeprom93xx.c qemu-kvm-0.15.0+noroms/hw/eeprom93xx.c --- qemu-kvm-0.14.1+noroms/hw/eeprom93xx.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/eeprom93xx.c 2011-08-09 07:40:29.000000000 -0500 @@ -75,7 +75,7 @@ uint8_t tick; uint8_t address; uint8_t command; - uint8_t writeable; + uint8_t writable; uint8_t eecs; uint8_t eesk; @@ -130,7 +130,7 @@ VMSTATE_UINT8(tick, eeprom_t), VMSTATE_UINT8(address, eeprom_t), VMSTATE_UINT8(command, eeprom_t), - VMSTATE_UINT8(writeable, eeprom_t), + VMSTATE_UINT8(writable, eeprom_t), VMSTATE_UINT8(eecs, eeprom_t), VMSTATE_UINT8(eesk, eeprom_t), @@ -165,7 +165,7 @@ address = 0x0; } else if (eeprom->eecs && ! eecs) { /* End chip select cycle. This triggers write / erase. */ - if (eeprom->writeable) { + if (eeprom->writable) { uint8_t subcommand = address >> (eeprom->addrbits - 2); if (command == 0 && subcommand == 2) { /* Erase all. */ @@ -232,7 +232,7 @@ switch (address >> (eeprom->addrbits - 2)) { case 0: logout("write disable command\n"); - eeprom->writeable = 0; + eeprom->writable = 0; break; case 1: logout("write all command\n"); @@ -242,7 +242,7 @@ break; case 3: logout("write enable command\n"); - eeprom->writeable = 1; + eeprom->writable = 1; break; } } else { diff -Nru qemu-kvm-0.14.1+noroms/hw/empty_slot.c qemu-kvm-0.15.0+noroms/hw/empty_slot.c --- qemu-kvm-0.14.1+noroms/hw/empty_slot.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/empty_slot.c 2011-08-09 07:40:29.000000000 -0500 @@ -53,18 +53,21 @@ void empty_slot_init(target_phys_addr_t addr, uint64_t slot_size) { - DeviceState *dev; - SysBusDevice *s; - EmptySlot *e; + if (slot_size > 0) { + /* Only empty slots larger than 0 byte need handling. */ + 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; + dev = qdev_create(NULL, "empty_slot"); + s = sysbus_from_qdev(dev); + e = FROM_SYSBUS(EmptySlot, s); + e->size = slot_size; - qdev_init_nofail(dev); + qdev_init_nofail(dev); - sysbus_mmio_map(s, 0, addr); + sysbus_mmio_map(s, 0, addr); + } } static int empty_slot_init1(SysBusDevice *dev) diff -Nru qemu-kvm-0.14.1+noroms/hw/es1370.c qemu-kvm-0.15.0+noroms/hw/es1370.c --- qemu-kvm-0.14.1+noroms/hw/es1370.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/es1370.c 2011-08-09 07:40:29.000000000 -0500 @@ -998,21 +998,9 @@ ES1370State *s = DO_UPCAST (ES1370State, dev, dev); uint8_t *c = s->dev.config; - pci_config_set_vendor_id (c, PCI_VENDOR_ID_ENSONIQ); - pci_config_set_device_id (c, PCI_DEVICE_ID_ENSONIQ_ES1370); c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8; - pci_config_set_class (c, PCI_CLASS_MULTIMEDIA_AUDIO); -#if 1 - 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[PCI_SUBSYSTEM_VENDOR_ID] = 0x74; - c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x12; - c[PCI_SUBSYSTEM_ID] = 0x71; - c[PCI_SUBSYSTEM_ID + 1] = 0x13; +#if 0 c[PCI_CAPABILITY_LIST] = 0xdc; c[PCI_INTERRUPT_LINE] = 10; c[0xdc] = 0x00; @@ -1043,6 +1031,16 @@ .qdev.size = sizeof (ES1370State), .qdev.vmsd = &vmstate_es1370, .init = es1370_initfn, + .vendor_id = PCI_VENDOR_ID_ENSONIQ, + .device_id = PCI_DEVICE_ID_ENSONIQ_ES1370, + .class_id = PCI_CLASS_MULTIMEDIA_AUDIO, +#if 1 + .subsystem_vendor_id = 0x4942, + .subsystem_id = 0x4c4c, +#else + .subsystem_vendor_id = 0x1274, + .subsystem_id = 0x1371, +#endif }; static void es1370_register (void) diff -Nru qemu-kvm-0.14.1+noroms/hw/esp.c qemu-kvm-0.15.0+noroms/hw/esp.c --- qemu-kvm-0.14.1+noroms/hw/esp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/esp.c 2011-08-09 07:40:29.000000000 -0500 @@ -61,10 +61,11 @@ int32_t ti_size; uint32_t ti_rptr, ti_wptr; uint8_t ti_buf[TI_BUFSZ]; - uint32_t sense; + uint32_t status; uint32_t dma; SCSIBus bus; SCSIDevice *current_dev; + SCSIRequest *current_req; uint8_t cmdbuf[TI_BUFSZ]; uint32_t cmdlen; uint32_t do_cmd; @@ -187,6 +188,17 @@ } } +static void esp_request_cancelled(SCSIRequest *req) +{ + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); + + if (req == s->current_req) { + scsi_req_unref(s->current_req); + s->current_req = NULL; + s->current_dev = NULL; + } +} + static uint32_t get_cmd(ESPState *s, uint8_t *buf) { uint32_t dmalen; @@ -199,7 +211,7 @@ } else { dmalen = s->ti_size; memcpy(buf, s->ti_buf, dmalen); - buf[0] = 0; + buf[0] = buf[2] >> 5; } DPRINTF("get_cmd: len %d target %d\n", dmalen, target); @@ -207,9 +219,9 @@ s->ti_rptr = 0; s->ti_wptr = 0; - if (s->current_dev) { + if (s->current_req) { /* Started a new command before the old one finished. Cancel it. */ - s->current_dev->info->cancel_io(s->current_dev, 0); + scsi_req_cancel(s->current_req); s->async_len = 0; } @@ -232,7 +244,8 @@ DPRINTF("do_busid_cmd: busid 0x%x\n", busid); lun = busid & 7; - datalen = s->current_dev->info->send_command(s->current_dev, 0, buf, lun); + s->current_req = scsi_req_new(s->current_dev, 0, lun, NULL); + datalen = scsi_req_enqueue(s->current_req, buf); s->ti_size = datalen; if (datalen != 0) { s->rregs[ESP_RSTAT] = STAT_TC; @@ -240,11 +253,10 @@ s->dma_counter = 0; if (datalen > 0) { s->rregs[ESP_RSTAT] |= STAT_DI; - s->current_dev->info->read_data(s->current_dev, 0); } else { s->rregs[ESP_RSTAT] |= STAT_DO; - s->current_dev->info->write_data(s->current_dev, 0); } + scsi_req_continue(s->current_req); } s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; s->rregs[ESP_RSEQ] = SEQ_CD; @@ -306,8 +318,8 @@ static void write_response(ESPState *s) { - DPRINTF("Transfer status (sense=%d)\n", s->sense); - s->ti_buf[0] = s->sense; + DPRINTF("Transfer status (status=%d)\n", s->status); + s->ti_buf[0] = s->status; s->ti_buf[1] = 0; if (s->dma) { s->dma_memory_write(s->dma_opaque, s->ti_buf, 2); @@ -370,53 +382,56 @@ else s->ti_size -= len; if (s->async_len == 0) { - if (to_device) { - // ti_size is negative - s->current_dev->info->write_data(s->current_dev, 0); - } else { - s->current_dev->info->read_data(s->current_dev, 0); - /* If there is still data to be read from the device then - complete the DMA operation immediately. Otherwise defer - until the scsi layer has completed. */ - if (s->dma_left == 0 && s->ti_size > 0) { - esp_dma_done(s); - } + scsi_req_continue(s->current_req); + /* If there is still data to be read from the device then + complete the DMA operation immediately. Otherwise defer + until the scsi layer has completed. */ + if (to_device || s->dma_left != 0 || s->ti_size == 0) { + return; } - } else { - /* Partially filled a scsi buffer. Complete immediately. */ - esp_dma_done(s); } + + /* Partially filled a scsi buffer. Complete immediately. */ + esp_dma_done(s); } -static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void esp_command_complete(SCSIRequest *req, uint32_t status) { - ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent); + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); - if (reason == SCSI_REASON_DONE) { - DPRINTF("SCSI Command complete\n"); - if (s->ti_size != 0) - DPRINTF("SCSI command completed unexpectedly\n"); - s->ti_size = 0; - s->dma_left = 0; - s->async_len = 0; - if (arg) - DPRINTF("Command failed\n"); - s->sense = arg; - s->rregs[ESP_RSTAT] = STAT_ST; - esp_dma_done(s); + DPRINTF("SCSI Command complete\n"); + if (s->ti_size != 0) { + DPRINTF("SCSI command completed unexpectedly\n"); + } + s->ti_size = 0; + s->dma_left = 0; + s->async_len = 0; + if (status) { + DPRINTF("Command failed\n"); + } + s->status = status; + s->rregs[ESP_RSTAT] = STAT_ST; + esp_dma_done(s); + if (s->current_req) { + scsi_req_unref(s->current_req); + s->current_req = NULL; s->current_dev = NULL; - } else { - DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); - s->async_len = arg; - s->async_buf = s->current_dev->info->get_buf(s->current_dev, 0); - if (s->dma_left) { - esp_do_dma(s); - } else if (s->dma_counter != 0 && s->ti_size <= 0) { - /* If this was the last part of a DMA transfer then the - completion interrupt is deferred to here. */ - esp_dma_done(s); - } + } +} + +static void esp_transfer_data(SCSIRequest *req, uint32_t len) +{ + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); + + DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); + s->async_len = len; + s->async_buf = scsi_req_get_buf(req); + if (s->dma_left) { + esp_do_dma(s); + } else if (s->dma_counter != 0 && s->ti_size <= 0) { + /* If this was the last part of a DMA transfer then the + completion interrupt is deferred to here. */ + esp_dma_done(s); } } @@ -678,7 +693,7 @@ VMSTATE_UINT32(ti_rptr, ESPState), VMSTATE_UINT32(ti_wptr, ESPState), VMSTATE_BUFFER(ti_buf, ESPState), - VMSTATE_UINT32(sense, ESPState), + VMSTATE_UINT32(status, ESPState), VMSTATE_UINT32(dma, ESPState), VMSTATE_BUFFER(cmdbuf, ESPState), VMSTATE_UINT32(cmdlen, ESPState), @@ -714,6 +729,12 @@ *dma_enable = qdev_get_gpio_in(dev, 1); } +static const struct SCSIBusOps esp_scsi_ops = { + .transfer_data = esp_transfer_data, + .complete = esp_command_complete, + .cancel = esp_request_cancelled +}; + static int esp_init1(SysBusDevice *dev) { ESPState *s = FROM_SYSBUS(ESPState, dev); @@ -728,7 +749,7 @@ 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_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops); return scsi_bus_legacy_handle_cmdline(&s->bus); } diff -Nru qemu-kvm-0.14.1+noroms/hw/etraxfs.c qemu-kvm-0.15.0+noroms/hw/etraxfs.c --- qemu-kvm-0.14.1+noroms/hw/etraxfs.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/etraxfs.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include "sysbus.h" #include "boards.h" -#include "sysemu.h" #include "net.h" #include "flash.h" #include "etraxfs.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/etraxfs_eth.c qemu-kvm-0.15.0+noroms/hw/etraxfs_eth.c --- qemu-kvm-0.14.1+noroms/hw/etraxfs_eth.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/etraxfs_eth.c 2011-08-09 07:40:29.000000000 -0500 @@ -602,7 +602,7 @@ DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory (base, 0x5c, eth->ethregs); - memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + eth->conf.macaddr = nd->macaddr; eth->conf.vlan = nd->vlan; eth->conf.peer = nd->netdev; diff -Nru qemu-kvm-0.14.1+noroms/hw/etraxfs_ser.c qemu-kvm-0.15.0+noroms/hw/etraxfs_ser.c --- qemu-kvm-0.14.1+noroms/hw/etraxfs_ser.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/etraxfs_ser.c 2011-08-09 07:40:29.000000000 -0500 @@ -190,15 +190,23 @@ } -static int etraxfs_ser_init(SysBusDevice *dev) +static void etraxfs_ser_reset(DeviceState *d) { - struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); - int ser_regs; + struct etrax_serial *s = container_of(d, typeof(*s), busdev.qdev); /* transmitter begins ready and idle. */ s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY); s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE); + s->regs[RW_REC_CTRL] = 0x10000; + +} + +static int etraxfs_ser_init(SysBusDevice *dev) +{ + struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); + int ser_regs; + sysbus_init_irq(dev, &s->irq); ser_regs = cpu_register_io_memory(ser_read, ser_write, s, DEVICE_NATIVE_ENDIAN); @@ -211,10 +219,16 @@ return 0; } +static SysBusDeviceInfo etraxfs_ser_info = { + .init = etraxfs_ser_init, + .qdev.name = "etraxfs,serial", + .qdev.size = sizeof(struct etrax_serial), + .qdev.reset = etraxfs_ser_reset, +}; + static void etraxfs_serial_register(void) { - sysbus_register_dev("etraxfs,serial", sizeof (struct etrax_serial), - etraxfs_ser_init); + sysbus_register_withprop(&etraxfs_ser_info); } device_init(etraxfs_serial_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/etraxfs_timer.c qemu-kvm-0.15.0+noroms/hw/etraxfs_timer.c --- qemu-kvm-0.14.1+noroms/hw/etraxfs_timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/etraxfs_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -85,7 +85,7 @@ r = ptimer_get_count(t->ptimer_t1); break; case R_TIME: - r = qemu_get_clock(vm_clock) / 10; + r = qemu_get_clock_ns(vm_clock) / 10; break; case RW_INTR_MASK: r = t->rw_intr_mask; diff -Nru qemu-kvm-0.14.1+noroms/hw/fdc.c qemu-kvm-0.15.0+noroms/hw/fdc.c --- qemu-kvm-0.14.1+noroms/hw/fdc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/fdc.c 2011-08-09 07:40:29.000000000 -0500 @@ -63,21 +63,6 @@ #define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ /* Floppy disk drive emulation */ -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 */ -} FDiskType; - -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 */ -} FDriveType; - typedef enum FDiskFlags { FDISK_DBL_SIDES = 0x01, } FDiskFlags; @@ -178,111 +163,23 @@ drv->sect = 1; } -/* Recognize floppy formats */ -typedef struct FDFormat { - FDriveType drive; - FDiskType disk; - uint8_t last_sect; - uint8_t max_track; - uint8_t max_head; - const char *str; -} FDFormat; - -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", }, - { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", }, - /* 2.88 MB 3"1/2 floppy disks */ - { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", }, - { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", }, - { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", }, - { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", }, - { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", }, - /* 720 kB 3"1/2 floppy disks */ - { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", }, - { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", }, - /* 1.2 MB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", }, - /* 720 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", }, - /* 360 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", }, - /* 320 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", }, - { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", }, - /* 360 kB must match 5"1/4 better than 3"1/2... */ - { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", }, - /* end */ - { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, }, -}; - /* Revalidate a disk drive after a disk change */ static void fd_revalidate(FDrive *drv) { - const FDFormat *parse; - uint64_t nb_sectors, size; - int i, first_match, match; int nb_heads, max_track, last_sect, ro; + FDriveType drive; FLOPPY_DPRINTF("revalidate\n"); if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { ro = bdrv_is_read_only(drv->bs); - bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); + bdrv_get_floppy_geometry_hint(drv->bs, &nb_heads, &max_track, + &last_sect, drv->drive, &drive); if (nb_heads != 0 && max_track != 0 && last_sect != 0) { FLOPPY_DPRINTF("User defined disk (%d %d %d)", nb_heads - 1, max_track, last_sect); } else { - bdrv_get_geometry(drv->bs, &nb_sectors); - match = -1; - first_match = -1; - for (i = 0;; i++) { - parse = &fd_formats[i]; - if (parse->drive == FDRIVE_DRV_NONE) - break; - if (drv->drive == parse->drive || - drv->drive == FDRIVE_DRV_NONE) { - size = (parse->max_head + 1) * parse->max_track * - parse->last_sect; - if (nb_sectors == size) { - match = i; - break; - } - if (first_match == -1) - first_match = i; - } - } - if (match == -1) { - if (first_match == -1) - match = 1; - else - match = first_match; - parse = &fd_formats[match]; - } - nb_heads = parse->max_head + 1; - max_track = parse->max_track; - last_sect = parse->last_sect; - drv->drive = parse->drive; - FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str, - nb_heads, max_track, last_sect, ro ? "ro" : "rw"); + FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads, + max_track, last_sect, ro ? "ro" : "rw"); } if (nb_heads == 1) { drv->flags &= ~FDISK_DBL_SIDES; @@ -292,6 +189,7 @@ drv->max_track = max_track; drv->last_sect = last_sect; drv->ro = ro; + drv->drive = drive; } else { FLOPPY_DPRINTF("No disk in drive\n"); drv->last_sect = 0; @@ -303,6 +201,8 @@ /********************************************************/ /* Intel 82078 floppy disk controller emulation */ +typedef struct FDCtrl 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, @@ -728,12 +628,6 @@ } } -/* XXX: may change if moved to bdrv */ -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 *fdctrl) { @@ -1529,7 +1423,7 @@ /* XXX: should set main status register to busy */ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; qemu_mod_timer(fdctrl->result_timer, - qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 50)); + qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50)); } static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction) @@ -1877,23 +1771,8 @@ return 0; } -FDCtrl *fdctrl_init_isa(DriveInfo **fds) -{ - ISADevice *dev; - - dev = isa_create("isa-fdc"); - if (fds[0]) { - qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv); - } - if (fds[1]) { - qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv); - } - qdev_init_nofail(&dev->qdev); - return &(DO_UPCAST(FDCtrlISABus, busdev, dev)->state); -} - -FDCtrl *fdctrl_init_sysbus(qemu_irq irq, int dma_chann, - target_phys_addr_t mmio_base, DriveInfo **fds) +void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, + target_phys_addr_t mmio_base, DriveInfo **fds) { FDCtrl *fdctrl; DeviceState *dev; @@ -1912,16 +1791,13 @@ qdev_init_nofail(dev); sysbus_connect_irq(&sys->busdev, 0, irq); sysbus_mmio_map(&sys->busdev, 0, mmio_base); - - return fdctrl; } -FDCtrl *sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base, - DriveInfo **fds, qemu_irq *fdc_tc) +void sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base, + DriveInfo **fds, qemu_irq *fdc_tc) { DeviceState *dev; FDCtrlSysBus *sys; - FDCtrl *fdctrl; dev = qdev_create(NULL, "SUNW,fdtwo"); if (fds[0]) { @@ -1929,12 +1805,9 @@ } qdev_init_nofail(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); *fdc_tc = qdev_get_gpio_in(dev, 0); - - return fdctrl; } static int fdctrl_init_common(FDCtrl *fdctrl) @@ -1957,7 +1830,7 @@ FLOPPY_DPRINTF("init controller\n"); fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN); fdctrl->fifo_size = 512; - fdctrl->result_timer = qemu_new_timer(vm_clock, + fdctrl->result_timer = qemu_new_timer_ns(vm_clock, fdctrl_result_timer, fdctrl); fdctrl->version = 0x90; /* Intel 82078 controller */ diff -Nru qemu-kvm-0.14.1+noroms/hw/fdc.h qemu-kvm-0.15.0+noroms/hw/fdc.h --- qemu-kvm-0.14.1+noroms/hw/fdc.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/fdc.h 2011-08-09 07:40:29.000000000 -0500 @@ -1,16 +1,31 @@ #ifndef HW_FDC_H #define HW_FDC_H +#include "isa.h" +#include "blockdev.h" + /* fdc.c */ #define MAX_FD 2 -typedef struct FDCtrl FDCtrl; +static inline void fdctrl_init_isa(DriveInfo **fds) +{ + ISADevice *dev; -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); + dev = isa_try_create("isa-fdc"); + if (!dev) { + return; + } + if (fds[0]) { + qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv); + } + if (fds[1]) { + qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv); + } + qdev_init_nofail(&dev->qdev); +} +void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, + target_phys_addr_t mmio_base, DriveInfo **fds); +void sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base, + DriveInfo **fds, qemu_irq *fdc_tc); #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/file-op-9p.h qemu-kvm-0.15.0+noroms/hw/file-op-9p.h --- qemu-kvm-0.14.1+noroms/hw/file-op-9p.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/file-op-9p.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,107 +0,0 @@ -/* - * 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.14.1+noroms/hw/flash.h qemu-kvm-0.15.0+noroms/hw/flash.h --- qemu-kvm-0.14.1+noroms/hw/flash.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/flash.h 2011-08-09 07:40:29.000000000 -0500 @@ -21,8 +21,8 @@ typedef struct NANDFlashState NANDFlashState; NANDFlashState *nand_init(int manf_id, int chip_id); void nand_done(NANDFlashState *s); -void nand_setpins(NANDFlashState *s, - int cle, int ale, int ce, int wp, int gnd); +void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, + uint8_t ce, uint8_t wp, uint8_t gnd); void nand_getpins(NANDFlashState *s, int *rb); void nand_setio(NANDFlashState *s, uint8_t value); uint8_t nand_getio(NANDFlashState *s); diff -Nru qemu-kvm-0.14.1+noroms/hw/fmopl.c qemu-kvm-0.15.0+noroms/hw/fmopl.c --- qemu-kvm-0.14.1+noroms/hw/fmopl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/fmopl.c 2011-08-09 07:40:29.000000000 -0500 @@ -45,6 +45,10 @@ #define PI 3.14159265358979323846 #endif +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + /* -------------------- for debug --------------------- */ /* #define OPL_OUTPUT_LOG */ #ifdef OPL_OUTPUT_LOG @@ -595,7 +599,7 @@ OPL->AR_TABLE[i] = rate / ARRATE; OPL->DR_TABLE[i] = rate / DRRATE; } - for (i = 60;i < 76;i++) + for (i = 60; i < ARRAY_SIZE(OPL->AR_TABLE); i++) { OPL->AR_TABLE[i] = EG_AED-1; OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; diff -Nru qemu-kvm-0.14.1+noroms/hw/framebuffer.c qemu-kvm-0.15.0+noroms/hw/framebuffer.c --- qemu-kvm-0.14.1+noroms/hw/framebuffer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/framebuffer.c 2011-08-09 07:40:29.000000000 -0500 @@ -78,6 +78,9 @@ dest = ds_get_data(ds); if (dest_col_pitch < 0) dest -= dest_col_pitch * (cols - 1); + if (dest_row_pitch < 0) { + dest -= dest_row_pitch * (rows - 1); + } first = -1; addr = pd; diff -Nru qemu-kvm-0.14.1+noroms/hw/fw_cfg.c qemu-kvm-0.15.0+noroms/hw/fw_cfg.c --- qemu-kvm-0.14.1+noroms/hw/fw_cfg.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/fw_cfg.c 2011-08-09 07:40:29.000000000 -0500 @@ -316,7 +316,7 @@ return 1; } -static void fw_cfg_machine_ready(struct Notifier* n) +static void fw_cfg_machine_ready(struct Notifier *n, void *data) { uint32_t len; FWCfgState *s = container_of(n, FWCfgState, machine_ready); diff -Nru qemu-kvm-0.14.1+noroms/hw/grackle_pci.c qemu-kvm-0.15.0+noroms/hw/grackle_pci.c --- qemu-kvm-0.14.1+noroms/hw/grackle_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/grackle_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -57,23 +57,6 @@ qemu_set_irq(pic[irq_num + 0x15], level); } -static void pci_grackle_save(QEMUFile* f, void *opaque) -{ - PCIDevice *d = opaque; - - pci_device_save(d, f); -} - -static int pci_grackle_load(QEMUFile* f, void *opaque, int version_id) -{ - PCIDevice *d = opaque; - - if (version_id != 1) - return -EINVAL; - - return pci_device_load(d, f); -} - static void pci_grackle_reset(void *opaque) { } @@ -115,19 +98,13 @@ sysbus_init_mmio(dev, 0x1000, pci_mem_config); sysbus_init_mmio(dev, 0x1000, pci_mem_data); - 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 grackle_pci_host_init(PCIDevice *d) { - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MOTOROLA); - pci_config_set_device_id(d->config, PCI_DEVICE_ID_MOTOROLA_MPC106); - d->config[0x08] = 0x00; // revision d->config[0x09] = 0x01; - pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); return 0; } @@ -135,6 +112,10 @@ .qdev.name = "grackle", .qdev.size = sizeof(PCIDevice), .init = grackle_pci_host_init, + .vendor_id = PCI_VENDOR_ID_MOTOROLA, + .device_id = PCI_DEVICE_ID_MOTOROLA_MPC106, + .revision = 0x00, + .class_id = PCI_CLASS_BRIDGE_HOST, }; static void grackle_register_devices(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/grlib_apbuart.c qemu-kvm-0.15.0+noroms/hw/grlib_apbuart.c --- qemu-kvm-0.14.1+noroms/hw/grlib_apbuart.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/grlib_apbuart.c 2011-08-09 07:40:29.000000000 -0500 @@ -133,7 +133,7 @@ break; } - trace_grlib_apbuart_unknown_register("write", addr); + trace_grlib_apbuart_writel_unknown(addr, value); } static CPUReadMemoryFunc * const grlib_apbuart_read[] = { diff -Nru qemu-kvm-0.14.1+noroms/hw/grlib_gptimer.c qemu-kvm-0.15.0+noroms/hw/grlib_gptimer.c --- qemu-kvm-0.14.1+noroms/hw/grlib_gptimer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/grlib_gptimer.c 2011-08-09 07:40:29.000000000 -0500 @@ -165,15 +165,15 @@ /* Unit registers */ switch (addr) { case SCALER_OFFSET: - trace_grlib_gptimer_readl(-1, "scaler:", unit->scaler); + trace_grlib_gptimer_readl(-1, addr, unit->scaler); return unit->scaler; case SCALER_RELOAD_OFFSET: - trace_grlib_gptimer_readl(-1, "reload:", unit->reload); + trace_grlib_gptimer_readl(-1, addr, unit->reload); return unit->reload; case CONFIG_OFFSET: - trace_grlib_gptimer_readl(-1, "config:", unit->config); + trace_grlib_gptimer_readl(-1, addr, unit->config); return unit->config; default: @@ -189,17 +189,16 @@ switch (timer_addr) { case COUNTER_OFFSET: value = ptimer_get_count(unit->timers[id].ptimer); - trace_grlib_gptimer_readl(id, "counter value:", value); + trace_grlib_gptimer_readl(id, addr, value); return value; case COUNTER_RELOAD_OFFSET: value = unit->timers[id].reload; - trace_grlib_gptimer_readl(id, "reload value:", value); + trace_grlib_gptimer_readl(id, addr, value); return value; case CONFIG_OFFSET: - trace_grlib_gptimer_readl(id, "scaler value:", - unit->timers[id].config); + trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); return unit->timers[id].config; default: @@ -208,7 +207,7 @@ } - trace_grlib_gptimer_unknown_register("read", addr); + trace_grlib_gptimer_readl(-1, addr, 0); return 0; } @@ -226,19 +225,19 @@ case SCALER_OFFSET: value &= 0xFFFF; /* clean up the value */ unit->scaler = value; - trace_grlib_gptimer_writel(-1, "scaler:", unit->scaler); + trace_grlib_gptimer_writel(-1, addr, unit->scaler); return; case SCALER_RELOAD_OFFSET: value &= 0xFFFF; /* clean up the value */ unit->reload = value; - trace_grlib_gptimer_writel(-1, "reload:", unit->reload); + trace_grlib_gptimer_writel(-1, addr, 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); + trace_grlib_gptimer_writel(-1, addr, 0); return; default: @@ -253,18 +252,18 @@ /* GPTimer registers */ switch (timer_addr) { case COUNTER_OFFSET: - trace_grlib_gptimer_writel(id, "counter:", value); + trace_grlib_gptimer_writel(id, addr, value); unit->timers[id].counter = value; grlib_gptimer_enable(&unit->timers[id]); return; case COUNTER_RELOAD_OFFSET: - trace_grlib_gptimer_writel(id, "reload:", value); + trace_grlib_gptimer_writel(id, addr, value); unit->timers[id].reload = value; return; case CONFIG_OFFSET: - trace_grlib_gptimer_writel(id, "config:", value); + trace_grlib_gptimer_writel(id, addr, value); if (value & GPTIMER_INT_PENDING) { /* clear pending bit */ @@ -297,7 +296,7 @@ } - trace_grlib_gptimer_unknown_register("write", addr); + trace_grlib_gptimer_writel(-1, addr, value); } static CPUReadMemoryFunc * const grlib_gptimer_read[] = { diff -Nru qemu-kvm-0.14.1+noroms/hw/grlib_irqmp.c qemu-kvm-0.15.0+noroms/hw/grlib_irqmp.c --- qemu-kvm-0.14.1+noroms/hw/grlib_irqmp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/grlib_irqmp.c 2011-08-09 07:40:29.000000000 -0500 @@ -220,7 +220,7 @@ return state->extended[cpu]; } - trace_grlib_irqmp_unknown_register("read", addr); + trace_grlib_irqmp_readl_unknown(addr); return 0; } @@ -308,7 +308,7 @@ return; } - trace_grlib_irqmp_unknown_register("write", addr); + trace_grlib_irqmp_writel_unknown(addr, value); } static CPUReadMemoryFunc * const grlib_irqmp_read[] = { diff -Nru qemu-kvm-0.14.1+noroms/hw/gt64xxx.c qemu-kvm-0.15.0+noroms/hw/gt64xxx.c --- qemu-kvm-0.14.1+noroms/hw/gt64xxx.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/gt64xxx.c 2011-08-09 07:40:29.000000000 -0500 @@ -1080,25 +1080,6 @@ gt64120_pci_mapping(s); } -static void gt64120_save(QEMUFile* f, void *opaque) -{ - PCIDevice *d = opaque; - pci_device_save(d, f); -} - -static int gt64120_load(QEMUFile* f, void *opaque, int version_id) -{ - PCIDevice *d = opaque; - int ret; - - if (version_id != 1) - return -EINVAL; - ret = pci_device_load(d, f); - if (ret < 0) - return ret; - return 0; -} - PCIBus *gt64120_register(qemu_irq *pic) { SysBusDevice *s; @@ -1131,22 +1112,16 @@ 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); 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); @@ -1162,6 +1137,10 @@ .qdev.name = "gt64120_pci", .qdev.size = sizeof(PCIDevice), .init = gt64120_pci_init, + .vendor_id = PCI_VENDOR_ID_MARVELL, + .device_id = PCI_DEVICE_ID_MARVELL_GT6412X, + .revision = 0x10, + .class_id = PCI_CLASS_BRIDGE_HOST, }; static void gt64120_pci_register_devices(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/gumstix.c qemu-kvm-0.15.0+noroms/hw/gumstix.c --- qemu-kvm-0.14.1+noroms/hw/gumstix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/gumstix.c 2011-08-09 07:40:29.000000000 -0500 @@ -35,7 +35,6 @@ #include "pxa.h" #include "net.h" #include "flash.h" -#include "sysemu.h" #include "devices.h" #include "boards.h" #include "blockdev.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/heathrow_pic.c qemu-kvm-0.15.0+noroms/hw/heathrow_pic.c --- qemu-kvm-0.14.1+noroms/hw/heathrow_pic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/heathrow_pic.c 2011-08-09 07:40:29.000000000 -0500 @@ -159,42 +159,31 @@ heathrow_pic_update(s); } -static void heathrow_pic_save_one(QEMUFile *f, HeathrowPIC *s) -{ - qemu_put_be32s(f, &s->events); - qemu_put_be32s(f, &s->mask); - qemu_put_be32s(f, &s->levels); - qemu_put_be32s(f, &s->level_triggered); -} - -static void heathrow_pic_save(QEMUFile *f, void *opaque) -{ - HeathrowPICS *s = (HeathrowPICS *)opaque; - - heathrow_pic_save_one(f, &s->pics[0]); - heathrow_pic_save_one(f, &s->pics[1]); -} - -static void heathrow_pic_load_one(QEMUFile *f, HeathrowPIC *s) -{ - qemu_get_be32s(f, &s->events); - qemu_get_be32s(f, &s->mask); - qemu_get_be32s(f, &s->levels); - qemu_get_be32s(f, &s->level_triggered); -} - -static int heathrow_pic_load(QEMUFile *f, void *opaque, int version_id) -{ - HeathrowPICS *s = (HeathrowPICS *)opaque; - - if (version_id != 1) - return -EINVAL; - - heathrow_pic_load_one(f, &s->pics[0]); - heathrow_pic_load_one(f, &s->pics[1]); - - return 0; -} +static const VMStateDescription vmstate_heathrow_pic_one = { + .name = "heathrow_pic_one", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(events, HeathrowPIC), + VMSTATE_UINT32(mask, HeathrowPIC), + VMSTATE_UINT32(levels, HeathrowPIC), + VMSTATE_UINT32(level_triggered, HeathrowPIC), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_heathrow_pic = { + .name = "heathrow_pic", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(pics, HeathrowPICS, 2, 1, + vmstate_heathrow_pic_one, HeathrowPIC), + VMSTATE_END_OF_LIST() + } +}; static void heathrow_pic_reset_one(HeathrowPIC *s) { @@ -223,8 +212,7 @@ *pmem_index = cpu_register_io_memory(pic_read, pic_write, s, DEVICE_LITTLE_ENDIAN); - register_savevm(NULL, "heathrow_pic", -1, 1, heathrow_pic_save, - heathrow_pic_load, s); + vmstate_register(NULL, -1, &vmstate_heathrow_pic, s); qemu_register_reset(heathrow_pic_reset, s); return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64); } diff -Nru qemu-kvm-0.14.1+noroms/hw/hpet.c qemu-kvm-0.15.0+noroms/hw/hpet.c --- qemu-kvm-0.14.1+noroms/hw/hpet.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/hpet.c 2011-08-09 07:40:29.000000000 -0500 @@ -74,8 +74,6 @@ 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) { return s->config & HPET_CFG_LEGACY; @@ -145,7 +143,7 @@ static uint64_t hpet_get_ticks(HPETState *s) { - return ns_to_ticks(qemu_get_clock(vm_clock) + s->hpet_offset); + return ns_to_ticks(qemu_get_clock_ns(vm_clock) + s->hpet_offset); } /* @@ -194,7 +192,7 @@ qemu_irq_lower(s->irqs[route]); } } else if (timer_fsb_route(timer)) { - stl_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); + stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); } else if (timer->config & HPET_TN_TYPE_LEVEL) { s->isr |= mask; qemu_irq_raise(s->irqs[route]); @@ -226,7 +224,7 @@ HPETState *s = opaque; /* Recalculate the offset between the main counter and guest time */ - s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock); + s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock); /* Push number of timers into capability returned via HPET_ID */ s->capability &= ~HPET_ID_NUM_TIM_MASK; @@ -305,11 +303,11 @@ } 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_get_clock_ns(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) + + qemu_mod_timer(t->qemu_timer, qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff)); t->wrap_flag = 0; } @@ -338,7 +336,7 @@ } } qemu_mod_timer(t->qemu_timer, - qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff)); + qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff)); } static void hpet_del_timer(HPETTimer *t) @@ -554,7 +552,7 @@ 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); + ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock); for (i = 0; i < s->num_timers; i++) { if ((&s->timer[i])->cmp != ~0ULL) { hpet_set_timer(&s->timer[i]); @@ -710,7 +708,7 @@ } for (i = 0; i < HPET_MAX_TIMERS; i++) { timer = &s->timer[i]; - timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer); + timer->qemu_timer = qemu_new_timer_ns(vm_clock, hpet_timer, timer); timer->tn = i; timer->state = s; } diff -Nru qemu-kvm-0.14.1+noroms/hw/hw.h qemu-kvm-0.15.0+noroms/hw/hw.h --- qemu-kvm-0.14.1+noroms/hw/hw.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/hw.h 2011-08-09 07:40:29.000000000 -0500 @@ -298,6 +298,8 @@ VMS_VARRAY_UINT16 = 0x080, /* Array with size in uint16_t field */ VMS_VBUFFER = 0x100, /* Buffer with size in int32_t field */ VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */ + VMS_VARRAY_UINT8 = 0x400, /* Array with size in uint8_t field*/ + VMS_VARRAY_UINT32 = 0x800, /* Array with size in uint32_t field*/ }; typedef struct { @@ -343,6 +345,7 @@ extern const VMStateInfo vmstate_info_uint8_equal; extern const VMStateInfo vmstate_info_uint16_equal; extern const VMStateInfo vmstate_info_int32_equal; +extern const VMStateInfo vmstate_info_uint32_equal; extern const VMStateInfo vmstate_info_int32_le; extern const VMStateInfo vmstate_info_uint8; @@ -350,10 +353,6 @@ extern const VMStateInfo vmstate_info_uint32; extern const VMStateInfo vmstate_info_uint64; -#ifdef __linux__ -extern const VMStateInfo vmstate_info_u64; -#endif - extern const VMStateInfo vmstate_info_timer; extern const VMStateInfo vmstate_info_ptimer; extern const VMStateInfo vmstate_info_buffer; @@ -400,6 +399,15 @@ .offset = vmstate_offset_value(_state, _field, _type), \ } +#define VMSTATE_POINTER_TEST(_field, _state, _test, _info, _type) { \ + .name = (stringify(_field)), \ + .info = &(_info), \ + .field_exists = (_test), \ + .size = sizeof(_type), \ + .flags = VMS_SINGLE|VMS_POINTER, \ + .offset = vmstate_offset_value(_state, _field, _type), \ +} + #define VMSTATE_ARRAY(_field, _state, _num, _version, _info, _type) {\ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -430,6 +438,15 @@ .offset = vmstate_offset_sub_array(_state, _field, _type, _start), \ } +#define VMSTATE_ARRAY_INT32_UNSAFE(_field, _state, _field_num, _info, _type) {\ + .name = (stringify(_field)), \ + .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \ + .info = &(_info), \ + .size = sizeof(_type), \ + .flags = VMS_VARRAY_INT32, \ + .offset = offsetof(_state, _field), \ +} + #define VMSTATE_VARRAY_INT32(_field, _state, _field_num, _version, _info, _type) {\ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -440,6 +457,16 @@ .offset = vmstate_offset_pointer(_state, _field, _type), \ } +#define VMSTATE_VARRAY_UINT32(_field, _state, _field_num, _version, _info, _type) {\ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\ + .info = &(_info), \ + .size = sizeof(_type), \ + .flags = VMS_VARRAY_UINT32|VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ +} + #define VMSTATE_VARRAY_UINT16_UNSAFE(_field, _state, _field_num, _version, _info, _type) {\ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -479,19 +506,50 @@ .offset = vmstate_offset_array(_state, _field, _type, _num), \ } -#define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) { \ +#define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .num = (_num), \ + .field_exists = (_test), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_ARRAY, \ + .offset = vmstate_offset_array(_state, _field, _type, _num),\ +} + +#define VMSTATE_STRUCT_VARRAY_UINT8(_field, _state, _field_num, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ - .num = (_num), \ + .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \ .version_id = (_version), \ .vmsd = &(_vmsd), \ .size = sizeof(_type), \ - .flags = VMS_STRUCT|VMS_ARRAY, \ - .offset = vmstate_offset_array(_state, _field, _type, _num), \ + .flags = VMS_STRUCT|VMS_VARRAY_UINT8, \ + .offset = offsetof(_state, _field), \ } -#define VMSTATE_STRUCT_VARRAY_UINT8(_field, _state, _field_num, _version, _vmsd, _type) { \ +#define VMSTATE_STRUCT_VARRAY_POINTER_INT32(_field, _state, _field_num, _vmsd, _type) { \ .name = (stringify(_field)), \ - .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \ + .version_id = 0, \ + .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \ + .size = sizeof(_type), \ + .vmsd = &(_vmsd), \ + .flags = VMS_POINTER | VMS_VARRAY_INT32 | VMS_STRUCT, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ +} + +#define VMSTATE_STRUCT_VARRAY_POINTER_UINT16(_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), \ +} + +#define VMSTATE_STRUCT_VARRAY_INT32(_field, _state, _field_num, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \ .version_id = (_version), \ .vmsd = &(_vmsd), \ .size = sizeof(_type), \ @@ -499,6 +557,16 @@ .offset = offsetof(_state, _field), \ } +#define VMSTATE_STRUCT_VARRAY_UINT32(_field, _state, _field_num, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_VARRAY_UINT32, \ + .offset = offsetof(_state, _field), \ +} + #define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -570,6 +638,14 @@ .offset = vmstate_offset_value(_state, _field, PCIDevice), \ } +#define VMSTATE_PCI_DEVICE_POINTER(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pci_device, \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, PCIDevice), \ +} + extern const VMStateDescription vmstate_pcie_device; #define VMSTATE_PCIE_DEVICE(_field, _state) { \ @@ -613,6 +689,17 @@ .offset = vmstate_offset_macaddr(_state, _field), \ } +extern const VMStateDescription vmstate_ptimer; + +#define VMSTATE_PTIMER(_field, _state) { \ + .name = (stringify(_field)), \ + .version_id = (1), \ + .vmsd = &vmstate_ptimer, \ + .size = sizeof(ptimer_state *), \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, ptimer_state), \ +} + /* _f : field name _f_n : num of elements field_name _n : num of elements @@ -629,6 +716,10 @@ #define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ VMSTATE_STRUCT_POINTER_TEST(_field, _state, NULL, _vmsd, _type) +#define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) \ + VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, \ + _vmsd, _type) + #define VMSTATE_BOOL_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_bool, bool) @@ -671,15 +762,6 @@ #define VMSTATE_UINT64(_f, _s) \ VMSTATE_UINT64_V(_f, _s, 0) -/* This is needed because on linux __u64 is unsigned long long - and on glibc uint64_t is unsigned long on 64 bits */ -#ifdef __linux__ -#define VMSTATE_U64_V(_f, _s, _v) \ - VMSTATE_SINGLE(_f, _s, _v, vmstate_info_u64, __u64) -#define VMSTATE_U64(_f, _s) \ - VMSTATE_U64_V(_f, _s, 0) -#endif - #define VMSTATE_UINT8_EQUAL(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_uint8_equal, uint8_t) @@ -692,30 +774,30 @@ #define VMSTATE_INT32_EQUAL(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_equal, int32_t) +#define VMSTATE_UINT32_EQUAL(_f, _s) \ + VMSTATE_SINGLE(_f, _s, 0, vmstate_info_uint32_equal, uint32_t) + #define VMSTATE_INT32_LE(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t) +#define VMSTATE_UINT8_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint8, uint8_t) + #define VMSTATE_UINT16_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint16, uint16_t) #define VMSTATE_UINT32_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint32, uint32_t) -#define VMSTATE_TIMER_V(_f, _s, _v) \ - VMSTATE_POINTER(_f, _s, _v, vmstate_info_timer, QEMUTimer *) +#define VMSTATE_TIMER_TEST(_f, _s, _test) \ + VMSTATE_POINTER_TEST(_f, _s, _test, vmstate_info_timer, QEMUTimer *) #define VMSTATE_TIMER(_f, _s) \ - VMSTATE_TIMER_V(_f, _s, 0) + VMSTATE_TIMER_TEST(_f, _s, NULL) #define VMSTATE_TIMER_ARRAY(_f, _s, _n) \ VMSTATE_ARRAY_OF_POINTER(_f, _s, _n, 0, vmstate_info_timer, QEMUTimer *) -#define VMSTATE_PTIMER_V(_f, _s, _v) \ - VMSTATE_POINTER(_f, _s, _v, vmstate_info_ptimer, ptimer_state *) - -#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) @@ -764,6 +846,12 @@ #define VMSTATE_UINT32_ARRAY(_f, _s, _n) \ VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_INT64_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_int64, int64_t) + +#define VMSTATE_INT64_ARRAY(_f, _s, _n) \ + VMSTATE_INT64_ARRAY_V(_f, _s, _n, 0) + #define VMSTATE_BUFFER_V(_f, _s, _v) \ VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, 0, sizeof(typeof_field(_s, _f))) diff -Nru qemu-kvm-0.14.1+noroms/hw/i2c.c qemu-kvm-0.15.0+noroms/hw/i2c.c --- qemu-kvm-0.14.1+noroms/hw/i2c.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/i2c.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "i2c.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/i2c.h qemu-kvm-0.15.0+noroms/hw/i2c.h --- qemu-kvm-0.14.1+noroms/hw/i2c.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/i2c.h 2011-08-09 07:40:29.000000000 -0500 @@ -59,11 +59,6 @@ DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr); -/* max7310.c */ -void max7310_reset(i2c_slave *i2c); -qemu_irq *max7310_gpio_in_get(i2c_slave *i2c); -void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler); - /* wm8750.c */ void wm8750_data_req_set(DeviceState *dev, void (*data_req)(void *, int, int), void *opaque); diff -Nru qemu-kvm-0.14.1+noroms/hw/i8254.c qemu-kvm-0.15.0+noroms/hw/i8254.c --- qemu-kvm-0.14.1+noroms/hw/i8254.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/i8254.c 2011-08-09 07:40:29.000000000 -0500 @@ -39,7 +39,7 @@ uint64_t d; int counter; - d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, + d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ, get_ticks_per_sec()); switch(s->mode) { case 0: @@ -92,8 +92,9 @@ return out; } -int pit_get_out(PITState *pit, int channel, int64_t current_time) +int pit_get_out(ISADevice *dev, int channel, int64_t current_time) { + PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s = &pit->channels[channel]; return pit_get_out1(s, current_time); } @@ -152,8 +153,9 @@ } /* val must be 0 or 1 */ -void pit_set_gate(PITState *pit, int channel, int val) +void pit_set_gate(ISADevice *dev, int channel, int val) { + PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s = &pit->channels[channel]; switch(s->mode) { @@ -166,7 +168,7 @@ case 5: if (s->gate < val) { /* restart counting on rising edge */ - s->count_load_time = qemu_get_clock(vm_clock); + s->count_load_time = qemu_get_clock_ns(vm_clock); pit_irq_timer_update(s, s->count_load_time); } break; @@ -174,7 +176,7 @@ case 3: if (s->gate < val) { /* restart counting on rising edge */ - s->count_load_time = qemu_get_clock(vm_clock); + s->count_load_time = qemu_get_clock_ns(vm_clock); pit_irq_timer_update(s, s->count_load_time); } /* XXX: disable/enable counting */ @@ -183,20 +185,23 @@ s->gate = val; } -int pit_get_gate(PITState *pit, int channel) +int pit_get_gate(ISADevice *dev, int channel) { + PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s = &pit->channels[channel]; return s->gate; } -int pit_get_initial_count(PITState *pit, int channel) +int pit_get_initial_count(ISADevice *dev, int channel) { + PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s = &pit->channels[channel]; return s->count; } -int pit_get_mode(PITState *pit, int channel) +int pit_get_mode(ISADevice *dev, int channel) { + PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s = &pit->channels[channel]; return s->mode; } @@ -205,7 +210,7 @@ { if (val == 0) val = 0x10000; - s->channels[chan].count_load_time = qemu_get_clock(vm_clock); + s->channels[chan].count_load_time = qemu_get_clock_ns(vm_clock); s->channels[chan].count = val; #ifdef TARGET_I386 if (chan == 0 && pit_state.flags & PIT_FLAGS_HPET_LEGACY) { @@ -244,7 +249,7 @@ if (!(val & 0x10) && !s->status_latched) { /* status latch */ /* XXX: add BCD and null count */ - s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) | + s->status = (pit_get_out1(s, qemu_get_clock_ns(vm_clock)) << 7) | (s->rw_mode << 4) | (s->mode << 1) | s->bcd; @@ -467,9 +472,9 @@ } }; -void pit_reset(void *opaque) +static void pit_reset(DeviceState *dev) { - PITState *pit = opaque; + PITState *pit = container_of(dev, PITState, dev.qdev); PITChannelState *s; int i; @@ -482,6 +487,9 @@ s->gate = (i != 2); pit_load_count(pit, 0, i); } + if (vmstate_pit.post_load) { + vmstate_pit.post_load(pit, 2); + } } #ifdef TARGET_I386 @@ -492,7 +500,7 @@ PITChannelState *s = &pit_state.channels[0]; if (kvm_enabled() && kvm_pit_in_kernel()) { - if (qemu_kvm_has_pit_state2()) { + if (kvm_has_pit_state2()) { kvm_hpet_disable_kpit(); } else { fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__); @@ -516,7 +524,7 @@ PITChannelState *s = &pit->channels[0]; if (kvm_enabled() && kvm_pit_in_kernel()) { - if (qemu_kvm_has_pit_state2()) { + if (kvm_has_pit_state2()) { kvm_hpet_enable_kpit(); } else { fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__); @@ -529,20 +537,50 @@ } #endif -PITState *pit_init(int base, qemu_irq irq) +static int pit_initfn(ISADevice *dev) { - PITState *pit = &pit_state; + PITState *pit = DO_UPCAST(PITState, dev, dev); PITChannelState *s; +#ifdef CONFIG_KVM_PIT + if (kvm_enabled() && kvm_pit_in_kernel()) + kvm_pit_init(pit); + else { +#endif + s = &pit->channels[0]; /* the timer 0 is connected to an IRQ */ - s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s); - s->irq = irq; + s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s); + s->irq = isa_get_irq(pit->irq); + + register_ioport_write(pit->iobase, 4, 1, pit_ioport_write, pit); + register_ioport_read(pit->iobase, 3, 1, pit_ioport_read, pit); + isa_init_ioport(dev, pit->iobase); + +#ifdef CONFIG_KVM_PIT + } +#endif + qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2); - 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); + return 0; +} - return pit; +static ISADeviceInfo pit_info = { + .qdev.name = "isa-pit", + .qdev.size = sizeof(PITState), + .qdev.vmsd = &vmstate_pit, + .qdev.reset = pit_reset, + .qdev.no_user = 1, + .init = pit_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("irq", PITState, irq, -1), + DEFINE_PROP_HEX32("iobase", PITState, iobase, -1), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void pit_register(void) +{ + isa_qdev_register(&pit_info); } +device_init(pit_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/i8254.h qemu-kvm-0.15.0+noroms/hw/i8254.h --- qemu-kvm-0.14.1+noroms/hw/i8254.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/i8254.h 2011-08-09 07:40:29.000000000 -0500 @@ -56,6 +56,9 @@ } PITChannelState; struct PITState { + ISADevice dev; + uint32_t irq; + uint32_t iobase; PITChannelState channels[3]; uint32_t flags; }; @@ -64,6 +67,9 @@ int pit_load(QEMUFile *f, void *opaque, int version_id); -void pit_reset(void *opaque); +typedef struct PITState PITState; + +/* i8254-kvm.c */ +void kvm_pit_init(PITState *pit); #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/i8254-kvm.c qemu-kvm-0.15.0+noroms/hw/i8254-kvm.c --- qemu-kvm-0.14.1+noroms/hw/i8254-kvm.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/i8254-kvm.c 2011-08-09 07:40:29.000000000 -0500 @@ -30,8 +30,6 @@ extern VMStateDescription vmstate_pit; -static PITState pit_state; - static void kvm_pit_pre_save(void *opaque) { PITState *s = (void *)opaque; @@ -40,12 +38,12 @@ struct PITChannelState *sc; int i; - if(qemu_kvm_has_pit_state2()) { - kvm_get_pit2(kvm_context, &pit2); + if (kvm_has_pit_state2()) { + kvm_get_pit2(kvm_state, &pit2); s->flags = pit2.flags; } else { /* pit2 is superset of pit struct so just cast it and use it */ - kvm_get_pit(kvm_context, (struct kvm_pit_state *)&pit2); + kvm_get_pit(kvm_state, (struct kvm_pit_state *)&pit2); } for (i = 0; i < 3; i++) { c = &pit2.channels[i]; @@ -93,10 +91,10 @@ c->count_load_time = sc->count_load_time; } - if(qemu_kvm_has_pit_state2()) { - kvm_set_pit2(kvm_context, &pit2); + if (kvm_has_pit_state2()) { + kvm_set_pit2(kvm_state, &pit2); } else { - kvm_set_pit(kvm_context, (struct kvm_pit_state *)&pit2); + kvm_set_pit(kvm_state, (struct kvm_pit_state *)&pit2); } return 0; } @@ -105,18 +103,13 @@ { } -PITState *kvm_pit_init(int base, qemu_irq irq) +void kvm_pit_init(PITState *pit) { - PITState *pit = &pit_state; PITChannelState *s; s = &pit->channels[0]; - s->irq_timer = qemu_new_timer(vm_clock, dummy_timer, s); + s->irq_timer = qemu_new_timer_ns(vm_clock, dummy_timer, s); vmstate_pit.pre_save = kvm_pit_pre_save; vmstate_pit.post_load = kvm_pit_post_load; - vmstate_register(NULL, base, &vmstate_pit, pit); - qemu_register_reset(pit_reset, pit); - pit_reset(pit); - - return pit; + return; } diff -Nru qemu-kvm-0.14.1+noroms/hw/i8259.c qemu-kvm-0.15.0+noroms/hw/i8259.c --- qemu-kvm-0.14.1+noroms/hw/i8259.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/i8259.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,7 +23,6 @@ */ #include "hw.h" #include "pc.h" -#include "apic.h" #include "isa.h" #include "monitor.h" #include "qemu-timer.h" @@ -192,6 +191,7 @@ static void i8259_set_irq(void *opaque, int irq, int level) { PicState2 *s = opaque; + #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) if (level != irq_level[irq]) { DPRINTF("i8259_set_irq: irq=%d level=%d\n", irq, level); @@ -204,7 +204,7 @@ #endif #ifdef DEBUG_IRQ_LATENCY if (level) { - irq_time[irq] = qemu_get_clock(vm_clock); + irq_time[irq] = qemu_get_clock_ns(vm_clock); } #endif pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); @@ -220,11 +220,9 @@ } else { s->isr |= (1 << irq); } - /* We don't clear a level sensitive interrupt here */ if (!(s->elcr & (1 << irq))) s->irr &= ~(1 << irq); - } extern int time_drift_fix; @@ -236,7 +234,6 @@ irq = pic_get_irq(&s->pics[0]); if (irq >= 0) { - pic_intack(&s->pics[0], irq); #ifdef TARGET_I386 if (time_drift_fix && irq == 0) { @@ -274,13 +271,15 @@ #ifdef DEBUG_IRQ_LATENCY printf("IRQ%d latency=%0.3fus\n", irq, - (double)(qemu_get_clock(vm_clock) - + (double)(qemu_get_clock_ns(vm_clock) - irq_time[irq]) * 1000000.0 / get_ticks_per_sec()); #endif DPRINTF("pic_interrupt: irq=%d\n", irq); return intno; } +static int kvm_kernel_pic_load_from_user(PicState *s); + static void pic_reset(void *opaque) { PicState *s = opaque; @@ -301,6 +300,10 @@ s->init4 = 0; s->single_mode = 0; /* Note: ELCR is not reset */ + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_kernel_pic_load_from_user(s); + } } static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) @@ -468,7 +471,6 @@ } static void kvm_kernel_pic_save_to_user(PicState *s); -static int kvm_kernel_pic_load_from_user(PicState *s); static void pic_pre_save(void *opaque) { @@ -590,7 +592,7 @@ chip.chip_id = (&s->pics_state->pics[0] == s) ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE; - kvm_get_irqchip(kvm_context, &chip); + kvm_get_irqchip(kvm_state, &chip); kpic = &chip.chip.pic; s->last_irr = kpic->last_irr; @@ -640,7 +642,7 @@ kpic->elcr = s->elcr; kpic->elcr_mask = s->elcr_mask; - kvm_set_irqchip(kvm_context, &chip); + kvm_set_irqchip(kvm_state, &chip); #endif return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/ahci.c qemu-kvm-0.15.0+noroms/hw/ide/ahci.c --- qemu-kvm-0.14.1+noroms/hw/ide/ahci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/ahci.c 2011-08-09 07:40:29.000000000 -0500 @@ -884,8 +884,31 @@ } if (ide_state->drive_kind != IDE_CD) { - ide_set_sector(ide_state, (cmd_fis[6] << 16) | (cmd_fis[5] << 8) | - cmd_fis[4]); + /* + * We set the sector depending on the sector defined in the FIS. + * Unfortunately, the spec isn't exactly obvious on this one. + * + * Apparently LBA48 commands set fis bytes 10,9,8,6,5,4 to the + * 48 bit sector number. ATA_CMD_READ_DMA_EXT is an example for + * such a command. + * + * Non-LBA48 commands however use 7[lower 4 bits],6,5,4 to define a + * 28-bit sector number. ATA_CMD_READ_DMA is an example for such + * a command. + * + * Since the spec doesn't explicitly state what each field should + * do, I simply assume non-used fields as reserved and OR everything + * together, independent of the command. + */ + ide_set_sector(ide_state, ((uint64_t)cmd_fis[10] << 40) + | ((uint64_t)cmd_fis[9] << 32) + /* This is used for LBA48 commands */ + | ((uint64_t)cmd_fis[8] << 24) + /* This is used for non-LBA48 commands */ + | ((uint64_t)(cmd_fis[7] & 0xf) << 24) + | ((uint64_t)cmd_fis[6] << 16) + | ((uint64_t)cmd_fis[5] << 8) + | cmd_fis[4]); } /* Copy the ACMD field (ATAPI packet, if any) from the AHCI command @@ -1066,9 +1089,11 @@ 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); + if (!ad->check_bh) { + /* 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; } @@ -1129,15 +1154,6 @@ 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; diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/ahci.h qemu-kvm-0.15.0+noroms/hw/ide/ahci.h --- qemu-kvm-0.14.1+noroms/hw/ide/ahci.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/ahci.h 2011-08-09 07:40:29.000000000 -0500 @@ -325,9 +325,6 @@ 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.14.1+noroms/hw/ide/atapi.c qemu-kvm-0.15.0+noroms/hw/ide/atapi.c --- qemu-kvm-0.14.1+noroms/hw/ide/atapi.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/ide/atapi.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,1138 @@ +/* + * QEMU ATAPI Emulation + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2006 Openedhand Ltd. + * + * 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/ide/internal.h" +#include "hw/scsi.h" + +static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret); + +static void padstr8(uint8_t *buf, int buf_size, const char *src) +{ + int i; + for(i = 0; i < buf_size; i++) { + if (*src) + buf[i] = *src++; + else + buf[i] = ' '; + } +} + +static inline void cpu_to_ube16(uint8_t *buf, int val) +{ + buf[0] = val >> 8; + buf[1] = val & 0xff; +} + +static inline void cpu_to_ube32(uint8_t *buf, unsigned int val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val & 0xff; +} + +static inline int ube16_to_cpu(const uint8_t *buf) +{ + return (buf[0] << 8) | buf[1]; +} + +static inline int ube32_to_cpu(const uint8_t *buf) +{ + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +static void lba_to_msf(uint8_t *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; +} + +static inline int media_present(IDEState *s) +{ + return (s->nb_sectors > 0); +} + +/* XXX: DVDs that could fit on a CD will be reported as a CD */ +static inline int media_is_dvd(IDEState *s) +{ + return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS); +} + +static inline int media_is_cd(IDEState *s) +{ + return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS); +} + +static void cd_data_to_raw(uint8_t *buf, int lba) +{ + /* sync bytes */ + buf[0] = 0x00; + memset(buf + 1, 0xff, 10); + buf[11] = 0x00; + buf += 12; + /* MSF */ + lba_to_msf(buf, lba); + buf[3] = 0x01; /* mode 1 data */ + buf += 4; + /* data */ + buf += 2048; + /* XXX: ECC not computed */ + memset(buf, 0, 288); +} + +static int cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf, + int sector_size) +{ + int ret; + + switch(sector_size) { + case 2048: + ret = bdrv_read(bs, (int64_t)lba << 2, buf, 4); + break; + case 2352: + ret = bdrv_read(bs, (int64_t)lba << 2, buf + 16, 4); + if (ret < 0) + return ret; + cd_data_to_raw(buf, lba); + break; + default: + ret = -EIO; + break; + } + return ret; +} + +void ide_atapi_cmd_ok(IDEState *s) +{ + s->error = 0; + s->status = READY_STAT | SEEK_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; + ide_set_irq(s->bus); +} + +void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) +{ +#ifdef DEBUG_IDE_ATAPI + printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc); +#endif + s->error = sense_key << 4; + s->status = READY_STAT | ERR_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; + s->sense_key = sense_key; + s->asc = asc; + ide_set_irq(s->bus); +} + +void ide_atapi_io_error(IDEState *s, int ret) +{ + /* XXX: handle more errors */ + if (ret == -ENOMEDIUM) { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + } else { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_LOGICAL_BLOCK_OOR); + } +} + +/* The whole ATAPI transfer logic is handled in this function */ +void ide_atapi_cmd_reply_end(IDEState *s) +{ + int byte_count_limit, size, ret; +#ifdef DEBUG_IDE_ATAPI + printf("reply: tx_size=%d elem_tx_size=%d index=%d\n", + s->packet_transfer_size, + s->elementary_transfer_size, + s->io_buffer_index); +#endif + if (s->packet_transfer_size <= 0) { + /* end of transfer */ + ide_transfer_stop(s); + s->status = READY_STAT | SEEK_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; + ide_set_irq(s->bus); +#ifdef DEBUG_IDE_ATAPI + printf("status=0x%x\n", s->status); +#endif + } else { + /* see if a new sector must be read */ + if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { + ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size); + if (ret < 0) { + ide_transfer_stop(s); + ide_atapi_io_error(s, ret); + return; + } + s->lba++; + s->io_buffer_index = 0; + } + if (s->elementary_transfer_size > 0) { + /* there are some data left to transmit in this elementary + transfer */ + size = s->cd_sector_size - s->io_buffer_index; + if (size > s->elementary_transfer_size) + size = s->elementary_transfer_size; + 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; + byte_count_limit = s->lcyl | (s->hcyl << 8); +#ifdef DEBUG_IDE_ATAPI + printf("byte_count_limit=%d\n", byte_count_limit); +#endif + if (byte_count_limit == 0xffff) + byte_count_limit--; + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) + byte_count_limit--; + size = byte_count_limit; + } + s->lcyl = size; + s->hcyl = size >> 8; + s->elementary_transfer_size = size; + /* we cannot transmit more than one sector at a time */ + if (s->lba != -1) { + if (size > (s->cd_sector_size - s->io_buffer_index)) + size = (s->cd_sector_size - s->io_buffer_index); + } + 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); +#endif + } + } +} + +/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ +static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) +{ + if (size > max_size) + size = max_size; + s->lba = -1; /* no sector read */ + s->packet_transfer_size = size; + s->io_buffer_size = size; /* dma: send the reply data as one chunk */ + s->elementary_transfer_size = 0; + s->io_buffer_index = 0; + + if (s->atapi_dma) { + s->status = READY_STAT | SEEK_STAT | DRQ_STAT; + 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); + } +} + +/* start a CD-CDROM read command */ +static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, + int sector_size) +{ + s->lba = lba; + s->packet_transfer_size = nb_sectors * sector_size; + s->elementary_transfer_size = 0; + s->io_buffer_index = sector_size; + s->cd_sector_size = sector_size; + + s->status = READY_STAT | SEEK_STAT; + ide_atapi_cmd_reply_end(s); +} + +static void ide_atapi_cmd_check_status(IDEState *s) +{ +#ifdef DEBUG_IDE_ATAPI + printf("atapi_cmd_check_status\n"); +#endif + s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4); + s->status = ERR_STAT; + s->nsector = 0; + ide_set_irq(s->bus); +} +/* ATAPI DMA support */ + +/* XXX: handle read errors */ +static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) +{ + IDEState *s = opaque; + int data_offset, n; + + if (ret < 0) { + ide_atapi_io_error(s, ret); + goto eot; + } + + if (s->io_buffer_size > 0) { + /* + * For a cdrom read sector command (s->lba != -1), + * adjust the lba for the next s->io_buffer_size chunk + * and dma the current chunk. + * For a command != read (s->lba == -1), just transfer + * the reply data. + */ + if (s->lba != -1) { + if (s->cd_sector_size == 2352) { + n = 1; + cd_data_to_raw(s->io_buffer, s->lba); + } else { + n = s->io_buffer_size >> 11; + } + s->lba += n; + } + s->packet_transfer_size -= s->io_buffer_size; + if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0) + goto eot; + } + + if (s->packet_transfer_size <= 0) { + s->status = READY_STAT | SEEK_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; + ide_set_irq(s->bus); + eot: + s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); + ide_set_inactive(s); + return; + } + + s->io_buffer_index = 0; + if (s->cd_sector_size == 2352) { + n = 1; + s->io_buffer_size = s->cd_sector_size; + data_offset = 16; + } else { + n = s->packet_transfer_size >> 11; + if (n > (IDE_DMA_BUF_SECTORS / 4)) + n = (IDE_DMA_BUF_SECTORS / 4); + s->io_buffer_size = n * 2048; + data_offset = 0; + } +#ifdef DEBUG_AIO + printf("aio_read_cd: lba=%u n=%d\n", s->lba, n); +#endif + 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); + goto eot; + } +} + +/* start a CD-CDROM read command with DMA */ +/* XXX: test if DMA is available */ +static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, + int sector_size) +{ + s->lba = lba; + s->packet_transfer_size = nb_sectors * sector_size; + s->io_buffer_index = 0; + s->io_buffer_size = 0; + s->cd_sector_size = sector_size; + + /* XXX: check if BUSY_STAT should be set */ + s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; + 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, + int sector_size) +{ +#ifdef DEBUG_IDE_ATAPI + printf("read %s: LBA=%d nb_sectors=%d\n", s->atapi_dma ? "dma" : "pio", + lba, nb_sectors); +#endif + if (s->atapi_dma) { + ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size); + } else { + ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size); + } +} + +static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index, + uint16_t profile) +{ + uint8_t *buf_profile = buf + 12; /* start of profiles */ + + buf_profile += ((*index) * 4); /* start of indexed profile */ + cpu_to_ube16 (buf_profile, profile); + buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7])); + + /* each profile adds 4 bytes to the response */ + (*index)++; + buf[11] += 4; /* Additional Length */ + + return 4; +} + +static int ide_dvd_read_structure(IDEState *s, int format, + const uint8_t *packet, uint8_t *buf) +{ + switch (format) { + case 0x0: /* Physical format information */ + { + int layer = packet[6]; + uint64_t total_sectors; + + if (layer != 0) + return -ASC_INV_FIELD_IN_CMD_PACKET; + + total_sectors = s->nb_sectors >> 2; + if (total_sectors == 0) { + return -ASC_MEDIUM_NOT_PRESENT; + } + + buf[4] = 1; /* DVD-ROM, part version 1 */ + buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ + buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ + buf[7] = 0; /* default densities */ + + /* FIXME: 0x30000 per spec? */ + cpu_to_ube32(buf + 8, 0); /* start sector */ + cpu_to_ube32(buf + 12, total_sectors - 1); /* end sector */ + cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */ + + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 2048 + 2); + + /* 2k data + 4 byte header */ + return (2048 + 4); + } + + case 0x01: /* DVD copyright information */ + buf[4] = 0; /* no copyright data */ + buf[5] = 0; /* no region restrictions */ + + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 4 + 2); + + /* 4 byte header + 4 byte data */ + return (4 + 4); + + case 0x03: /* BCA information - invalid field for no BCA info */ + return -ASC_INV_FIELD_IN_CMD_PACKET; + + case 0x04: /* DVD disc manufacturing information */ + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 2048 + 2); + + /* 2k data + 4 byte header */ + return (2048 + 4); + + case 0xff: + /* + * This lists all the command capabilities above. Add new ones + * in order and update the length and buffer return values. + */ + + buf[4] = 0x00; /* Physical format */ + buf[5] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 6), 2048 + 4); + + buf[8] = 0x01; /* Copyright info */ + buf[9] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 10), 4 + 4); + + buf[12] = 0x03; /* BCA info */ + buf[13] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 14), 188 + 4); + + buf[16] = 0x04; /* Manufacturing info */ + buf[17] = 0x40; /* Not writable, is readable */ + cpu_to_be16wu((uint16_t *)(buf + 18), 2048 + 4); + + /* Size of buffer, not including 2 byte size field */ + cpu_to_be16wu((uint16_t *)buf, 16 + 2); + + /* data written + 4 byte header */ + return (16 + 4); + + default: /* TODO: formats beyond DVD-ROM requires */ + return -ASC_INV_FIELD_IN_CMD_PACKET; + } +} + +static unsigned int event_status_media(IDEState *s, + uint8_t *buf) +{ + enum media_event_code { + MEC_NO_CHANGE = 0, /* Status unchanged */ + MEC_EJECT_REQUESTED, /* received a request from user to eject */ + MEC_NEW_MEDIA, /* new media inserted and ready for access */ + MEC_MEDIA_REMOVAL, /* only for media changers */ + MEC_MEDIA_CHANGED, /* only for media changers */ + MEC_BG_FORMAT_COMPLETED, /* MRW or DVD+RW b/g format completed */ + MEC_BG_FORMAT_RESTARTED, /* MRW or DVD+RW b/g format restarted */ + }; + enum media_status { + MS_TRAY_OPEN = 1, + MS_MEDIA_PRESENT = 2, + }; + uint8_t event_code, media_status; + + media_status = 0; + if (s->bs->tray_open) { + media_status = MS_TRAY_OPEN; + } else if (bdrv_is_inserted(s->bs)) { + media_status = MS_MEDIA_PRESENT; + } + + /* Event notification descriptor */ + event_code = MEC_NO_CHANGE; + if (media_status != MS_TRAY_OPEN && s->events.new_media) { + event_code = MEC_NEW_MEDIA; + s->events.new_media = false; + } + + buf[4] = event_code; + buf[5] = media_status; + + /* These fields are reserved, just clear them. */ + buf[6] = 0; + buf[7] = 0; + + return 8; /* We wrote to 4 extra bytes from the header */ +} + +static void cmd_get_event_status_notification(IDEState *s, + uint8_t *buf) +{ + const uint8_t *packet = buf; + + struct { + uint8_t opcode; + uint8_t polled; /* lsb bit is polled; others are reserved */ + uint8_t reserved2[2]; + uint8_t class; + uint8_t reserved3[2]; + uint16_t len; + uint8_t control; + } __attribute__((packed)) *gesn_cdb; + + struct { + uint16_t len; + uint8_t notification_class; + uint8_t supported_events; + } __attribute((packed)) *gesn_event_header; + + enum notification_class_request_type { + NCR_RESERVED1 = 1 << 0, + NCR_OPERATIONAL_CHANGE = 1 << 1, + NCR_POWER_MANAGEMENT = 1 << 2, + NCR_EXTERNAL_REQUEST = 1 << 3, + NCR_MEDIA = 1 << 4, + NCR_MULTI_HOST = 1 << 5, + NCR_DEVICE_BUSY = 1 << 6, + NCR_RESERVED2 = 1 << 7, + }; + enum event_notification_class_field { + ENC_NO_EVENTS = 0, + ENC_OPERATIONAL_CHANGE, + ENC_POWER_MANAGEMENT, + ENC_EXTERNAL_REQUEST, + ENC_MEDIA, + ENC_MULTIPLE_HOSTS, + ENC_DEVICE_BUSY, + ENC_RESERVED, + }; + unsigned int max_len, used_len; + + gesn_cdb = (void *)packet; + gesn_event_header = (void *)buf; + + max_len = be16_to_cpu(gesn_cdb->len); + + /* It is fine by the MMC spec to not support async mode operations */ + if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */ + /* Only polling is supported, asynchronous mode is not. */ + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + return; + } + + /* polling mode operation */ + + /* + * These are the supported events. + * + * We currently only support requests of the 'media' type. + */ + gesn_event_header->supported_events = NCR_MEDIA; + + /* + * We use |= below to set the class field; other bits in this byte + * are reserved now but this is useful to do if we have to use the + * reserved fields later. + */ + gesn_event_header->notification_class = 0; + + /* + * Responses to requests are to be based on request priority. The + * notification_class_request_type enum above specifies the + * priority: upper elements are higher prio than lower ones. + */ + if (gesn_cdb->class & NCR_MEDIA) { + gesn_event_header->notification_class |= ENC_MEDIA; + used_len = event_status_media(s, buf); + } else { + gesn_event_header->notification_class = 0x80; /* No event available */ + used_len = sizeof(*gesn_event_header); + } + gesn_event_header->len = cpu_to_be16(used_len + - sizeof(*gesn_event_header)); + ide_atapi_cmd_reply(s, used_len, max_len); +} + +static void cmd_request_sense(IDEState *s, uint8_t *buf) +{ + int max_len = buf[4]; + + memset(buf, 0, 18); + buf[0] = 0x70 | (1 << 7); + buf[2] = s->sense_key; + buf[7] = 10; + buf[12] = s->asc; + + if (s->sense_key == SENSE_UNIT_ATTENTION) { + s->sense_key = SENSE_NONE; + } + + ide_atapi_cmd_reply(s, 18, max_len); +} + +static void cmd_inquiry(IDEState *s, uint8_t *buf) +{ + int max_len = buf[4]; + + buf[0] = 0x05; /* CD-ROM */ + buf[1] = 0x80; /* removable */ + buf[2] = 0x00; /* ISO */ + buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ + buf[4] = 31; /* additional length */ + buf[5] = 0; /* reserved */ + buf[6] = 0; /* reserved */ + buf[7] = 0; /* reserved */ + padstr8(buf + 8, 8, "QEMU"); + padstr8(buf + 16, 16, "QEMU DVD-ROM"); + padstr8(buf + 32, 4, s->version); + ide_atapi_cmd_reply(s, 36, max_len); +} + +static void cmd_get_configuration(IDEState *s, uint8_t *buf) +{ + uint32_t len; + uint8_t index = 0; + int max_len; + + /* only feature 0 is supported */ + if (buf[2] != 0 || buf[3] != 0) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + return; + } + + /* XXX: could result in alignment problems in some architectures */ + max_len = ube16_to_cpu(buf + 7); + + /* + * XXX: avoid overflow for io_buffer if max_len is bigger than + * the size of that buffer (dimensioned to max number of + * sectors to transfer at once) + * + * Only a problem if the feature/profiles grow. + */ + if (max_len > 512) { + /* XXX: assume 1 sector */ + max_len = 512; + } + + memset(buf, 0, max_len); + /* + * the number of sectors from the media tells us which profile + * to use as current. 0 means there is no media + */ + if (media_is_dvd(s)) { + cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM); + } else if (media_is_cd(s)) { + cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM); + } + + buf[10] = 0x02 | 0x01; /* persistent and current */ + len = 12; /* headers: 8 + 4 */ + len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM); + len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM); + cpu_to_ube32(buf, len - 4); /* data length */ + + ide_atapi_cmd_reply(s, len, max_len); +} + +static void cmd_mode_sense(IDEState *s, uint8_t *buf) +{ + int action, code; + int max_len; + + if (buf[0] == GPCMD_MODE_SENSE_10) { + max_len = ube16_to_cpu(buf + 7); + } else { + max_len = buf[4]; + } + + action = buf[2] >> 6; + code = buf[2] & 0x3f; + + switch(action) { + case 0: /* current values */ + switch(code) { + case GPMODE_R_W_ERROR_PAGE: /* error recovery */ + cpu_to_ube16(&buf[0], 16 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + buf[8] = 0x01; + buf[9] = 0x06; + buf[10] = 0x00; + buf[11] = 0x05; + buf[12] = 0x00; + buf[13] = 0x00; + buf[14] = 0x00; + buf[15] = 0x00; + ide_atapi_cmd_reply(s, 16, max_len); + break; + case GPMODE_AUDIO_CTL_PAGE: + cpu_to_ube16(&buf[0], 24 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + /* Fill with CDROM audio volume */ + buf[17] = 0; + buf[19] = 0; + buf[21] = 0; + buf[23] = 0; + + ide_atapi_cmd_reply(s, 24, max_len); + break; + case GPMODE_CAPABILITIES_PAGE: + cpu_to_ube16(&buf[0], 28 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + buf[8] = 0x2a; + buf[9] = 0x12; + buf[10] = 0x00; + buf[11] = 0x00; + + /* Claim PLAY_AUDIO capability (0x01) since some Linux + code checks for this to automount media. */ + buf[12] = 0x71; + buf[13] = 3 << 5; + buf[14] = (1 << 0) | (1 << 3) | (1 << 5); + if (bdrv_is_locked(s->bs)) + buf[6] |= 1 << 1; + buf[15] = 0x00; + cpu_to_ube16(&buf[16], 706); + buf[18] = 0; + buf[19] = 2; + cpu_to_ube16(&buf[20], 512); + cpu_to_ube16(&buf[22], 706); + buf[24] = 0; + buf[25] = 0; + buf[26] = 0; + buf[27] = 0; + ide_atapi_cmd_reply(s, 28, max_len); + break; + default: + goto error_cmd; + } + break; + case 1: /* changeable values */ + goto error_cmd; + case 2: /* default values */ + goto error_cmd; + default: + case 3: /* saved values */ + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_SAVING_PARAMETERS_NOT_SUPPORTED); + break; + } + return; + +error_cmd: + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); +} + +static void cmd_test_unit_ready(IDEState *s, uint8_t *buf) +{ + /* Not Ready Conditions are already handled in ide_atapi_cmd(), so if we + * come here, we know that it's ready. */ + ide_atapi_cmd_ok(s); +} + +static void cmd_prevent_allow_medium_removal(IDEState *s, uint8_t* buf) +{ + bdrv_set_locked(s->bs, buf[4] & 1); + ide_atapi_cmd_ok(s); +} + +static void cmd_read(IDEState *s, uint8_t* buf) +{ + int nb_sectors, lba; + + if (buf[0] == GPCMD_READ_10) { + nb_sectors = ube16_to_cpu(buf + 7); + } else { + nb_sectors = ube32_to_cpu(buf + 6); + } + + lba = ube32_to_cpu(buf + 2); + if (nb_sectors == 0) { + ide_atapi_cmd_ok(s); + return; + } + + ide_atapi_cmd_read(s, lba, nb_sectors, 2048); +} + +static void cmd_read_cd(IDEState *s, uint8_t* buf) +{ + int nb_sectors, lba, transfer_request; + + nb_sectors = (buf[6] << 16) | (buf[7] << 8) | buf[8]; + lba = ube32_to_cpu(buf + 2); + + if (nb_sectors == 0) { + ide_atapi_cmd_ok(s); + return; + } + + transfer_request = buf[9]; + switch(transfer_request & 0xf8) { + case 0x00: + /* nothing */ + ide_atapi_cmd_ok(s); + break; + case 0x10: + /* normal read */ + ide_atapi_cmd_read(s, lba, nb_sectors, 2048); + break; + case 0xf8: + /* read all data */ + ide_atapi_cmd_read(s, lba, nb_sectors, 2352); + break; + default: + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + break; + } +} + +static void cmd_seek(IDEState *s, uint8_t* buf) +{ + unsigned int lba; + uint64_t total_sectors = s->nb_sectors >> 2; + + lba = ube32_to_cpu(buf + 2); + if (lba >= total_sectors) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); + return; + } + + ide_atapi_cmd_ok(s); +} + +static void cmd_start_stop_unit(IDEState *s, uint8_t* buf) +{ + int start, eject, sense, err = 0; + start = buf[4] & 1; + eject = (buf[4] >> 1) & 1; + + if (eject) { + err = bdrv_eject(s->bs, !start); + } + + switch (err) { + case 0: + ide_atapi_cmd_ok(s); + break; + case -EBUSY: + sense = SENSE_NOT_READY; + if (bdrv_is_inserted(s->bs)) { + sense = SENSE_ILLEGAL_REQUEST; + } + ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED); + break; + default: + ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + break; + } +} + +static void cmd_mechanism_status(IDEState *s, uint8_t* buf) +{ + int max_len = ube16_to_cpu(buf + 8); + + cpu_to_ube16(buf, 0); + /* no current LBA */ + buf[2] = 0; + buf[3] = 0; + buf[4] = 0; + buf[5] = 1; + cpu_to_ube16(buf + 6, 0); + ide_atapi_cmd_reply(s, 8, max_len); +} + +static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf) +{ + int format, msf, start_track, len; + int max_len; + uint64_t total_sectors = s->nb_sectors >> 2; + + max_len = ube16_to_cpu(buf + 7); + format = buf[9] >> 6; + msf = (buf[1] >> 1) & 1; + start_track = buf[6]; + + switch(format) { + case 0: + len = cdrom_read_toc(total_sectors, buf, msf, start_track); + if (len < 0) + goto error_cmd; + ide_atapi_cmd_reply(s, len, max_len); + break; + case 1: + /* multi session : only a single session defined */ + memset(buf, 0, 12); + buf[1] = 0x0a; + buf[2] = 0x01; + buf[3] = 0x01; + ide_atapi_cmd_reply(s, 12, max_len); + break; + case 2: + len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track); + if (len < 0) + goto error_cmd; + ide_atapi_cmd_reply(s, len, max_len); + break; + default: + error_cmd: + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + } +} + +static void cmd_read_cdvd_capacity(IDEState *s, uint8_t* buf) +{ + uint64_t total_sectors = s->nb_sectors >> 2; + + /* NOTE: it is really the number of sectors minus 1 */ + cpu_to_ube32(buf, total_sectors - 1); + cpu_to_ube32(buf + 4, 2048); + ide_atapi_cmd_reply(s, 8, 8); +} + +static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf) +{ + int max_len; + int media = buf[1]; + int format = buf[7]; + int ret; + + max_len = ube16_to_cpu(buf + 8); + + if (format < 0xff) { + if (media_is_cd(s)) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INCOMPATIBLE_FORMAT); + return; + } else if (!media_present(s)) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + return; + } + } + + memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ? + IDE_DMA_BUF_SECTORS * 512 + 4 : max_len); + + switch (format) { + case 0x00 ... 0x7f: + case 0xff: + if (media == 0) { + ret = ide_dvd_read_structure(s, format, buf, buf); + + if (ret < 0) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret); + } else { + ide_atapi_cmd_reply(s, ret, max_len); + } + + break; + } + /* TODO: BD support, fall through for now */ + + /* Generic disk structures */ + case 0x80: /* TODO: AACS volume identifier */ + case 0x81: /* TODO: AACS media serial number */ + case 0x82: /* TODO: AACS media identifier */ + case 0x83: /* TODO: AACS media key block */ + case 0x90: /* TODO: List of recognized format layers */ + case 0xc0: /* TODO: Write protection status */ + default: + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + break; + } +} + +static void cmd_set_speed(IDEState *s, uint8_t* buf) +{ + ide_atapi_cmd_ok(s); +} + +enum { + /* + * Only commands flagged as ALLOW_UA are allowed to run under a + * unit attention condition. (See MMC-5, section 4.1.6.1) + */ + ALLOW_UA = 0x01, + + /* + * Commands flagged with CHECK_READY can only execute if a medium is present. + * Otherwise they report the Not Ready Condition. (See MMC-5, section + * 4.1.8) + */ + CHECK_READY = 0x02, +}; + +static const struct { + void (*handler)(IDEState *s, uint8_t *buf); + int flags; +} atapi_cmd_table[0x100] = { + [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY }, + [ 0x03 ] = { cmd_request_sense, ALLOW_UA }, + [ 0x12 ] = { cmd_inquiry, ALLOW_UA }, + [ 0x1a ] = { cmd_mode_sense, /* (6) */ 0 }, + [ 0x1b ] = { cmd_start_stop_unit, 0 }, + [ 0x1e ] = { cmd_prevent_allow_medium_removal, 0 }, + [ 0x25 ] = { cmd_read_cdvd_capacity, CHECK_READY }, + [ 0x28 ] = { cmd_read, /* (10) */ 0 }, + [ 0x2b ] = { cmd_seek, CHECK_READY }, + [ 0x43 ] = { cmd_read_toc_pma_atip, CHECK_READY }, + [ 0x46 ] = { cmd_get_configuration, ALLOW_UA }, + [ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA }, + [ 0x5a ] = { cmd_mode_sense, /* (10) */ 0 }, + [ 0xa8 ] = { cmd_read, /* (12) */ 0 }, + [ 0xad ] = { cmd_read_dvd_structure, 0 }, + [ 0xbb ] = { cmd_set_speed, 0 }, + [ 0xbd ] = { cmd_mechanism_status, 0 }, + [ 0xbe ] = { cmd_read_cd, 0 }, +}; + +void ide_atapi_cmd(IDEState *s) +{ + uint8_t *buf; + + buf = s->io_buffer; +#ifdef DEBUG_IDE_ATAPI + { + int i; + printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8)); + for(i = 0; i < ATAPI_PACKET_SIZE; i++) { + printf(" %02x", buf[i]); + } + printf("\n"); + } +#endif + /* + * If there's a UNIT_ATTENTION condition pending, only command flagged with + * ALLOW_UA are allowed to complete. with other commands getting a CHECK + * condition response unless a higher priority status, defined by the drive + * here, is pending. + */ + if (s->sense_key == SENSE_UNIT_ATTENTION && + !(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) { + ide_atapi_cmd_check_status(s); + return; + } + /* + * When a CD gets changed, we have to report an ejected state and + * then a loaded state to guests so that they detect tray + * open/close and media change events. Guests that do not use + * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close + * states rely on this behavior. + */ + if (bdrv_is_inserted(s->bs) && s->cdrom_changed) { + ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + + s->cdrom_changed = 0; + s->sense_key = SENSE_UNIT_ATTENTION; + s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED; + return; + } + + /* Report a Not Ready condition if appropriate for the command */ + if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) && + (!media_present(s) || !bdrv_is_inserted(s->bs))) + { + ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + return; + } + + /* Execute the command */ + if (atapi_cmd_table[s->io_buffer[0]].handler) { + atapi_cmd_table[s->io_buffer[0]].handler(s, buf); + return; + } + + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE); +} diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/cmd646.c qemu-kvm-0.15.0+noroms/hw/ide/cmd646.c --- qemu-kvm-0.14.1+noroms/hw/ide/cmd646.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/cmd646.c 2011-08-09 07:40:29.000000000 -0500 @@ -226,14 +226,8 @@ 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[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[0x51] = 0x04; // enable IDE0 if (d->secondary) { /* XXX: if not enabled, really disable the seconday IDE controller */ @@ -282,6 +276,10 @@ .qdev.name = "cmd646-ide", .qdev.size = sizeof(PCIIDEState), .init = pci_cmd646_ide_initfn, + .vendor_id = PCI_VENDOR_ID_CMD, + .device_id = PCI_DEVICE_ID_CMD_646, + .revision = 0x07, // IDE controller revision + .class_id = PCI_CLASS_STORAGE_IDE, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/core.c qemu-kvm-0.15.0+noroms/hw/ide/core.c --- qemu-kvm-0.14.1+noroms/hw/ide/core.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/core.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,7 +25,6 @@ #include #include #include -#include #include "qemu-error.h" #include "qemu-timer.h" #include "sysemu.h" @@ -34,33 +33,30 @@ #include -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 */ - { 0x04, 0x02, 0x64, 0x64, 0x14}, /* start stop count */ - { 0x05, 0x03, 0x64, 0x64, 0x36}, /* remapped sectors */ - { 0x00, 0x00, 0x00, 0x00, 0x00} +/* These values were based on a Seagate ST3500418AS but have been modified + to make more sense in QEMU */ +static const int smart_attributes[][12] = { + /* id, flags, hflags, val, wrst, raw (6 bytes), threshold */ + /* raw read error rate*/ + { 0x01, 0x03, 0x00, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + /* spin up */ + { 0x03, 0x03, 0x00, 0x64, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + /* start stop count */ + { 0x04, 0x02, 0x00, 0x64, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14}, + /* remapped sectors */ + { 0x05, 0x03, 0x00, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24}, + /* power on hours */ + { 0x09, 0x03, 0x00, 0x64, 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + /* power cycle count */ + { 0x0c, 0x03, 0x00, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + /* airflow-temperature-celsius */ + { 190, 0x03, 0x00, 0x45, 0x45, 0x1f, 0x00, 0x1f, 0x1f, 0x00, 0x00, 0x32}, + /* end of list */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; -/* XXX: DVDs that could fit on a CD will be reported as a CD */ -static inline int media_present(IDEState *s) -{ - return (s->nb_sectors > 0); -} - -static inline int media_is_dvd(IDEState *s) -{ - return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS); -} - -static inline int media_is_cd(IDEState *s) -{ - return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS); -} - -static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret); static int ide_handle_rw_error(IDEState *s, int error, int op); +static void ide_dummy_transfer_stop(IDEState *s); static void padstr(char *str, const char *src, int len) { @@ -74,17 +70,6 @@ } } -static void padstr8(uint8_t *buf, int buf_size, const char *src) -{ - int i; - for(i = 0; i < buf_size; i++) { - if (*src) - buf[i] = *src++; - else - buf[i] = ' '; - } -} - static void put_le16(uint16_t *p, unsigned int v) { *p = cpu_to_le16(v); @@ -94,7 +79,7 @@ { uint16_t *p; unsigned int oldsize; - IDEDevice *dev; + IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; if (s->identify_set) { memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); @@ -140,6 +125,9 @@ put_le16(p + 66, 120); put_le16(p + 67, 120); put_le16(p + 68, 120); + if (dev && dev->conf.discard_granularity) { + put_le16(p + 69, (1 << 14)); /* determinate TRIM behavior */ + } if (s->ncq_queues) { put_le16(p + 75, s->ncq_queues - 1); @@ -170,9 +158,12 @@ 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)); + if (dev && dev->conf.discard_granularity) { + put_le16(p + 169, 1); /* TRIM support */ + } memcpy(s->identify_data, p, sizeof(s->identify_data)); s->identify_set = 1; @@ -315,6 +306,74 @@ } } +typedef struct TrimAIOCB { + BlockDriverAIOCB common; + QEMUBH *bh; + int ret; +} TrimAIOCB; + +static void trim_aio_cancel(BlockDriverAIOCB *acb) +{ + TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common); + + qemu_bh_delete(iocb->bh); + iocb->bh = NULL; + qemu_aio_release(iocb); +} + +static AIOPool trim_aio_pool = { + .aiocb_size = sizeof(TrimAIOCB), + .cancel = trim_aio_cancel, +}; + +static void ide_trim_bh_cb(void *opaque) +{ + TrimAIOCB *iocb = opaque; + + iocb->common.cb(iocb->common.opaque, iocb->ret); + + qemu_bh_delete(iocb->bh); + iocb->bh = NULL; + + qemu_aio_release(iocb); +} + +BlockDriverAIOCB *ide_issue_trim(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + TrimAIOCB *iocb; + int i, j, ret; + + iocb = qemu_aio_get(&trim_aio_pool, bs, cb, opaque); + iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); + iocb->ret = 0; + + for (j = 0; j < qiov->niov; j++) { + uint64_t *buffer = qiov->iov[j].iov_base; + + for (i = 0; i < qiov->iov[j].iov_len / 8; i++) { + /* 6-byte LBA + 2-byte range per entry */ + uint64_t entry = le64_to_cpu(buffer[i]); + uint64_t sector = entry & 0x0000ffffffffffffULL; + uint16_t count = entry >> 48; + + if (count == 0) { + break; + } + + ret = bdrv_discard(bs, sector, count); + if (!iocb->ret) { + iocb->ret = ret; + } + } + } + + qemu_bh_schedule(iocb->bh); + + return &iocb->common; +} + static inline void ide_abort_command(IDEState *s) { s->status = READY_STAT | ERR_STAT; @@ -322,8 +381,8 @@ } /* 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) +void ide_transfer_start(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func) { s->end_transfer_func = end_transfer_func; s->data_ptr = buf; @@ -334,7 +393,7 @@ s->bus->dma->ops->start_transfer(s->bus->dma); } -static void ide_transfer_stop(IDEState *s) +void ide_transfer_stop(IDEState *s) { s->end_transfer_func = ide_transfer_stop; s->data_ptr = s->io_buffer; @@ -434,7 +493,7 @@ qemu_sglist_destroy(&s->sg); } -static void ide_set_inactive(IDEState *s) +void ide_set_inactive(IDEState *s) { s->bus->dma->aiocb = NULL; s->bus->dma->ops->set_inactive(s->bus->dma); @@ -446,7 +505,6 @@ 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); } @@ -463,9 +521,9 @@ if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { s->bus->dma->ops->set_unit(s->bus->dma, s->unit); - s->bus->dma->ops->add_status(s->bus->dma, op); + s->bus->error_status = op; bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); - vm_stop(0); + vm_stop(VMSTOP_DISKFULL); } else { if (op & BM_STATUS_DMA_RETRY) { dma_buf_commit(s, 0); @@ -489,8 +547,11 @@ if (ret < 0) { int op = BM_STATUS_DMA_RETRY; - if (s->is_read) + if (s->dma_cmd == IDE_DMA_READ) op |= BM_STATUS_RETRY_READ; + else if (s->dma_cmd == IDE_DMA_TRIM) + op |= BM_STATUS_RETRY_TRIM; + if (ide_handle_rw_error(s, -ret, op)) { return; } @@ -499,7 +560,7 @@ n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); if (n > 0) { - dma_buf_commit(s, s->is_read); + dma_buf_commit(s, ide_cmd_is_read(s)); sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; @@ -516,20 +577,30 @@ n = s->nsector; s->io_buffer_index = 0; s->io_buffer_size = n * 512; - if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->is_read) == 0) + if (s->bus->dma->ops->prepare_buf(s->bus->dma, ide_cmd_is_read(s)) == 0) { + /* The PRDs were too short. Reset the Active bit, but don't raise an + * interrupt. */ goto eot; + } #ifdef DEBUG_AIO - printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, is_read=%d\n", - sector_num, n, s->is_read); + printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, cmd_cmd=%d\n", + sector_num, n, s->dma_cmd); #endif - if (s->is_read) { + switch (s->dma_cmd) { + case IDE_DMA_READ: s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, ide_dma_cb, s); - } else { + break; + case IDE_DMA_WRITE: s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, ide_dma_cb, s); + break; + case IDE_DMA_TRIM: + s->bus->dma->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num, + ide_issue_trim, ide_dma_cb, s, 1); + break; } if (!s->bus->dma->aiocb) { @@ -539,16 +610,15 @@ return; eot: - s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); ide_set_inactive(s); } -static void ide_sector_start_dma(IDEState *s, int is_read) +static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) { s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; s->io_buffer_index = 0; s->io_buffer_size = 0; - s->is_read = is_read; + s->dma_cmd = dma_cmd; s->bus->dma->ops->start_dma(s->bus->dma, s, ide_dma_cb); } @@ -598,44 +668,12 @@ option _only_ to install Windows 2000. You must disable it for normal use. */ qemu_mod_timer(s->sector_write_timer, - qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 1000)); + qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 1000)); } else { ide_set_irq(s->bus); } } -void ide_atapi_cmd_ok(IDEState *s) -{ - s->error = 0; - s->status = READY_STAT | SEEK_STAT; - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - ide_set_irq(s->bus); -} - -void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) -{ -#ifdef DEBUG_IDE_ATAPI - printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc); -#endif - s->error = sense_key << 4; - s->status = READY_STAT | ERR_STAT; - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - s->sense_key = sense_key; - s->asc = asc; - ide_set_irq(s->bus); -} - -static void ide_atapi_cmd_check_status(IDEState *s) -{ -#ifdef DEBUG_IDE_ATAPI - printf("atapi_cmd_check_status\n"); -#endif - s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4); - s->status = ERR_STAT; - s->nsector = 0; - ide_set_irq(s->bus); -} - static void ide_flush_cb(void *opaque, int ret) { IDEState *s = opaque; @@ -666,870 +704,6 @@ } } -static inline void cpu_to_ube16(uint8_t *buf, int val) -{ - buf[0] = val >> 8; - buf[1] = val & 0xff; -} - -static inline void cpu_to_ube32(uint8_t *buf, unsigned int val) -{ - buf[0] = val >> 24; - buf[1] = val >> 16; - buf[2] = val >> 8; - buf[3] = val & 0xff; -} - -static inline int ube16_to_cpu(const uint8_t *buf) -{ - return (buf[0] << 8) | buf[1]; -} - -static inline int ube32_to_cpu(const uint8_t *buf) -{ - return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; -} - -static void lba_to_msf(uint8_t *buf, int lba) -{ - lba += 150; - buf[0] = (lba / 75) / 60; - buf[1] = (lba / 75) % 60; - buf[2] = lba % 75; -} - -static void cd_data_to_raw(uint8_t *buf, int lba) -{ - /* sync bytes */ - buf[0] = 0x00; - memset(buf + 1, 0xff, 10); - buf[11] = 0x00; - buf += 12; - /* MSF */ - lba_to_msf(buf, lba); - buf[3] = 0x01; /* mode 1 data */ - buf += 4; - /* data */ - buf += 2048; - /* XXX: ECC not computed */ - memset(buf, 0, 288); -} - -static int cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf, - int sector_size) -{ - int ret; - - switch(sector_size) { - case 2048: - ret = bdrv_read(bs, (int64_t)lba << 2, buf, 4); - break; - case 2352: - ret = bdrv_read(bs, (int64_t)lba << 2, buf + 16, 4); - if (ret < 0) - return ret; - cd_data_to_raw(buf, lba); - break; - default: - ret = -EIO; - break; - } - return ret; -} - -void ide_atapi_io_error(IDEState *s, int ret) -{ - /* XXX: handle more errors */ - if (ret == -ENOMEDIUM) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - } else { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_LOGICAL_BLOCK_OOR); - } -} - -/* The whole ATAPI transfer logic is handled in this function */ -static void ide_atapi_cmd_reply_end(IDEState *s) -{ - int byte_count_limit, size, ret; -#ifdef DEBUG_IDE_ATAPI - printf("reply: tx_size=%d elem_tx_size=%d index=%d\n", - s->packet_transfer_size, - s->elementary_transfer_size, - s->io_buffer_index); -#endif - if (s->packet_transfer_size <= 0) { - /* end of transfer */ - ide_transfer_stop(s); - s->status = READY_STAT | SEEK_STAT; - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - ide_set_irq(s->bus); -#ifdef DEBUG_IDE_ATAPI - printf("status=0x%x\n", s->status); -#endif - } else { - /* see if a new sector must be read */ - if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { - ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size); - if (ret < 0) { - ide_transfer_stop(s); - ide_atapi_io_error(s, ret); - return; - } - s->lba++; - s->io_buffer_index = 0; - } - if (s->elementary_transfer_size > 0) { - /* there are some data left to transmit in this elementary - transfer */ - size = s->cd_sector_size - s->io_buffer_index; - if (size > s->elementary_transfer_size) - size = s->elementary_transfer_size; - 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; - byte_count_limit = s->lcyl | (s->hcyl << 8); -#ifdef DEBUG_IDE_ATAPI - printf("byte_count_limit=%d\n", byte_count_limit); -#endif - if (byte_count_limit == 0xffff) - byte_count_limit--; - size = s->packet_transfer_size; - if (size > byte_count_limit) { - /* byte count limit must be even if this case */ - if (byte_count_limit & 1) - byte_count_limit--; - size = byte_count_limit; - } - s->lcyl = size; - s->hcyl = size >> 8; - s->elementary_transfer_size = size; - /* we cannot transmit more than one sector at a time */ - if (s->lba != -1) { - if (size > (s->cd_sector_size - s->io_buffer_index)) - size = (s->cd_sector_size - s->io_buffer_index); - } - 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); -#endif - } - } -} - -/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ -static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) -{ - if (size > max_size) - size = max_size; - s->lba = -1; /* no sector read */ - s->packet_transfer_size = size; - s->io_buffer_size = size; /* dma: send the reply data as one chunk */ - s->elementary_transfer_size = 0; - s->io_buffer_index = 0; - - if (s->atapi_dma) { - s->status = READY_STAT | SEEK_STAT | DRQ_STAT; - 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); - } -} - -/* start a CD-CDROM read command */ -static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, - int sector_size) -{ - s->lba = lba; - s->packet_transfer_size = nb_sectors * sector_size; - s->elementary_transfer_size = 0; - s->io_buffer_index = sector_size; - s->cd_sector_size = sector_size; - - s->status = READY_STAT | SEEK_STAT; - ide_atapi_cmd_reply_end(s); -} - -/* ATAPI DMA support */ - -/* XXX: handle read errors */ -static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) -{ - IDEState *s = opaque; - int data_offset, n; - - if (ret < 0) { - ide_atapi_io_error(s, ret); - goto eot; - } - - if (s->io_buffer_size > 0) { - /* - * For a cdrom read sector command (s->lba != -1), - * adjust the lba for the next s->io_buffer_size chunk - * and dma the current chunk. - * For a command != read (s->lba == -1), just transfer - * the reply data. - */ - if (s->lba != -1) { - if (s->cd_sector_size == 2352) { - n = 1; - cd_data_to_raw(s->io_buffer, s->lba); - } else { - n = s->io_buffer_size >> 11; - } - s->lba += n; - } - s->packet_transfer_size -= s->io_buffer_size; - if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0) - goto eot; - } - - if (s->packet_transfer_size <= 0) { - s->status = READY_STAT | SEEK_STAT; - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - ide_set_irq(s->bus); - eot: - s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); - ide_set_inactive(s); - return; - } - - s->io_buffer_index = 0; - if (s->cd_sector_size == 2352) { - n = 1; - s->io_buffer_size = s->cd_sector_size; - data_offset = 16; - } else { - n = s->packet_transfer_size >> 11; - if (n > (IDE_DMA_BUF_SECTORS / 4)) - n = (IDE_DMA_BUF_SECTORS / 4); - s->io_buffer_size = n * 2048; - data_offset = 0; - } -#ifdef DEBUG_AIO - printf("aio_read_cd: lba=%u n=%d\n", s->lba, n); -#endif - 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); - goto eot; - } -} - -/* start a CD-CDROM read command with DMA */ -/* XXX: test if DMA is available */ -static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, - int sector_size) -{ - s->lba = lba; - s->packet_transfer_size = nb_sectors * sector_size; - s->io_buffer_index = 0; - s->io_buffer_size = 0; - s->cd_sector_size = sector_size; - - /* XXX: check if BUSY_STAT should be set */ - s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - 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, - int sector_size) -{ -#ifdef DEBUG_IDE_ATAPI - printf("read %s: LBA=%d nb_sectors=%d\n", s->atapi_dma ? "dma" : "pio", - lba, nb_sectors); -#endif - if (s->atapi_dma) { - ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size); - } else { - ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size); - } -} - -static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index, - uint16_t profile) -{ - uint8_t *buf_profile = buf + 12; /* start of profiles */ - - buf_profile += ((*index) * 4); /* start of indexed profile */ - cpu_to_ube16 (buf_profile, profile); - buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7])); - - /* each profile adds 4 bytes to the response */ - (*index)++; - buf[11] += 4; /* Additional Length */ - - return 4; -} - -static int ide_dvd_read_structure(IDEState *s, int format, - const uint8_t *packet, uint8_t *buf) -{ - switch (format) { - case 0x0: /* Physical format information */ - { - int layer = packet[6]; - uint64_t total_sectors; - - if (layer != 0) - return -ASC_INV_FIELD_IN_CMD_PACKET; - - bdrv_get_geometry(s->bs, &total_sectors); - total_sectors >>= 2; - if (total_sectors == 0) - return -ASC_MEDIUM_NOT_PRESENT; - - buf[4] = 1; /* DVD-ROM, part version 1 */ - buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ - buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ - buf[7] = 0; /* default densities */ - - /* FIXME: 0x30000 per spec? */ - cpu_to_ube32(buf + 8, 0); /* start sector */ - cpu_to_ube32(buf + 12, total_sectors - 1); /* end sector */ - cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */ - - /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 2048 + 2); - - /* 2k data + 4 byte header */ - return (2048 + 4); - } - - case 0x01: /* DVD copyright information */ - buf[4] = 0; /* no copyright data */ - buf[5] = 0; /* no region restrictions */ - - /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 4 + 2); - - /* 4 byte header + 4 byte data */ - return (4 + 4); - - case 0x03: /* BCA information - invalid field for no BCA info */ - return -ASC_INV_FIELD_IN_CMD_PACKET; - - case 0x04: /* DVD disc manufacturing information */ - /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 2048 + 2); - - /* 2k data + 4 byte header */ - return (2048 + 4); - - case 0xff: - /* - * This lists all the command capabilities above. Add new ones - * in order and update the length and buffer return values. - */ - - buf[4] = 0x00; /* Physical format */ - buf[5] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 6), 2048 + 4); - - buf[8] = 0x01; /* Copyright info */ - buf[9] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 10), 4 + 4); - - buf[12] = 0x03; /* BCA info */ - buf[13] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 14), 188 + 4); - - buf[16] = 0x04; /* Manufacturing info */ - buf[17] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 18), 2048 + 4); - - /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 16 + 2); - - /* data written + 4 byte header */ - return (16 + 4); - - default: /* TODO: formats beyond DVD-ROM requires */ - return -ASC_INV_FIELD_IN_CMD_PACKET; - } -} - -static void ide_atapi_cmd(IDEState *s) -{ - const uint8_t *packet; - uint8_t *buf; - int max_len; - - packet = s->io_buffer; - buf = s->io_buffer; -#ifdef DEBUG_IDE_ATAPI - { - int i; - printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8)); - for(i = 0; i < ATAPI_PACKET_SIZE; i++) { - printf(" %02x", packet[i]); - } - printf("\n"); - } -#endif - /* If there's a UNIT_ATTENTION condition pending, only - REQUEST_SENSE and INQUIRY commands are allowed to complete. */ - if (s->sense_key == SENSE_UNIT_ATTENTION && - s->io_buffer[0] != GPCMD_REQUEST_SENSE && - s->io_buffer[0] != GPCMD_INQUIRY) { - ide_atapi_cmd_check_status(s); - return; - } - switch(s->io_buffer[0]) { - case GPCMD_TEST_UNIT_READY: - if (bdrv_is_inserted(s->bs) && !s->cdrom_changed) { - ide_atapi_cmd_ok(s); - } else { - s->cdrom_changed = 0; - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - } - break; - case GPCMD_MODE_SENSE_6: - case GPCMD_MODE_SENSE_10: - { - int action, code; - if (packet[0] == GPCMD_MODE_SENSE_10) - max_len = ube16_to_cpu(packet + 7); - else - max_len = packet[4]; - action = packet[2] >> 6; - code = packet[2] & 0x3f; - switch(action) { - case 0: /* current values */ - switch(code) { - case GPMODE_R_W_ERROR_PAGE: /* error recovery */ - cpu_to_ube16(&buf[0], 16 + 6); - buf[2] = 0x70; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - - buf[8] = 0x01; - buf[9] = 0x06; - buf[10] = 0x00; - buf[11] = 0x05; - buf[12] = 0x00; - buf[13] = 0x00; - buf[14] = 0x00; - buf[15] = 0x00; - ide_atapi_cmd_reply(s, 16, max_len); - break; - case GPMODE_AUDIO_CTL_PAGE: - cpu_to_ube16(&buf[0], 24 + 6); - buf[2] = 0x70; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - - /* Fill with CDROM audio volume */ - buf[17] = 0; - buf[19] = 0; - buf[21] = 0; - buf[23] = 0; - - ide_atapi_cmd_reply(s, 24, max_len); - break; - case GPMODE_CAPABILITIES_PAGE: - cpu_to_ube16(&buf[0], 28 + 6); - buf[2] = 0x70; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - - buf[8] = 0x2a; - buf[9] = 0x12; - buf[10] = 0x00; - buf[11] = 0x00; - - /* Claim PLAY_AUDIO capability (0x01) since some Linux - code checks for this to automount media. */ - buf[12] = 0x71; - buf[13] = 3 << 5; - buf[14] = (1 << 0) | (1 << 3) | (1 << 5); - if (bdrv_is_locked(s->bs)) - buf[6] |= 1 << 1; - buf[15] = 0x00; - cpu_to_ube16(&buf[16], 706); - buf[18] = 0; - buf[19] = 2; - cpu_to_ube16(&buf[20], 512); - cpu_to_ube16(&buf[22], 706); - buf[24] = 0; - buf[25] = 0; - buf[26] = 0; - buf[27] = 0; - ide_atapi_cmd_reply(s, 28, max_len); - break; - default: - goto error_cmd; - } - break; - case 1: /* changeable values */ - goto error_cmd; - case 2: /* default values */ - goto error_cmd; - default: - case 3: /* saved values */ - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_SAVING_PARAMETERS_NOT_SUPPORTED); - break; - } - } - break; - case GPCMD_REQUEST_SENSE: - max_len = packet[4]; - memset(buf, 0, 18); - buf[0] = 0x70 | (1 << 7); - buf[2] = s->sense_key; - buf[7] = 10; - buf[12] = s->asc; - if (s->sense_key == SENSE_UNIT_ATTENTION) - s->sense_key = SENSE_NONE; - ide_atapi_cmd_reply(s, 18, max_len); - break; - case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL: - if (bdrv_is_inserted(s->bs)) { - bdrv_set_locked(s->bs, packet[4] & 1); - ide_atapi_cmd_ok(s); - } else { - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - } - break; - case GPCMD_READ_10: - case GPCMD_READ_12: - { - int nb_sectors, lba; - - if (packet[0] == GPCMD_READ_10) - nb_sectors = ube16_to_cpu(packet + 7); - else - nb_sectors = ube32_to_cpu(packet + 6); - lba = ube32_to_cpu(packet + 2); - if (nb_sectors == 0) { - ide_atapi_cmd_ok(s); - break; - } - ide_atapi_cmd_read(s, lba, nb_sectors, 2048); - } - break; - case GPCMD_READ_CD: - { - int nb_sectors, lba, transfer_request; - - nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8]; - lba = ube32_to_cpu(packet + 2); - if (nb_sectors == 0) { - ide_atapi_cmd_ok(s); - break; - } - transfer_request = packet[9]; - switch(transfer_request & 0xf8) { - case 0x00: - /* nothing */ - ide_atapi_cmd_ok(s); - break; - case 0x10: - /* normal read */ - ide_atapi_cmd_read(s, lba, nb_sectors, 2048); - break; - case 0xf8: - /* read all data */ - ide_atapi_cmd_read(s, lba, nb_sectors, 2352); - break; - default: - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - break; - } - } - break; - case GPCMD_SEEK: - { - unsigned int lba; - uint64_t total_sectors; - - bdrv_get_geometry(s->bs, &total_sectors); - total_sectors >>= 2; - if (total_sectors == 0) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - break; - } - lba = ube32_to_cpu(packet + 2); - if (lba >= total_sectors) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_LOGICAL_BLOCK_OOR); - break; - } - ide_atapi_cmd_ok(s); - } - break; - case GPCMD_START_STOP_UNIT: - { - int start, eject, err = 0; - start = packet[4] & 1; - eject = (packet[4] >> 1) & 1; - - if (eject) { - err = bdrv_eject(s->bs, !start); - } - - switch (err) { - case 0: - ide_atapi_cmd_ok(s); - break; - case -EBUSY: - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIA_REMOVAL_PREVENTED); - break; - default: - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - break; - } - } - break; - case GPCMD_MECHANISM_STATUS: - { - max_len = ube16_to_cpu(packet + 8); - cpu_to_ube16(buf, 0); - /* no current LBA */ - buf[2] = 0; - buf[3] = 0; - buf[4] = 0; - buf[5] = 1; - cpu_to_ube16(buf + 6, 0); - ide_atapi_cmd_reply(s, 8, max_len); - } - break; - case GPCMD_READ_TOC_PMA_ATIP: - { - int format, msf, start_track, len; - uint64_t total_sectors; - - bdrv_get_geometry(s->bs, &total_sectors); - total_sectors >>= 2; - if (total_sectors == 0) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - break; - } - max_len = ube16_to_cpu(packet + 7); - format = packet[9] >> 6; - msf = (packet[1] >> 1) & 1; - start_track = packet[6]; - switch(format) { - case 0: - len = cdrom_read_toc(total_sectors, buf, msf, start_track); - if (len < 0) - goto error_cmd; - ide_atapi_cmd_reply(s, len, max_len); - break; - case 1: - /* multi session : only a single session defined */ - memset(buf, 0, 12); - buf[1] = 0x0a; - buf[2] = 0x01; - buf[3] = 0x01; - ide_atapi_cmd_reply(s, 12, max_len); - break; - case 2: - len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track); - if (len < 0) - goto error_cmd; - ide_atapi_cmd_reply(s, len, max_len); - break; - default: - error_cmd: - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - break; - } - } - break; - case GPCMD_READ_CDVD_CAPACITY: - { - uint64_t total_sectors; - - bdrv_get_geometry(s->bs, &total_sectors); - total_sectors >>= 2; - if (total_sectors == 0) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - break; - } - /* NOTE: it is really the number of sectors minus 1 */ - cpu_to_ube32(buf, total_sectors - 1); - cpu_to_ube32(buf + 4, 2048); - ide_atapi_cmd_reply(s, 8, 8); - } - break; - case GPCMD_READ_DVD_STRUCTURE: - { - int media = packet[1]; - int format = packet[7]; - int ret; - - max_len = ube16_to_cpu(packet + 8); - - if (format < 0xff) { - if (media_is_cd(s)) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_INCOMPATIBLE_FORMAT); - break; - } else if (!media_present(s)) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - break; - } - } - - memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ? - IDE_DMA_BUF_SECTORS * 512 + 4 : max_len); - - switch (format) { - case 0x00 ... 0x7f: - case 0xff: - if (media == 0) { - ret = ide_dvd_read_structure(s, format, packet, buf); - - if (ret < 0) - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret); - else - ide_atapi_cmd_reply(s, ret, max_len); - - break; - } - /* TODO: BD support, fall through for now */ - - /* Generic disk structures */ - case 0x80: /* TODO: AACS volume identifier */ - case 0x81: /* TODO: AACS media serial number */ - case 0x82: /* TODO: AACS media identifier */ - case 0x83: /* TODO: AACS media key block */ - case 0x90: /* TODO: List of recognized format layers */ - case 0xc0: /* TODO: Write protection status */ - default: - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - break; - } - } - break; - case GPCMD_SET_SPEED: - ide_atapi_cmd_ok(s); - break; - case GPCMD_INQUIRY: - max_len = packet[4]; - buf[0] = 0x05; /* CD-ROM */ - buf[1] = 0x80; /* removable */ - buf[2] = 0x00; /* ISO */ - buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ - buf[4] = 31; /* additional length */ - buf[5] = 0; /* reserved */ - buf[6] = 0; /* reserved */ - buf[7] = 0; /* reserved */ - padstr8(buf + 8, 8, "QEMU"); - padstr8(buf + 16, 16, "QEMU DVD-ROM"); - padstr8(buf + 32, 4, s->version); - ide_atapi_cmd_reply(s, 36, max_len); - break; - case GPCMD_GET_CONFIGURATION: - { - uint32_t len; - uint8_t index = 0; - - /* only feature 0 is supported */ - if (packet[2] != 0 || packet[3] != 0) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - break; - } - - /* XXX: could result in alignment problems in some architectures */ - max_len = ube16_to_cpu(packet + 7); - - /* - * XXX: avoid overflow for io_buffer if max_len is bigger than - * the size of that buffer (dimensioned to max number of - * sectors to transfer at once) - * - * Only a problem if the feature/profiles grow. - */ - if (max_len > 512) /* XXX: assume 1 sector */ - max_len = 512; - - memset(buf, 0, max_len); - /* - * the number of sectors from the media tells us which profile - * to use as current. 0 means there is no media - */ - if (media_is_dvd(s)) - cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM); - else if (media_is_cd(s)) - cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM); - - buf[10] = 0x02 | 0x01; /* persistent and current */ - len = 12; /* headers: 8 + 4 */ - len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM); - len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM); - cpu_to_ube32(buf, len - 4); /* data length */ - - 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); - break; - } -} - static void ide_cfata_metadata_inquiry(IDEState *s) { uint16_t *p; @@ -1596,9 +770,15 @@ bdrv_get_geometry(s->bs, &nb_sectors); s->nb_sectors = nb_sectors; - s->sense_key = SENSE_UNIT_ATTENTION; - s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED; + /* + * First indicate to the guest that a CD has been removed. That's + * done on the next command the guest sends us. + * + * Then we set SENSE_UNIT_ATTENTION, by which the guest will + * detect a new CD in the drive. See ide_atapi_cmd() for details. + */ s->cdrom_changed = 1; + s->events.new_media = true; ide_set_irq(s->bus); } @@ -1720,6 +900,18 @@ return; switch(val) { + case WIN_DSM: + switch (s->feature) { + case DSM_TRIM: + if (!s->bs) { + goto abort_cmd; + } + ide_sector_start_dma(s, IDE_DMA_TRIM); + break; + default: + goto abort_cmd; + } + break; case WIN_IDENTIFY: if (s->bs && s->drive_kind != IDE_CD) { if (s->drive_kind != IDE_CFATA) @@ -1821,7 +1013,7 @@ if (!s->bs) goto abort_cmd; ide_cmd_lba48_transform(s, lba48); - ide_sector_start_dma(s, 1); + ide_sector_start_dma(s, IDE_DMA_READ); break; case WIN_WRITEDMA_EXT: lba48 = 1; @@ -1830,7 +1022,7 @@ if (!s->bs) goto abort_cmd; ide_cmd_lba48_transform(s, lba48); - ide_sector_start_dma(s, 0); + ide_sector_start_dma(s, IDE_DMA_WRITE); s->media_changed = 1; break; case WIN_READ_NATIVE_MAX_EXT: @@ -1843,6 +1035,7 @@ break; case WIN_CHECKPOWERMODE1: case WIN_CHECKPOWERMODE2: + s->error = 0; s->nsector = 0xff; /* device active or idle */ s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); @@ -2097,7 +1290,7 @@ 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+1+(n*12)] = smart_attributes[n][11]; } for (n=0; n<511; n++) /* checksum */ s->io_buffer[511] += s->io_buffer[n]; @@ -2110,12 +1303,13 @@ 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]; + } + int i; + for(i = 0; i < 11; i++) { + s->io_buffer[2+i+(n*12)] = smart_attributes[n][i]; + } } s->io_buffer[362] = 0x02 | (s->smart_autosave?0x80:0x00); if (s->smart_selftest_count == 0) { @@ -2339,15 +1533,36 @@ bus->cmd = val; } +/* + * Returns true if the running PIO transfer is a PIO out (i.e. data is + * transferred from the device to the guest), false if it's a PIO in + */ +static bool ide_is_pio_out(IDEState *s) +{ + if (s->end_transfer_func == ide_sector_write || + s->end_transfer_func == ide_atapi_cmd) { + return false; + } else if (s->end_transfer_func == ide_sector_read || + s->end_transfer_func == ide_transfer_stop || + s->end_transfer_func == ide_atapi_cmd_reply_end || + s->end_transfer_func == ide_dummy_transfer_stop) { + return true; + } + + abort(); +} + void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; IDEState *s = idebus_active_if(bus); uint8_t *p; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a write + * during PIO out is indeterminate, just ignore it. */ + if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) { return; + } p = s->data_ptr; *(uint16_t *)p = le16_to_cpu(val); @@ -2364,9 +1579,11 @@ uint8_t *p; int ret; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a read + * during PIO in is indeterminate, return 0 and don't move forward. */ + if (!(s->status & DRQ_STAT) || !ide_is_pio_out(s)) { return 0; + } p = s->data_ptr; ret = cpu_to_le16(*(uint16_t *)p); @@ -2383,9 +1600,11 @@ IDEState *s = idebus_active_if(bus); uint8_t *p; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a write + * during PIO out is indeterminate, just ignore it. */ + if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) { return; + } p = s->data_ptr; *(uint32_t *)p = le32_to_cpu(val); @@ -2402,9 +1621,11 @@ uint8_t *p; int ret; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a read + * during PIO in is indeterminate, return 0 and don't move forward. */ + if (!(s->status & DRQ_STAT) || !ide_is_pio_out(s)) { return 0; + } p = s->data_ptr; ret = cpu_to_le32(*(uint32_t *)p); @@ -2496,13 +1717,15 @@ bus->dma->ops->reset(bus->dma); } -int ide_init_drive(IDEState *s, BlockDriverState *bs, +int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial) { int cylinders, heads, secs; uint64_t nb_sectors; s->bs = bs; + s->drive_kind = kind; + bdrv_get_geometry(bs, &nb_sectors); bdrv_guess_geometry(bs, &cylinders, &heads, &secs); if (cylinders < 1 || cylinders > 16383) { @@ -2527,8 +1750,7 @@ 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; + if (kind == IDE_CD) { bdrv_set_change_cb(bs, cdrom_change_cb, s); bs->buffer_alignment = 2048; } else { @@ -2567,10 +1789,14 @@ 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->io_buffer = qemu_memalign(2048, s->io_buffer_total_len); + memset(s->io_buffer, 0, s->io_buffer_total_len); + s->smart_selftest_data = qemu_blockalign(s->bs, 512); - s->sector_write_timer = qemu_new_timer(vm_clock, + memset(s->smart_selftest_data, 0, 512); + + s->sector_write_timer = qemu_new_timer_ns(vm_clock, ide_sector_write_timer_cb, s); } @@ -2633,7 +1859,8 @@ dinfo = i == 0 ? hd0 : hd1; ide_init1(bus, i); if (dinfo) { - if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, NULL, + if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, + dinfo->media_cd ? IDE_CD : IDE_HD, NULL, *dinfo->serial ? dinfo->serial : NULL) < 0) { error_report("Can't set up IDE drive %s", dinfo->id); exit(1); @@ -2738,9 +1965,37 @@ { IDEState *s = opaque; - return (s->status & DRQ_STAT) != 0; + return ((s->status & DRQ_STAT) != 0) + || (s->bus->error_status & BM_STATUS_PIO_RETRY); +} + +static bool ide_atapi_gesn_needed(void *opaque) +{ + IDEState *s = opaque; + + return s->events.new_media || s->events.eject_request; } +static bool ide_error_needed(void *opaque) +{ + IDEBus *bus = opaque; + + return (bus->error_status != 0); +} + +/* Fields for GET_EVENT_STATUS_NOTIFICATION ATAPI command */ +const VMStateDescription vmstate_ide_atapi_gesn_state = { + .name ="ide_drive/atapi/gesn_state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_BOOL(events.new_media, IDEState), + VMSTATE_BOOL(events.eject_request, IDEState), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_ide_drive_pio_state = { .name = "ide_drive/pio_state", .version_id = 1, @@ -2795,11 +2050,25 @@ .vmsd = &vmstate_ide_drive_pio_state, .needed = ide_drive_pio_state_needed, }, { + .vmsd = &vmstate_ide_atapi_gesn_state, + .needed = ide_atapi_gesn_needed, + }, { /* empty */ } } }; +const VMStateDescription vmstate_ide_error_status = { + .name ="ide_bus/error", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_INT32(error_status, IDEBus), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_ide_bus = { .name = "ide_bus", .version_id = 1, @@ -2809,5 +2078,27 @@ VMSTATE_UINT8(cmd, IDEBus), VMSTATE_UINT8(unit, IDEBus), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_ide_error_status, + .needed = ide_error_needed, + }, { + /* empty */ + } } }; + +void ide_drive_get(DriveInfo **hd, int max_bus) +{ + int i; + + if (drive_get_max_bus(IF_IDE) >= max_bus) { + fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); + exit(1); + } + + for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { + hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); + } +} diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/ich.c qemu-kvm-0.15.0+noroms/hw/ide/ich.c --- qemu-kvm-0.14.1+noroms/hw/ide/ich.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/ich.c 2011-08-09 07:40:29.000000000 -0500 @@ -67,7 +67,6 @@ #include #include "block.h" #include "block_int.h" -#include "sysemu.h" #include "dma.h" #include @@ -78,11 +77,8 @@ 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); + ahci_init(&d->ahci, &dev->qdev, 6); - 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 */ @@ -94,15 +90,12 @@ 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]; + /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ + pci_register_bar_simple(&d->card, 5, 0x1000, 0, d->ahci.mem); + return 0; } @@ -111,10 +104,7 @@ struct AHCIPCIState *d; d = DO_UPCAST(struct AHCIPCIState, card, dev); - if (msi_enabled(dev)) { - msi_uninit(dev); - } - + msi_uninit(dev); qemu_unregister_reset(ahci_reset, d); ahci_uninit(&d->ahci); @@ -136,6 +126,10 @@ .init = pci_ich9_ahci_init, .exit = pci_ich9_uninit, .config_write = pci_ich9_write_config, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801IR, + .revision = 0x02, + .class_id = PCI_CLASS_STORAGE_SATA, },{ /* end of list */ } diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/internal.h qemu-kvm-0.15.0+noroms/hw/ide/internal.h --- qemu-kvm-0.14.1+noroms/hw/ide/internal.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/internal.h 2011-08-09 07:40:29.000000000 -0500 @@ -9,6 +9,7 @@ #include #include "block_int.h" #include "iorange.h" +#include "dma.h" /* debug IDE devices */ //#define DEBUG_IDE @@ -61,7 +62,11 @@ */ #define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */ /* - * 0x04->0x07 Reserved + * 0x04->0x05 Reserved + */ +#define WIN_DSM 0x06 +/* + * 0x07 Reserved */ #define WIN_SRST 0x08 /* ATAPI soft reset command */ #define WIN_DEVICE_RESET 0x08 @@ -189,6 +194,9 @@ #define IDE_DMA_BUF_SECTORS 256 +/* feature values for Data Set Management */ +#define DSM_TRIM 0x01 + #if (IDE_DMA_BUF_SECTORS < MAX_MULT_SECTORS) #error "IDE_DMA_BUF_SECTORS must be bigger or equal to MAX_MULT_SECTORS" #endif @@ -373,6 +381,20 @@ typedef int DMAIntFunc(IDEDMA *, int); typedef void DMARestartFunc(void *, int, int); +struct unreported_events { + bool eject_request; + bool new_media; +}; + +enum ide_dma_cmd { + IDE_DMA_READ, + IDE_DMA_WRITE, + IDE_DMA_TRIM, +}; + +#define ide_cmd_is_read(s) \ + ((s)->dma_cmd == IDE_DMA_READ) + /* NOTE: IDEState represents in fact one drive */ struct IDEState { IDEBus *bus; @@ -408,6 +430,7 @@ BlockDriverState *bs; char version[9]; /* ATAPI specific */ + struct unreported_events events; uint8_t sense_key; uint8_t asc; uint8_t cdrom_changed; @@ -439,7 +462,7 @@ uint32_t mdata_size; uint8_t *mdata_storage; int media_changed; - int is_read; + enum ide_dma_cmd dma_cmd; /* SMART */ uint8_t smart_enabled; uint8_t smart_autosave; @@ -479,6 +502,8 @@ uint8_t unit; uint8_t cmd; qemu_irq irq; + + int error_status; }; struct IDEDevice { @@ -498,10 +523,17 @@ #define BM_STATUS_DMAING 0x01 #define BM_STATUS_ERROR 0x02 #define BM_STATUS_INT 0x04 + +/* FIXME These are not status register bits */ #define BM_STATUS_DMA_RETRY 0x08 #define BM_STATUS_PIO_RETRY 0x10 #define BM_STATUS_RETRY_READ 0x20 #define BM_STATUS_RETRY_FLUSH 0x40 +#define BM_STATUS_RETRY_TRIM 0x80 + +#define BM_MIGRATION_COMPAT_STATUS_BITS \ + (BM_STATUS_DMA_RETRY | BM_STATUS_PIO_RETRY | \ + BM_STATUS_RETRY_READ | BM_STATUS_RETRY_FLUSH) #define BM_CMD_START 0x01 #define BM_CMD_READ 0x08 @@ -551,7 +583,7 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val); uint32_t ide_data_readl(void *opaque, uint32_t addr); -int ide_init_drive(IDEState *s, BlockDriverState *bs, +int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, 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, @@ -564,6 +596,18 @@ void ide_sector_read(IDEState *s); void ide_flush_cache(IDEState *s); +void ide_transfer_start(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func); +void ide_transfer_stop(IDEState *s); +void ide_set_inactive(IDEState *s); +BlockDriverAIOCB *ide_issue_trim(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque); + +/* hw/ide/atapi.c */ +void ide_atapi_cmd(IDEState *s); +void ide_atapi_cmd_reply_end(IDEState *s); + /* hw/ide/qdev.c */ void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id); IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive); diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/isa.c qemu-kvm-0.15.0+noroms/hw/ide/isa.c --- qemu-kvm-0.14.1+noroms/hw/ide/isa.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/isa.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ #include #include "block.h" #include "block_int.h" -#include "sysemu.h" #include "dma.h" #include diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/macio.c qemu-kvm-0.15.0+noroms/hw/ide/macio.c --- qemu-kvm-0.14.1+noroms/hw/ide/macio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/macio.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ #include #include "block.h" #include "block_int.h" -#include "sysemu.h" #include "dma.h" #include @@ -146,12 +145,21 @@ io->addr += io->len; io->len = 0; - if (s->is_read) + switch (s->dma_cmd) { + case IDE_DMA_READ: m->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, pmac_ide_transfer_cb, io); - else + break; + case IDE_DMA_WRITE: m->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, pmac_ide_transfer_cb, io); + break; + case IDE_DMA_TRIM: + m->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num, + ide_issue_trim, pmac_ide_transfer_cb, s, 1); + break; + } + if (!m->aiocb) pmac_ide_transfer_cb(io, -1); } diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/microdrive.c qemu-kvm-0.15.0+noroms/hw/ide/microdrive.c --- qemu-kvm-0.14.1+noroms/hw/ide/microdrive.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/microdrive.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ #include #include "block.h" #include "block_int.h" -#include "sysemu.h" #include "dma.h" #include diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/mmio.c qemu-kvm-0.15.0+noroms/hw/ide/mmio.c --- qemu-kvm-0.14.1+noroms/hw/ide/mmio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/mmio.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,7 +25,6 @@ #include #include "block.h" #include "block_int.h" -#include "sysemu.h" #include "dma.h" #include diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/pci.c qemu-kvm-0.15.0+noroms/hw/ide/pci.c --- qemu-kvm-0.14.1+noroms/hw/ide/pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -28,7 +28,6 @@ #include #include "block.h" #include "block_int.h" -#include "sysemu.h" #include "dma.h" #include @@ -170,7 +169,7 @@ return 0; } -static void bmdma_restart_dma(BMDMAState *bm, int is_read) +static void bmdma_restart_dma(BMDMAState *bm, enum ide_dma_cmd dma_cmd) { IDEState *s = bmdma_active_if(bm); @@ -178,33 +177,48 @@ s->io_buffer_index = 0; s->io_buffer_size = 0; s->nsector = bm->nsector; - s->is_read = is_read; + s->dma_cmd = dma_cmd; bm->cur_addr = bm->addr; bm->dma_cb = ide_dma_cb; bmdma_start_dma(&bm->dma, s, bm->dma_cb); } +/* TODO This should be common IDE code */ static void bmdma_restart_bh(void *opaque) { BMDMAState *bm = opaque; + IDEBus *bus = bm->bus; int is_read; + int error_status; qemu_bh_delete(bm->bh); bm->bh = NULL; - is_read = !!(bm->status & BM_STATUS_RETRY_READ); + if (bm->unit == (uint8_t) -1) { + return; + } + + is_read = !!(bus->error_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); + /* The error status must be cleared before resubmitting the request: The + * request may fail again, and this case can only be distinguished if the + * called function can set a new error status. */ + error_status = bus->error_status; + bus->error_status = 0; + + if (error_status & BM_STATUS_DMA_RETRY) { + if (error_status & BM_STATUS_RETRY_TRIM) { + bmdma_restart_dma(bm, IDE_DMA_TRIM); + } else { + bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE); + } + } else if (error_status & BM_STATUS_PIO_RETRY) { 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) { + } else if (error_status & BM_STATUS_RETRY_FLUSH) { ide_flush_cache(bmdma_active_if(bm)); } } @@ -297,12 +311,8 @@ */ 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 + assert(bm->bus->dma->aiocb == NULL); + assert((bm->status & BM_STATUS_DMAING) == 0); } } else { bm->cur_addr = bm->addr; @@ -356,6 +366,43 @@ return (bm->cur_prd_len != 0); } +static bool ide_bmdma_status_needed(void *opaque) +{ + BMDMAState *bm = opaque; + + /* Older versions abused some bits in the status register for internal + * error state. If any of these bits are set, we must add a subsection to + * transfer the real status register */ + uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; + + return ((bm->status & abused_bits) != 0); +} + +static void ide_bmdma_pre_save(void *opaque) +{ + BMDMAState *bm = opaque; + uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; + + bm->migration_compat_status = + (bm->status & ~abused_bits) | (bm->bus->error_status & abused_bits); +} + +/* This function accesses bm->bus->error_status which is loaded only after + * BMDMA itself. This is why the function is called from ide_pci_post_load + * instead of being registered with VMState where it would run too early. */ +static int ide_bmdma_post_load(void *opaque, int version_id) +{ + BMDMAState *bm = opaque; + uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; + + if (bm->status == 0) { + bm->status = bm->migration_compat_status & ~abused_bits; + bm->bus->error_status |= bm->migration_compat_status & abused_bits; + } + + return 0; +} + static const VMStateDescription vmstate_bmdma_current = { .name = "ide bmdma_current", .version_id = 1, @@ -370,15 +417,26 @@ } }; +const VMStateDescription vmstate_bmdma_status = { + .name ="ide bmdma/status", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(status, BMDMAState), + VMSTATE_END_OF_LIST() + } +}; static const VMStateDescription vmstate_bmdma = { .name = "ide bmdma", .version_id = 3, .minimum_version_id = 0, .minimum_version_id_old = 0, + .pre_save = ide_bmdma_pre_save, .fields = (VMStateField []) { VMSTATE_UINT8(cmd, BMDMAState), - VMSTATE_UINT8(status, BMDMAState), + VMSTATE_UINT8(migration_compat_status, BMDMAState), VMSTATE_UINT32(addr, BMDMAState), VMSTATE_INT64(sector_num, BMDMAState), VMSTATE_UINT32(nsector, BMDMAState), @@ -390,6 +448,9 @@ .vmsd = &vmstate_bmdma_current, .needed = ide_bmdma_current_needed, }, { + .vmsd = &vmstate_bmdma_status, + .needed = ide_bmdma_status_needed, + }, { /* empty */ } } @@ -404,7 +465,9 @@ /* current versions always store 0/1, but older version stored bigger values. We only need last bit */ d->bmdma[i].unit &= 1; + ide_bmdma_post_load(&d->bmdma[i], -1); } + return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/pci.h qemu-kvm-0.15.0+noroms/hw/ide/pci.h --- qemu-kvm-0.14.1+noroms/hw/ide/pci.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/pci.h 2011-08-09 07:40:29.000000000 -0500 @@ -22,6 +22,10 @@ IORange addr_ioport; QEMUBH *bh; qemu_irq irq; + + /* Bit 0-2 and 7: BM status register + * Bit 3-6: bus->error_status */ + uint8_t migration_compat_status; } BMDMAState; typedef struct PCIIDEState { diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/piix.c qemu-kvm-0.15.0+noroms/hw/ide/piix.c --- qemu-kvm-0.14.1+noroms/hw/ide/piix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/piix.c 2011-08-09 07:40:29.000000000 -0500 @@ -131,12 +131,12 @@ } } -static int pci_piix_ide_initfn(PCIIDEState *d) +static int pci_piix_ide_initfn(PCIDevice *dev) { + PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); uint8_t *pci_conf = d->dev.config; pci_conf[PCI_CLASS_PROG] = 0x80; // legacy ATA mode - pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE); qemu_register_reset(piix3_reset, d); @@ -149,24 +149,6 @@ return 0; } -static int pci_piix3_ide_initfn(PCIDevice *dev) -{ - PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); - - pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL); - pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82371SB_1); - return pci_piix_ide_initfn(d); -} - -static int pci_piix4_ide_initfn(PCIDevice *dev) -{ - PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); - - pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL); - pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82371AB); - return pci_piix_ide_initfn(d); -} - /* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) @@ -195,13 +177,19 @@ .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, .no_hotplug = 1, - .init = pci_piix3_ide_initfn, + .init = pci_piix_ide_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_1, + .class_id = PCI_CLASS_STORAGE_IDE, },{ .qdev.name = "piix4-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, .no_hotplug = 1, - .init = pci_piix4_ide_initfn, + .init = pci_piix_ide_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371AB, + .class_id = PCI_CLASS_STORAGE_IDE, },{ /* end of list */ } diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/qdev.c qemu-kvm-0.15.0+noroms/hw/ide/qdev.c --- qemu-kvm-0.14.1+noroms/hw/ide/qdev.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/qdev.c 2011-08-09 07:40:29.000000000 -0500 @@ -31,6 +31,10 @@ .name = "IDE", .size = sizeof(IDEBus), .get_fw_dev_path = idebus_get_fw_dev_path, + .props = (Property[]) { + DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), + DEFINE_PROP_END_OF_LIST(), + }, }; void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id) @@ -98,7 +102,7 @@ { DeviceState *dev; - dev = qdev_create(&bus->qbus, "ide-drive"); + dev = qdev_create(&bus->qbus, drive->media_cd ? "ide-cd" : "ide-hd"); qdev_prop_set_uint32(dev, "unit", unit); qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv); qdev_init_nofail(dev); @@ -118,13 +122,18 @@ IDEDevice dev; } IDEDrive; -static int ide_drive_initfn(IDEDevice *dev) +static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) { IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEState *s = bus->ifs + dev->unit; const char *serial; DriveInfo *dinfo; + if (dev->conf.discard_granularity && dev->conf.discard_granularity != 512) { + error_report("discard_granularity must be 512 for ide"); + return -1; + } + serial = dev->serial; if (!serial) { /* try to fall back to value set with legacy -drive serial=... */ @@ -134,7 +143,7 @@ } } - if (ide_init_drive(s, dev->conf.bs, dev->version, serial) < 0) { + if (ide_init_drive(s, dev->conf.bs, kind, dev->version, serial) < 0) { return -1; } @@ -151,22 +160,68 @@ 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_BLOCK_PROPERTIES(IDEDrive, dev.conf), - DEFINE_PROP_STRING("ver", IDEDrive, dev.version), - DEFINE_PROP_STRING("serial", IDEDrive, dev.serial), - DEFINE_PROP_END_OF_LIST(), +static int ide_hd_initfn(IDEDevice *dev) +{ + return ide_dev_initfn(dev, IDE_HD); +} + +static int ide_cd_initfn(IDEDevice *dev) +{ + return ide_dev_initfn(dev, IDE_CD); +} + +static int ide_drive_initfn(IDEDevice *dev) +{ + DriveInfo *dinfo = drive_get_by_blockdev(dev->conf.bs); + + return ide_dev_initfn(dev, dinfo->media_cd ? IDE_CD : IDE_HD); +} + +#define DEFINE_IDE_DEV_PROPERTIES() \ + DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ + DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ + DEFINE_PROP_STRING("serial", IDEDrive, dev.serial) + +static IDEDeviceInfo ide_dev_info[] = { + { + .qdev.name = "ide-hd", + .qdev.fw_name = "drive", + .qdev.desc = "virtual IDE disk", + .qdev.size = sizeof(IDEDrive), + .init = ide_hd_initfn, + .qdev.props = (Property[]) { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + } + },{ + .qdev.name = "ide-cd", + .qdev.fw_name = "drive", + .qdev.desc = "virtual IDE CD-ROM", + .qdev.size = sizeof(IDEDrive), + .init = ide_cd_initfn, + .qdev.props = (Property[]) { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + } + },{ + .qdev.name = "ide-drive", /* legacy -device ide-drive */ + .qdev.fw_name = "drive", + .qdev.desc = "virtual IDE disk or CD-ROM (legacy)", + .qdev.size = sizeof(IDEDrive), + .init = ide_drive_initfn, + .qdev.props = (Property[]) { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + } } }; -static void ide_drive_register(void) +static void ide_dev_register(void) { - ide_qdev_register(&ide_drive_info); + int i; + + for (i = 0; i < ARRAY_SIZE(ide_dev_info); i++) { + ide_qdev_register(&ide_dev_info[i]); + } } -device_init(ide_drive_register); +device_init(ide_dev_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/ide/via.c qemu-kvm-0.15.0+noroms/hw/ide/via.c --- qemu-kvm-0.14.1+noroms/hw/ide/via.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide/via.c 2011-08-09 07:40:29.000000000 -0500 @@ -160,11 +160,7 @@ 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); @@ -191,6 +187,10 @@ .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, .init = vt82c686b_ide_initfn, + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_IDE, + .revision = 0x06, + .class_id = PCI_CLASS_STORAGE_IDE, }; static void via_ide_register(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/ide.h qemu-kvm-0.15.0+noroms/hw/ide.h --- qemu-kvm-0.14.1+noroms/hw/ide.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ide.h 2011-08-09 07:40:29.000000000 -0500 @@ -28,4 +28,7 @@ void ide_get_bs(BlockDriverState *bs[], BusState *qbus); +/* ide/core.c */ +void ide_drive_get(DriveInfo **hd, int max_bus); + #endif /* HW_IDE_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/integratorcp.c qemu-kvm-0.15.0+noroms/hw/integratorcp.c --- qemu-kvm-0.14.1+noroms/hw/integratorcp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/integratorcp.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,13 +4,12 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" #include "primecell.h" #include "devices.h" -#include "sysemu.h" #include "boards.h" #include "arm-misc.h" #include "net.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/intel-hda.c qemu-kvm-0.15.0+noroms/hw/intel-hda.c --- qemu-kvm-0.14.1+noroms/hw/intel-hda.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/intel-hda.c 2011-08-09 07:40:29.000000000 -0500 @@ -224,19 +224,6 @@ 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; @@ -341,7 +328,7 @@ rp = (d->corb_rp + 1) & 0xff; addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); - verb = ldl_phys_le(addr + 4*rp); + verb = ldl_le_phys(addr + 4*rp); d->corb_rp = rp; dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb); @@ -373,8 +360,8 @@ 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); + stl_le_phys(addr + 8*wp, response); + stl_le_phys(addr + 8*wp + 4, ex); d->rirb_wp = wp; dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n", @@ -461,7 +448,7 @@ } if (d->dp_lbase & 0x01) { addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); - stl_phys_le(addr + 8*s, st->lpib); + stl_le_phys(addr + 8*s, st->lpib); } dprint(d, 3, "dma: --\n"); @@ -1109,14 +1096,6 @@ 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) @@ -1126,7 +1105,7 @@ HDACodecDevice *cdev; intel_hda_regs_reset(d); - d->wall_base_ns = qemu_get_clock(vm_clock); + d->wall_base_ns = qemu_get_clock_ns(vm_clock); /* reset codecs */ QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) { @@ -1146,10 +1125,6 @@ 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 */ @@ -1158,8 +1133,7 @@ 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); + pci_register_bar_simple(&d->pci, 0, 0x4000, 0, d->mmio_addr); if (d->msi) { msi_init(&d->pci, 0x50, 1, true, false); } @@ -1174,9 +1148,7 @@ { IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci); - if (d->msi) { - msi_uninit(&d->pci); - } + msi_uninit(&d->pci); cpu_unregister_io_memory(d->mmio_addr); return 0; } @@ -1276,6 +1248,10 @@ .init = intel_hda_init, .exit = intel_hda_exit, .config_write = intel_hda_write_config, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = 0x2668, + .revision = 1, + .class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1), diff -Nru qemu-kvm-0.14.1+noroms/hw/ioapic.c qemu-kvm-0.15.0+noroms/hw/ioapic.c --- qemu-kvm-0.14.1+noroms/hw/ioapic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ioapic.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,8 +23,6 @@ #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" @@ -41,7 +39,6 @@ #define DPRINTF(fmt, ...) #endif -#define IOAPIC_DEFAULT_BASE_ADDRESS 0xfec00000 #define MAX_IOAPICS 1 #define IOAPIC_VERSION 0x11 @@ -93,7 +90,6 @@ SysBusDevice busdev; uint8_t id; uint8_t ioregsel; - uint64_t base_address; uint32_t irr; uint64_t ioredtbl[IOAPIC_NUM_PINS]; }; @@ -149,7 +145,7 @@ * 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) { + if (vector == 0) { vector = 2; } if (vector >= 0 && vector < IOAPIC_NUM_PINS) { @@ -166,8 +162,9 @@ s->irr &= ~mask; } } else { - /* edge triggered */ - if (level) { + /* According to the 82093AA manual, we must ignore edge requests + * if the input pin is masked. */ + if (level && !(entry & IOAPIC_LVT_MASKED)) { s->irr |= mask; ioapic_service(s); } @@ -281,12 +278,11 @@ int i; chip.chip_id = KVM_IRQCHIP_IOAPIC; - kvm_get_irqchip(kvm_context, &chip); + kvm_get_irqchip(kvm_state, &chip); kioapic = &chip.chip.ioapic; s->id = kioapic->id; s->ioregsel = kioapic->ioregsel; - s->base_address = kioapic->base_address; s->irr = kioapic->irr; for (i = 0; i < IOAPIC_NUM_PINS; i++) { s->ioredtbl[i] = kioapic->redirtbl[i].bits; @@ -306,35 +302,25 @@ kioapic->id = s->id; kioapic->ioregsel = s->ioregsel; - kioapic->base_address = s->base_address; + kioapic->base_address = s->busdev.mmio[0].addr; kioapic->irr = s->irr; for (i = 0; i < IOAPIC_NUM_PINS; i++) { kioapic->redirtbl[i].bits = s->ioredtbl[i]; } - kvm_set_irqchip(kvm_context, &chip); + kvm_set_irqchip(kvm_state, &chip); #endif } static void ioapic_pre_save(void *opaque) { IOAPICState *s = (void *)opaque; - + if (kvm_enabled() && kvm_irqchip_in_kernel()) { kvm_kernel_ioapic_save_to_user(s); } } -static int ioapic_pre_load(void *opaque) -{ - IOAPICState *s = opaque; - - /* in case we are doing version 1, we just set these to sane values */ - s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS; - s->irr = 0; - return 0; -} - static int ioapic_post_load(void *opaque, int version_id) { IOAPICState *s = opaque; @@ -354,15 +340,14 @@ static const VMStateDescription vmstate_ioapic = { .name = "ioapic", .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[]) { VMSTATE_UINT8(id, IOAPICState), VMSTATE_UINT8(ioregsel, IOAPICState), - VMSTATE_UINT64_V(base_address, IOAPICState, 2), + VMSTATE_UNUSED_V(2, 8), /* to account for qemu-kvm's v2 format */ VMSTATE_UINT32_V(irr, IOAPICState, 2), VMSTATE_UINT64_ARRAY(ioredtbl, IOAPICState, IOAPIC_NUM_PINS), VMSTATE_END_OF_LIST() @@ -374,7 +359,6 @@ IOAPICState *s = DO_UPCAST(IOAPICState, busdev.qdev, d); int i; - s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS; s->id = 0; s->ioregsel = 0; s->irr = 0; diff -Nru qemu-kvm-0.14.1+noroms/hw/ioh3420.c qemu-kvm-0.15.0+noroms/hw/ioh3420.c --- qemu-kvm-0.14.1+noroms/hw/ioh3420.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ioh3420.c 2011-08-09 07:40:29.000000000 -0500 @@ -104,12 +104,8 @@ 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) { @@ -217,6 +213,9 @@ .config_write = ioh3420_write_config, .init = ioh3420_initfn, .exit = ioh3420_exitfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_IOH_EPORT, + .revision = PCI_DEVICE_ID_IOH_REV, .qdev.props = (Property[]) { DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), diff -Nru qemu-kvm-0.14.1+noroms/hw/irq.c qemu-kvm-0.15.0+noroms/hw/irq.c --- qemu-kvm-0.14.1+noroms/hw/irq.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/irq.c 2011-08-09 07:40:29.000000000 -0500 @@ -75,3 +75,18 @@ qemu_irq_raise(irq); return qemu_allocate_irqs(qemu_notirq, irq, 1)[0]; } + +static void qemu_splitirq(void *opaque, int line, int level) +{ + struct IRQState **irq = opaque; + irq[0]->handler(irq[0]->opaque, irq[0]->n, level); + irq[1]->handler(irq[1]->opaque, irq[1]->n, level); +} + +qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2) +{ + qemu_irq *s = qemu_mallocz(2 * sizeof(qemu_irq)); + s[0] = irq1; + s[1] = irq2; + return qemu_allocate_irqs(qemu_splitirq, s, 1)[0]; +} diff -Nru qemu-kvm-0.14.1+noroms/hw/irq.h qemu-kvm-0.15.0+noroms/hw/irq.h --- qemu-kvm-0.14.1+noroms/hw/irq.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/irq.h 2011-08-09 07:40:29.000000000 -0500 @@ -3,9 +3,7 @@ /* Generic IRQ/GPIO pin infrastructure. */ -/* FIXME: Rmove one of these. */ typedef void (*qemu_irq_handler)(void *opaque, int n, int level); -typedef void SetIRQFunc(void *opaque, int irq_num, int level); void qemu_set_irq(qemu_irq irq, int level); @@ -32,4 +30,7 @@ /* Returns a new IRQ with opposite polarity. */ qemu_irq qemu_irq_invert(qemu_irq irq); +/* Returns a new IRQ which feeds into both the passed IRQs */ +qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2); + #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/isa-bus.c qemu-kvm-0.15.0+noroms/hw/isa-bus.c --- qemu-kvm-0.14.1+noroms/hw/isa-bus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/isa-bus.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,7 +17,6 @@ * License along with this library; if not, see . */ #include "hw.h" -#include "sysemu.h" #include "monitor.h" #include "sysbus.h" #include "isa.h" @@ -67,7 +66,7 @@ */ qemu_irq isa_get_irq(int isairq) { - if (isairq < 0 || isairq > 15) { + if (isairq < 0 || isairq > 23) { hw_error("isa irq %d invalid", isairq); } return isabus->irqs[isairq]; @@ -136,6 +135,18 @@ return DO_UPCAST(ISADevice, qdev, dev); } +ISADevice *isa_try_create(const char *name) +{ + DeviceState *dev; + + if (!isabus) { + hw_error("Tried to create isa device %s with no isa bus present.", + name); + } + dev = qdev_try_create(&isabus->qbus, name); + return DO_UPCAST(ISADevice, qdev, dev); +} + ISADevice *isa_create_simple(const char *name) { ISADevice *dev; diff -Nru qemu-kvm-0.14.1+noroms/hw/isa.h qemu-kvm-0.15.0+noroms/hw/isa.h --- qemu-kvm-0.14.1+noroms/hw/isa.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/isa.h 2011-08-09 07:40:29.000000000 -0500 @@ -32,6 +32,7 @@ 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_try_create(const char *name); ISADevice *isa_create_simple(const char *name); extern target_phys_addr_t isa_mem_base; diff -Nru qemu-kvm-0.14.1+noroms/hw/ivshmem.c qemu-kvm-0.15.0+noroms/hw/ivshmem.c --- qemu-kvm-0.14.1+noroms/hw/ivshmem.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ivshmem.c 2011-08-09 07:40:29.000000000 -0500 @@ -706,12 +706,7 @@ } 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); @@ -809,6 +804,9 @@ .qdev.reset = ivshmem_reset, .init = pci_ivshmem_init, .exit = pci_ivshmem_uninit, + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = 0x1110, + .class_id = PCI_CLASS_MEMORY_RAM, .qdev.props = (Property[]) { DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), DEFINE_PROP_STRING("size", IVShmemState, sizearg), diff -Nru qemu-kvm-0.14.1+noroms/hw/kvmclock.c qemu-kvm-0.15.0+noroms/hw/kvmclock.c --- qemu-kvm-0.14.1+noroms/hw/kvmclock.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/kvmclock.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,120 @@ +/* + * QEMU KVM support, paravirtual clock device + * + * Copyright (C) 2011 Siemens AG + * + * Authors: + * Jan Kiszka + * + * 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 "sysemu.h" +#include "sysbus.h" +#include "kvm.h" +#include "kvmclock.h" + +#include +#include + +typedef struct KVMClockState { + SysBusDevice busdev; + uint64_t clock; + bool clock_valid; +} KVMClockState; + +static void kvmclock_pre_save(void *opaque) +{ + KVMClockState *s = opaque; + struct kvm_clock_data data; + int ret; + + if (s->clock_valid) { + return; + } + ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data); + if (ret < 0) { + fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret)); + data.clock = 0; + } + s->clock = data.clock; + /* + * If the VM is stopped, declare the clock state valid to avoid re-reading + * it on next vmsave (which would return a different value). Will be reset + * when the VM is continued. + */ + s->clock_valid = !vm_running; +} + +static int kvmclock_post_load(void *opaque, int version_id) +{ + KVMClockState *s = opaque; + struct kvm_clock_data data; + + data.clock = s->clock; + data.flags = 0; + return kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data); +} + +static void kvmclock_vm_state_change(void *opaque, int running, int reason) +{ + KVMClockState *s = opaque; + + if (running) { + s->clock_valid = false; + } +} + +static int kvmclock_init(SysBusDevice *dev) +{ + KVMClockState *s = FROM_SYSBUS(KVMClockState, dev); + + qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s); + return 0; +} + +static const VMStateDescription kvmclock_vmsd = { + .name = "kvmclock", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = kvmclock_pre_save, + .post_load = kvmclock_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(clock, KVMClockState), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo kvmclock_info = { + .qdev.name = "kvmclock", + .qdev.size = sizeof(KVMClockState), + .qdev.vmsd = &kvmclock_vmsd, + .qdev.no_user = 1, + .init = kvmclock_init, +}; + +/* Note: Must be called after VCPU initialization. */ +void kvmclock_create(void) +{ + if (kvm_enabled() && + first_cpu->cpuid_kvm_features & ((1ULL << KVM_FEATURE_CLOCKSOURCE) +#ifdef KVM_FEATURE_CLOCKSOURCE2 + || (1ULL << KVM_FEATURE_CLOCKSOURCE2) +#endif + )) { + sysbus_create_simple("kvmclock", -1, NULL); + } +} + +static void kvmclock_register_device(void) +{ + if (kvm_enabled()) { + sysbus_register_withprop(&kvmclock_info); + } +} + +device_init(kvmclock_register_device); diff -Nru qemu-kvm-0.14.1+noroms/hw/kvmclock.h qemu-kvm-0.15.0+noroms/hw/kvmclock.h --- qemu-kvm-0.14.1+noroms/hw/kvmclock.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/kvmclock.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,24 @@ +/* + * QEMU KVM support, paravirtual clock device + * + * Copyright (C) 2011 Siemens AG + * + * Authors: + * Jan Kiszka + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#ifdef CONFIG_KVM + +void kvmclock_create(void); + +#else /* CONFIG_KVM */ + +static inline void kvmclock_create(void) +{ +} + +#endif /* !CONFIG_KVM */ diff -Nru qemu-kvm-0.14.1+noroms/hw/lan9118.c qemu-kvm-0.15.0+noroms/hw/lan9118.c --- qemu-kvm-0.14.1+noroms/hw/lan9118.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/lan9118.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2 + * This code is licensed under the GNU GPL v2 */ #include "sysbus.h" @@ -228,6 +228,12 @@ if ((s->irq_cfg & IRQ_EN) == 0) { level = 0; } + if ((s->irq_cfg & (IRQ_TYPE | IRQ_POL)) != (IRQ_TYPE | IRQ_POL)) { + /* Interrupt is active low unless we're configured as + * active-high polarity, push-pull type. + */ + level = !level; + } qemu_set_irq(s->irq, level); } @@ -294,8 +300,7 @@ static void lan9118_reset(DeviceState *d) { lan9118_state *s = FROM_SYSBUS(lan9118_state, sysbus_from_qdev(d)); - - s->irq_cfg &= ~(IRQ_TYPE | IRQ_POL); + s->irq_cfg &= (IRQ_TYPE | IRQ_POL); s->int_sts = 0; s->int_en = 0; s->fifo_int = 0x48000000; @@ -327,7 +332,7 @@ s->afc_cfg = 0; s->e2p_cmd = 0; s->e2p_data = 0; - s->free_timer_start = qemu_get_clock(vm_clock) / 40; + s->free_timer_start = qemu_get_clock_ns(vm_clock) / 40; ptimer_stop(s->timer); ptimer_set_count(s->timer, 0xffff); @@ -721,7 +726,7 @@ break; } s->phy_control = val & 0x7980; - /* Complete autonegotiation imediately. */ + /* Complete autonegotiation immediately. */ if (val & 0x1000) { s->phy_status |= 0x0020; } @@ -904,7 +909,8 @@ switch (offset) { case CSR_IRQ_CFG: /* TODO: Implement interrupt deassertion intervals. */ - s->irq_cfg = (s->irq_cfg & IRQ_INT) | (val & IRQ_EN); + val &= (IRQ_EN | IRQ_POL | IRQ_TYPE); + s->irq_cfg = (s->irq_cfg & IRQ_INT) | val; break; case CSR_INT_STS: s->int_sts &= ~val; @@ -1076,7 +1082,7 @@ case CSR_WORD_SWAP: return s->word_swap; case CSR_FREE_RUN: - return (qemu_get_clock(vm_clock) / 40) - s->free_timer_start; + return (qemu_get_clock_ns(vm_clock) / 40) - s->free_timer_start; case CSR_RX_DROP: /* TODO: Implement dropped frames counter. */ return 0; diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_boards.c qemu-kvm-0.15.0+noroms/hw/lm32_boards.c --- qemu-kvm-0.14.1+noroms/hw/lm32_boards.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_boards.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,304 @@ +/* + * QEMU models for LatticeMico32 uclinux and evr32 boards. + * + * Copyright (c) 2010 Michael Walle + * + * 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 "sysbus.h" +#include "hw.h" +#include "net.h" +#include "flash.h" +#include "devices.h" +#include "boards.h" +#include "loader.h" +#include "blockdev.h" +#include "elf.h" +#include "lm32_hwsetup.h" +#include "lm32.h" + +typedef struct { + CPUState *env; + target_phys_addr_t bootstrap_pc; + target_phys_addr_t flash_base; + target_phys_addr_t hwsetup_base; + target_phys_addr_t initrd_base; + size_t initrd_size; + target_phys_addr_t cmdline_base; +} ResetInfo; + +static void cpu_irq_handler(void *opaque, int irq, int level) +{ + CPUState *env = opaque; + + if (level) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void main_cpu_reset(void *opaque) +{ + ResetInfo *reset_info = opaque; + CPUState *env = reset_info->env; + + cpu_reset(env); + + /* init defaults */ + env->pc = (uint32_t)reset_info->bootstrap_pc; + env->regs[R_R1] = (uint32_t)reset_info->hwsetup_base; + env->regs[R_R2] = (uint32_t)reset_info->cmdline_base; + env->regs[R_R3] = (uint32_t)reset_info->initrd_base; + env->regs[R_R4] = (uint32_t)(reset_info->initrd_base + + reset_info->initrd_size); + env->eba = reset_info->flash_base; + env->deba = reset_info->flash_base; +} + +static void lm32_evr_init(ram_addr_t ram_size_not_used, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUState *env; + DriveInfo *dinfo; + ram_addr_t phys_ram; + ram_addr_t phys_flash; + qemu_irq *cpu_irq, irq[32]; + ResetInfo *reset_info; + int i; + + /* memory map */ + target_phys_addr_t flash_base = 0x04000000; + size_t flash_sector_size = 256 * 1024; + size_t flash_size = 32 * 1024 * 1024; + target_phys_addr_t ram_base = 0x08000000; + size_t ram_size = 64 * 1024 * 1024; + target_phys_addr_t timer0_base = 0x80002000; + target_phys_addr_t uart0_base = 0x80006000; + target_phys_addr_t timer1_base = 0x8000a000; + int uart0_irq = 0; + int timer0_irq = 1; + int timer1_irq = 3; + + reset_info = qemu_mallocz(sizeof(ResetInfo)); + + if (cpu_model == NULL) { + cpu_model = "lm32-full"; + } + env = cpu_init(cpu_model); + reset_info->env = env; + + reset_info->flash_base = flash_base; + + phys_ram = qemu_ram_alloc(NULL, "lm32_evr.sdram", ram_size); + cpu_register_physical_memory(ram_base, ram_size, phys_ram | IO_MEM_RAM); + + phys_flash = qemu_ram_alloc(NULL, "lm32_evr.flash", flash_size); + dinfo = drive_get(IF_PFLASH, 0, 0); + /* Spansion S29NS128P */ + pflash_cfi02_register(flash_base, phys_flash, + dinfo ? dinfo->bdrv : NULL, flash_sector_size, + flash_size / flash_sector_size, 1, 2, + 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); + + /* create irq lines */ + cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1); + env->pic_state = lm32_pic_init(*cpu_irq); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(env->pic_state, i); + } + + sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]); + sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); + sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); + + /* make sure juart isn't the first chardev */ + env->juart_state = lm32_juart_init(); + + reset_info->bootstrap_pc = flash_base; + + if (kernel_filename) { + uint64_t entry; + int kernel_size; + + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + 1, ELF_MACHINE, 0); + reset_info->bootstrap_pc = entry; + + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, ram_base, + ram_size); + reset_info->bootstrap_pc = ram_base; + } + + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + } + + qemu_register_reset(main_cpu_reset, reset_info); +} + +static void lm32_uclinux_init(ram_addr_t ram_size_not_used, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUState *env; + DriveInfo *dinfo; + ram_addr_t phys_ram; + ram_addr_t phys_flash; + qemu_irq *cpu_irq, irq[32]; + HWSetup *hw; + ResetInfo *reset_info; + int i; + + /* memory map */ + target_phys_addr_t flash_base = 0x04000000; + size_t flash_sector_size = 256 * 1024; + size_t flash_size = 32 * 1024 * 1024; + target_phys_addr_t ram_base = 0x08000000; + size_t ram_size = 64 * 1024 * 1024; + target_phys_addr_t uart0_base = 0x80000000; + target_phys_addr_t timer0_base = 0x80002000; + target_phys_addr_t timer1_base = 0x80010000; + target_phys_addr_t timer2_base = 0x80012000; + int uart0_irq = 0; + int timer0_irq = 1; + int timer1_irq = 20; + int timer2_irq = 21; + target_phys_addr_t hwsetup_base = 0x0bffe000; + target_phys_addr_t cmdline_base = 0x0bfff000; + target_phys_addr_t initrd_base = 0x08400000; + size_t initrd_max = 0x01000000; + + reset_info = qemu_mallocz(sizeof(ResetInfo)); + + if (cpu_model == NULL) { + cpu_model = "lm32-full"; + } + env = cpu_init(cpu_model); + reset_info->env = env; + + reset_info->flash_base = flash_base; + + phys_ram = qemu_ram_alloc(NULL, "lm32_uclinux.sdram", ram_size); + cpu_register_physical_memory(ram_base, ram_size, phys_ram | IO_MEM_RAM); + + phys_flash = qemu_ram_alloc(NULL, "lm32_uclinux.flash", flash_size); + dinfo = drive_get(IF_PFLASH, 0, 0); + /* Spansion S29NS128P */ + pflash_cfi02_register(flash_base, phys_flash, + dinfo ? dinfo->bdrv : NULL, flash_sector_size, + flash_size / flash_sector_size, 1, 2, + 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); + + /* create irq lines */ + cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1); + env->pic_state = lm32_pic_init(*cpu_irq); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(env->pic_state, i); + } + + sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]); + sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); + sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); + sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]); + + /* make sure juart isn't the first chardev */ + env->juart_state = lm32_juart_init(); + + reset_info->bootstrap_pc = flash_base; + + if (kernel_filename) { + uint64_t entry; + int kernel_size; + + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + 1, ELF_MACHINE, 0); + reset_info->bootstrap_pc = entry; + + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, ram_base, + ram_size); + reset_info->bootstrap_pc = ram_base; + } + + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + } + + /* generate a rom with the hardware description */ + hw = hwsetup_init(); + hwsetup_add_cpu(hw, "LM32", 75000000); + hwsetup_add_flash(hw, "flash", flash_base, flash_size); + hwsetup_add_ddr_sdram(hw, "ddr_sdram", ram_base, ram_size); + hwsetup_add_timer(hw, "timer0", timer0_base, timer0_irq); + hwsetup_add_timer(hw, "timer1_dev_only", timer1_base, timer1_irq); + hwsetup_add_timer(hw, "timer2_dev_only", timer2_base, timer2_irq); + hwsetup_add_uart(hw, "uart", uart0_base, uart0_irq); + hwsetup_add_trailer(hw); + hwsetup_create_rom(hw, hwsetup_base); + hwsetup_free(hw); + + reset_info->hwsetup_base = hwsetup_base; + + if (kernel_cmdline && strlen(kernel_cmdline)) { + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, + kernel_cmdline); + reset_info->cmdline_base = cmdline_base; + } + + if (initrd_filename) { + size_t initrd_size; + initrd_size = load_image_targphys(initrd_filename, initrd_base, + initrd_max); + reset_info->initrd_base = initrd_base; + reset_info->initrd_size = initrd_size; + } + + qemu_register_reset(main_cpu_reset, reset_info); +} + +static QEMUMachine lm32_evr_machine = { + .name = "lm32-evr", + .desc = "LatticeMico32 EVR32 eval system", + .init = lm32_evr_init, + .is_default = 1 +}; + +static QEMUMachine lm32_uclinux_machine = { + .name = "lm32-uclinux", + .desc = "lm32 platform for uClinux and u-boot by Theobroma Systems", + .init = lm32_uclinux_init, + .is_default = 0 +}; + +static void lm32_machine_init(void) +{ + qemu_register_machine(&lm32_uclinux_machine); + qemu_register_machine(&lm32_evr_machine); +} + +machine_init(lm32_machine_init); diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32.h qemu-kvm-0.15.0+noroms/hw/lm32.h --- qemu-kvm-0.14.1+noroms/hw/lm32.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,25 @@ + +#include "qemu-common.h" + +static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq) +{ + DeviceState *dev; + SysBusDevice *d; + + dev = qdev_create(NULL, "lm32-pic"); + qdev_init_nofail(dev); + d = sysbus_from_qdev(dev); + sysbus_connect_irq(d, 0, cpu_irq); + + return dev; +} + +static inline DeviceState *lm32_juart_init(void) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "lm32-juart"); + qdev_init_nofail(dev); + + return dev; +} diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_hwsetup.h qemu-kvm-0.15.0+noroms/hw/lm32_hwsetup.h --- qemu-kvm-0.14.1+noroms/hw/lm32_hwsetup.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_hwsetup.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,178 @@ +/* + * LatticeMico32 hwsetup helper functions. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + */ + +/* + * These are helper functions for creating the hardware description blob used + * in the Theobroma's uClinux port. + */ + +#ifndef QEMU_HW_LM32_HWSETUP_H +#define QEMU_HW_LM32_HWSETUP_H + +#include "qemu-common.h" +#include "loader.h" + +typedef struct { + void *data; + void *ptr; +} HWSetup; + +enum hwsetup_tag { + HWSETUP_TAG_EOL = 0, + HWSETUP_TAG_CPU = 1, + HWSETUP_TAG_ASRAM = 2, + HWSETUP_TAG_FLASH = 3, + HWSETUP_TAG_SDRAM = 4, + HWSETUP_TAG_OCM = 5, + HWSETUP_TAG_DDR_SDRAM = 6, + HWSETUP_TAG_DDR2_SDRAM = 7, + HWSETUP_TAG_TIMER = 8, + HWSETUP_TAG_UART = 9, + HWSETUP_TAG_GPIO = 10, + HWSETUP_TAG_TRISPEEDMAC = 11, + HWSETUP_TAG_I2CM = 12, + HWSETUP_TAG_LEDS = 13, + HWSETUP_TAG_7SEG = 14, + HWSETUP_TAG_SPI_S = 15, + HWSETUP_TAG_SPI_M = 16, +}; + +static inline HWSetup *hwsetup_init(void) +{ + HWSetup *hw; + + hw = qemu_malloc(sizeof(HWSetup)); + hw->data = qemu_mallocz(TARGET_PAGE_SIZE); + hw->ptr = hw->data; + + return hw; +} + +static inline void hwsetup_free(HWSetup *hw) +{ + qemu_free(hw->data); + qemu_free(hw); +} + +static inline void hwsetup_create_rom(HWSetup *hw, + target_phys_addr_t base) +{ + rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, base); +} + +static inline void hwsetup_add_u8(HWSetup *hw, uint8_t u) +{ + stb_p(hw->ptr, u); + hw->ptr += 1; +} + +static inline void hwsetup_add_u32(HWSetup *hw, uint32_t u) +{ + stl_p(hw->ptr, u); + hw->ptr += 4; +} + +static inline void hwsetup_add_tag(HWSetup *hw, enum hwsetup_tag t) +{ + stl_p(hw->ptr, t); + hw->ptr += 4; +} + +static inline void hwsetup_add_str(HWSetup *hw, const char *str) +{ + strncpy(hw->ptr, str, 31); /* make sure last byte is zero */ + hw->ptr += 32; +} + +static inline void hwsetup_add_trailer(HWSetup *hw) +{ + hwsetup_add_u32(hw, 8); /* size */ + hwsetup_add_tag(hw, HWSETUP_TAG_EOL); +} + +static inline void hwsetup_add_cpu(HWSetup *hw, + const char *name, uint32_t frequency) +{ + hwsetup_add_u32(hw, 44); /* size */ + hwsetup_add_tag(hw, HWSETUP_TAG_CPU); + hwsetup_add_str(hw, name); + hwsetup_add_u32(hw, frequency); +} + +static inline void hwsetup_add_flash(HWSetup *hw, + const char *name, uint32_t base, uint32_t size) +{ + hwsetup_add_u32(hw, 52); /* size */ + hwsetup_add_tag(hw, HWSETUP_TAG_FLASH); + hwsetup_add_str(hw, name); + hwsetup_add_u32(hw, base); + hwsetup_add_u32(hw, size); + hwsetup_add_u8(hw, 8); /* read latency */ + hwsetup_add_u8(hw, 8); /* write latency */ + hwsetup_add_u8(hw, 25); /* address width */ + hwsetup_add_u8(hw, 32); /* data width */ +} + +static inline void hwsetup_add_ddr_sdram(HWSetup *hw, + const char *name, uint32_t base, uint32_t size) +{ + hwsetup_add_u32(hw, 48); /* size */ + hwsetup_add_tag(hw, HWSETUP_TAG_DDR_SDRAM); + hwsetup_add_str(hw, name); + hwsetup_add_u32(hw, base); + hwsetup_add_u32(hw, size); +} + +static inline void hwsetup_add_timer(HWSetup *hw, + const char *name, uint32_t base, uint32_t irq) +{ + hwsetup_add_u32(hw, 56); /* size */ + hwsetup_add_tag(hw, HWSETUP_TAG_TIMER); + hwsetup_add_str(hw, name); + hwsetup_add_u32(hw, base); + hwsetup_add_u8(hw, 1); /* wr_tickcount */ + hwsetup_add_u8(hw, 1); /* rd_tickcount */ + hwsetup_add_u8(hw, 1); /* start_stop_control */ + hwsetup_add_u8(hw, 32); /* counter_width */ + hwsetup_add_u32(hw, 20); /* reload_ticks */ + hwsetup_add_u8(hw, irq); + hwsetup_add_u8(hw, 0); /* padding */ + hwsetup_add_u8(hw, 0); /* padding */ + hwsetup_add_u8(hw, 0); /* padding */ +} + +static inline void hwsetup_add_uart(HWSetup *hw, + const char *name, uint32_t base, uint32_t irq) +{ + hwsetup_add_u32(hw, 56); /* size */ + hwsetup_add_tag(hw, HWSETUP_TAG_UART); + hwsetup_add_str(hw, name); + hwsetup_add_u32(hw, base); + hwsetup_add_u32(hw, 115200); /* baudrate */ + hwsetup_add_u8(hw, 8); /* databits */ + hwsetup_add_u8(hw, 1); /* stopbits */ + hwsetup_add_u8(hw, 1); /* use_interrupt */ + hwsetup_add_u8(hw, 1); /* block_on_transmit */ + hwsetup_add_u8(hw, 1); /* block_on_receive */ + hwsetup_add_u8(hw, 4); /* rx_buffer_size */ + hwsetup_add_u8(hw, 4); /* tx_buffer_size */ + hwsetup_add_u8(hw, irq); +} + +#endif /* QEMU_HW_LM32_HWSETUP_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_juart.c qemu-kvm-0.15.0+noroms/hw/lm32_juart.c --- qemu-kvm-0.14.1+noroms/hw/lm32_juart.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_juart.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,150 @@ +/* + * LatticeMico32 JTAG UART model. + * + * Copyright (c) 2010 Michael Walle + * + * 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 "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-char.h" + +#include "lm32_juart.h" + +enum { + LM32_JUART_MIN_SAVE_VERSION = 0, + LM32_JUART_CURRENT_SAVE_VERSION = 0, + LM32_JUART_MAX_SAVE_VERSION = 0, +}; + +enum { + JTX_FULL = (1<<8), +}; + +enum { + JRX_FULL = (1<<8), +}; + +struct LM32JuartState { + SysBusDevice busdev; + CharDriverState *chr; + + uint32_t jtx; + uint32_t jrx; +}; +typedef struct LM32JuartState LM32JuartState; + +uint32_t lm32_juart_get_jtx(DeviceState *d) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + trace_lm32_juart_get_jtx(s->jtx); + return s->jtx; +} + +uint32_t lm32_juart_get_jrx(DeviceState *d) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + trace_lm32_juart_get_jrx(s->jrx); + return s->jrx; +} + +void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + unsigned char ch = jtx & 0xff; + + trace_lm32_juart_set_jtx(s->jtx); + + s->jtx = jtx; + if (s->chr) { + qemu_chr_write(s->chr, &ch, 1); + } +} + +void lm32_juart_set_jrx(DeviceState *d, uint32_t jtx) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + trace_lm32_juart_set_jrx(s->jrx); + s->jrx &= ~JRX_FULL; +} + +static void juart_rx(void *opaque, const uint8_t *buf, int size) +{ + LM32JuartState *s = opaque; + + s->jrx = *buf | JRX_FULL; +} + +static int juart_can_rx(void *opaque) +{ + LM32JuartState *s = opaque; + + return !(s->jrx & JRX_FULL); +} + +static void juart_event(void *opaque, int event) +{ +} + +static void juart_reset(DeviceState *d) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + s->jtx = 0; + s->jrx = 0; +} + +static int lm32_juart_init(SysBusDevice *dev) +{ + LM32JuartState *s = FROM_SYSBUS(typeof(*s), dev); + + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { + qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s); + } + + return 0; +} + +static const VMStateDescription vmstate_lm32_juart = { + .name = "lm32-juart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(jtx, LM32JuartState), + VMSTATE_UINT32(jrx, LM32JuartState), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo lm32_juart_info = { + .init = lm32_juart_init, + .qdev.name = "lm32-juart", + .qdev.size = sizeof(LM32JuartState), + .qdev.vmsd = &vmstate_lm32_juart, + .qdev.reset = juart_reset, +}; + +static void lm32_juart_register(void) +{ + sysbus_register_withprop(&lm32_juart_info); +} + +device_init(lm32_juart_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_juart.h qemu-kvm-0.15.0+noroms/hw/lm32_juart.h --- qemu-kvm-0.14.1+noroms/hw/lm32_juart.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_juart.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,11 @@ +#ifndef QEMU_HW_LM32_JUART_H +#define QEMU_HW_LM32_JUART_H + +#include "qemu-common.h" + +uint32_t lm32_juart_get_jtx(DeviceState *d); +uint32_t lm32_juart_get_jrx(DeviceState *d); +void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx); +void lm32_juart_set_jrx(DeviceState *d, uint32_t jrx); + +#endif /* QEMU_HW_LM32_JUART_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_pic.c qemu-kvm-0.15.0+noroms/hw/lm32_pic.c --- qemu-kvm-0.14.1+noroms/hw/lm32_pic.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_pic.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,190 @@ +/* + * LatticeMico32 CPU interrupt controller logic. + * + * Copyright (c) 2010 Michael Walle + * + * 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 "hw.h" +#include "pc.h" +#include "monitor.h" +#include "sysbus.h" +#include "trace.h" +#include "lm32_pic.h" + +struct LM32PicState { + SysBusDevice busdev; + qemu_irq parent_irq; + uint32_t im; /* interrupt mask */ + uint32_t ip; /* interrupt pending */ + uint32_t irq_state; + + /* statistics */ + uint32_t stats_irq_count[32]; +}; +typedef struct LM32PicState LM32PicState; + +static LM32PicState *pic; +void pic_info(Monitor *mon) +{ + if (pic == NULL) { + return; + } + + monitor_printf(mon, "lm32-pic: im=%08x ip=%08x irq_state=%08x\n", + pic->im, pic->ip, pic->irq_state); +} + +void irq_info(Monitor *mon) +{ + int i; + uint32_t count; + + if (pic == NULL) { + return; + } + + monitor_printf(mon, "IRQ statistics:\n"); + for (i = 0; i < 32; i++) { + count = pic->stats_irq_count[i]; + if (count > 0) { + monitor_printf(mon, "%2d: %u\n", i, count); + } + } +} + +static void update_irq(LM32PicState *s) +{ + s->ip |= s->irq_state; + + if (s->ip & s->im) { + trace_lm32_pic_raise_irq(); + qemu_irq_raise(s->parent_irq); + } else { + trace_lm32_pic_lower_irq(); + qemu_irq_lower(s->parent_irq); + } +} + +static void irq_handler(void *opaque, int irq, int level) +{ + LM32PicState *s = opaque; + + assert(irq < 32); + trace_lm32_pic_interrupt(irq, level); + + if (level) { + s->irq_state |= (1 << irq); + s->stats_irq_count[irq]++; + } else { + s->irq_state &= ~(1 << irq); + } + + update_irq(s); +} + +void lm32_pic_set_im(DeviceState *d, uint32_t im) +{ + LM32PicState *s = container_of(d, LM32PicState, busdev.qdev); + + trace_lm32_pic_set_im(im); + s->im = im; + + update_irq(s); +} + +void lm32_pic_set_ip(DeviceState *d, uint32_t ip) +{ + LM32PicState *s = container_of(d, LM32PicState, busdev.qdev); + + trace_lm32_pic_set_ip(ip); + + /* ack interrupt */ + s->ip &= ~ip; + + update_irq(s); +} + +uint32_t lm32_pic_get_im(DeviceState *d) +{ + LM32PicState *s = container_of(d, LM32PicState, busdev.qdev); + + trace_lm32_pic_get_im(s->im); + return s->im; +} + +uint32_t lm32_pic_get_ip(DeviceState *d) +{ + LM32PicState *s = container_of(d, LM32PicState, busdev.qdev); + + trace_lm32_pic_get_ip(s->ip); + return s->ip; +} + +static void pic_reset(DeviceState *d) +{ + LM32PicState *s = container_of(d, LM32PicState, busdev.qdev); + int i; + + s->im = 0; + s->ip = 0; + s->irq_state = 0; + for (i = 0; i < 32; i++) { + s->stats_irq_count[i] = 0; + } +} + +static int lm32_pic_init(SysBusDevice *dev) +{ + LM32PicState *s = FROM_SYSBUS(typeof(*s), dev); + + qdev_init_gpio_in(&dev->qdev, irq_handler, 32); + sysbus_init_irq(dev, &s->parent_irq); + + pic = s; + + return 0; +} + +static const VMStateDescription vmstate_lm32_pic = { + .name = "lm32-pic", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(im, LM32PicState), + VMSTATE_UINT32(ip, LM32PicState), + VMSTATE_UINT32(irq_state, LM32PicState), + VMSTATE_UINT32_ARRAY(stats_irq_count, LM32PicState, 32), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo lm32_pic_info = { + .init = lm32_pic_init, + .qdev.name = "lm32-pic", + .qdev.size = sizeof(LM32PicState), + .qdev.vmsd = &vmstate_lm32_pic, + .qdev.reset = pic_reset, +}; + +static void lm32_pic_register(void) +{ + sysbus_register_withprop(&lm32_pic_info); +} + +device_init(lm32_pic_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_pic.h qemu-kvm-0.15.0+noroms/hw/lm32_pic.h --- qemu-kvm-0.14.1+noroms/hw/lm32_pic.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_pic.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,11 @@ +#ifndef QEMU_HW_LM32_PIC_H +#define QEMU_HW_LM32_PIC_H + +#include "qemu-common.h" + +uint32_t lm32_pic_get_ip(DeviceState *d); +uint32_t lm32_pic_get_im(DeviceState *d); +void lm32_pic_set_ip(DeviceState *d, uint32_t ip); +void lm32_pic_set_im(DeviceState *d, uint32_t im); + +#endif /* QEMU_HW_LM32_PIC_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_sys.c qemu-kvm-0.15.0+noroms/hw/lm32_sys.c --- qemu-kvm-0.14.1+noroms/hw/lm32_sys.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_sys.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,161 @@ +/* + * QEMU model of the LatticeMico32 system control block. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + */ + +/* + * This model is mainly intended for testing purposes and doesn't fit to any + * real hardware. On the one hand it provides a control register (R_CTRL) on + * the other hand it supports the lm32 tests. + * + * A write to the control register causes a system shutdown. + * Tests first write the pointer to a test name to the test name register + * (R_TESTNAME) and then write a zero to the pass/fail register (R_PASSFAIL) if + * the test is passed or any non-zero value to it if the test is failed. + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-log.h" +#include "qemu-error.h" +#include "sysemu.h" +#include "qemu-log.h" + +enum { + R_CTRL = 0, + R_PASSFAIL, + R_TESTNAME, + R_MAX +}; + +#define MAX_TESTNAME_LEN 16 + +struct LM32SysState { + SysBusDevice busdev; + uint32_t base; + uint32_t regs[R_MAX]; + uint8_t testname[MAX_TESTNAME_LEN]; +}; +typedef struct LM32SysState LM32SysState; + +static void copy_testname(LM32SysState *s) +{ + cpu_physical_memory_read(s->regs[R_TESTNAME], s->testname, + MAX_TESTNAME_LEN); + s->testname[MAX_TESTNAME_LEN - 1] = '\0'; +} + +static void sys_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + LM32SysState *s = opaque; + char *testname; + + trace_lm32_sys_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTRL: + qemu_system_shutdown_request(); + break; + case R_PASSFAIL: + s->regs[addr] = value; + testname = (char *)s->testname; + qemu_log("TC %-16s %s\n", testname, (value) ? "FAILED" : "OK"); + break; + case R_TESTNAME: + s->regs[addr] = value; + copy_testname(s); + break; + + default: + error_report("lm32_sys: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const sys_read_fn[] = { + NULL, + NULL, + NULL, +}; + +static CPUWriteMemoryFunc * const sys_write_fn[] = { + NULL, + NULL, + &sys_write, +}; + +static void sys_reset(DeviceState *d) +{ + LM32SysState *s = container_of(d, LM32SysState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + memset(s->testname, 0, MAX_TESTNAME_LEN); +} + +static int lm32_sys_init(SysBusDevice *dev) +{ + LM32SysState *s = FROM_SYSBUS(typeof(*s), dev); + int sys_regs; + + sys_regs = cpu_register_io_memory(sys_read_fn, sys_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, sys_regs); + + /* Note: This device is not created in the board initialization, + * instead it has to be added with the -device parameter. Therefore, + * the device maps itself. */ + sysbus_mmio_map(dev, 0, s->base); + + return 0; +} + +static const VMStateDescription vmstate_lm32_sys = { + .name = "lm32-sys", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, LM32SysState, R_MAX), + VMSTATE_BUFFER(testname, LM32SysState), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo lm32_sys_info = { + .init = lm32_sys_init, + .qdev.name = "lm32-sys", + .qdev.size = sizeof(LM32SysState), + .qdev.vmsd = &vmstate_lm32_sys, + .qdev.reset = sys_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("base", LM32SysState, base, 0xffff0000), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void lm32_sys_register(void) +{ + sysbus_register_withprop(&lm32_sys_info); +} + +device_init(lm32_sys_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_timer.c qemu-kvm-0.15.0+noroms/hw/lm32_timer.c --- qemu-kvm-0.14.1+noroms/hw/lm32_timer.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,222 @@ +/* + * QEMU model of the LatticeMico32 timer block. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.latticesemi.com/documents/mico32timer.pdf + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-timer.h" +#include "qemu-error.h" + +#define DEFAULT_FREQUENCY (50*1000000) + +enum { + R_SR = 0, + R_CR, + R_PERIOD, + R_SNAPSHOT, + R_MAX +}; + +enum { + SR_TO = (1 << 0), + SR_RUN = (1 << 1), +}; + +enum { + CR_ITO = (1 << 0), + CR_CONT = (1 << 1), + CR_START = (1 << 2), + CR_STOP = (1 << 3), +}; + +struct LM32TimerState { + SysBusDevice busdev; + + QEMUBH *bh; + ptimer_state *ptimer; + + qemu_irq irq; + uint32_t freq_hz; + + uint32_t regs[R_MAX]; +}; +typedef struct LM32TimerState LM32TimerState; + +static void timer_update_irq(LM32TimerState *s) +{ + int state = (s->regs[R_SR] & SR_TO) && (s->regs[R_CR] & CR_ITO); + + trace_lm32_timer_irq_state(state); + qemu_set_irq(s->irq, state); +} + +static uint32_t timer_read(void *opaque, target_phys_addr_t addr) +{ + LM32TimerState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_SR: + case R_CR: + case R_PERIOD: + r = s->regs[addr]; + break; + case R_SNAPSHOT: + r = (uint32_t)ptimer_get_count(s->ptimer); + break; + default: + error_report("lm32_timer: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_lm32_timer_memory_read(addr << 2, r); + return r; +} + +static void timer_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + LM32TimerState *s = opaque; + + trace_lm32_timer_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_SR: + s->regs[R_SR] &= ~SR_TO; + break; + case R_CR: + s->regs[R_CR] = value; + if (s->regs[R_CR] & CR_START) { + ptimer_run(s->ptimer, 1); + } + if (s->regs[R_CR] & CR_STOP) { + ptimer_stop(s->ptimer); + } + break; + case R_PERIOD: + s->regs[R_PERIOD] = value; + ptimer_set_count(s->ptimer, value); + break; + case R_SNAPSHOT: + error_report("lm32_timer: write access to read only register 0x" + TARGET_FMT_plx, addr << 2); + break; + default: + error_report("lm32_timer: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + timer_update_irq(s); +} + +static CPUReadMemoryFunc * const timer_read_fn[] = { + NULL, + NULL, + &timer_read, +}; + +static CPUWriteMemoryFunc * const timer_write_fn[] = { + NULL, + NULL, + &timer_write, +}; + +static void timer_hit(void *opaque) +{ + LM32TimerState *s = opaque; + + trace_lm32_timer_hit(); + + s->regs[R_SR] |= SR_TO; + + if (s->regs[R_CR] & CR_CONT) { + ptimer_set_count(s->ptimer, s->regs[R_PERIOD]); + ptimer_run(s->ptimer, 1); + } + timer_update_irq(s); +} + +static void timer_reset(DeviceState *d) +{ + LM32TimerState *s = container_of(d, LM32TimerState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + ptimer_stop(s->ptimer); +} + +static int lm32_timer_init(SysBusDevice *dev) +{ + LM32TimerState *s = FROM_SYSBUS(typeof(*s), dev); + int timer_regs; + + sysbus_init_irq(dev, &s->irq); + + s->bh = qemu_bh_new(timer_hit, s); + s->ptimer = ptimer_init(s->bh); + ptimer_set_freq(s->ptimer, s->freq_hz); + + timer_regs = cpu_register_io_memory(timer_read_fn, timer_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, timer_regs); + + return 0; +} + +static const VMStateDescription vmstate_lm32_timer = { + .name = "lm32-timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(ptimer, LM32TimerState), + VMSTATE_UINT32(freq_hz, LM32TimerState), + VMSTATE_UINT32_ARRAY(regs, LM32TimerState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo lm32_timer_info = { + .init = lm32_timer_init, + .qdev.name = "lm32-timer", + .qdev.size = sizeof(LM32TimerState), + .qdev.vmsd = &vmstate_lm32_timer, + .qdev.reset = timer_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32( + "frequency", LM32TimerState, freq_hz, DEFAULT_FREQUENCY + ), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void lm32_timer_register(void) +{ + sysbus_register_withprop(&lm32_timer_info); +} + +device_init(lm32_timer_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/lm32_uart.c qemu-kvm-0.15.0+noroms/hw/lm32_uart.c --- qemu-kvm-0.14.1+noroms/hw/lm32_uart.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/lm32_uart.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,288 @@ +/* + * QEMU model of the LatticeMico32 UART block. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.latticesemi.com/documents/mico32uart.pdf + */ + + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-char.h" +#include "qemu-error.h" + +enum { + R_RXTX = 0, + R_IER, + R_IIR, + R_LCR, + R_MCR, + R_LSR, + R_MSR, + R_DIV, + R_MAX +}; + +enum { + IER_RBRI = (1<<0), + IER_THRI = (1<<1), + IER_RLSI = (1<<2), + IER_MSI = (1<<3), +}; + +enum { + IIR_STAT = (1<<0), + IIR_ID0 = (1<<1), + IIR_ID1 = (1<<2), +}; + +enum { + LCR_WLS0 = (1<<0), + LCR_WLS1 = (1<<1), + LCR_STB = (1<<2), + LCR_PEN = (1<<3), + LCR_EPS = (1<<4), + LCR_SP = (1<<5), + LCR_SB = (1<<6), +}; + +enum { + MCR_DTR = (1<<0), + MCR_RTS = (1<<1), +}; + +enum { + LSR_DR = (1<<0), + LSR_OE = (1<<1), + LSR_PE = (1<<2), + LSR_FE = (1<<3), + LSR_BI = (1<<4), + LSR_THRE = (1<<5), + LSR_TEMT = (1<<6), +}; + +enum { + MSR_DCTS = (1<<0), + MSR_DDSR = (1<<1), + MSR_TERI = (1<<2), + MSR_DDCD = (1<<3), + MSR_CTS = (1<<4), + MSR_DSR = (1<<5), + MSR_RI = (1<<6), + MSR_DCD = (1<<7), +}; + +struct LM32UartState { + SysBusDevice busdev; + CharDriverState *chr; + qemu_irq irq; + + uint32_t regs[R_MAX]; +}; +typedef struct LM32UartState LM32UartState; + +static void uart_update_irq(LM32UartState *s) +{ + unsigned int irq; + + if ((s->regs[R_LSR] & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) + && (s->regs[R_IER] & IER_RLSI)) { + irq = 1; + s->regs[R_IIR] = IIR_ID1 | IIR_ID0; + } else if ((s->regs[R_LSR] & LSR_DR) && (s->regs[R_IER] & IER_RBRI)) { + irq = 1; + s->regs[R_IIR] = IIR_ID1; + } else if ((s->regs[R_LSR] & LSR_THRE) && (s->regs[R_IER] & IER_THRI)) { + irq = 1; + s->regs[R_IIR] = IIR_ID0; + } else if ((s->regs[R_MSR] & 0x0f) && (s->regs[R_IER] & IER_MSI)) { + irq = 1; + s->regs[R_IIR] = 0; + } else { + irq = 0; + s->regs[R_IIR] = IIR_STAT; + } + + trace_lm32_uart_irq_state(irq); + qemu_set_irq(s->irq, irq); +} + +static uint32_t uart_read(void *opaque, target_phys_addr_t addr) +{ + LM32UartState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_RXTX: + r = s->regs[R_RXTX]; + s->regs[R_LSR] &= ~LSR_DR; + uart_update_irq(s); + break; + case R_IIR: + case R_LSR: + case R_MSR: + r = s->regs[addr]; + break; + case R_IER: + case R_LCR: + case R_MCR: + case R_DIV: + error_report("lm32_uart: read access to write only register 0x" + TARGET_FMT_plx, addr << 2); + break; + default: + error_report("lm32_uart: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_lm32_uart_memory_read(addr << 2, r); + return r; +} + +static void uart_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + LM32UartState *s = opaque; + unsigned char ch = value; + + trace_lm32_uart_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_RXTX: + if (s->chr) { + qemu_chr_write(s->chr, &ch, 1); + } + break; + case R_IER: + case R_LCR: + case R_MCR: + case R_DIV: + s->regs[addr] = value; + break; + case R_IIR: + case R_LSR: + case R_MSR: + error_report("lm32_uart: write access to read only register 0x" + TARGET_FMT_plx, addr << 2); + break; + default: + error_report("lm32_uart: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + uart_update_irq(s); +} + +static CPUReadMemoryFunc * const uart_read_fn[] = { + NULL, + NULL, + &uart_read, +}; + +static CPUWriteMemoryFunc * const uart_write_fn[] = { + NULL, + NULL, + &uart_write, +}; + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + LM32UartState *s = opaque; + + if (s->regs[R_LSR] & LSR_DR) { + s->regs[R_LSR] |= LSR_OE; + } + + s->regs[R_LSR] |= LSR_DR; + s->regs[R_RXTX] = *buf; + + uart_update_irq(s); +} + +static int uart_can_rx(void *opaque) +{ + LM32UartState *s = opaque; + + return !(s->regs[R_LSR] & LSR_DR); +} + +static void uart_event(void *opaque, int event) +{ +} + +static void uart_reset(DeviceState *d) +{ + LM32UartState *s = container_of(d, LM32UartState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + /* defaults */ + s->regs[R_LSR] = LSR_THRE | LSR_TEMT; +} + +static int lm32_uart_init(SysBusDevice *dev) +{ + LM32UartState *s = FROM_SYSBUS(typeof(*s), dev); + int uart_regs; + + sysbus_init_irq(dev, &s->irq); + + uart_regs = cpu_register_io_memory(uart_read_fn, uart_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, uart_regs); + + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); + } + + return 0; +} + +static const VMStateDescription vmstate_lm32_uart = { + .name = "lm32-uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, LM32UartState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo lm32_uart_info = { + .init = lm32_uart_init, + .qdev.name = "lm32-uart", + .qdev.size = sizeof(LM32UartState), + .qdev.vmsd = &vmstate_lm32_uart, + .qdev.reset = uart_reset, +}; + +static void lm32_uart_register(void) +{ + sysbus_register_withprop(&lm32_uart_info); +} + +device_init(lm32_uart_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/lm832x.c qemu-kvm-0.15.0+noroms/hw/lm832x.c --- qemu-kvm-0.14.1+noroms/hw/lm832x.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/lm832x.c 2011-08-09 07:40:29.000000000 -0500 @@ -463,9 +463,9 @@ LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c); s->model = 0x8323; - s->pwm.tm[0] = qemu_new_timer(vm_clock, lm_kbd_pwm0_tick, s); - s->pwm.tm[1] = qemu_new_timer(vm_clock, lm_kbd_pwm1_tick, s); - s->pwm.tm[2] = qemu_new_timer(vm_clock, lm_kbd_pwm2_tick, s); + s->pwm.tm[0] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm0_tick, s); + s->pwm.tm[1] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm1_tick, s); + s->pwm.tm[2] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm2_tick, s); qdev_init_gpio_out(&i2c->qdev, &s->nirq, 1); lm_kbd_reset(s); diff -Nru qemu-kvm-0.14.1+noroms/hw/lsi53c895a.c qemu-kvm-0.15.0+noroms/hw/lsi53c895a.c --- qemu-kvm-0.14.1+noroms/hw/lsi53c895a.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/lsi53c895a.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* ??? Need to check if the {read,write}[wl] routines work properly on @@ -174,6 +174,7 @@ #define LSI_TAG_VALID (1 << 16) typedef struct lsi_request { + SCSIRequest *req; uint32_t tag; uint32_t dma_len; uint8_t *dma_buf; @@ -189,7 +190,7 @@ uint32_t script_ram_base; int carry; /* ??? Should this be an a visible register somewhere? */ - int sense; + int status; /* Action to take at the end of a MSG IN phase. 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */ int msg_action; @@ -567,11 +568,9 @@ s->csbc += count; s->dnad += count; s->dbc -= count; - - if (s->current->dma_buf == NULL) { - s->current->dma_buf = dev->info->get_buf(dev, s->current->tag); + if (s->current->dma_buf == NULL) { + s->current->dma_buf = scsi_req_get_buf(s->current->req); } - /* ??? Set SFBR to first data byte. */ if (out) { cpu_physical_memory_read(addr, s->current->dma_buf, count); @@ -581,13 +580,7 @@ s->current->dma_len -= count; if (s->current->dma_len == 0) { s->current->dma_buf = NULL; - if (out) { - /* Write the data. */ - dev->info->write_data(dev, s->current->tag); - } else { - /* Request any remaining data. */ - dev->info->read_data(dev, s->current->tag); - } + scsi_req_continue(s->current->req); } else { s->current->dma_buf += count; lsi_resume_script(s); @@ -652,82 +645,116 @@ } } -/* Record that data is available for a queued command. Returns zero if - the device was reselected, nonzero if the IO is deferred. */ -static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg) +static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) { lsi_request *p; QTAILQ_FOREACH(p, &s->queue, next) { if (p->tag == tag) { - if (p->pending) { - BADF("Multiple IO pending for tag %d\n", tag); - } - p->pending = arg; - /* Reselect if waiting for it, or if reselection triggers an IRQ - and the bus is free. - Since no interrupt stacking is implemented in the emulation, it - is also required that there are no pending interrupts waiting - for service from the device driver. */ - if (s->waiting == 1 || - (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && - !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { - /* Reselect device. */ - lsi_reselect(s, p); - return 0; - } else { - DPRINTF("Queueing IO tag=0x%x\n", tag); - p->pending = arg; - return 1; - } + return p; } } - BADF("IO with unknown tag %d\n", tag); - return 1; + + return NULL; +} + +static void lsi_request_cancelled(SCSIRequest *req) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); + lsi_request *p = req->hba_private; + + if (s->current && req == s->current->req) { + scsi_req_unref(req); + qemu_free(s->current); + s->current = NULL; + return; + } + + if (p) { + QTAILQ_REMOVE(&s->queue, p, next); + scsi_req_unref(req); + qemu_free(p); + } } -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +/* Record that data is available for a queued command. Returns zero if + the device was reselected, nonzero if the IO is deferred. */ +static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) { - LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent); + lsi_request *p = req->hba_private; + + if (p->pending) { + BADF("Multiple IO pending for request %p\n", p); + } + p->pending = len; + /* Reselect if waiting for it, or if reselection triggers an IRQ + and the bus is free. + Since no interrupt stacking is implemented in the emulation, it + is also required that there are no pending interrupts waiting + for service from the device driver. */ + if (s->waiting == 1 || + (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && + !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { + /* Reselect device. */ + lsi_reselect(s, p); + return 0; + } else { + DPRINTF("Queueing IO tag=0x%x\n", tag); + p->pending = len; + return 1; + } +} + + /* Callback to indicate that the SCSI layer has completed a command. */ +static void lsi_command_complete(SCSIRequest *req, uint32_t status) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); int out; out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - if (reason == SCSI_REASON_DONE) { - DPRINTF("Command complete sense=%d\n", (int)arg); - s->sense = arg; - s->command_complete = 2; - if (s->waiting && s->dbc != 0) { - /* Raise phase mismatch for short transfers. */ - lsi_bad_phase(s, out, PHASE_ST); - } else { - lsi_set_phase(s, PHASE_ST); - } + DPRINTF("Command complete status=%d\n", (int)status); + s->status = status; + s->command_complete = 2; + if (s->waiting && s->dbc != 0) { + /* Raise phase mismatch for short transfers. */ + lsi_bad_phase(s, out, PHASE_ST); + } else { + lsi_set_phase(s, PHASE_ST); + } + if (s->current && req == s->current->req) { + scsi_req_unref(s->current->req); qemu_free(s->current); s->current = NULL; - - lsi_resume_script(s); - return; } + lsi_resume_script(s); +} - if (s->waiting == 1 || !s->current || tag != s->current->tag || + /* Callback to indicate that the SCSI layer has completed a transfer. */ +static void lsi_transfer_data(SCSIRequest *req, uint32_t len) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); + int out; + + if (s->waiting == 1 || !s->current || req->hba_private != s->current || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { - if (lsi_queue_tag(s, tag, arg)) + if (lsi_queue_req(s, req, len)) { return; + } } + out = (s->sstat1 & PHASE_MASK) == PHASE_DO; + /* host adapter (re)connected */ - DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg); - s->current->dma_len = arg; + DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len); + s->current->dma_len = len; s->command_complete = 1; - if (!s->waiting) - return; - if (s->waiting == 1 || s->dbc == 0) { - lsi_resume_script(s); - } else { - lsi_do_dma(s, out); + if (s->waiting) { + if (s->waiting == 1 || s->dbc == 0) { + lsi_resume_script(s); + } else { + lsi_do_dma(s, out); + } } } @@ -755,16 +782,18 @@ assert(s->current == NULL); s->current = qemu_mallocz(sizeof(lsi_request)); s->current->tag = s->select_tag; + s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, + s->current); - n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun); - if (n > 0) { - lsi_set_phase(s, PHASE_DI); - dev->info->read_data(dev, s->current->tag); - } else if (n < 0) { - lsi_set_phase(s, PHASE_DO); - dev->info->write_data(dev, s->current->tag); + n = scsi_req_enqueue(s->current->req, buf); + if (n) { + if (n > 0) { + lsi_set_phase(s, PHASE_DI); + } else if (n < 0) { + lsi_set_phase(s, PHASE_DO); + } + scsi_req_continue(s->current->req); } - if (!s->command_complete) { if (n) { /* Command did not complete immediately so disconnect. */ @@ -783,14 +812,14 @@ static void lsi_do_status(LSIState *s) { - uint8_t sense; - DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense); + uint8_t status; + DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status); if (s->dbc != 1) BADF("Bad Status move\n"); s->dbc = 1; - sense = s->sense; - s->sfbr = sense; - cpu_physical_memory_write(s->dnad, &sense, 1); + status = s->status; + s->sfbr = status; + cpu_physical_memory_write(s->dnad, &status, 1); lsi_set_phase(s, PHASE_MI); s->msg_action = 1; lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ @@ -854,17 +883,17 @@ uint8_t msg; int len; uint32_t current_tag; - SCSIDevice *current_dev; - lsi_request *p, *p_next; + lsi_request *current_req, *p, *p_next; int id; if (s->current) { current_tag = s->current->tag; + current_req = s->current; } else { current_tag = s->select_tag; + current_req = lsi_find_by_tag(s, current_tag); } id = (current_tag >> 8) & 0xf; - current_dev = s->bus.devs[id]; DPRINTF("MSG out len=%d\n", s->dbc); while (s->dbc) { @@ -913,7 +942,9 @@ 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); + if (current_req) { + scsi_req_cancel(current_req->req); + } lsi_disconnect(s); break; case 0x06: @@ -936,7 +967,9 @@ } /* clear the current I/O process */ - current_dev->info->cancel_io(current_dev, current_tag); + if (s->current) { + scsi_req_cancel(s->current->req); + } /* As the current implemented devices scsi_disk and scsi_generic only support one LUN, we don't need to keep track of LUNs. @@ -948,8 +981,7 @@ 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); + scsi_req_cancel(p->req); } } @@ -2094,15 +2126,6 @@ cpu_register_physical_memory(addr + 0, 0x2000, s->ram_io_addr); } -static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num, - pcibus_t addr, pcibus_t size, int type) -{ - LSIState *s = DO_UPCAST(LSIState, dev, pci_dev); - - DPRINTF("Mapping registers at %08"FMT_PCIBUS"\n", addr); - 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); @@ -2131,7 +2154,7 @@ VMSTATE_PCI_DEVICE(dev, LSIState), VMSTATE_INT32(carry, LSIState), - VMSTATE_INT32(sense, LSIState), + VMSTATE_INT32(status, LSIState), VMSTATE_INT32(msg_action, LSIState), VMSTATE_INT32(msg_len, LSIState), VMSTATE_BUFFER(msg, LSIState), @@ -2214,6 +2237,12 @@ return 0; } +static const struct SCSIBusOps lsi_scsi_ops = { + .transfer_data = lsi_transfer_data, + .complete = lsi_command_complete, + .cancel = lsi_request_cancelled +}; + static int lsi_scsi_init(PCIDevice *dev) { LSIState *s = DO_UPCAST(LSIState, dev, dev); @@ -2221,15 +2250,6 @@ pci_conf = s->dev.config; - /* PCI Vendor ID (word) */ - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_LSI_LOGIC); - /* PCI device ID (word) */ - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_LSI_53C895A); - /* PCI base class code */ - pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_SCSI); - /* PCI subsystem ID */ - pci_conf[PCI_SUBSYSTEM_ID] = 0x00; - pci_conf[PCI_SUBSYSTEM_ID + 1] = 0x10; /* PCI latency timer = 255 */ pci_conf[PCI_LATENCY_TIMER] = 0xff; /* TODO: RST# value should be 0 */ @@ -2245,13 +2265,12 @@ pci_register_bar(&s->dev, 0, 256, PCI_BASE_ADDRESS_SPACE_IO, lsi_io_mapfunc); - pci_register_bar(&s->dev, 1, 0x400, - PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_mmio_mapfunc); + pci_register_bar_simple(&s->dev, 1, 0x400, 0, s->mmio_io_addr); pci_register_bar(&s->dev, 2, 0x2000, PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc); QTAILQ_INIT(&s->queue); - scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete); + scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops); if (!dev->qdev.hotplugged) { return scsi_bus_legacy_handle_cmdline(&s->bus); } @@ -2266,6 +2285,10 @@ .qdev.vmsd = &vmstate_lsi_scsi, .init = lsi_scsi_init, .exit = lsi_scsi_uninit, + .vendor_id = PCI_VENDOR_ID_LSI_LOGIC, + .device_id = PCI_DEVICE_ID_LSI_53C895A, + .class_id = PCI_CLASS_STORAGE_SCSI, + .subsystem_id = 0x1000, }; static void lsi53c895a_register_devices(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/m48t59.c qemu-kvm-0.15.0+noroms/hw/m48t59.c --- qemu-kvm-0.14.1+noroms/hw/m48t59.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/m48t59.c 2011-08-09 07:40:29.000000000 -0500 @@ -123,7 +123,7 @@ /* Repeat once a second */ next_time = 1; } - qemu_mod_timer(NVRAM->alrm_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(NVRAM->alrm_timer, qemu_get_clock_ns(vm_clock) + next_time * 1000); qemu_set_irq(NVRAM->IRQ, 0); } @@ -585,28 +585,18 @@ &nvram_readl, }; -static void m48t59_save(QEMUFile *f, void *opaque) -{ - M48t59State *s = opaque; - - qemu_put_8s(f, &s->lock); - qemu_put_be16s(f, &s->addr); - qemu_put_buffer(f, s->buffer, s->size); -} - -static int m48t59_load(QEMUFile *f, void *opaque, int version_id) -{ - M48t59State *s = opaque; - - if (version_id != 1) - return -EINVAL; - - qemu_get_8s(f, &s->lock); - qemu_get_be16s(f, &s->addr); - qemu_get_buffer(f, s->buffer, s->size); - - return 0; -} +static const VMStateDescription vmstate_m48t59 = { + .name = "m48t59", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(lock, M48t59State), + VMSTATE_UINT16(addr, M48t59State), + VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, 0, size), + VMSTATE_END_OF_LIST() + } +}; static void m48t59_reset_common(M48t59State *NVRAM) { @@ -691,12 +681,12 @@ { s->buffer = qemu_mallocz(s->size); if (s->type == 59) { - s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s); - s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s); + s->alrm_timer = qemu_new_timer_ns(vm_clock, &alarm_cb, s); + s->wd_timer = qemu_new_timer_ns(vm_clock, &watchdog_cb, s); } qemu_get_timedate(&s->alarm, 0); - register_savevm(NULL, "m48t59", -1, 1, m48t59_save, m48t59_load, s); + vmstate_register(NULL, -1, &vmstate_m48t59, s); } static int m48t59_init_isa1(ISADevice *dev) diff -Nru qemu-kvm-0.14.1+noroms/hw/mac_dbdma.c qemu-kvm-0.15.0+noroms/hw/mac_dbdma.c --- qemu-kvm-0.14.1+noroms/hw/mac_dbdma.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mac_dbdma.c 2011-08-09 07:40:29.000000000 -0500 @@ -165,6 +165,10 @@ int processing; } DBDMA_channel; +typedef struct { + DBDMA_channel channels[DBDMA_CHANNELS]; +} DBDMAState; + #ifdef DEBUG_DBDMA static void dump_dbdma_cmd(dbdma_cmd *cmd) { @@ -617,31 +621,34 @@ } } -static void DBDMA_run (DBDMA_channel *ch) +static void DBDMA_run(DBDMAState *s) { int channel; - for (channel = 0; channel < DBDMA_CHANNELS; channel++, ch++) { - uint32_t status = ch->regs[DBDMA_STATUS]; - if (!ch->processing && (status & RUN) && (status & ACTIVE)) - channel_run(ch); + for (channel = 0; channel < DBDMA_CHANNELS; channel++) { + DBDMA_channel *ch = &s->channels[channel]; + uint32_t status = ch->regs[DBDMA_STATUS]; + if (!ch->processing && (status & RUN) && (status & ACTIVE)) { + channel_run(ch); + } } } static void DBDMA_run_bh(void *opaque) { - DBDMA_channel *ch = opaque; + DBDMAState *s = opaque; DBDMA_DPRINTF("DBDMA_run_bh\n"); - DBDMA_run(ch); + DBDMA_run(s); } void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, DBDMA_rw rw, DBDMA_flush flush, void *opaque) { - DBDMA_channel *ch = ( DBDMA_channel *)dbdma + nchan; + DBDMAState *s = dbdma; + DBDMA_channel *ch = &s->channels[nchan]; DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan); @@ -700,7 +707,8 @@ target_phys_addr_t addr, uint32_t value) { int channel = addr >> DBDMA_CHANNEL_SHIFT; - DBDMA_channel *ch = (DBDMA_channel *)opaque + channel; + DBDMAState *s = opaque; + DBDMA_channel *ch = &s->channels[channel]; int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value); @@ -749,7 +757,8 @@ { uint32_t value; int channel = addr >> DBDMA_CHANNEL_SHIFT; - DBDMA_channel *ch = (DBDMA_channel *)opaque + channel; + DBDMAState *s = opaque; + DBDMA_channel *ch = &s->channels[channel]; int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; value = ch->regs[reg]; @@ -801,49 +810,47 @@ dbdma_readl, }; -static void dbdma_save(QEMUFile *f, void *opaque) -{ - DBDMA_channel *s = opaque; - unsigned int i, j; - - for (i = 0; i < DBDMA_CHANNELS; i++) - for (j = 0; j < DBDMA_REGS; j++) - qemu_put_be32s(f, &s[i].regs[j]); -} - -static int dbdma_load(QEMUFile *f, void *opaque, int version_id) -{ - DBDMA_channel *s = opaque; - unsigned int i, j; - - if (version_id != 2) - return -EINVAL; - - for (i = 0; i < DBDMA_CHANNELS; i++) - for (j = 0; j < DBDMA_REGS; j++) - qemu_get_be32s(f, &s[i].regs[j]); +static const VMStateDescription vmstate_dbdma_channel = { + .name = "dbdma_channel", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS), + VMSTATE_END_OF_LIST() + } +}; - return 0; -} +static const VMStateDescription vmstate_dbdma = { + .name = "dbdma", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1, + vmstate_dbdma_channel, DBDMA_channel), + VMSTATE_END_OF_LIST() + } +}; static void dbdma_reset(void *opaque) { - DBDMA_channel *s = opaque; + DBDMAState *s = opaque; int i; for (i = 0; i < DBDMA_CHANNELS; i++) - memset(s[i].regs, 0, DBDMA_SIZE); + memset(s->channels[i].regs, 0, DBDMA_SIZE); } void* DBDMA_init (int *dbdma_mem_index) { - DBDMA_channel *s; + DBDMAState *s; - s = qemu_mallocz(sizeof(DBDMA_channel) * DBDMA_CHANNELS); + s = qemu_mallocz(sizeof(DBDMAState)); *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); + vmstate_register(NULL, -1, &vmstate_dbdma, s); qemu_register_reset(dbdma_reset, s); dbdma_bh = qemu_bh_new(DBDMA_run_bh, s); diff -Nru qemu-kvm-0.14.1+noroms/hw/mac_nvram.c qemu-kvm-0.15.0+noroms/hw/mac_nvram.c --- qemu-kvm-0.14.1+noroms/hw/mac_nvram.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mac_nvram.c 2011-08-09 07:40:29.000000000 -0500 @@ -38,7 +38,7 @@ #endif struct MacIONVRAMState { - target_phys_addr_t size; + uint32_t size; int mem_index; unsigned int it_shift; uint8_t *data; @@ -105,24 +105,17 @@ &macio_nvram_readb, }; -static void macio_nvram_save(QEMUFile *f, void *opaque) -{ - MacIONVRAMState *s = (MacIONVRAMState *)opaque; - - qemu_put_buffer(f, s->data, s->size); -} - -static int macio_nvram_load(QEMUFile *f, void *opaque, int version_id) -{ - MacIONVRAMState *s = (MacIONVRAMState *)opaque; - - if (version_id != 1) - return -EINVAL; - - qemu_get_buffer(f, s->data, s->size); +static const VMStateDescription vmstate_macio_nvram = { + .name = "macio_nvram", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_VBUFFER_UINT32(data, MacIONVRAMState, 0, NULL, 0, size), + VMSTATE_END_OF_LIST() + } +}; - return 0; -} static void macio_nvram_reset(void *opaque) { @@ -141,8 +134,7 @@ s->mem_index = cpu_register_io_memory(nvram_read, nvram_write, s, DEVICE_NATIVE_ENDIAN); *mem_index = s->mem_index; - register_savevm(NULL, "macio_nvram", -1, 1, macio_nvram_save, - macio_nvram_load, s); + vmstate_register(NULL, -1, &vmstate_macio_nvram, s); qemu_register_reset(macio_nvram_reset, s); return s; diff -Nru qemu-kvm-0.14.1+noroms/hw/mainstone.c qemu-kvm-0.15.0+noroms/hw/mainstone.c --- qemu-kvm-0.14.1+noroms/hw/mainstone.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mainstone.c 2011-08-09 07:40:29.000000000 -0500 @@ -14,10 +14,31 @@ #include "net.h" #include "devices.h" #include "boards.h" -#include "mainstone.h" -#include "sysemu.h" #include "flash.h" #include "blockdev.h" +#include "sysbus.h" + +/* Device addresses */ +#define MST_FPGA_PHYS 0x08000000 +#define MST_ETH_PHYS 0x10000300 +#define MST_FLASH_0 0x00000000 +#define MST_FLASH_1 0x04000000 + +/* IRQ definitions */ +#define MMC_IRQ 0 +#define USIM_IRQ 1 +#define USBC_IRQ 2 +#define ETHERNET_IRQ 3 +#define AC97_IRQ 4 +#define PEN_IRQ 5 +#define MSINS_IRQ 6 +#define EXBRD_IRQ 7 +#define S0_CD_IRQ 9 +#define S0_STSCHG_IRQ 10 +#define S0_IRQ 11 +#define S1_CD_IRQ 13 +#define S1_STSCHG_IRQ 14 +#define S1_IRQ 15 static struct keymap map[0xE0] = { [0 ... 0xDF] = { -1, -1 }, @@ -77,7 +98,7 @@ uint32_t sector_len = 256 * 1024; target_phys_addr_t mainstone_flash_base[] = { MST_FLASH_0, MST_FLASH_1 }; PXA2xxState *cpu; - qemu_irq *mst_irq; + DeviceState *mst_irq; DriveInfo *dinfo; int i; int be; @@ -117,16 +138,25 @@ } } - mst_irq = mst_irq_init(cpu, MST_FPGA_PHYS, PXA2XX_PIC_GPIO_0); + mst_irq = sysbus_create_simple("mainstone-fpga", MST_FPGA_PHYS, + qdev_get_gpio_in(cpu->gpio, 0)); /* setup keypad */ printf("map addr %p\n", &map); pxa27x_register_keypad(cpu->kp, map, 0xe0); /* MMC/SD host */ - pxa2xx_mmci_handlers(cpu->mmc, NULL, mst_irq[MMC_IRQ]); + pxa2xx_mmci_handlers(cpu->mmc, NULL, qdev_get_gpio_in(mst_irq, MMC_IRQ)); + + pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], + qdev_get_gpio_in(mst_irq, S0_IRQ), + qdev_get_gpio_in(mst_irq, S0_CD_IRQ)); + pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], + qdev_get_gpio_in(mst_irq, S1_IRQ), + qdev_get_gpio_in(mst_irq, S1_CD_IRQ)); - smc91c111_init(&nd_table[0], MST_ETH_PHYS, mst_irq[ETHERNET_IRQ]); + smc91c111_init(&nd_table[0], MST_ETH_PHYS, + qdev_get_gpio_in(mst_irq, ETHERNET_IRQ)); mainstone_binfo.kernel_filename = kernel_filename; mainstone_binfo.kernel_cmdline = kernel_cmdline; diff -Nru qemu-kvm-0.14.1+noroms/hw/mainstone.h qemu-kvm-0.15.0+noroms/hw/mainstone.h --- qemu-kvm-0.14.1+noroms/hw/mainstone.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mainstone.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,38 +0,0 @@ -/* - * PXA270-based Intel Mainstone platforms. - * - * Copyright (c) 2007 by Armin Kuster or - * - * - * This code is licensed under the GNU GPL v2. - */ - -#ifndef __MAINSTONE_H__ -#define __MAINSTONE_H__ - -/* Device addresses */ -#define MST_FPGA_PHYS 0x08000000 -#define MST_ETH_PHYS 0x10000300 -#define MST_FLASH_0 0x00000000 -#define MST_FLASH_1 0x04000000 - -/* IRQ definitions */ -#define MMC_IRQ 0 -#define USIM_IRQ 1 -#define USBC_IRQ 2 -#define ETHERNET_IRQ 3 -#define AC97_IRQ 4 -#define PEN_IRQ 5 -#define MSINS_IRQ 6 -#define EXBRD_IRQ 7 -#define S0_CD_IRQ 9 -#define S0_STSCHG_IRQ 10 -#define S0_IRQ 11 -#define S1_CD_IRQ 13 -#define S1_STSCHG_IRQ 14 -#define S1_IRQ 15 - -extern qemu_irq -*mst_irq_init(PXA2xxState *cpu, uint32_t base, int irq); - -#endif /* __MAINSTONE_H__ */ diff -Nru qemu-kvm-0.14.1+noroms/hw/marvell_88w8618_audio.c qemu-kvm-0.15.0+noroms/hw/marvell_88w8618_audio.c --- qemu-kvm-0.14.1+noroms/hw/marvell_88w8618_audio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/marvell_88w8618_audio.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" #include "hw.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/max111x.c qemu-kvm-0.15.0+noroms/hw/max111x.c --- qemu-kvm-0.14.1+noroms/hw/max111x.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/max111x.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,7 +15,7 @@ uint8_t tb1, rb2, rb3; int cycle; - int input[8]; + uint8_t input[8]; int inputs, com; } MAX111xState; @@ -94,36 +94,22 @@ return max111x_read(s); } -static void max111x_save(QEMUFile *f, void *opaque) -{ - MAX111xState *s = (MAX111xState *) opaque; - int i; - - qemu_put_8s(f, &s->tb1); - qemu_put_8s(f, &s->rb2); - qemu_put_8s(f, &s->rb3); - qemu_put_be32(f, s->inputs); - qemu_put_be32(f, s->com); - for (i = 0; i < s->inputs; i ++) - qemu_put_byte(f, s->input[i]); -} - -static int max111x_load(QEMUFile *f, void *opaque, int version_id) -{ - MAX111xState *s = (MAX111xState *) opaque; - int i; - - qemu_get_8s(f, &s->tb1); - qemu_get_8s(f, &s->rb2); - qemu_get_8s(f, &s->rb3); - if (s->inputs != qemu_get_be32(f)) - return -EINVAL; - s->com = qemu_get_be32(f); - for (i = 0; i < s->inputs; i ++) - s->input[i] = qemu_get_byte(f); - - return 0; -} +static const VMStateDescription vmstate_max111x = { + .name = "max111x", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(tb1, MAX111xState), + VMSTATE_UINT8(rb2, MAX111xState), + VMSTATE_UINT8(rb3, MAX111xState), + VMSTATE_INT32_EQUAL(inputs, MAX111xState), + VMSTATE_INT32(com, MAX111xState), + VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs, + vmstate_info_uint8, uint8_t), + VMSTATE_END_OF_LIST() + } +}; static int max111x_init(SSISlave *dev, int inputs) { @@ -143,8 +129,7 @@ s->input[7] = 0x80; s->com = 0; - register_savevm(&dev->qdev, "max111x", -1, 0, - max111x_save, max111x_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_max111x, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/max7310.c qemu-kvm-0.15.0+noroms/hw/max7310.c --- qemu-kvm-0.14.1+noroms/hw/max7310.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/max7310.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,9 +23,9 @@ qemu_irq *gpio_in; } MAX7310State; -void max7310_reset(i2c_slave *i2c) +static void max7310_reset(DeviceState *dev) { - MAX7310State *s = (MAX7310State *) i2c; + MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE_FROM_QDEV(dev)); s->level &= s->direction; s->direction = 0xff; s->polarity = 0xf0; @@ -179,33 +179,17 @@ { MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, i2c); - s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s, - ARRAY_SIZE(s->handler)); - - max7310_reset(&s->i2c); + qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8); + qdev_init_gpio_out(&i2c->qdev, s->handler, 8); return 0; } -qemu_irq *max7310_gpio_in_get(i2c_slave *i2c) -{ - MAX7310State *s = (MAX7310State *) i2c; - return s->gpio_in; -} - -void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler) -{ - MAX7310State *s = (MAX7310State *) i2c; - if (line >= ARRAY_SIZE(s->handler) || line < 0) - hw_error("bad GPIO line"); - - s->handler[line] = handler; -} - static I2CSlaveInfo max7310_info = { .qdev.name = "max7310", .qdev.size = sizeof(MAX7310State), .qdev.vmsd = &vmstate_max7310, + .qdev.reset = max7310_reset, .init = max7310_init, .event = max7310_event, .recv = max7310_rx, diff -Nru qemu-kvm-0.14.1+noroms/hw/mc146818rtc.c qemu-kvm-0.15.0+noroms/hw/mc146818rtc.c --- qemu-kvm-0.14.1+noroms/hw/mc146818rtc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mc146818rtc.c 2011-08-09 07:40:29.000000000 -0500 @@ -99,6 +99,7 @@ QEMUTimer *coalesced_timer; QEMUTimer *second_timer; QEMUTimer *second_timer2; + Notifier clock_reset_notifier; } RTCState; static void rtc_set_time(RTCState *s); @@ -112,7 +113,7 @@ } else { /* divide each RTC interval to 2 - 8 smaller intervals */ int c = MIN(s->irq_coalesced, 7) + 1; - int64_t next_clock = qemu_get_clock(rtc_clock) + + int64_t next_clock = qemu_get_clock_ns(rtc_clock) + muldiv64(s->period / c, get_ticks_per_sec(), 32768); qemu_mod_timer(s->coalesced_timer, next_clock); } @@ -234,7 +235,7 @@ /* UIP bit is read only */ s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | (s->cmos_data[RTC_REG_A] & REG_A_UIP); - rtc_timer_update(s, qemu_get_clock(rtc_clock)); + rtc_timer_update(s, qemu_get_clock_ns(rtc_clock)); break; case RTC_REG_B: if (data & REG_B_SET) { @@ -256,7 +257,7 @@ } else { s->cmos_data[RTC_REG_B] = data; } - rtc_timer_update(s, qemu_get_clock(rtc_clock)); + rtc_timer_update(s, qemu_get_clock_ns(rtc_clock)); break; case RTC_REG_C: case RTC_REG_D: @@ -572,6 +573,22 @@ } }; +static void rtc_notify_clock_reset(Notifier *notifier, void *data) +{ + RTCState *s = container_of(notifier, RTCState, clock_reset_notifier); + int64_t now = *(int64_t *)data; + + rtc_set_date_from_host(&s->dev); + s->next_second_time = now + (get_ticks_per_sec() * 99) / 100; + qemu_mod_timer(s->second_timer2, s->next_second_time); + rtc_timer_update(s, now); +#ifdef TARGET_I386 + if (rtc_td_hack) { + rtc_coalesced_timer_update(s); + } +#endif +} + static void rtc_reset(void *opaque) { RTCState *s = opaque; @@ -599,17 +616,20 @@ rtc_set_date_from_host(dev); - s->periodic_timer = qemu_new_timer(rtc_clock, rtc_periodic_timer, s); + s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s); #ifdef TARGET_I386 if (rtc_td_hack) s->coalesced_timer = - qemu_new_timer(rtc_clock, rtc_coalesced_timer, s); + qemu_new_timer_ns(rtc_clock, rtc_coalesced_timer, s); #endif - s->second_timer = qemu_new_timer(rtc_clock, rtc_update_second, s); - s->second_timer2 = qemu_new_timer(rtc_clock, rtc_update_second2, s); + s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s); + s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s); + + s->clock_reset_notifier.notify = rtc_notify_clock_reset; + qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier); s->next_second_time = - qemu_get_clock(rtc_clock) + (get_ticks_per_sec() * 99) / 100; + qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100; qemu_mod_timer(s->second_timer2, s->next_second_time); register_ioport_write(base, 2, 1, cmos_ioport_write, s); diff -Nru qemu-kvm-0.14.1+noroms/hw/mcf5206.c qemu-kvm-0.15.0+noroms/hw/mcf5206.c --- qemu-kvm-0.14.1+noroms/hw/mcf5206.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mcf5206.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/mcf5208.c qemu-kvm-0.15.0+noroms/hw/mcf5208.c --- qemu-kvm-0.14.1+noroms/hw/mcf5208.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mcf5208.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/mcf_fec.c qemu-kvm-0.15.0+noroms/hw/mcf_fec.c --- qemu-kvm-0.14.1+noroms/hw/mcf_fec.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mcf_fec.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "net.h" @@ -471,7 +471,7 @@ DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x400, s->mmio_index); - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff -Nru qemu-kvm-0.14.1+noroms/hw/mcf_intc.c qemu-kvm-0.15.0+noroms/hw/mcf_intc.c --- qemu-kvm-0.14.1+noroms/hw/mcf_intc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mcf_intc.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/mcf_uart.c qemu-kvm-0.15.0+noroms/hw/mcf_uart.c --- qemu-kvm-0.14.1+noroms/hw/mcf_uart.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mcf_uart.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-ac97.c qemu-kvm-0.15.0+noroms/hw/milkymist-ac97.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-ac97.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-ac97.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,335 @@ +/* + * QEMU model of the Milkymist System Controller. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/ac97.pdf + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "audio/audio.h" +#include "qemu-error.h" + +enum { + R_AC97_CTRL = 0, + R_AC97_ADDR, + R_AC97_DATAOUT, + R_AC97_DATAIN, + R_D_CTRL, + R_D_ADDR, + R_D_REMAINING, + R_RESERVED, + R_U_CTRL, + R_U_ADDR, + R_U_REMAINING, + R_MAX +}; + +enum { + AC97_CTRL_RQEN = (1<<0), + AC97_CTRL_WRITE = (1<<1), +}; + +enum { + CTRL_EN = (1<<0), +}; + +struct MilkymistAC97State { + SysBusDevice busdev; + + QEMUSoundCard card; + SWVoiceIn *voice_in; + SWVoiceOut *voice_out; + + uint32_t regs[R_MAX]; + + qemu_irq crrequest_irq; + qemu_irq crreply_irq; + qemu_irq dmar_irq; + qemu_irq dmaw_irq; +}; +typedef struct MilkymistAC97State MilkymistAC97State; + +static void update_voices(MilkymistAC97State *s) +{ + if (s->regs[R_D_CTRL] & CTRL_EN) { + AUD_set_active_out(s->voice_out, 1); + } else { + AUD_set_active_out(s->voice_out, 0); + } + + if (s->regs[R_U_CTRL] & CTRL_EN) { + AUD_set_active_in(s->voice_in, 1); + } else { + AUD_set_active_in(s->voice_in, 0); + } +} + +static uint32_t ac97_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistAC97State *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_AC97_CTRL: + case R_AC97_ADDR: + case R_AC97_DATAOUT: + case R_AC97_DATAIN: + case R_D_CTRL: + case R_D_ADDR: + case R_D_REMAINING: + case R_U_CTRL: + case R_U_ADDR: + case R_U_REMAINING: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_ac97: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_ac97_memory_read(addr << 2, r); + + return r; +} + +static void ac97_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistAC97State *s = opaque; + + trace_milkymist_ac97_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_AC97_CTRL: + /* always raise an IRQ according to the direction */ + if (value & AC97_CTRL_RQEN) { + if (value & AC97_CTRL_WRITE) { + trace_milkymist_ac97_pulse_irq_crrequest(); + qemu_irq_pulse(s->crrequest_irq); + } else { + trace_milkymist_ac97_pulse_irq_crreply(); + qemu_irq_pulse(s->crreply_irq); + } + } + + /* RQEN is self clearing */ + s->regs[addr] = value & ~AC97_CTRL_RQEN; + break; + case R_D_CTRL: + case R_U_CTRL: + s->regs[addr] = value; + update_voices(s); + break; + case R_AC97_ADDR: + case R_AC97_DATAOUT: + case R_AC97_DATAIN: + case R_D_ADDR: + case R_D_REMAINING: + case R_U_ADDR: + case R_U_REMAINING: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_ac97: write access to unknown register 0x" + TARGET_FMT_plx, addr); + break; + } + +} + +static CPUReadMemoryFunc * const ac97_read_fn[] = { + NULL, + NULL, + &ac97_read, +}; + +static CPUWriteMemoryFunc * const ac97_write_fn[] = { + NULL, + NULL, + &ac97_write, +}; + +static void ac97_in_cb(void *opaque, int avail_b) +{ + MilkymistAC97State *s = opaque; + uint8_t buf[4096]; + uint32_t remaining = s->regs[R_U_REMAINING]; + int temp = audio_MIN(remaining, avail_b); + uint32_t addr = s->regs[R_U_ADDR]; + int transferred = 0; + + trace_milkymist_ac97_in_cb(avail_b, remaining); + + /* prevent from raising an IRQ */ + if (temp == 0) { + return; + } + + while (temp) { + int acquired, to_copy; + + to_copy = audio_MIN(temp, sizeof(buf)); + acquired = AUD_read(s->voice_in, buf, to_copy); + if (!acquired) { + break; + } + + cpu_physical_memory_write(addr, buf, acquired); + + temp -= acquired; + addr += acquired; + transferred += acquired; + } + + trace_milkymist_ac97_in_cb_transferred(transferred); + + s->regs[R_U_ADDR] = addr; + s->regs[R_U_REMAINING] -= transferred; + + if ((s->regs[R_U_CTRL] & CTRL_EN) && (s->regs[R_U_REMAINING] == 0)) { + trace_milkymist_ac97_pulse_irq_dmaw(); + qemu_irq_pulse(s->dmaw_irq); + } +} + +static void ac97_out_cb(void *opaque, int free_b) +{ + MilkymistAC97State *s = opaque; + uint8_t buf[4096]; + uint32_t remaining = s->regs[R_D_REMAINING]; + int temp = audio_MIN(remaining, free_b); + uint32_t addr = s->regs[R_D_ADDR]; + int transferred = 0; + + trace_milkymist_ac97_out_cb(free_b, remaining); + + /* prevent from raising an IRQ */ + if (temp == 0) { + return; + } + + while (temp) { + int copied, to_copy; + + to_copy = audio_MIN(temp, sizeof(buf)); + cpu_physical_memory_read(addr, buf, to_copy); + copied = AUD_write(s->voice_out, buf, to_copy); + if (!copied) { + break; + } + temp -= copied; + addr += copied; + transferred += copied; + } + + trace_milkymist_ac97_out_cb_transferred(transferred); + + s->regs[R_D_ADDR] = addr; + s->regs[R_D_REMAINING] -= transferred; + + if ((s->regs[R_D_CTRL] & CTRL_EN) && (s->regs[R_D_REMAINING] == 0)) { + trace_milkymist_ac97_pulse_irq_dmar(); + qemu_irq_pulse(s->dmar_irq); + } +} + +static void milkymist_ac97_reset(DeviceState *d) +{ + MilkymistAC97State *s = container_of(d, MilkymistAC97State, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + AUD_set_active_in(s->voice_in, 0); + AUD_set_active_out(s->voice_out, 0); +} + +static int ac97_post_load(void *opaque, int version_id) +{ + MilkymistAC97State *s = opaque; + + update_voices(s); + + return 0; +} + +static int milkymist_ac97_init(SysBusDevice *dev) +{ + MilkymistAC97State *s = FROM_SYSBUS(typeof(*s), dev); + int ac97_regs; + + struct audsettings as; + sysbus_init_irq(dev, &s->crrequest_irq); + sysbus_init_irq(dev, &s->crreply_irq); + sysbus_init_irq(dev, &s->dmar_irq); + sysbus_init_irq(dev, &s->dmaw_irq); + + AUD_register_card("Milkymist AC'97", &s->card); + + as.freq = 48000; + as.nchannels = 2; + as.fmt = AUD_FMT_S16; + as.endianness = 1; + + s->voice_in = AUD_open_in(&s->card, s->voice_in, + "mm_ac97.in", s, ac97_in_cb, &as); + s->voice_out = AUD_open_out(&s->card, s->voice_out, + "mm_ac97.out", s, ac97_out_cb, &as); + + ac97_regs = cpu_register_io_memory(ac97_read_fn, ac97_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, ac97_regs); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_ac97 = { + .name = "milkymist-ac97", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = ac97_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistAC97State, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_ac97_info = { + .init = milkymist_ac97_init, + .qdev.name = "milkymist-ac97", + .qdev.size = sizeof(MilkymistAC97State), + .qdev.vmsd = &vmstate_milkymist_ac97, + .qdev.reset = milkymist_ac97_reset, +}; + +static void milkymist_ac97_register(void) +{ + sysbus_register_withprop(&milkymist_ac97_info); +} + +device_init(milkymist_ac97_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist.c qemu-kvm-0.15.0+noroms/hw/milkymist.c --- qemu-kvm-0.14.1+noroms/hw/milkymist.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,216 @@ +/* + * QEMU model for the Milkymist board. + * + * Copyright (c) 2010 Michael Walle + * + * 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 "sysbus.h" +#include "hw.h" +#include "net.h" +#include "flash.h" +#include "sysemu.h" +#include "devices.h" +#include "boards.h" +#include "loader.h" +#include "elf.h" +#include "blockdev.h" +#include "milkymist-hw.h" +#include "lm32.h" + +#define BIOS_FILENAME "mmone-bios.bin" +#define BIOS_OFFSET 0x00860000 +#define BIOS_SIZE (512*1024) +#define KERNEL_LOAD_ADDR 0x40000000 + +typedef struct { + CPUState *env; + target_phys_addr_t bootstrap_pc; + target_phys_addr_t flash_base; + target_phys_addr_t initrd_base; + size_t initrd_size; + target_phys_addr_t cmdline_base; +} ResetInfo; + +static void cpu_irq_handler(void *opaque, int irq, int level) +{ + CPUState *env = opaque; + + if (level) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void main_cpu_reset(void *opaque) +{ + ResetInfo *reset_info = opaque; + CPUState *env = reset_info->env; + + cpu_reset(env); + + /* init defaults */ + env->pc = reset_info->bootstrap_pc; + env->regs[R_R1] = reset_info->cmdline_base; + env->regs[R_R2] = reset_info->initrd_base; + env->regs[R_R3] = reset_info->initrd_base + reset_info->initrd_size; + env->eba = reset_info->flash_base; + env->deba = reset_info->flash_base; +} + +static void +milkymist_init(ram_addr_t ram_size_not_used, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUState *env; + int kernel_size; + DriveInfo *dinfo; + ram_addr_t phys_sdram; + ram_addr_t phys_flash; + qemu_irq irq[32], *cpu_irq; + int i; + char *bios_filename; + ResetInfo *reset_info; + + /* memory map */ + target_phys_addr_t flash_base = 0x00000000; + size_t flash_sector_size = 128 * 1024; + size_t flash_size = 32 * 1024 * 1024; + target_phys_addr_t sdram_base = 0x40000000; + size_t sdram_size = 128 * 1024 * 1024; + + target_phys_addr_t initrd_base = sdram_base + 0x1002000; + target_phys_addr_t cmdline_base = sdram_base + 0x1000000; + size_t initrd_max = sdram_size - 0x1002000; + + reset_info = qemu_mallocz(sizeof(ResetInfo)); + + if (cpu_model == NULL) { + cpu_model = "lm32-full"; + } + env = cpu_init(cpu_model); + reset_info->env = env; + + cpu_lm32_set_phys_msb_ignore(env, 1); + + phys_sdram = qemu_ram_alloc(NULL, "milkymist.sdram", sdram_size); + cpu_register_physical_memory(sdram_base, sdram_size, + phys_sdram | IO_MEM_RAM); + + phys_flash = qemu_ram_alloc(NULL, "milkymist.flash", flash_size); + dinfo = drive_get(IF_PFLASH, 0, 0); + /* Numonyx JS28F256J3F105 */ + pflash_cfi01_register(flash_base, phys_flash, + dinfo ? dinfo->bdrv : NULL, flash_sector_size, + flash_size / flash_sector_size, 2, + 0x00, 0x89, 0x00, 0x1d, 1); + + /* create irq lines */ + cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1); + env->pic_state = lm32_pic_init(*cpu_irq); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(env->pic_state, i); + } + + /* load bios rom */ + if (bios_name == NULL) { + bios_name = BIOS_FILENAME; + } + bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + + if (bios_filename) { + load_image_targphys(bios_filename, BIOS_OFFSET, BIOS_SIZE); + } + + reset_info->bootstrap_pc = BIOS_OFFSET; + + /* if no kernel is given no valid bios rom is a fatal error */ + if (!kernel_filename && !dinfo && !bios_filename) { + fprintf(stderr, "qemu: could not load Milkymist One bios '%s'\n", + bios_name); + exit(1); + } + + milkymist_uart_create(0x60000000, irq[0], irq[1]); + milkymist_sysctl_create(0x60001000, irq[2], irq[3], irq[4], + 80000000, 0x10014d31, 0x0000041f, 0x00000001); + milkymist_hpdmc_create(0x60002000); + milkymist_vgafb_create(0x60003000, 0x40000000, 0x0fffffff); + milkymist_memcard_create(0x60004000); + milkymist_ac97_create(0x60005000, irq[5], irq[6], irq[7], irq[8]); + milkymist_pfpu_create(0x60006000, irq[9]); + milkymist_tmu2_create(0x60007000, irq[10]); + milkymist_minimac2_create(0x60008000, 0x30000000, irq[11], irq[12]); + milkymist_softusb_create(0x6000f000, irq[17], + 0x20000000, 0x1000, 0x20020000, 0x2000); + + /* make sure juart isn't the first chardev */ + env->juart_state = lm32_juart_init(); + + if (kernel_filename) { + uint64_t entry; + + /* Boots a kernel elf binary. */ + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + 1, ELF_MACHINE, 0); + reset_info->bootstrap_pc = entry; + + if (kernel_size < 0) { + kernel_size = load_image_targphys(kernel_filename, sdram_base, + sdram_size); + reset_info->bootstrap_pc = sdram_base; + } + + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + } + + if (kernel_cmdline && strlen(kernel_cmdline)) { + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, + kernel_cmdline); + reset_info->cmdline_base = (uint32_t)cmdline_base; + } + + if (initrd_filename) { + size_t initrd_size; + initrd_size = load_image_targphys(initrd_filename, initrd_base, + initrd_max); + reset_info->initrd_base = (uint32_t)initrd_base; + reset_info->initrd_size = (uint32_t)initrd_size; + } + + qemu_register_reset(main_cpu_reset, reset_info); +} + +static QEMUMachine milkymist_machine = { + .name = "milkymist", + .desc = "Milkymist One", + .init = milkymist_init, + .is_default = 0 +}; + +static void milkymist_machine_init(void) +{ + qemu_register_machine(&milkymist_machine); +} + +machine_init(milkymist_machine_init); diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-hpdmc.c qemu-kvm-0.15.0+noroms/hw/milkymist-hpdmc.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-hpdmc.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-hpdmc.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,161 @@ +/* + * QEMU model of the Milkymist High Performance Dynamic Memory Controller. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/hpdmc.pdf + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-error.h" + +enum { + R_SYSTEM = 0, + R_BYPASS, + R_TIMING, + R_IODELAY, + R_MAX +}; + +enum { + IODELAY_DQSDELAY_RDY = (1<<5), + IODELAY_PLL1_LOCKED = (1<<6), + IODELAY_PLL2_LOCKED = (1<<7), +}; + +struct MilkymistHpdmcState { + SysBusDevice busdev; + + uint32_t regs[R_MAX]; +}; +typedef struct MilkymistHpdmcState MilkymistHpdmcState; + +static uint32_t hpdmc_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistHpdmcState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_SYSTEM: + case R_BYPASS: + case R_TIMING: + case R_IODELAY: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_hpdmc: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_hpdmc_memory_read(addr << 2, r); + + return r; +} + +static void hpdmc_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistHpdmcState *s = opaque; + + trace_milkymist_hpdmc_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_SYSTEM: + case R_BYPASS: + case R_TIMING: + s->regs[addr] = value; + break; + case R_IODELAY: + /* ignore writes */ + break; + + default: + error_report("milkymist_hpdmc: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const hpdmc_read_fn[] = { + NULL, + NULL, + &hpdmc_read, +}; + +static CPUWriteMemoryFunc * const hpdmc_write_fn[] = { + NULL, + NULL, + &hpdmc_write, +}; + +static void milkymist_hpdmc_reset(DeviceState *d) +{ + MilkymistHpdmcState *s = container_of(d, MilkymistHpdmcState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + /* defaults */ + s->regs[R_IODELAY] = IODELAY_DQSDELAY_RDY | IODELAY_PLL1_LOCKED + | IODELAY_PLL2_LOCKED; +} + +static int milkymist_hpdmc_init(SysBusDevice *dev) +{ + MilkymistHpdmcState *s = FROM_SYSBUS(typeof(*s), dev); + int hpdmc_regs; + + hpdmc_regs = cpu_register_io_memory(hpdmc_read_fn, hpdmc_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, hpdmc_regs); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_hpdmc = { + .name = "milkymist-hpdmc", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistHpdmcState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_hpdmc_info = { + .init = milkymist_hpdmc_init, + .qdev.name = "milkymist-hpdmc", + .qdev.size = sizeof(MilkymistHpdmcState), + .qdev.vmsd = &vmstate_milkymist_hpdmc, + .qdev.reset = milkymist_hpdmc_reset, +}; + +static void milkymist_hpdmc_register(void) +{ + sysbus_register_withprop(&milkymist_hpdmc_info); +} + +device_init(milkymist_hpdmc_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-hw.h qemu-kvm-0.15.0+noroms/hw/milkymist-hw.h --- qemu-kvm-0.14.1+noroms/hw/milkymist-hw.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-hw.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,224 @@ +#ifndef QEMU_HW_MILKYMIST_H +#define QEMU_HW_MILKYMIST_H + +#include "qdev.h" +#include "qdev-addr.h" + +static inline DeviceState *milkymist_uart_create(target_phys_addr_t base, + qemu_irq rx_irq, qemu_irq tx_irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-uart"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq); + + return dev; +} + +static inline DeviceState *milkymist_hpdmc_create(target_phys_addr_t base) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-hpdmc"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + return dev; +} + +static inline DeviceState *milkymist_memcard_create(target_phys_addr_t base) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-memcard"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + return dev; +} + +static inline DeviceState *milkymist_vgafb_create(target_phys_addr_t base, + uint32_t fb_offset, uint32_t fb_mask) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-vgafb"); + qdev_prop_set_uint32(dev, "fb_offset", fb_offset); + qdev_prop_set_uint32(dev, "fb_mask", fb_mask); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + return dev; +} + +static inline DeviceState *milkymist_sysctl_create(target_phys_addr_t base, + qemu_irq gpio_irq, qemu_irq timer0_irq, qemu_irq timer1_irq, + uint32_t freq_hz, uint32_t system_id, uint32_t capabilities, + uint32_t gpio_strappings) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-sysctl"); + qdev_prop_set_uint32(dev, "frequency", freq_hz); + qdev_prop_set_uint32(dev, "systemid", system_id); + qdev_prop_set_uint32(dev, "capabilities", capabilities); + qdev_prop_set_uint32(dev, "gpio_strappings", gpio_strappings); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, gpio_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, timer0_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 2, timer1_irq); + + return dev; +} + +static inline DeviceState *milkymist_pfpu_create(target_phys_addr_t base, + qemu_irq irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-pfpu"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + return dev; +} + +#ifdef CONFIG_OPENGL +#include +#include +static const int glx_fbconfig_attr[] = { + GLX_GREEN_SIZE, 5, + GLX_GREEN_SIZE, 6, + GLX_BLUE_SIZE, 5, + None +}; +#endif + +static inline DeviceState *milkymist_tmu2_create(target_phys_addr_t base, + qemu_irq irq) +{ +#ifdef CONFIG_OPENGL + DeviceState *dev; + Display *d; + GLXFBConfig *configs; + int nelements; + int ver_major, ver_minor; + + if (display_type == DT_NOGRAPHIC) { + return NULL; + } + + /* check that GLX will work */ + d = XOpenDisplay(NULL); + if (d == NULL) { + return NULL; + } + + if (!glXQueryVersion(d, &ver_major, &ver_minor)) { + /* Yeah, sometimes getting the GLX version can fail. + * Isn't X beautiful? */ + XCloseDisplay(d); + return NULL; + } + + if ((ver_major < 1) || ((ver_major == 1) && (ver_minor < 3))) { + printf("Your GLX version is %d.%d," + "but TMU emulation needs at least 1.3. TMU disabled.\n", + ver_major, ver_minor); + XCloseDisplay(d); + return NULL; + } + + configs = glXChooseFBConfig(d, 0, glx_fbconfig_attr, &nelements); + if (configs == NULL) { + XCloseDisplay(d); + return NULL; + } + + XFree(configs); + XCloseDisplay(d); + + dev = qdev_create(NULL, "milkymist-tmu2"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + + return dev; +#else + return NULL; +#endif +} + +static inline DeviceState *milkymist_ac97_create(target_phys_addr_t base, + qemu_irq crrequest_irq, qemu_irq crreply_irq, qemu_irq dmar_irq, + qemu_irq dmaw_irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-ac97"); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, crrequest_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, crreply_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 2, dmar_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 3, dmaw_irq); + + return dev; +} + +static inline DeviceState *milkymist_minimac_create(target_phys_addr_t base, + qemu_irq rx_irq, qemu_irq tx_irq) +{ + DeviceState *dev; + + qemu_check_nic_model(&nd_table[0], "minimac"); + dev = qdev_create(NULL, "milkymist-minimac"); + qdev_set_nic_properties(dev, &nd_table[0]); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq); + + return dev; +} + +static inline DeviceState *milkymist_minimac2_create(target_phys_addr_t base, + target_phys_addr_t buffers_base, qemu_irq rx_irq, qemu_irq tx_irq) +{ + DeviceState *dev; + + qemu_check_nic_model(&nd_table[0], "minimac2"); + dev = qdev_create(NULL, "milkymist-minimac2"); + qdev_prop_set_taddr(dev, "buffers_base", buffers_base); + qdev_set_nic_properties(dev, &nd_table[0]); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq); + + return dev; +} + +static inline DeviceState *milkymist_softusb_create(target_phys_addr_t base, + qemu_irq irq, uint32_t pmem_base, uint32_t pmem_size, + uint32_t dmem_base, uint32_t dmem_size) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "milkymist-softusb"); + qdev_prop_set_uint32(dev, "pmem_base", pmem_base); + qdev_prop_set_uint32(dev, "pmem_size", pmem_size); + qdev_prop_set_uint32(dev, "dmem_base", dmem_base); + qdev_prop_set_uint32(dev, "dmem_size", dmem_size); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + + return dev; +} + +#endif /* QEMU_HW_MILKYMIST_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-memcard.c qemu-kvm-0.15.0+noroms/hw/milkymist-memcard.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-memcard.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-memcard.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,294 @@ +/* + * QEMU model of the Milkymist SD Card Controller. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/memcard.pdf + */ + +#include "hw.h" +#include "sysbus.h" +#include "sysemu.h" +#include "trace.h" +#include "qemu-error.h" +#include "blockdev.h" +#include "sd.h" + +enum { + ENABLE_CMD_TX = (1<<0), + ENABLE_CMD_RX = (1<<1), + ENABLE_DAT_TX = (1<<2), + ENABLE_DAT_RX = (1<<3), +}; + +enum { + PENDING_CMD_TX = (1<<0), + PENDING_CMD_RX = (1<<1), + PENDING_DAT_TX = (1<<2), + PENDING_DAT_RX = (1<<3), +}; + +enum { + START_CMD_TX = (1<<0), + START_DAT_RX = (1<<1), +}; + +enum { + R_CLK2XDIV = 0, + R_ENABLE, + R_PENDING, + R_START, + R_CMD, + R_DAT, + R_MAX +}; + +struct MilkymistMemcardState { + SysBusDevice busdev; + SDState *card; + + int command_write_ptr; + int response_read_ptr; + int response_len; + int ignore_next_cmd; + int enabled; + uint8_t command[6]; + uint8_t response[17]; + uint32_t regs[R_MAX]; +}; +typedef struct MilkymistMemcardState MilkymistMemcardState; + +static void update_pending_bits(MilkymistMemcardState *s) +{ + /* transmits are instantaneous, thus tx pending bits are never set */ + s->regs[R_PENDING] = 0; + /* if rx is enabled the corresponding pending bits are always set */ + if (s->regs[R_ENABLE] & ENABLE_CMD_RX) { + s->regs[R_PENDING] |= PENDING_CMD_RX; + } + if (s->regs[R_ENABLE] & ENABLE_DAT_RX) { + s->regs[R_PENDING] |= PENDING_DAT_RX; + } +} + +static void memcard_sd_command(MilkymistMemcardState *s) +{ + SDRequest req; + + req.cmd = s->command[0] & 0x3f; + req.arg = (s->command[1] << 24) | (s->command[2] << 16) + | (s->command[3] << 8) | s->command[4]; + req.crc = s->command[5]; + + s->response[0] = req.cmd; + s->response_len = sd_do_command(s->card, &req, s->response+1); + s->response_read_ptr = 0; + + if (s->response_len == 16) { + /* R2 response */ + s->response[0] = 0x3f; + s->response_len += 1; + } else if (s->response_len == 4) { + /* no crc calculation, insert dummy byte */ + s->response[5] = 0; + s->response_len += 2; + } + + if (req.cmd == 0) { + /* next write is a dummy byte to clock the initialization of the sd + * card */ + s->ignore_next_cmd = 1; + } +} + +static uint32_t memcard_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistMemcardState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CMD: + if (!s->enabled) { + r = 0xff; + } else { + r = s->response[s->response_read_ptr++]; + if (s->response_read_ptr > s->response_len) { + error_report("milkymist_memcard: " + "read more cmd bytes than available. Clipping."); + s->response_read_ptr = 0; + } + } + break; + case R_DAT: + if (!s->enabled) { + r = 0xffffffff; + } else { + r = 0; + r |= sd_read_data(s->card) << 24; + r |= sd_read_data(s->card) << 16; + r |= sd_read_data(s->card) << 8; + r |= sd_read_data(s->card); + } + break; + case R_CLK2XDIV: + case R_ENABLE: + case R_PENDING: + case R_START: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_memcard: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_memcard_memory_read(addr << 2, r); + + return r; +} + +static void memcard_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistMemcardState *s = opaque; + + trace_milkymist_memcard_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_PENDING: + /* clear rx pending bits */ + s->regs[R_PENDING] &= ~(value & (PENDING_CMD_RX | PENDING_DAT_RX)); + update_pending_bits(s); + break; + case R_CMD: + if (!s->enabled) { + break; + } + if (s->ignore_next_cmd) { + s->ignore_next_cmd = 0; + break; + } + s->command[s->command_write_ptr] = value & 0xff; + s->command_write_ptr = (s->command_write_ptr + 1) % 6; + if (s->command_write_ptr == 0) { + memcard_sd_command(s); + } + break; + case R_DAT: + if (!s->enabled) { + break; + } + sd_write_data(s->card, (value >> 24) & 0xff); + sd_write_data(s->card, (value >> 16) & 0xff); + sd_write_data(s->card, (value >> 8) & 0xff); + sd_write_data(s->card, value & 0xff); + break; + case R_ENABLE: + s->regs[addr] = value; + update_pending_bits(s); + break; + case R_CLK2XDIV: + case R_START: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_memcard: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const memcard_read_fn[] = { + NULL, + NULL, + &memcard_read, +}; + +static CPUWriteMemoryFunc * const memcard_write_fn[] = { + NULL, + NULL, + &memcard_write, +}; + +static void milkymist_memcard_reset(DeviceState *d) +{ + MilkymistMemcardState *s = + container_of(d, MilkymistMemcardState, busdev.qdev); + int i; + + s->command_write_ptr = 0; + s->response_read_ptr = 0; + s->response_len = 0; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } +} + +static int milkymist_memcard_init(SysBusDevice *dev) +{ + MilkymistMemcardState *s = FROM_SYSBUS(typeof(*s), dev); + DriveInfo *dinfo; + int memcard_regs; + + dinfo = drive_get_next(IF_SD); + s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0); + s->enabled = dinfo ? bdrv_is_inserted(dinfo->bdrv) : 0; + + memcard_regs = cpu_register_io_memory(memcard_read_fn, memcard_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, memcard_regs); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_memcard = { + .name = "milkymist-memcard", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(command_write_ptr, MilkymistMemcardState), + VMSTATE_INT32(response_read_ptr, MilkymistMemcardState), + VMSTATE_INT32(response_len, MilkymistMemcardState), + VMSTATE_INT32(ignore_next_cmd, MilkymistMemcardState), + VMSTATE_INT32(enabled, MilkymistMemcardState), + VMSTATE_UINT8_ARRAY(command, MilkymistMemcardState, 6), + VMSTATE_UINT8_ARRAY(response, MilkymistMemcardState, 17), + VMSTATE_UINT32_ARRAY(regs, MilkymistMemcardState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_memcard_info = { + .init = milkymist_memcard_init, + .qdev.name = "milkymist-memcard", + .qdev.size = sizeof(MilkymistMemcardState), + .qdev.vmsd = &vmstate_milkymist_memcard, + .qdev.reset = milkymist_memcard_reset, +}; + +static void milkymist_memcard_register(void) +{ + sysbus_register_withprop(&milkymist_memcard_info); +} + +device_init(milkymist_memcard_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-minimac2.c qemu-kvm-0.15.0+noroms/hw/milkymist-minimac2.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-minimac2.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-minimac2.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,539 @@ +/* + * QEMU model of the Milkymist minimac2 block. + * + * Copyright (c) 2011 Michael Walle + * + * 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 . + * + * + * Specification available at: + * not available yet + * + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "net.h" +#include "qemu-error.h" +#include "qdev-addr.h" + +#include + +enum { + R_SETUP = 0, + R_MDIO, + R_STATE0, + R_COUNT0, + R_STATE1, + R_COUNT1, + R_TXCOUNT, + R_MAX +}; + +enum { + SETUP_PHY_RST = (1<<0), +}; + +enum { + MDIO_DO = (1<<0), + MDIO_DI = (1<<1), + MDIO_OE = (1<<2), + MDIO_CLK = (1<<3), +}; + +enum { + STATE_EMPTY = 0, + STATE_LOADED = 1, + STATE_PENDING = 2, +}; + +enum { + MDIO_OP_WRITE = 1, + MDIO_OP_READ = 2, +}; + +enum mdio_state { + MDIO_STATE_IDLE, + MDIO_STATE_READING, + MDIO_STATE_WRITING, +}; + +enum { + R_PHY_ID1 = 2, + R_PHY_ID2 = 3, + R_PHY_MAX = 32 +}; + +#define MINIMAC2_MTU 1530 +#define MINIMAC2_BUFFER_SIZE 2048 + +struct MilkymistMinimac2MdioState { + int last_clk; + int count; + uint32_t data; + uint16_t data_out; + int state; + + uint8_t phy_addr; + uint8_t reg_addr; +}; +typedef struct MilkymistMinimac2MdioState MilkymistMinimac2MdioState; + +struct MilkymistMinimac2State { + SysBusDevice busdev; + NICState *nic; + NICConf conf; + char *phy_model; + target_phys_addr_t buffers_base; + + qemu_irq rx_irq; + qemu_irq tx_irq; + + uint32_t regs[R_MAX]; + + MilkymistMinimac2MdioState mdio; + + uint16_t phy_regs[R_PHY_MAX]; + + uint8_t *rx0_buf; + uint8_t *rx1_buf; + uint8_t *tx_buf; +}; +typedef struct MilkymistMinimac2State MilkymistMinimac2State; + +static const uint8_t preamble_sfd[] = { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5 +}; + +static void minimac2_mdio_write_reg(MilkymistMinimac2State *s, + uint8_t phy_addr, uint8_t reg_addr, uint16_t value) +{ + trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value); + + /* nop */ +} + +static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s, + uint8_t phy_addr, uint8_t reg_addr) +{ + uint16_t r = s->phy_regs[reg_addr]; + + trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r); + + return r; +} + +static void minimac2_update_mdio(MilkymistMinimac2State *s) +{ + MilkymistMinimac2MdioState *m = &s->mdio; + + /* detect rising clk edge */ + if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) { + /* shift data in */ + int bit = ((s->regs[R_MDIO] & MDIO_DO) + && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0; + m->data = (m->data << 1) | bit; + + /* check for sync */ + if (m->data == 0xffffffff) { + m->count = 32; + } + + if (m->count == 16) { + uint8_t start = (m->data >> 14) & 0x3; + uint8_t op = (m->data >> 12) & 0x3; + uint8_t ta = (m->data) & 0x3; + + if (start == 1 && op == MDIO_OP_WRITE && ta == 2) { + m->state = MDIO_STATE_WRITING; + } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) { + m->state = MDIO_STATE_READING; + } else { + m->state = MDIO_STATE_IDLE; + } + + if (m->state != MDIO_STATE_IDLE) { + m->phy_addr = (m->data >> 7) & 0x1f; + m->reg_addr = (m->data >> 2) & 0x1f; + } + + if (m->state == MDIO_STATE_READING) { + m->data_out = minimac2_mdio_read_reg(s, m->phy_addr, + m->reg_addr); + } + } + + if (m->count < 16 && m->state == MDIO_STATE_READING) { + int bit = (m->data_out & 0x8000) ? 1 : 0; + m->data_out <<= 1; + + if (bit) { + s->regs[R_MDIO] |= MDIO_DI; + } else { + s->regs[R_MDIO] &= ~MDIO_DI; + } + } + + if (m->count == 0 && m->state) { + if (m->state == MDIO_STATE_WRITING) { + uint16_t data = m->data & 0xffff; + minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data); + } + m->state = MDIO_STATE_IDLE; + } + m->count--; + } + + m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0; +} + +static size_t assemble_frame(uint8_t *buf, size_t size, + const uint8_t *payload, size_t payload_size) +{ + uint32_t crc; + + if (size < payload_size + 12) { + error_report("milkymist_minimac2: received too big ethernet frame"); + return 0; + } + + /* prepend preamble and sfd */ + memcpy(buf, preamble_sfd, 8); + + /* now copy the payload */ + memcpy(buf + 8, payload, payload_size); + + /* pad frame if needed */ + if (payload_size < 60) { + memset(buf + payload_size + 8, 0, 60 - payload_size); + payload_size = 60; + } + + /* append fcs */ + crc = cpu_to_le32(crc32(0, buf + 8, payload_size)); + memcpy(buf + payload_size + 8, &crc, 4); + + return payload_size + 12; +} + +static void minimac2_tx(MilkymistMinimac2State *s) +{ + uint32_t txcount = s->regs[R_TXCOUNT]; + uint8_t *buf = s->tx_buf; + + if (txcount < 64) { + error_report("milkymist_minimac2: ethernet frame too small (%u < %u)", + txcount, 64); + goto err; + } + + if (txcount > MINIMAC2_MTU) { + error_report("milkymist_minimac2: MTU exceeded (%u > %u)", + txcount, MINIMAC2_MTU); + goto err; + } + + if (memcmp(buf, preamble_sfd, 8) != 0) { + error_report("milkymist_minimac2: frame doesn't contain the preamble " + "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + goto err; + } + + trace_milkymist_minimac2_tx_frame(txcount - 12); + + /* send packet, skipping preamble and sfd */ + qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12); + + s->regs[R_TXCOUNT] = 0; + +err: + trace_milkymist_minimac2_pulse_irq_tx(); + qemu_irq_pulse(s->tx_irq); +} + +static void update_rx_interrupt(MilkymistMinimac2State *s) +{ + if (s->regs[R_STATE0] == STATE_PENDING + || s->regs[R_STATE1] == STATE_PENDING) { + trace_milkymist_minimac2_raise_irq_rx(); + qemu_irq_raise(s->rx_irq); + } else { + trace_milkymist_minimac2_lower_irq_rx(); + qemu_irq_lower(s->rx_irq); + } +} + +static ssize_t minimac2_rx(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + + uint32_t r_count; + uint32_t r_state; + uint8_t *rx_buf; + + size_t frame_size; + + trace_milkymist_minimac2_rx_frame(buf, size); + + /* choose appropriate slot */ + if (s->regs[R_STATE0] == STATE_LOADED) { + r_count = R_COUNT0; + r_state = R_STATE0; + rx_buf = s->rx0_buf; + } else if (s->regs[R_STATE1] == STATE_LOADED) { + r_count = R_COUNT1; + r_state = R_STATE1; + rx_buf = s->rx1_buf; + } else { + trace_milkymist_minimac2_drop_rx_frame(buf); + return size; + } + + /* assemble frame */ + frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size); + + if (frame_size == 0) { + return size; + } + + trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size); + + /* update slot */ + s->regs[r_count] = frame_size; + s->regs[r_state] = STATE_PENDING; + + update_rx_interrupt(s); + + return size; +} + +static uint32_t +minimac2_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistMinimac2State *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_SETUP: + case R_MDIO: + case R_STATE0: + case R_COUNT0: + case R_STATE1: + case R_COUNT1: + case R_TXCOUNT: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_minimac2: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_minimac2_memory_read(addr << 2, r); + + return r; +} + +static void +minimac2_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistMinimac2State *s = opaque; + + trace_milkymist_minimac2_memory_read(addr, value); + + addr >>= 2; + switch (addr) { + case R_MDIO: + { + /* MDIO_DI is read only */ + int mdio_di = (s->regs[R_MDIO] & MDIO_DI); + s->regs[R_MDIO] = value; + if (mdio_di) { + s->regs[R_MDIO] |= mdio_di; + } else { + s->regs[R_MDIO] &= ~mdio_di; + } + + minimac2_update_mdio(s); + } break; + case R_TXCOUNT: + s->regs[addr] = value; + if (value > 0) { + minimac2_tx(s); + } + break; + case R_STATE0: + case R_STATE1: + s->regs[addr] = value; + update_rx_interrupt(s); + break; + case R_SETUP: + case R_COUNT0: + case R_COUNT1: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_minimac2: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const minimac2_read_fn[] = { + NULL, + NULL, + &minimac2_read, +}; + +static CPUWriteMemoryFunc * const minimac2_write_fn[] = { + NULL, + NULL, + &minimac2_write, +}; + +static int minimac2_can_rx(VLANClientState *nc) +{ + MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + + if (s->regs[R_STATE0] == STATE_LOADED) { + return 1; + } + if (s->regs[R_STATE1] == STATE_LOADED) { + return 1; + } + + return 0; +} + +static void minimac2_cleanup(VLANClientState *nc) +{ + MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + + s->nic = NULL; +} + +static void milkymist_minimac2_reset(DeviceState *d) +{ + MilkymistMinimac2State *s = + container_of(d, MilkymistMinimac2State, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + for (i = 0; i < R_PHY_MAX; i++) { + s->phy_regs[i] = 0; + } + + /* defaults */ + s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */ + s->phy_regs[R_PHY_ID2] = 0x161a; +} + +static NetClientInfo net_milkymist_minimac2_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = minimac2_can_rx, + .receive = minimac2_rx, + .cleanup = minimac2_cleanup, +}; + +static int milkymist_minimac2_init(SysBusDevice *dev) +{ + MilkymistMinimac2State *s = FROM_SYSBUS(typeof(*s), dev); + int regs; + ram_addr_t buffers; + size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE); + + sysbus_init_irq(dev, &s->rx_irq); + sysbus_init_irq(dev, &s->tx_irq); + + regs = cpu_register_io_memory(minimac2_read_fn, minimac2_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, regs); + + /* register buffers memory */ + buffers = qemu_ram_alloc(NULL, "milkymist_minimac2.buffers", buffers_size); + s->rx0_buf = qemu_get_ram_ptr(buffers); + s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE; + s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE; + + cpu_register_physical_memory(s->buffers_base, buffers_size, + buffers | IO_MEM_RAM); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_minimac2_mdio = { + .name = "milkymist-minimac2-mdio", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState), + VMSTATE_INT32(count, MilkymistMinimac2MdioState), + VMSTATE_UINT32(data, MilkymistMinimac2MdioState), + VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState), + VMSTATE_INT32(state, MilkymistMinimac2MdioState), + VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState), + VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_milkymist_minimac2 = { + .name = "milkymist-minimac2", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX), + VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX), + VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0, + vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_minimac2_info = { + .init = milkymist_minimac2_init, + .qdev.name = "milkymist-minimac2", + .qdev.size = sizeof(MilkymistMinimac2State), + .qdev.vmsd = &vmstate_milkymist_minimac2, + .qdev.reset = milkymist_minimac2_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_TADDR("buffers_base", MilkymistMinimac2State, + buffers_base, 0), + DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf), + DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void milkymist_minimac2_register(void) +{ + sysbus_register_withprop(&milkymist_minimac2_info); +} + +device_init(milkymist_minimac2_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-pfpu.c qemu-kvm-0.15.0+noroms/hw/milkymist-pfpu.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-pfpu.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-pfpu.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,536 @@ +/* + * QEMU model of the Milkymist programmable FPU. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/pfpu.pdf + * + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-log.h" +#include "qemu-error.h" +#include + +/* #define TRACE_EXEC */ + +#ifdef TRACE_EXEC +# define D_EXEC(x) x +#else +# define D_EXEC(x) +#endif + +enum { + R_CTL = 0, + R_MESHBASE, + R_HMESHLAST, + R_VMESHLAST, + R_CODEPAGE, + R_VERTICES, + R_COLLISIONS, + R_STRAYWRITES, + R_LASTDMA, + R_PC, + R_DREGBASE, + R_CODEBASE, + R_MAX +}; + +enum { + CTL_START_BUSY = (1<<0), +}; + +enum { + OP_NOP = 0, + OP_FADD, + OP_FSUB, + OP_FMUL, + OP_FABS, + OP_F2I, + OP_I2F, + OP_VECTOUT, + OP_SIN, + OP_COS, + OP_ABOVE, + OP_EQUAL, + OP_COPY, + OP_IF, + OP_TSIGN, + OP_QUAKE, +}; + +enum { + GPR_X = 0, + GPR_Y = 1, + GPR_FLAGS = 2, +}; + +enum { + LATENCY_FADD = 5, + LATENCY_FSUB = 5, + LATENCY_FMUL = 7, + LATENCY_FABS = 2, + LATENCY_F2I = 2, + LATENCY_I2F = 3, + LATENCY_VECTOUT = 0, + LATENCY_SIN = 4, + LATENCY_COS = 4, + LATENCY_ABOVE = 2, + LATENCY_EQUAL = 2, + LATENCY_COPY = 2, + LATENCY_IF = 2, + LATENCY_TSIGN = 2, + LATENCY_QUAKE = 2, + MAX_LATENCY = 7 +}; + +#define GPR_BEGIN 0x100 +#define GPR_END 0x17f +#define MICROCODE_BEGIN 0x200 +#define MICROCODE_END 0x3ff +#define MICROCODE_WORDS 2048 + +#define REINTERPRET_CAST(type, val) (*((type *)&(val))) + +#ifdef TRACE_EXEC +static const char *opcode_to_str[] = { + "NOP", "FADD", "FSUB", "FMUL", "FABS", "F2I", "I2F", "VECTOUT", + "SIN", "COS", "ABOVE", "EQUAL", "COPY", "IF", "TSIGN", "QUAKE", +}; +#endif + +struct MilkymistPFPUState { + SysBusDevice busdev; + CharDriverState *chr; + qemu_irq irq; + + uint32_t regs[R_MAX]; + uint32_t gp_regs[128]; + uint32_t microcode[MICROCODE_WORDS]; + + int output_queue_pos; + uint32_t output_queue[MAX_LATENCY]; +}; +typedef struct MilkymistPFPUState MilkymistPFPUState; + +static inline target_phys_addr_t +get_dma_address(uint32_t base, uint32_t x, uint32_t y) +{ + return base + 8 * (128 * y + x); +} + +static inline void +output_queue_insert(MilkymistPFPUState *s, uint32_t val, int pos) +{ + s->output_queue[(s->output_queue_pos + pos) % MAX_LATENCY] = val; +} + +static inline uint32_t +output_queue_remove(MilkymistPFPUState *s) +{ + return s->output_queue[s->output_queue_pos]; +} + +static inline void +output_queue_advance(MilkymistPFPUState *s) +{ + s->output_queue[s->output_queue_pos] = 0; + s->output_queue_pos = (s->output_queue_pos + 1) % MAX_LATENCY; +} + +static int pfpu_decode_insn(MilkymistPFPUState *s) +{ + uint32_t pc = s->regs[R_PC]; + uint32_t insn = s->microcode[pc]; + uint32_t reg_a = (insn >> 18) & 0x7f; + uint32_t reg_b = (insn >> 11) & 0x7f; + uint32_t op = (insn >> 7) & 0xf; + uint32_t reg_d = insn & 0x7f; + uint32_t r = 0; + int latency = 0; + + switch (op) { + case OP_NOP: + break; + case OP_FADD: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); + float t = a + b; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_FADD; + D_EXEC(qemu_log("ADD a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); + } break; + case OP_FSUB: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); + float t = a - b; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_FSUB; + D_EXEC(qemu_log("SUB a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); + } break; + case OP_FMUL: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); + float t = a * b; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_FMUL; + D_EXEC(qemu_log("MUL a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); + } break; + case OP_FABS: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float t = fabsf(a); + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_FABS; + D_EXEC(qemu_log("ABS a=%f t=%f, r=%08x\n", a, t, r)); + } break; + case OP_F2I: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + int32_t t = a; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_F2I; + D_EXEC(qemu_log("F2I a=%f t=%d, r=%08x\n", a, t, r)); + } break; + case OP_I2F: + { + int32_t a = REINTERPRET_CAST(int32_t, s->gp_regs[reg_a]); + float t = a; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_I2F; + D_EXEC(qemu_log("I2F a=%08x t=%f, r=%08x\n", a, t, r)); + } break; + case OP_VECTOUT: + { + uint32_t a = cpu_to_be32(s->gp_regs[reg_a]); + uint32_t b = cpu_to_be32(s->gp_regs[reg_b]); + target_phys_addr_t dma_ptr = + get_dma_address(s->regs[R_MESHBASE], + s->gp_regs[GPR_X], s->gp_regs[GPR_Y]); + cpu_physical_memory_write(dma_ptr, (uint8_t *)&a, 4); + cpu_physical_memory_write(dma_ptr + 4, (uint8_t *)&b, 4); + s->regs[R_LASTDMA] = dma_ptr + 4; + D_EXEC(qemu_log("VECTOUT a=%08x b=%08x dma=%08x\n", a, b, dma_ptr)); + trace_milkymist_pfpu_vectout(a, b, dma_ptr); + } break; + case OP_SIN: + { + int32_t a = REINTERPRET_CAST(int32_t, s->gp_regs[reg_a]); + float t = sinf(a * (1.0f / (M_PI * 4096.0f))); + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_SIN; + D_EXEC(qemu_log("SIN a=%d t=%f, r=%08x\n", a, t, r)); + } break; + case OP_COS: + { + int32_t a = REINTERPRET_CAST(int32_t, s->gp_regs[reg_a]); + float t = cosf(a * (1.0f / (M_PI * 4096.0f))); + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_COS; + D_EXEC(qemu_log("COS a=%d t=%f, r=%08x\n", a, t, r)); + } break; + case OP_ABOVE: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); + float t = (a > b) ? 1.0f : 0.0f; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_ABOVE; + D_EXEC(qemu_log("ABOVE a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); + } break; + case OP_EQUAL: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); + float t = (a == b) ? 1.0f : 0.0f; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_EQUAL; + D_EXEC(qemu_log("EQUAL a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); + } break; + case OP_COPY: + { + r = s->gp_regs[reg_a]; + latency = LATENCY_COPY; + D_EXEC(qemu_log("COPY")); + } break; + case OP_IF: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); + uint32_t f = s->gp_regs[GPR_FLAGS]; + float t = (f != 0) ? a : b; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_IF; + D_EXEC(qemu_log("IF f=%u a=%f b=%f t=%f, r=%08x\n", f, a, b, t, r)); + } break; + case OP_TSIGN: + { + float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); + float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); + float t = (b < 0) ? -a : a; + r = REINTERPRET_CAST(uint32_t, t); + latency = LATENCY_TSIGN; + D_EXEC(qemu_log("TSIGN a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); + } break; + case OP_QUAKE: + { + uint32_t a = s->gp_regs[reg_a]; + r = 0x5f3759df - (a >> 1); + latency = LATENCY_QUAKE; + D_EXEC(qemu_log("QUAKE a=%d r=%08x\n", a, r)); + } break; + + default: + error_report("milkymist_pfpu: unknown opcode %d", op); + break; + } + + if (!reg_d) { + D_EXEC(qemu_log("%04d %8s R%03d, R%03d \n", + s->regs[R_PC], opcode_to_str[op], reg_a, reg_b, latency, + s->regs[R_PC] + latency)); + } else { + D_EXEC(qemu_log("%04d %8s R%03d, R%03d -> R%03d\n", + s->regs[R_PC], opcode_to_str[op], reg_a, reg_b, latency, + s->regs[R_PC] + latency, reg_d)); + } + + if (op == OP_VECTOUT) { + return 0; + } + + /* store output for this cycle */ + if (reg_d) { + uint32_t val = output_queue_remove(s); + D_EXEC(qemu_log("R%03d <- 0x%08x\n", reg_d, val)); + s->gp_regs[reg_d] = val; + } + + output_queue_advance(s); + + /* store op output */ + if (op != OP_NOP) { + output_queue_insert(s, r, latency-1); + } + + /* advance PC */ + s->regs[R_PC]++; + + return 1; +}; + +static void pfpu_start(MilkymistPFPUState *s) +{ + int x, y; + int i; + + for (y = 0; y <= s->regs[R_VMESHLAST]; y++) { + for (x = 0; x <= s->regs[R_HMESHLAST]; x++) { + D_EXEC(qemu_log("\nprocessing x=%d y=%d\n", x, y)); + + /* set current position */ + s->gp_regs[GPR_X] = x; + s->gp_regs[GPR_Y] = y; + + /* run microcode on this position */ + i = 0; + while (pfpu_decode_insn(s)) { + /* decode at most MICROCODE_WORDS instructions */ + if (i++ >= MICROCODE_WORDS) { + error_report("milkymist_pfpu: too many instructions " + "executed in microcode. No VECTOUT?"); + break; + } + } + + /* reset pc for next run */ + s->regs[R_PC] = 0; + } + } + + s->regs[R_VERTICES] = x * y; + + trace_milkymist_pfpu_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static inline int get_microcode_address(MilkymistPFPUState *s, uint32_t addr) +{ + return (512 * s->regs[R_CODEPAGE]) + addr - MICROCODE_BEGIN; +} + +static uint32_t pfpu_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistPFPUState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTL: + case R_MESHBASE: + case R_HMESHLAST: + case R_VMESHLAST: + case R_CODEPAGE: + case R_VERTICES: + case R_COLLISIONS: + case R_STRAYWRITES: + case R_LASTDMA: + case R_PC: + case R_DREGBASE: + case R_CODEBASE: + r = s->regs[addr]; + break; + case GPR_BEGIN ... GPR_END: + r = s->gp_regs[addr - GPR_BEGIN]; + break; + case MICROCODE_BEGIN ... MICROCODE_END: + r = s->microcode[get_microcode_address(s, addr)]; + break; + + default: + error_report("milkymist_pfpu: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_pfpu_memory_read(addr << 2, r); + + return r; +} + +static void +pfpu_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistPFPUState *s = opaque; + + trace_milkymist_pfpu_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTL: + if (value & CTL_START_BUSY) { + pfpu_start(s); + } + break; + case R_MESHBASE: + case R_HMESHLAST: + case R_VMESHLAST: + case R_CODEPAGE: + case R_VERTICES: + case R_COLLISIONS: + case R_STRAYWRITES: + case R_LASTDMA: + case R_PC: + case R_DREGBASE: + case R_CODEBASE: + s->regs[addr] = value; + break; + case GPR_BEGIN ... GPR_END: + s->gp_regs[addr - GPR_BEGIN] = value; + break; + case MICROCODE_BEGIN ... MICROCODE_END: + s->microcode[get_microcode_address(s, addr)] = value; + break; + + default: + error_report("milkymist_pfpu: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const pfpu_read_fn[] = { + NULL, + NULL, + &pfpu_read, +}; + +static CPUWriteMemoryFunc * const pfpu_write_fn[] = { + NULL, + NULL, + &pfpu_write, +}; + +static void milkymist_pfpu_reset(DeviceState *d) +{ + MilkymistPFPUState *s = container_of(d, MilkymistPFPUState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + for (i = 0; i < 128; i++) { + s->gp_regs[i] = 0; + } + for (i = 0; i < MICROCODE_WORDS; i++) { + s->microcode[i] = 0; + } + s->output_queue_pos = 0; + for (i = 0; i < MAX_LATENCY; i++) { + s->output_queue[i] = 0; + } +} + +static int milkymist_pfpu_init(SysBusDevice *dev) +{ + MilkymistPFPUState *s = FROM_SYSBUS(typeof(*s), dev); + int pfpu_regs; + + sysbus_init_irq(dev, &s->irq); + + pfpu_regs = cpu_register_io_memory(pfpu_read_fn, pfpu_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, MICROCODE_END * 4, pfpu_regs); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_pfpu = { + .name = "milkymist-pfpu", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistPFPUState, R_MAX), + VMSTATE_UINT32_ARRAY(gp_regs, MilkymistPFPUState, 128), + VMSTATE_UINT32_ARRAY(microcode, MilkymistPFPUState, MICROCODE_WORDS), + VMSTATE_INT32(output_queue_pos, MilkymistPFPUState), + VMSTATE_UINT32_ARRAY(output_queue, MilkymistPFPUState, MAX_LATENCY), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_pfpu_info = { + .init = milkymist_pfpu_init, + .qdev.name = "milkymist-pfpu", + .qdev.size = sizeof(MilkymistPFPUState), + .qdev.vmsd = &vmstate_milkymist_pfpu, + .qdev.reset = milkymist_pfpu_reset, +}; + +static void milkymist_pfpu_register(void) +{ + sysbus_register_withprop(&milkymist_pfpu_info); +} + +device_init(milkymist_pfpu_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-softusb.c qemu-kvm-0.15.0+noroms/hw/milkymist-softusb.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-softusb.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-softusb.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,372 @@ +/* + * QEMU model of the Milkymist SoftUSB block. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * not available yet + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "console.h" +#include "usb.h" +#include "qemu-error.h" + +enum { + R_CTRL = 0, + R_MAX +}; + +enum { + CTRL_RESET = (1<<0), +}; + +#define COMLOC_DEBUG_PRODUCE 0x1000 +#define COMLOC_DEBUG_BASE 0x1001 +#define COMLOC_MEVT_PRODUCE 0x1101 +#define COMLOC_MEVT_BASE 0x1102 +#define COMLOC_KEVT_PRODUCE 0x1142 +#define COMLOC_KEVT_BASE 0x1143 + +struct MilkymistSoftUsbState { + SysBusDevice busdev; + USBBus usbbus; + USBPort usbport[2]; + USBDevice *usbdev; + + qemu_irq irq; + + /* device properties */ + uint32_t pmem_base; + uint32_t pmem_size; + uint32_t dmem_base; + uint32_t dmem_size; + + /* device registers */ + uint32_t regs[R_MAX]; + + /* mouse state */ + int mouse_dx; + int mouse_dy; + int mouse_dz; + uint8_t mouse_buttons_state; + + /* keyboard state */ + uint8_t kbd_usb_buffer[8]; +}; +typedef struct MilkymistSoftUsbState MilkymistSoftUsbState; + +static uint32_t softusb_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistSoftUsbState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTRL: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_softusb: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_softusb_memory_read(addr << 2, r); + + return r; +} + +static void +softusb_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistSoftUsbState *s = opaque; + + trace_milkymist_softusb_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTRL: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_softusb: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const softusb_read_fn[] = { + NULL, + NULL, + &softusb_read, +}; + +static CPUWriteMemoryFunc * const softusb_write_fn[] = { + NULL, + NULL, + &softusb_write, +}; + +static inline void softusb_read_dmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->dmem_size) { + error_report("milkymist_softusb: read dmem out of bounds " + "at offset 0x%x, len %d", offset, len); + return; + } + + cpu_physical_memory_read(s->dmem_base + offset, buf, len); +} + +static inline void softusb_write_dmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->dmem_size) { + error_report("milkymist_softusb: write dmem out of bounds " + "at offset 0x%x, len %d", offset, len); + return; + } + + cpu_physical_memory_write(s->dmem_base + offset, buf, len); +} + +static inline void softusb_read_pmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->pmem_size) { + error_report("milkymist_softusb: read pmem out of bounds " + "at offset 0x%x, len %d", offset, len); + return; + } + + cpu_physical_memory_read(s->pmem_base + offset, buf, len); +} + +static inline void softusb_write_pmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->pmem_size) { + error_report("milkymist_softusb: write pmem out of bounds " + "at offset 0x%x, len %d", offset, len); + return; + } + + cpu_physical_memory_write(s->pmem_base + offset, buf, len); +} + +static void softusb_mouse_changed(MilkymistSoftUsbState *s) +{ + uint8_t m; + uint8_t buf[4]; + + buf[0] = s->mouse_buttons_state; + buf[1] = s->mouse_dx; + buf[2] = s->mouse_dy; + buf[3] = s->mouse_dz; + + softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); + trace_milkymist_softusb_mevt(m); + softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, buf, 4); + m = (m + 1) & 0xf; + softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); + + trace_milkymist_softusb_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static void softusb_kbd_changed(MilkymistSoftUsbState *s) +{ + uint8_t m; + + softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); + trace_milkymist_softusb_kevt(m); + softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_usb_buffer, 8); + m = (m + 1) & 0x7; + softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); + + trace_milkymist_softusb_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static void softusb_mouse_event(void *opaque, + int dx, int dy, int dz, int buttons_state) +{ + MilkymistSoftUsbState *s = opaque; + + /* if device is in reset, do nothing */ + if (s->regs[R_CTRL] & CTRL_RESET) { + return; + } + + trace_milkymist_softusb_mouse_event(dx, dy, dz, buttons_state); + + s->mouse_dx = dx; + s->mouse_dy = dy; + s->mouse_dz = dz; + s->mouse_buttons_state = buttons_state; + + softusb_mouse_changed(s); +} + +static void softusb_usbdev_datain(void *opaque) +{ + MilkymistSoftUsbState *s = opaque; + + USBPacket p; + + p.pid = USB_TOKEN_IN; + p.devep = 1; + p.data = s->kbd_usb_buffer; + p.len = sizeof(s->kbd_usb_buffer); + s->usbdev->info->handle_data(s->usbdev, &p); + + softusb_kbd_changed(s); +} + +static void softusb_attach(USBPort *port) +{ +} + +static void softusb_detach(USBPort *port) +{ +} + +static void softusb_child_detach(USBPort *port, USBDevice *child) +{ +} + +static USBPortOps softusb_ops = { + .attach = softusb_attach, + .detach = softusb_detach, + .child_detach = softusb_child_detach, +}; + +static USBBusOps softusb_bus_ops = { +}; + +static void milkymist_softusb_reset(DeviceState *d) +{ + MilkymistSoftUsbState *s = + container_of(d, MilkymistSoftUsbState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + s->mouse_dx = 0; + s->mouse_dy = 0; + s->mouse_dz = 0; + s->mouse_buttons_state = 0; + memset(s->kbd_usb_buffer, 0, sizeof(s->kbd_usb_buffer)); + + /* defaults */ + s->regs[R_CTRL] = CTRL_RESET; +} + +static int milkymist_softusb_init(SysBusDevice *dev) +{ + MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev); + int softusb_regs; + ram_addr_t pmem_ram; + ram_addr_t dmem_ram; + + sysbus_init_irq(dev, &s->irq); + + softusb_regs = cpu_register_io_memory(softusb_read_fn, softusb_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, softusb_regs); + + /* register pmem and dmem */ + pmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.pmem", s->pmem_size); + cpu_register_physical_memory(s->pmem_base, s->pmem_size, + pmem_ram | IO_MEM_RAM); + dmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.dmem", s->dmem_size); + cpu_register_physical_memory(s->dmem_base, s->dmem_size, + dmem_ram | IO_MEM_RAM); + + qemu_add_mouse_event_handler(softusb_mouse_event, s, 0, "Milkymist Mouse"); + + /* create our usb bus */ + usb_bus_new(&s->usbbus, &softusb_bus_ops, NULL); + + /* our two ports */ + /* FIXME: claim to support full speed devices. qemu mouse and keyboard + * report themselves as full speed devices. */ + usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + + /* and finally create an usb keyboard */ + s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd"); + usb_hid_datain_cb(s->usbdev, s, softusb_usbdev_datain); + s->usbdev->info->handle_reset(s->usbdev); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_softusb = { + .name = "milkymist-softusb", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX), + VMSTATE_INT32(mouse_dx, MilkymistSoftUsbState), + VMSTATE_INT32(mouse_dy, MilkymistSoftUsbState), + VMSTATE_INT32(mouse_dz, MilkymistSoftUsbState), + VMSTATE_UINT8(mouse_buttons_state, MilkymistSoftUsbState), + VMSTATE_BUFFER(kbd_usb_buffer, MilkymistSoftUsbState), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_softusb_info = { + .init = milkymist_softusb_init, + .qdev.name = "milkymist-softusb", + .qdev.size = sizeof(MilkymistSoftUsbState), + .qdev.vmsd = &vmstate_milkymist_softusb, + .qdev.reset = milkymist_softusb_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32( + "pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000 + ), + DEFINE_PROP_UINT32( + "pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000 + ), + DEFINE_PROP_UINT32( + "dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000 + ), + DEFINE_PROP_UINT32( + "dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000 + ), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void milkymist_softusb_register(void) +{ + sysbus_register_withprop(&milkymist_softusb_info); +} + +device_init(milkymist_softusb_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-sysctl.c qemu-kvm-0.15.0+noroms/hw/milkymist-sysctl.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-sysctl.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-sysctl.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,318 @@ +/* + * QEMU model of the Milkymist System Controller. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/sysctl.pdf + */ + +#include "hw.h" +#include "sysbus.h" +#include "sysemu.h" +#include "trace.h" +#include "qemu-timer.h" +#include "qemu-error.h" + +enum { + CTRL_ENABLE = (1<<0), + CTRL_AUTORESTART = (1<<1), +}; + +enum { + ICAP_READY = (1<<0), +}; + +enum { + R_GPIO_IN = 0, + R_GPIO_OUT, + R_GPIO_INTEN, + R_RESERVED0, + R_TIMER0_CONTROL, + R_TIMER0_COMPARE, + R_TIMER0_COUNTER, + R_RESERVED1, + R_TIMER1_CONTROL, + R_TIMER1_COMPARE, + R_TIMER1_COUNTER, + R_RESERVED2, + R_RESERVED3, + R_ICAP, + R_CAPABILITIES, + R_SYSTEM_ID, + R_MAX +}; + +struct MilkymistSysctlState { + SysBusDevice busdev; + + QEMUBH *bh0; + QEMUBH *bh1; + ptimer_state *ptimer0; + ptimer_state *ptimer1; + + uint32_t freq_hz; + uint32_t capabilities; + uint32_t systemid; + uint32_t strappings; + + uint32_t regs[R_MAX]; + + qemu_irq gpio_irq; + qemu_irq timer0_irq; + qemu_irq timer1_irq; +}; +typedef struct MilkymistSysctlState MilkymistSysctlState; + +static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value) +{ + trace_milkymist_sysctl_icap_write(value); + switch (value & 0xffff) { + case 0x000e: + qemu_system_shutdown_request(); + break; + } +} + +static uint32_t sysctl_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistSysctlState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_TIMER0_COUNTER: + r = (uint32_t)ptimer_get_count(s->ptimer0); + /* milkymist timer counts up */ + r = s->regs[R_TIMER0_COMPARE] - r; + break; + case R_TIMER1_COUNTER: + r = (uint32_t)ptimer_get_count(s->ptimer1); + /* milkymist timer counts up */ + r = s->regs[R_TIMER1_COMPARE] - r; + break; + case R_GPIO_IN: + case R_GPIO_OUT: + case R_GPIO_INTEN: + case R_TIMER0_CONTROL: + case R_TIMER0_COMPARE: + case R_TIMER1_CONTROL: + case R_TIMER1_COMPARE: + case R_ICAP: + case R_CAPABILITIES: + case R_SYSTEM_ID: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_sysctl: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_sysctl_memory_read(addr << 2, r); + + return r; +} + +static void sysctl_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistSysctlState *s = opaque; + + trace_milkymist_sysctl_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_GPIO_OUT: + case R_GPIO_INTEN: + case R_TIMER0_COUNTER: + case R_TIMER1_COUNTER: + s->regs[addr] = value; + break; + case R_TIMER0_COMPARE: + ptimer_set_limit(s->ptimer0, value, 0); + s->regs[addr] = value; + break; + case R_TIMER1_COMPARE: + ptimer_set_limit(s->ptimer1, value, 0); + s->regs[addr] = value; + break; + case R_TIMER0_CONTROL: + s->regs[addr] = value; + if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) { + trace_milkymist_sysctl_start_timer0(); + ptimer_set_count(s->ptimer0, + s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]); + ptimer_run(s->ptimer0, 0); + } else { + trace_milkymist_sysctl_stop_timer0(); + ptimer_stop(s->ptimer0); + } + break; + case R_TIMER1_CONTROL: + s->regs[addr] = value; + if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) { + trace_milkymist_sysctl_start_timer1(); + ptimer_set_count(s->ptimer1, + s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]); + ptimer_run(s->ptimer1, 0); + } else { + trace_milkymist_sysctl_stop_timer1(); + ptimer_stop(s->ptimer1); + } + break; + case R_ICAP: + sysctl_icap_write(s, value); + break; + case R_SYSTEM_ID: + qemu_system_reset_request(); + break; + + case R_GPIO_IN: + case R_CAPABILITIES: + error_report("milkymist_sysctl: write to read-only register 0x" + TARGET_FMT_plx, addr << 2); + break; + + default: + error_report("milkymist_sysctl: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const sysctl_read_fn[] = { + NULL, + NULL, + &sysctl_read, +}; + +static CPUWriteMemoryFunc * const sysctl_write_fn[] = { + NULL, + NULL, + &sysctl_write, +}; + +static void timer0_hit(void *opaque) +{ + MilkymistSysctlState *s = opaque; + + if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) { + s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE; + trace_milkymist_sysctl_stop_timer0(); + ptimer_stop(s->ptimer0); + } + + trace_milkymist_sysctl_pulse_irq_timer0(); + qemu_irq_pulse(s->timer0_irq); +} + +static void timer1_hit(void *opaque) +{ + MilkymistSysctlState *s = opaque; + + if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) { + s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE; + trace_milkymist_sysctl_stop_timer1(); + ptimer_stop(s->ptimer1); + } + + trace_milkymist_sysctl_pulse_irq_timer1(); + qemu_irq_pulse(s->timer1_irq); +} + +static void milkymist_sysctl_reset(DeviceState *d) +{ + MilkymistSysctlState *s = + container_of(d, MilkymistSysctlState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + ptimer_stop(s->ptimer0); + ptimer_stop(s->ptimer1); + + /* defaults */ + s->regs[R_ICAP] = ICAP_READY; + s->regs[R_SYSTEM_ID] = s->systemid; + s->regs[R_CAPABILITIES] = s->capabilities; + s->regs[R_GPIO_IN] = s->strappings; +} + +static int milkymist_sysctl_init(SysBusDevice *dev) +{ + MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev); + int sysctl_regs; + + sysbus_init_irq(dev, &s->gpio_irq); + sysbus_init_irq(dev, &s->timer0_irq); + sysbus_init_irq(dev, &s->timer1_irq); + + s->bh0 = qemu_bh_new(timer0_hit, s); + s->bh1 = qemu_bh_new(timer1_hit, s); + s->ptimer0 = ptimer_init(s->bh0); + s->ptimer1 = ptimer_init(s->bh1); + ptimer_set_freq(s->ptimer0, s->freq_hz); + ptimer_set_freq(s->ptimer1, s->freq_hz); + + sysctl_regs = cpu_register_io_memory(sysctl_read_fn, sysctl_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, sysctl_regs); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_sysctl = { + .name = "milkymist-sysctl", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX), + VMSTATE_PTIMER(ptimer0, MilkymistSysctlState), + VMSTATE_PTIMER(ptimer1, MilkymistSysctlState), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_sysctl_info = { + .init = milkymist_sysctl_init, + .qdev.name = "milkymist-sysctl", + .qdev.size = sizeof(MilkymistSysctlState), + .qdev.vmsd = &vmstate_milkymist_sysctl, + .qdev.reset = milkymist_sysctl_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("frequency", MilkymistSysctlState, + freq_hz, 80000000), + DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState, + capabilities, 0x00000000), + DEFINE_PROP_UINT32("systemid", MilkymistSysctlState, + systemid, 0x10014d31), + DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState, + strappings, 0x00000001), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void milkymist_sysctl_register(void) +{ + sysbus_register_withprop(&milkymist_sysctl_info); +} + +device_init(milkymist_sysctl_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-tmu2.c qemu-kvm-0.15.0+noroms/hw/milkymist-tmu2.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-tmu2.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-tmu2.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,481 @@ +/* + * QEMU model of the Milkymist texture mapping unit. + * + * Copyright (c) 2010 Michael Walle + * Copyright (c) 2010 Sebastien Bourdeauducq + * + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/tmu2.pdf + * + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-error.h" + +#include +#include +#include + +enum { + R_CTL = 0, + R_HMESHLAST, + R_VMESHLAST, + R_BRIGHTNESS, + R_CHROMAKEY, + R_VERTICESADDR, + R_TEXFBUF, + R_TEXHRES, + R_TEXVRES, + R_TEXHMASK, + R_TEXVMASK, + R_DSTFBUF, + R_DSTHRES, + R_DSTVRES, + R_DSTHOFFSET, + R_DSTVOFFSET, + R_DSTSQUAREW, + R_DSTSQUAREH, + R_ALPHA, + R_MAX +}; + +enum { + CTL_START_BUSY = (1<<0), + CTL_CHROMAKEY = (1<<1), +}; + +enum { + MAX_BRIGHTNESS = 63, + MAX_ALPHA = 63, +}; + +enum { + MESH_MAXSIZE = 128, +}; + +struct vertex { + int x; + int y; +} __attribute__((packed)); + +struct MilkymistTMU2State { + SysBusDevice busdev; + CharDriverState *chr; + qemu_irq irq; + + uint32_t regs[R_MAX]; + + Display *dpy; + GLXFBConfig glx_fb_config; + GLXContext glx_context; +}; +typedef struct MilkymistTMU2State MilkymistTMU2State; + +static const int glx_fbconfig_attr[] = { + GLX_GREEN_SIZE, 5, + GLX_GREEN_SIZE, 6, + GLX_BLUE_SIZE, 5, + None +}; + +static int tmu2_glx_init(MilkymistTMU2State *s) +{ + GLXFBConfig *configs; + int nelements; + + s->dpy = XOpenDisplay(NULL); /* FIXME: call XCloseDisplay() */ + if (s->dpy == NULL) { + return 1; + } + + configs = glXChooseFBConfig(s->dpy, 0, glx_fbconfig_attr, &nelements); + if (configs == NULL) { + return 1; + } + + s->glx_fb_config = *configs; + XFree(configs); + + /* FIXME: call glXDestroyContext() */ + s->glx_context = glXCreateNewContext(s->dpy, s->glx_fb_config, + GLX_RGBA_TYPE, NULL, 1); + if (s->glx_context == NULL) { + return 1; + } + + return 0; +} + +static void tmu2_gl_map(struct vertex *mesh, int texhres, int texvres, + int hmeshlast, int vmeshlast, int ho, int vo, int sw, int sh) +{ + int x, y; + int x0, y0, x1, y1; + int u0, v0, u1, v1, u2, v2, u3, v3; + double xscale = 1.0 / ((double)(64 * texhres)); + double yscale = 1.0 / ((double)(64 * texvres)); + + glLoadIdentity(); + glTranslatef(ho, vo, 0); + glEnable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + + for (y = 0; y < vmeshlast; y++) { + y0 = y * sh; + y1 = y0 + sh; + for (x = 0; x < hmeshlast; x++) { + x0 = x * sw; + x1 = x0 + sw; + + u0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].x); + v0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].y); + u1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].x); + v1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].y); + u2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].x); + v2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].y); + u3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].x); + v3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].y); + + glTexCoord2d(((double)u0) * xscale, ((double)v0) * yscale); + glVertex3i(x0, y0, 0); + glTexCoord2d(((double)u1) * xscale, ((double)v1) * yscale); + glVertex3i(x1, y0, 0); + glTexCoord2d(((double)u2) * xscale, ((double)v2) * yscale); + glVertex3i(x1, y1, 0); + glTexCoord2d(((double)u3) * xscale, ((double)v3) * yscale); + glVertex3i(x0, y1, 0); + } + } + + glEnd(); +} + +static void tmu2_start(MilkymistTMU2State *s) +{ + int pbuffer_attrib[6] = { + GLX_PBUFFER_WIDTH, + 0, + GLX_PBUFFER_HEIGHT, + 0, + GLX_PRESERVED_CONTENTS, + True + }; + + GLXPbuffer pbuffer; + GLuint texture; + void *fb; + target_phys_addr_t fb_len; + void *mesh; + target_phys_addr_t mesh_len; + float m; + + trace_milkymist_tmu2_start(); + + /* Create and set up a suitable OpenGL context */ + pbuffer_attrib[1] = s->regs[R_DSTHRES]; + pbuffer_attrib[3] = s->regs[R_DSTVRES]; + pbuffer = glXCreatePbuffer(s->dpy, s->glx_fb_config, pbuffer_attrib); + glXMakeContextCurrent(s->dpy, pbuffer, pbuffer, s->glx_context); + + /* Fixup endianness. TODO: would it work on BE hosts? */ + glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); + glPixelStorei(GL_PACK_SWAP_BYTES, 1); + + /* Row alignment */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + glPixelStorei(GL_PACK_ALIGNMENT, 2); + + /* Read the QEMU source framebuffer into an OpenGL texture */ + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + fb_len = 2*s->regs[R_TEXHRES]*s->regs[R_TEXVRES]; + fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0); + if (fb == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + glTexImage2D(GL_TEXTURE_2D, 0, 3, s->regs[R_TEXHRES], s->regs[R_TEXVRES], + 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fb); + cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); + + /* Set up texturing options */ + /* WARNING: + * Many cases of TMU2 masking are not supported by OpenGL. + * We only implement the most common ones: + * - full bilinear filtering vs. nearest texel + * - texture clamping vs. texture wrapping + */ + if ((s->regs[R_TEXHMASK] & 0x3f) > 0x20) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + if ((s->regs[R_TEXHMASK] >> 6) & s->regs[R_TEXHRES]) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + } + if ((s->regs[R_TEXVMASK] >> 6) & s->regs[R_TEXVRES]) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + /* Translucency and decay */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + m = (float)(s->regs[R_BRIGHTNESS] + 1) / 64.0f; + glColor4f(m, m, m, (float)(s->regs[R_ALPHA] + 1) / 64.0f); + + /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */ + fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; + fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0); + if (fb == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + + glDrawPixels(s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, fb); + cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); + glViewport(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES]); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, s->regs[R_DSTHRES], 0.0, s->regs[R_DSTVRES], -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + + /* Map the texture */ + mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex); + mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0); + if (mesh == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + + tmu2_gl_map((struct vertex *)mesh, + s->regs[R_TEXHRES], s->regs[R_TEXVRES], + s->regs[R_HMESHLAST], s->regs[R_VMESHLAST], + s->regs[R_DSTHOFFSET], s->regs[R_DSTVOFFSET], + s->regs[R_DSTSQUAREW], s->regs[R_DSTSQUAREH]); + cpu_physical_memory_unmap(mesh, mesh_len, 0, mesh_len); + + /* Write back the OpenGL framebuffer to the QEMU framebuffer */ + fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; + fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1); + if (fb == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + + glReadPixels(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, fb); + cpu_physical_memory_unmap(fb, fb_len, 1, fb_len); + + /* Free OpenGL allocs */ + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + + s->regs[R_CTL] &= ~CTL_START_BUSY; + + trace_milkymist_tmu2_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static uint32_t tmu2_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistTMU2State *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTL: + case R_HMESHLAST: + case R_VMESHLAST: + case R_BRIGHTNESS: + case R_CHROMAKEY: + case R_VERTICESADDR: + case R_TEXFBUF: + case R_TEXHRES: + case R_TEXVRES: + case R_TEXHMASK: + case R_TEXVMASK: + case R_DSTFBUF: + case R_DSTHRES: + case R_DSTVRES: + case R_DSTHOFFSET: + case R_DSTVOFFSET: + case R_DSTSQUAREW: + case R_DSTSQUAREH: + case R_ALPHA: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_tmu2: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_tmu2_memory_read(addr << 2, r); + + return r; +} + +static void tmu2_check_registers(MilkymistTMU2State *s) +{ + if (s->regs[R_BRIGHTNESS] > MAX_BRIGHTNESS) { + error_report("milkymist_tmu2: max brightness is %d", MAX_BRIGHTNESS); + } + + if (s->regs[R_ALPHA] > MAX_ALPHA) { + error_report("milkymist_tmu2: max alpha is %d", MAX_ALPHA); + } + + if (s->regs[R_VERTICESADDR] & 0x07) { + error_report("milkymist_tmu2: vertex mesh address has to be 64-bit " + "aligned"); + } + + if (s->regs[R_TEXFBUF] & 0x01) { + error_report("milkymist_tmu2: texture buffer address has to be " + "16-bit aligned"); + } +} + +static void tmu2_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistTMU2State *s = opaque; + + trace_milkymist_tmu2_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTL: + s->regs[addr] = value; + if (value & CTL_START_BUSY) { + tmu2_start(s); + } + break; + case R_BRIGHTNESS: + case R_HMESHLAST: + case R_VMESHLAST: + case R_CHROMAKEY: + case R_VERTICESADDR: + case R_TEXFBUF: + case R_TEXHRES: + case R_TEXVRES: + case R_TEXHMASK: + case R_TEXVMASK: + case R_DSTFBUF: + case R_DSTHRES: + case R_DSTVRES: + case R_DSTHOFFSET: + case R_DSTVOFFSET: + case R_DSTSQUAREW: + case R_DSTSQUAREH: + case R_ALPHA: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_tmu2: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + tmu2_check_registers(s); +} + +static CPUReadMemoryFunc * const tmu2_read_fn[] = { + NULL, + NULL, + &tmu2_read, +}; + +static CPUWriteMemoryFunc * const tmu2_write_fn[] = { + NULL, + NULL, + &tmu2_write, +}; + +static void milkymist_tmu2_reset(DeviceState *d) +{ + MilkymistTMU2State *s = container_of(d, MilkymistTMU2State, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } +} + +static int milkymist_tmu2_init(SysBusDevice *dev) +{ + MilkymistTMU2State *s = FROM_SYSBUS(typeof(*s), dev); + int tmu2_regs; + + if (tmu2_glx_init(s)) { + return 1; + } + + sysbus_init_irq(dev, &s->irq); + + tmu2_regs = cpu_register_io_memory(tmu2_read_fn, tmu2_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, tmu2_regs); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_tmu2 = { + .name = "milkymist-tmu2", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistTMU2State, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_tmu2_info = { + .init = milkymist_tmu2_init, + .qdev.name = "milkymist-tmu2", + .qdev.size = sizeof(MilkymistTMU2State), + .qdev.vmsd = &vmstate_milkymist_tmu2, + .qdev.reset = milkymist_tmu2_reset, +}; + +static void milkymist_tmu2_register(void) +{ + sysbus_register_withprop(&milkymist_tmu2_info); +} + +device_init(milkymist_tmu2_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-uart.c qemu-kvm-0.15.0+noroms/hw/milkymist-uart.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-uart.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-uart.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,180 @@ +/* + * QEMU model of the Milkymist UART block. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/uart.pdf + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-char.h" +#include "qemu-error.h" + +enum { + R_RXTX = 0, + R_DIV, + R_MAX +}; + +struct MilkymistUartState { + SysBusDevice busdev; + CharDriverState *chr; + qemu_irq rx_irq; + qemu_irq tx_irq; + + uint32_t regs[R_MAX]; +}; +typedef struct MilkymistUartState MilkymistUartState; + +static uint32_t uart_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistUartState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_RXTX: + case R_DIV: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_uart: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_uart_memory_read(addr << 2, r); + + return r; +} + +static void uart_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistUartState *s = opaque; + unsigned char ch = value; + + trace_milkymist_uart_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_RXTX: + if (s->chr) { + qemu_chr_write(s->chr, &ch, 1); + } + trace_milkymist_uart_pulse_irq_tx(); + qemu_irq_pulse(s->tx_irq); + break; + case R_DIV: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_uart: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const uart_read_fn[] = { + NULL, + NULL, + &uart_read, +}; + +static CPUWriteMemoryFunc * const uart_write_fn[] = { + NULL, + NULL, + &uart_write, +}; + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + MilkymistUartState *s = opaque; + + s->regs[R_RXTX] = *buf; + trace_milkymist_uart_pulse_irq_rx(); + qemu_irq_pulse(s->rx_irq); +} + +static int uart_can_rx(void *opaque) +{ + return 1; +} + +static void uart_event(void *opaque, int event) +{ +} + +static void milkymist_uart_reset(DeviceState *d) +{ + MilkymistUartState *s = container_of(d, MilkymistUartState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } +} + +static int milkymist_uart_init(SysBusDevice *dev) +{ + MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev); + int uart_regs; + + sysbus_init_irq(dev, &s->rx_irq); + sysbus_init_irq(dev, &s->tx_irq); + + uart_regs = cpu_register_io_memory(uart_read_fn, uart_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, uart_regs); + + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); + } + + return 0; +} + +static const VMStateDescription vmstate_milkymist_uart = { + .name = "milkymist-uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistUartState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_uart_info = { + .init = milkymist_uart_init, + .qdev.name = "milkymist-uart", + .qdev.size = sizeof(MilkymistUartState), + .qdev.vmsd = &vmstate_milkymist_uart, + .qdev.reset = milkymist_uart_reset, +}; + +static void milkymist_uart_register(void) +{ + sysbus_register_withprop(&milkymist_uart_info); +} + +device_init(milkymist_uart_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-vgafb.c qemu-kvm-0.15.0+noroms/hw/milkymist-vgafb.c --- qemu-kvm-0.14.1+noroms/hw/milkymist-vgafb.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-vgafb.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,321 @@ + +/* + * QEMU model of the Milkymist VGA framebuffer. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/vgafb.pdf + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "console.h" +#include "framebuffer.h" +#include "pixel_ops.h" +#include "qemu-error.h" + +#define BITS 8 +#include "milkymist-vgafb_template.h" +#define BITS 15 +#include "milkymist-vgafb_template.h" +#define BITS 16 +#include "milkymist-vgafb_template.h" +#define BITS 24 +#include "milkymist-vgafb_template.h" +#define BITS 32 +#include "milkymist-vgafb_template.h" + +enum { + R_CTRL = 0, + R_HRES, + R_HSYNC_START, + R_HSYNC_END, + R_HSCAN, + R_VRES, + R_VSYNC_START, + R_VSYNC_END, + R_VSCAN, + R_BASEADDRESS, + R_BASEADDRESS_ACT, + R_BURST_COUNT, + R_SOURCE_CLOCK, + R_MAX +}; + +enum { + CTRL_RESET = (1<<0), +}; + +struct MilkymistVgafbState { + SysBusDevice busdev; + DisplayState *ds; + + int invalidate; + uint32_t fb_offset; + uint32_t fb_mask; + + uint32_t regs[R_MAX]; +}; +typedef struct MilkymistVgafbState MilkymistVgafbState; + +static int vgafb_enabled(MilkymistVgafbState *s) +{ + return !(s->regs[R_CTRL] & CTRL_RESET); +} + +static void vgafb_update_display(void *opaque) +{ + MilkymistVgafbState *s = opaque; + int first = 0; + int last = 0; + drawfn fn; + + if (!vgafb_enabled(s)) { + return; + } + + int dest_width = s->regs[R_HRES]; + + switch (ds_get_bits_per_pixel(s->ds)) { + case 0: + return; + case 8: + fn = draw_line_8; + break; + case 15: + fn = draw_line_15; + dest_width *= 2; + break; + case 16: + fn = draw_line_16; + dest_width *= 2; + break; + case 24: + fn = draw_line_24; + dest_width *= 3; + break; + case 32: + fn = draw_line_32; + dest_width *= 4; + break; + default: + hw_error("milkymist_vgafb: bad color depth\n"); + break; + } + + framebuffer_update_display(s->ds, + s->regs[R_BASEADDRESS] + s->fb_offset, + s->regs[R_HRES], + s->regs[R_VRES], + s->regs[R_HRES] * 2, + dest_width, + 0, + s->invalidate, + fn, + NULL, + &first, &last); + + if (first >= 0) { + dpy_update(s->ds, 0, first, s->regs[R_HRES], last - first + 1); + } + s->invalidate = 0; +} + +static void vgafb_invalidate_display(void *opaque) +{ + MilkymistVgafbState *s = opaque; + s->invalidate = 1; +} + +static void vgafb_resize(MilkymistVgafbState *s) +{ + if (!vgafb_enabled(s)) { + return; + } + + qemu_console_resize(s->ds, s->regs[R_HRES], s->regs[R_VRES]); + s->invalidate = 1; +} + +static uint32_t vgafb_read(void *opaque, target_phys_addr_t addr) +{ + MilkymistVgafbState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTRL: + case R_HRES: + case R_HSYNC_START: + case R_HSYNC_END: + case R_HSCAN: + case R_VRES: + case R_VSYNC_START: + case R_VSYNC_END: + case R_VSCAN: + case R_BASEADDRESS: + case R_BURST_COUNT: + case R_SOURCE_CLOCK: + r = s->regs[addr]; + break; + case R_BASEADDRESS_ACT: + r = s->regs[R_BASEADDRESS]; + break; + + default: + error_report("milkymist_vgafb: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_vgafb_memory_read(addr << 2, r); + + return r; +} + +static void +vgafb_write(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MilkymistVgafbState *s = opaque; + + trace_milkymist_vgafb_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTRL: + s->regs[addr] = value; + vgafb_resize(s); + break; + case R_HSYNC_START: + case R_HSYNC_END: + case R_HSCAN: + case R_VSYNC_START: + case R_VSYNC_END: + case R_VSCAN: + case R_BURST_COUNT: + case R_SOURCE_CLOCK: + s->regs[addr] = value; + break; + case R_BASEADDRESS: + if (value & 0x1f) { + error_report("milkymist_vgafb: framebuffer base address have to " + "be 32 byte aligned"); + break; + } + s->regs[addr] = value & s->fb_mask; + s->invalidate = 1; + break; + case R_HRES: + case R_VRES: + s->regs[addr] = value; + vgafb_resize(s); + break; + case R_BASEADDRESS_ACT: + error_report("milkymist_vgafb: write to read-only register 0x" + TARGET_FMT_plx, addr << 2); + break; + + default: + error_report("milkymist_vgafb: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static CPUReadMemoryFunc * const vgafb_read_fn[] = { + NULL, + NULL, + &vgafb_read +}; + +static CPUWriteMemoryFunc * const vgafb_write_fn[] = { + NULL, + NULL, + &vgafb_write +}; + +static void milkymist_vgafb_reset(DeviceState *d) +{ + MilkymistVgafbState *s = container_of(d, MilkymistVgafbState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + /* defaults */ + s->regs[R_CTRL] = CTRL_RESET; + s->regs[R_HRES] = 640; + s->regs[R_VRES] = 480; + s->regs[R_BASEADDRESS] = 0; +} + +static int milkymist_vgafb_init(SysBusDevice *dev) +{ + MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev); + int vgafb_regs; + + vgafb_regs = cpu_register_io_memory(vgafb_read_fn, vgafb_write_fn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, vgafb_regs); + + s->ds = graphic_console_init(vgafb_update_display, + vgafb_invalidate_display, + NULL, NULL, s); + + return 0; +} + +static int vgafb_post_load(void *opaque, int version_id) +{ + vgafb_invalidate_display(opaque); + return 0; +} + +static const VMStateDescription vmstate_milkymist_vgafb = { + .name = "milkymist-vgafb", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = vgafb_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo milkymist_vgafb_info = { + .init = milkymist_vgafb_init, + .qdev.name = "milkymist-vgafb", + .qdev.size = sizeof(MilkymistVgafbState), + .qdev.vmsd = &vmstate_milkymist_vgafb, + .qdev.reset = milkymist_vgafb_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0), + DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void milkymist_vgafb_register(void) +{ + sysbus_register_withprop(&milkymist_vgafb_info); +} + +device_init(milkymist_vgafb_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/milkymist-vgafb_template.h qemu-kvm-0.15.0+noroms/hw/milkymist-vgafb_template.h --- qemu-kvm-0.14.1+noroms/hw/milkymist-vgafb_template.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/milkymist-vgafb_template.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,74 @@ +/* + * QEMU model of the Milkymist VGA framebuffer. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + */ + +#if BITS == 8 +#define COPY_PIXEL(to, r, g, b) \ + do { \ + *to = rgb_to_pixel8(r, g, b); \ + to += 1; \ + } while (0) +#elif BITS == 15 +#define COPY_PIXEL(to, r, g, b) \ + do { \ + *(uint16_t *)to = rgb_to_pixel15(r, g, b); \ + to += 2; \ + } while (0) +#elif BITS == 16 +#define COPY_PIXEL(to, r, g, b) \ + do { \ + *(uint16_t *)to = rgb_to_pixel16(r, g, b); \ + to += 2; \ + } while (0) +#elif BITS == 24 +#define COPY_PIXEL(to, r, g, b) \ + do { \ + uint32 tmp = rgb_to_pixel24(r, g, b); \ + *(to++) = tmp & 0xff; \ + *(to++) = (tmp >> 8) & 0xff; \ + *(to++) = (tmp >> 16) & 0xff; \ + } while (0) +#elif BITS == 32 +#define COPY_PIXEL(to, r, g, b) \ + do { \ + *(uint32_t *)to = rgb_to_pixel32(r, g, b); \ + to += 4; \ + } while (0) +#else +#error unknown bit depth +#endif + +static void glue(draw_line_, BITS)(void *opaque, uint8_t *d, const uint8_t *s, + int width, int deststep) +{ + uint16_t rgb565; + uint8_t r, g, b; + + while (width--) { + rgb565 = lduw_raw(s); + r = ((rgb565 >> 11) & 0x1f) << 3; + g = ((rgb565 >> 5) & 0x3f) << 2; + b = ((rgb565 >> 0) & 0x1f) << 3; + COPY_PIXEL(d, r, g, b); + s += 2; + } +} + +#undef BITS +#undef COPY_PIXEL diff -Nru qemu-kvm-0.14.1+noroms/hw/mips_fulong2e.c qemu-kvm-0.15.0+noroms/hw/mips_fulong2e.c --- qemu-kvm-0.14.1+noroms/hw/mips_fulong2e.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mips_fulong2e.c 2011-08-09 07:40:29.000000000 -0500 @@ -67,7 +67,7 @@ #define FULONG2E_ATI_SLOT 6 #define FULONG2E_RTL8139_SLOT 7 -static PITState *pit; +static ISADevice *pit; static struct _loaderparams { int ram_size; @@ -263,11 +263,9 @@ 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 */ @@ -338,14 +336,7 @@ 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); - } + ide_drive_get(hd, MAX_IDE_BUS); via_devfn = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0)); if (via_devfn < 0) { @@ -360,16 +351,11 @@ 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); + smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd)); /* init other devices */ - pit = pit_init(0x40, isa_get_irq(0)); + pit = pit_init(0x40, 0); cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); DMA_init(0, cpu_exit_irq); diff -Nru qemu-kvm-0.14.1+noroms/hw/mips.h qemu-kvm-0.15.0+noroms/hw/mips.h --- qemu-kvm-0.14.1+noroms/hw/mips.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mips.h 2011-08-09 07:40:29.000000000 -0500 @@ -8,10 +8,6 @@ /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); -/* ds1225y.c */ -void *ds1225y_init(target_phys_addr_t mem_base, const char *filename); -void ds1225y_set_protection(void *opaque, int protection); - /* g364fb.c */ int g364fb_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift, diff -Nru qemu-kvm-0.14.1+noroms/hw/mips_jazz.c qemu-kvm-0.15.0+noroms/hw/mips_jazz.c --- qemu-kvm-0.14.1+noroms/hw/mips_jazz.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mips_jazz.c 2011-08-09 07:40:29.000000000 -0500 @@ -37,6 +37,7 @@ #include "loader.h" #include "mc146818rtc.h" #include "blockdev.h" +#include "sysbus.h" enum jazz_model_e { @@ -115,7 +116,9 @@ void* rc4030_opaque; int s_rtc, s_dma_dummy; NICInfo *nd; - PITState *pit; + DeviceState *dev; + SysBusDevice *sysbus; + ISADevice *pit; DriveInfo *fds[MAX_FD]; qemu_irq esp_reset, dma_enable; qemu_irq *cpu_exit_irq; @@ -181,7 +184,7 @@ isa_bus_irqs(i8259); cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); DMA_init(0, cpu_exit_irq); - pit = pit_init(0x40, i8259[0]); + pit = pit_init(0x40, 0); pcspk_init(pit); /* ISA IO space at 0x90000000 */ @@ -266,8 +269,11 @@ /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */ audio_init(i8259, NULL); - /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */ - ds1225y_init(0x80009000, "nvram"); + /* NVRAM */ + dev = qdev_create(NULL, "ds1225y"); + qdev_init_nofail(dev); + sysbus = sysbus_from_qdev(dev); + sysbus_mmio_map(sysbus, 0, 0x80009000); /* LED indicator */ jazz_led_init(0x8000f000); diff -Nru qemu-kvm-0.14.1+noroms/hw/mips_malta.c qemu-kvm-0.15.0+noroms/hw/mips_malta.c --- qemu-kvm-0.14.1+noroms/hw/mips_malta.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mips_malta.c 2011-08-09 07:40:29.000000000 -0500 @@ -68,7 +68,7 @@ SerialState *uart; } MaltaFPGAState; -static PITState *pit; +static ISADevice *pit; static struct _loaderparams { int ram_size; @@ -770,7 +770,6 @@ qemu_irq *i8259; qemu_irq *cpu_exit_irq; int piix4_devfn; - uint8_t *eeprom_buf; i2c_bus *smbus; int i; DriveInfo *dinfo; @@ -905,15 +904,7 @@ pci_bus = gt64120_register(i8259); /* Southbridge */ - - 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); - } + ide_drive_get(hd, MAX_IDE_BUS); piix4_devfn = piix4_init(pci_bus, 80); isa_bus_irqs(i8259); @@ -921,16 +912,9 @@ usb_uhci_piix4_init(pci_bus, piix4_devfn + 2); 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. */ - 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); - } - pit = pit_init(0x40, isa_get_irq(0)); + /* TODO: Populate SPD eeprom data. */ + smbus_eeprom_init(smbus, 8, NULL, 0); + pit = pit_init(0x40, 0); cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); DMA_init(0, cpu_exit_irq); @@ -957,7 +941,11 @@ if (cirrus_vga_enabled) { pci_cirrus_vga_init(pci_bus); } else if (vmsvga_enabled) { - pci_vmsvga_init(pci_bus); + if (!pci_vmsvga_init(pci_bus)) { + fprintf(stderr, "Warning: vmware_vga not available," + " using standard VGA instead\n"); + pci_vga_init(pci_bus); + } } else if (std_vga_enabled) { pci_vga_init(pci_bus); } diff -Nru qemu-kvm-0.14.1+noroms/hw/mipsnet.c qemu-kvm-0.15.0+noroms/hw/mipsnet.c --- qemu-kvm-0.14.1+noroms/hw/mipsnet.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mipsnet.c 2011-08-09 07:40:29.000000000 -0500 @@ -202,44 +202,29 @@ } } -static void mipsnet_save(QEMUFile *f, void *opaque) -{ - MIPSnetState *s = opaque; - - qemu_put_be32s(f, &s->busy); - qemu_put_be32s(f, &s->rx_count); - qemu_put_be32s(f, &s->rx_read); - qemu_put_be32s(f, &s->tx_count); - qemu_put_be32s(f, &s->tx_written); - qemu_put_be32s(f, &s->intctl); - qemu_put_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE); - qemu_put_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE); -} - -static int mipsnet_load(QEMUFile *f, void *opaque, int version_id) -{ - MIPSnetState *s = opaque; - - if (version_id > 0) - return -EINVAL; - - qemu_get_be32s(f, &s->busy); - qemu_get_be32s(f, &s->rx_count); - qemu_get_be32s(f, &s->rx_read); - qemu_get_be32s(f, &s->tx_count); - qemu_get_be32s(f, &s->tx_written); - qemu_get_be32s(f, &s->intctl); - qemu_get_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE); - qemu_get_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE); - - return 0; -} +static const VMStateDescription vmstate_mipsnet = { + .name = "mipsnet", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(busy, MIPSnetState), + VMSTATE_UINT32(rx_count, MIPSnetState), + VMSTATE_UINT32(rx_read, MIPSnetState), + VMSTATE_UINT32(tx_count, MIPSnetState), + VMSTATE_UINT32(tx_written, MIPSnetState), + VMSTATE_UINT32(intctl, MIPSnetState), + VMSTATE_BUFFER(rx_buffer, MIPSnetState), + VMSTATE_BUFFER(tx_buffer, MIPSnetState), + VMSTATE_END_OF_LIST() + } +}; static void mipsnet_cleanup(VLANClientState *nc) { MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; - unregister_savevm(NULL, "mipsnet", s); + vmstate_unregister(NULL, &vmstate_mipsnet, s); isa_unassign_ioport(s->io_base, 36); @@ -273,7 +258,7 @@ s->irq = irq; if (nd) { - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; @@ -284,5 +269,5 @@ } mipsnet_reset(s); - register_savevm(NULL, "mipsnet", 0, 0, mipsnet_save, mipsnet_load, s); + vmstate_register(NULL, 0, &vmstate_mipsnet, s); } diff -Nru qemu-kvm-0.14.1+noroms/hw/mips_r4k.c qemu-kvm-0.15.0+noroms/hw/mips_r4k.c --- qemu-kvm-0.14.1+noroms/hw/mips_r4k.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mips_r4k.c 2011-08-09 07:40:29.000000000 -0500 @@ -30,7 +30,7 @@ static const int ide_iobase2[2] = { 0x3f6, 0x376 }; static const int ide_irq[2] = { 14, 15 }; -static PITState *pit; /* PIT i8254 */ +static ISADevice *pit; /* PIT i8254 */ /* i8254 PIT is attached to the IRQ0 at PIC i8259 */ @@ -274,7 +274,7 @@ isa_mmio_init(0x14000000, 0x00010000); isa_mem_base = 0x10000000; - pit = pit_init(0x40, i8259[0]); + pit = pit_init(0x40, 0); for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { @@ -287,15 +287,7 @@ if (nd_table[0].vlan) isa_ne2000_init(0x300, 9, &nd_table[0]); - 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); - } - + ide_drive_get(hd, MAX_IDE_BUS); 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], diff -Nru qemu-kvm-0.14.1+noroms/hw/mips_timer.c qemu-kvm-0.15.0+noroms/hw/mips_timer.c --- qemu-kvm-0.14.1+noroms/hw/mips_timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mips_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -47,7 +47,7 @@ uint64_t now, next; uint32_t wait; - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); wait = env->CP0_Compare - env->CP0_Count - (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec()); next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ); @@ -71,7 +71,7 @@ } else { uint64_t now; - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); if (qemu_timer_pending(env->timer) && qemu_timer_expired(env->timer, now)) { /* The timer has already expired. */ @@ -90,7 +90,7 @@ else { /* Store new count register */ env->CP0_Count = - count - (uint32_t)muldiv64(qemu_get_clock(vm_clock), + count - (uint32_t)muldiv64(qemu_get_clock_ns(vm_clock), TIMER_FREQ, get_ticks_per_sec()); /* Update timer timer */ cpu_mips_timer_update(env); @@ -115,7 +115,7 @@ void cpu_mips_stop_count(CPUState *env) { /* Store the current value */ - env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock(vm_clock), + env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock_ns(vm_clock), TIMER_FREQ, get_ticks_per_sec()); } @@ -141,7 +141,7 @@ void cpu_mips_clock_init (CPUState *env) { - env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env); + env->timer = qemu_new_timer_ns(vm_clock, &mips_timer_cb, env); env->CP0_Compare = 0; cpu_mips_store_count(env, 1); } diff -Nru qemu-kvm-0.14.1+noroms/hw/mpc8544_guts.c qemu-kvm-0.15.0+noroms/hw/mpc8544_guts.c --- qemu-kvm-0.14.1+noroms/hw/mpc8544_guts.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/mpc8544_guts.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,135 @@ +/* + * QEMU PowerPC MPC8544 global util pseudo-device + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, + * + * This 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 documentation for this device is noted in the MPC8544 documentation, + * file name "MPC8544ERM.pdf". You can easily find it on the web. + * + */ + +#include "hw.h" +#include "sysemu.h" +#include "sysbus.h" + +#define MPC8544_GUTS_MMIO_SIZE 0x1000 +#define MPC8544_GUTS_RSTCR_RESET 0x02 + +#define MPC8544_GUTS_ADDR_PORPLLSR 0x00 +#define MPC8544_GUTS_ADDR_PORBMSR 0x04 +#define MPC8544_GUTS_ADDR_PORIMPSCR 0x08 +#define MPC8544_GUTS_ADDR_PORDEVSR 0x0C +#define MPC8544_GUTS_ADDR_PORDBGMSR 0x10 +#define MPC8544_GUTS_ADDR_PORDEVSR2 0x14 +#define MPC8544_GUTS_ADDR_GPPORCR 0x20 +#define MPC8544_GUTS_ADDR_GPIOCR 0x30 +#define MPC8544_GUTS_ADDR_GPOUTDR 0x40 +#define MPC8544_GUTS_ADDR_GPINDR 0x50 +#define MPC8544_GUTS_ADDR_PMUXCR 0x60 +#define MPC8544_GUTS_ADDR_DEVDISR 0x70 +#define MPC8544_GUTS_ADDR_POWMGTCSR 0x80 +#define MPC8544_GUTS_ADDR_MCPSUMR 0x90 +#define MPC8544_GUTS_ADDR_RSTRSCR 0x94 +#define MPC8544_GUTS_ADDR_PVR 0xA0 +#define MPC8544_GUTS_ADDR_SVR 0xA4 +#define MPC8544_GUTS_ADDR_RSTCR 0xB0 +#define MPC8544_GUTS_ADDR_IOVSELSR 0xC0 +#define MPC8544_GUTS_ADDR_DDRCSR 0xB20 +#define MPC8544_GUTS_ADDR_DDRCDR 0xB24 +#define MPC8544_GUTS_ADDR_DDRCLKDR 0xB28 +#define MPC8544_GUTS_ADDR_CLKOCR 0xE00 +#define MPC8544_GUTS_ADDR_SRDS1CR1 0xF04 +#define MPC8544_GUTS_ADDR_SRDS2CR1 0xF10 +#define MPC8544_GUTS_ADDR_SRDS2CR3 0xF18 + +struct GutsState { + SysBusDevice busdev; +}; + +typedef struct GutsState GutsState; + +static uint32_t mpc8544_guts_read32(void *opaque, target_phys_addr_t addr) +{ + uint32_t value = 0; + CPUState *env = cpu_single_env; + + addr &= MPC8544_GUTS_MMIO_SIZE - 1; + switch (addr) { + case MPC8544_GUTS_ADDR_PVR: + value = env->spr[SPR_PVR]; + break; + case MPC8544_GUTS_ADDR_SVR: + value = env->spr[SPR_E500_SVR]; + break; + default: + fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr); + break; + } + + return value; +} + +static CPUReadMemoryFunc * const mpc8544_guts_read[] = { + NULL, + NULL, + &mpc8544_guts_read32, +}; + +static void mpc8544_guts_write32(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + addr &= MPC8544_GUTS_MMIO_SIZE - 1; + + switch (addr) { + case MPC8544_GUTS_ADDR_RSTCR: + if (value & MPC8544_GUTS_RSTCR_RESET) { + qemu_system_reset_request(); + } + break; + default: + fprintf(stderr, "guts: Unknown register write: %x = %x\n", + (int)addr, value); + break; + } +} + +static CPUWriteMemoryFunc * const mpc8544_guts_write[] = { + NULL, + NULL, + &mpc8544_guts_write32, +}; + +static int mpc8544_guts_initfn(SysBusDevice *dev) +{ + GutsState *s; + int iomem; + + s = FROM_SYSBUS(GutsState, sysbus_from_qdev(dev)); + + iomem = cpu_register_io_memory(mpc8544_guts_read, mpc8544_guts_write, s, + DEVICE_BIG_ENDIAN); + sysbus_init_mmio(dev, MPC8544_GUTS_MMIO_SIZE, iomem); + + return 0; +} + +static SysBusDeviceInfo mpc8544_guts_info = { + .init = mpc8544_guts_initfn, + .qdev.name = "mpc8544-guts", + .qdev.size = sizeof(GutsState), +}; + +static void mpc8544_guts_register(void) +{ + sysbus_register_withprop(&mpc8544_guts_info); +} +device_init(mpc8544_guts_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/mpcore.c qemu-kvm-0.15.0+noroms/hw/mpcore.c --- qemu-kvm-0.14.1+noroms/hw/mpcore.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mpcore.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -63,7 +63,7 @@ if (s->count == 0) return; if (restart) - s->tick = qemu_get_clock(vm_clock); + s->tick = qemu_get_clock_ns(vm_clock); s->tick += (int64_t)s->count * mpcore_timer_scale(s); qemu_mod_timer(s->timer, s->tick); } @@ -92,7 +92,7 @@ if (((s->control & 1) == 0) || (s->count == 0)) return 0; /* Slow and ugly, but hopefully won't happen too often. */ - val = s->tick - qemu_get_clock(vm_clock); + val = s->tick - qemu_get_clock_ns(vm_clock); val /= mpcore_timer_scale(s); if (val < 0) val = 0; @@ -145,7 +145,7 @@ { s->id = id; s->mpcore = mpcore; - s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s); + s->timer = qemu_new_timer_ns(vm_clock, mpcore_timer_tick, s); } diff -Nru qemu-kvm-0.14.1+noroms/hw/msi.c qemu-kvm-0.15.0+noroms/hw/msi.c --- qemu-kvm-0.14.1+noroms/hw/msi.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/msi.c 2011-08-09 07:40:29.000000000 -0500 @@ -20,6 +20,7 @@ #include "msi.h" #include "range.h" +#include "kvm.h" /* Eventually those constants should go to Linux pci_regs.h */ #define PCI_MSI_PENDING_32 0x10 @@ -109,6 +110,94 @@ PCI_MSI_FLAGS_ENABLE); } +static void kvm_msi_message_from_vector(PCIDevice *dev, unsigned vector, + KVMMsiMessage *kmm) +{ + 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); + + kmm->addr_lo = pci_get_long(dev->config + msi_address_lo_off(dev)); + if (msi64bit) { + kmm->addr_hi = pci_get_long(dev->config + msi_address_hi_off(dev)); + } else { + kmm->addr_hi = 0; + } + + kmm->data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); + if (nr_vectors > 1) { + kmm->data &= ~(nr_vectors - 1); + kmm->data |= vector; + } +} + +static void kvm_msi_update(PCIDevice *dev) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + unsigned int max_vectors = 1 << + ((flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1)); + unsigned int nr_vectors = msi_nr_vectors(flags); + KVMMsiMessage new_entry, *entry; + bool changed = false; + unsigned int vector; + int r; + + for (vector = 0; vector < max_vectors; vector++) { + entry = dev->msi_irq_entries + vector; + + if (vector >= nr_vectors) { + if (vector < dev->msi_entries_nr) { + kvm_msi_message_del(entry); + changed = true; + } + } else if (vector >= dev->msi_entries_nr) { + kvm_msi_message_from_vector(dev, vector, entry); + r = kvm_msi_message_add(entry); + if (r) { + fprintf(stderr, "%s: kvm_msi_add failed: %s\n", __func__, + strerror(-r)); + exit(1); + } + changed = true; + } else { + kvm_msi_message_from_vector(dev, vector, &new_entry); + r = kvm_msi_message_update(entry, &new_entry); + if (r < 0) { + fprintf(stderr, "%s: kvm_update_msi failed: %s\n", + __func__, strerror(-r)); + exit(1); + } + if (r > 0) { + *entry = new_entry; + changed = true; + } + } + } + dev->msi_entries_nr = nr_vectors; + if (changed) { + r = kvm_commit_irq_routes(); + if (r) { + fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, + strerror(-r)); + exit(1); + } + } +} + +/* KVM specific MSI helpers */ +static void kvm_msi_free(PCIDevice *dev) +{ + unsigned int vector; + + for (vector = 0; vector < dev->msi_entries_nr; ++vector) { + kvm_msi_message_del(&dev->msi_irq_entries[vector]); + } + if (dev->msi_entries_nr > 0) { + kvm_commit_irq_routes(); + } + dev->msi_entries_nr = 0; +} + int msi_init(struct PCIDevice *dev, uint8_t offset, unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) { @@ -121,6 +210,10 @@ " 64bit %d mask %d\n", offset, nr_vectors, msi64bit, msi_per_vector_mask); + if (kvm_enabled() && kvm_irqchip_in_kernel() && !kvm_has_gsi_routing()) { + return -ENOTSUP; + } + assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */ assert(nr_vectors > 0); assert(nr_vectors <= PCI_MSI_VECTORS_MAX); @@ -155,18 +248,38 @@ 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. */ + /* Make mask bits 0 to nr_vectors - 1 writable. */ pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); } + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + dev->msi_irq_entries = qemu_malloc(nr_vectors * + sizeof(*dev->msix_irq_entries)); + } + 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); + uint16_t flags; + uint8_t cap_size; + + if (!(dev->cap_present & QEMU_PCI_CAP_MSI)) { + return; + } + flags = pci_get_word(dev->config + msi_flags_off(dev)); + cap_size = msi_cap_sizeof(flags); + + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_msi_free(dev); + qemu_free(dev->msi_irq_entries); + } + + pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size); + dev->cap_present &= ~QEMU_PCI_CAP_MSI; + MSI_DEV_PRINTF(dev, "uninit\n"); } @@ -175,6 +288,10 @@ uint16_t flags; bool msi64bit; + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_msi_free(dev); + } + 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; @@ -224,6 +341,11 @@ return; } + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_set_irq(dev->msi_irq_entries[vector].gsi, 1, NULL); + return; + } + if (msi64bit) { address = pci_get_quad(dev->config + msi_address_lo_off(dev)); } else { @@ -241,7 +363,7 @@ "notify vector 0x%x" " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", vector, address, data); - stl_phys(address, data); + stl_le_phys(address, data); } /* call this function after updating configs by pci_default_write_config(). */ @@ -312,6 +434,10 @@ pci_set_word(dev->config + msi_flags_off(dev), flags); } + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + kvm_msi_update(dev); + } + if (!msi_per_vector_mask) { /* if per vector masking isn't supported, there is no pending interrupt. */ @@ -342,3 +468,16 @@ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); return msi_nr_vectors(flags); } + +void msi_post_load(PCIDevice *dev) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + + if (kvm_enabled() && dev->msi_irq_entries) { + kvm_msi_free(dev); + + if (flags & PCI_MSI_FLAGS_ENABLE) { + kvm_msi_update(dev); + } + } +} diff -Nru qemu-kvm-0.14.1+noroms/hw/msi.h qemu-kvm-0.15.0+noroms/hw/msi.h --- qemu-kvm-0.14.1+noroms/hw/msi.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/msi.h 2011-08-09 07:40:29.000000000 -0500 @@ -32,6 +32,7 @@ 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); +void msi_post_load(PCIDevice *dev); static inline bool msi_present(const PCIDevice *dev) { diff -Nru qemu-kvm-0.14.1+noroms/hw/msix.c qemu-kvm-0.15.0+noroms/hw/msix.c --- qemu-kvm-0.14.1+noroms/hw/msix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/msix.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,9 +17,6 @@ #include "range.h" #include "kvm.h" -/* MSI-X capability structure */ -#define MSIX_TABLE_OFFSET 4 -#define MSIX_PBA_OFFSET 8 #define MSIX_CAP_LENGTH 12 /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ @@ -27,14 +24,6 @@ #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) -/* MSI-X table format */ -#define MSIX_MSG_ADDR 0 -#define MSIX_MSG_UPPER_ADDR 4 -#define MSIX_MSG_DATA 8 -#define MSIX_VECTOR_CTRL 12 -#define MSIX_ENTRY_SIZE 16 -#define MSIX_VECTOR_MASK 0x1 - /* How much space does an MSIX table need. */ /* The spec requires giving the table structure * a 4K aligned region all by itself. */ @@ -51,12 +40,10 @@ 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]) { - kmm = &dev->msix_irq_entries[vector]; - kvm_del_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data); + kvm_msi_message_del(&dev->msix_irq_entries[vector]); changed = 1; } } @@ -66,20 +53,22 @@ } static void kvm_msix_message_from_vector(PCIDevice *dev, unsigned vector, - struct kvm_msix_message *kmm) + KVMMsiMessage *kmm) { - uint8_t *table_entry = dev->msix_table_page + vector * MSIX_ENTRY_SIZE; + uint8_t *table_entry = dev->msix_table_page + vector * PCI_MSIX_ENTRY_SIZE; - 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); + kmm->addr_lo = pci_get_long(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + kmm->addr_hi = pci_get_long(table_entry + PCI_MSIX_ENTRY_UPPER_ADDR); + kmm->data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); } static void kvm_msix_update(PCIDevice *dev, int vector, int was_masked, int is_masked) { - struct kvm_msix_message e = {}, *entry; + KVMMsiMessage new_entry, *entry; int mask_cleared = was_masked && !is_masked; + int r; + /* 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. */ if (!mask_cleared) { @@ -88,21 +77,17 @@ if (!dev->msix_entry_used[vector]) { return; } + entry = dev->msix_irq_entries + vector; - e.gsi = entry->gsi; - kvm_msix_message_from_vector(dev, vector, &e); - if (memcmp(entry, &e, sizeof e) != 0) { - int r; - - 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_msix failed: %s\n", __func__, - strerror(-r)); - exit(1); - } - *entry = e; + kvm_msix_message_from_vector(dev, vector, &new_entry); + r = kvm_msi_message_update(entry, &new_entry); + if (r < 0) { + fprintf(stderr, "%s: kvm_update_msix failed: %s\n", __func__, + strerror(-r)); + exit(1); + } + if (r > 0) { + *entry = new_entry; r = kvm_commit_irq_routes(); if (r) { fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, @@ -112,26 +97,13 @@ } } -static int kvm_msix_add(PCIDevice *dev, unsigned vector) +static int kvm_msix_vector_add(PCIDevice *dev, unsigned vector) { - struct kvm_msix_message *kmm = dev->msix_irq_entries + vector; + KVMMsiMessage *kmm = dev->msix_irq_entries + vector; int r; - 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(); - if (r < 0) { - fprintf(stderr, "%s: kvm_get_irq_route_gsi failed: %s\n", __func__, strerror(-r)); - return r; - } - 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); + r = kvm_msi_message_add(kmm); if (r < 0) { fprintf(stderr, "%s: kvm_add_msix failed: %s\n", __func__, strerror(-r)); return r; @@ -145,15 +117,9 @@ return 0; } -static void kvm_msix_del(PCIDevice *dev, unsigned vector) +static void kvm_msix_vector_del(PCIDevice *dev, unsigned vector) { - struct kvm_msix_message *kmm; - - if (dev->msix_entry_used[vector]) { - return; - } - kmm = &dev->msix_irq_entries[vector]; - kvm_del_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data); + kvm_msi_message_del(&dev->msix_irq_entries[vector]); kvm_commit_irq_routes(); } @@ -199,13 +165,13 @@ 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); + pci_set_long(config + PCI_MSIX_TABLE, bar_size | bar_nr); /* Pending bits on top of that */ - pci_set_long(config + MSIX_PBA_OFFSET, (bar_size + MSIX_PAGE_PENDING) | + pci_set_long(config + PCI_MSIX_PBA, (bar_size + MSIX_PAGE_PENDING) | bar_nr); } pdev->msix_cap = config_offset; - /* Make flags bit writeable. */ + /* Make flags bit writable. */ pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | MSIX_MASKALL_MASK; return 0; @@ -258,9 +224,10 @@ static int msix_is_masked(PCIDevice *dev, int vector) { - unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL; + unsigned offset = + vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; return msix_function_masked(dev) || - dev->msix_table_page[offset] & MSIX_VECTOR_MASK; + dev->msix_table_page[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT; } static void msix_handle_mask_update(PCIDevice *dev, int vector) @@ -302,7 +269,7 @@ { PCIDevice *dev = opaque; unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; - int vector = offset / MSIX_ENTRY_SIZE; + int vector = offset / PCI_MSIX_ENTRY_SIZE; int was_masked = msix_is_masked(dev, vector); pci_set_long(dev->msix_table_page + offset, val); if (kvm_enabled() && kvm_irqchip_in_kernel()) { @@ -335,7 +302,7 @@ pcibus_t addr, pcibus_t size, int type) { uint8_t *config = d->config + d->msix_cap; - uint32_t table = pci_get_long(config + MSIX_TABLE_OFFSET); + uint32_t table = pci_get_long(config + PCI_MSIX_TABLE); uint32_t offset = table & ~(MSIX_PAGE_SIZE - 1); /* TODO: for assigned devices, we'll want to make it possible to map * pending bits separately in case they are in a separate bar. */ @@ -354,9 +321,10 @@ { int vector, r; for (vector = 0; vector < nentries; ++vector) { - unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL; + unsigned offset = + vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; int was_masked = msix_is_masked(dev, vector); - dev->msix_table_page[offset] |= MSIX_VECTOR_MASK; + dev->msix_table_page[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT; if (was_masked != msix_is_masked(dev, vector) && dev->msix_mask_notifier) { r = dev->msix_mask_notifier(dev, vector, @@ -373,8 +341,10 @@ { int ret; /* Nothing to do if MSI is not supported by interrupt controller */ - if (!msix_supported) + if (!msix_supported || + (kvm_enabled() && kvm_irqchip_in_kernel() && !kvm_has_gsi_routing())) { return -ENOTSUP; + } if (nentries > MSIX_MAX_ENTRIES) return -EINVAL; @@ -463,7 +433,7 @@ if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) { return; } - qemu_put_buffer(f, dev->msix_table_page, n * MSIX_ENTRY_SIZE); + qemu_put_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE); qemu_put_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8); } @@ -480,7 +450,7 @@ } msix_free_irq_entries(dev); - qemu_get_buffer(f, dev->msix_table_page, n * MSIX_ENTRY_SIZE); + qemu_get_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE); qemu_get_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8); } @@ -508,7 +478,7 @@ /* Send an MSI-X message */ void msix_notify(PCIDevice *dev, unsigned vector) { - uint8_t *table_entry = dev->msix_table_page + vector * MSIX_ENTRY_SIZE; + uint8_t *table_entry = dev->msix_table_page + vector * PCI_MSIX_ENTRY_SIZE; uint64_t address; uint32_t data; @@ -524,10 +494,9 @@ return; } - address = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR); - address = (address << 32) | pci_get_long(table_entry + MSIX_MSG_ADDR); - data = pci_get_long(table_entry + MSIX_MSG_DATA); - stl_phys(address, data); + address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); + stl_le_phys(address, data); } void msix_reset(PCIDevice *dev) @@ -555,11 +524,9 @@ int ret; if (vector >= dev->msix_entries_nr) return -EINVAL; - if (dev->msix_entry_used[vector]) { - return 0; - } - if (kvm_enabled() && kvm_irqchip_in_kernel()) { - ret = kvm_msix_add(dev, vector); + if (kvm_enabled() && kvm_irqchip_in_kernel() && + !dev->msix_entry_used[vector]) { + ret = kvm_msix_vector_add(dev, vector); if (ret) { return ret; } @@ -578,7 +545,7 @@ return; } if (kvm_enabled() && kvm_irqchip_in_kernel()) { - kvm_msix_del(dev, vector); + kvm_msix_vector_del(dev, vector); } msix_clr_pending(dev, vector); } diff -Nru qemu-kvm-0.14.1+noroms/hw/msmouse.c qemu-kvm-0.15.0+noroms/hw/msmouse.c --- qemu-kvm-0.14.1+noroms/hw/msmouse.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/msmouse.c 2011-08-09 07:40:29.000000000 -0500 @@ -64,7 +64,7 @@ qemu_free (chr); } -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; @@ -74,5 +74,6 @@ qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); - return chr; + *_chr = chr; + return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/msmouse.h qemu-kvm-0.15.0+noroms/hw/msmouse.h --- qemu-kvm-0.14.1+noroms/hw/msmouse.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/msmouse.h 2011-08-09 07:40:29.000000000 -0500 @@ -1,2 +1,2 @@ /* msmouse.c */ -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts); +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr); diff -Nru qemu-kvm-0.14.1+noroms/hw/mst_fpga.c qemu-kvm-0.15.0+noroms/hw/mst_fpga.c --- qemu-kvm-0.14.1+noroms/hw/mst_fpga.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/mst_fpga.c 2011-08-09 07:40:29.000000000 -0500 @@ -8,8 +8,7 @@ * This code is licensed under the GNU GPL v2. */ #include "hw.h" -#include "pxa.h" -#include "mainstone.h" +#include "sysbus.h" /* Mainstone FPGA for extern irqs */ #define FPGA_GPIO_PIN 0 @@ -27,9 +26,16 @@ #define MST_PCMCIA0 0xe0 #define MST_PCMCIA1 0xe4 +#define MST_PCMCIAx_READY (1 << 10) +#define MST_PCMCIAx_nCD (1 << 5) + +#define MST_PCMCIA_CD0_IRQ 9 +#define MST_PCMCIA_CD1_IRQ 13 + typedef struct mst_irq_state{ - qemu_irq *parent; - qemu_irq *pins; + SysBusDevice busdev; + + qemu_irq parent; uint32_t prev_level; uint32_t leddat1; @@ -47,33 +53,36 @@ }mst_irq_state; static void -mst_fpga_update_gpio(mst_irq_state *s) -{ - uint32_t level, diff; - int bit; - level = s->prev_level ^ s->intsetclr; - - for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { - bit = ffs(diff) - 1; - qemu_set_irq(s->pins[bit], (level >> bit) & 1 ); - } - s->prev_level = level; -} - -static void mst_fpga_set_irq(void *opaque, int irq, int level) { mst_irq_state *s = (mst_irq_state *)opaque; + uint32_t oldint = s->intsetclr & s->intmskena; if (level) s->prev_level |= 1u << irq; else s->prev_level &= ~(1u << irq); - if(s->intmskena & (1u << irq)) { - s->intsetclr = 1u << irq; - qemu_set_irq(s->parent[0], level); + switch(irq) { + case MST_PCMCIA_CD0_IRQ: + if (level) + s->pcmcia0 &= ~MST_PCMCIAx_nCD; + else + s->pcmcia0 |= MST_PCMCIAx_nCD; + break; + case MST_PCMCIA_CD1_IRQ: + if (level) + s->pcmcia1 &= ~MST_PCMCIAx_nCD; + else + s->pcmcia1 |= MST_PCMCIAx_nCD; + break; } + + if ((s->intmskena & (1u << irq)) && level) + s->intsetclr |= 1u << irq; + + if (oldint != (s->intsetclr & s->intmskena)) + qemu_set_irq(s->parent, s->intsetclr & s->intmskena); } @@ -109,7 +118,7 @@ return s->pcmcia1; default: printf("Mainstone - mst_fpga_readb: Bad register offset " - REG_FMT " \n", addr); + "0x" TARGET_FMT_plx " \n", addr); } return 0; } @@ -145,22 +154,24 @@ case MST_MSCRD: s->mscrd = value; break; - case MST_INTMSKENA: /* Mask interupt */ + case MST_INTMSKENA: /* Mask interrupt */ s->intmskena = (value & 0xFEEFF); - mst_fpga_update_gpio(s); + qemu_set_irq(s->parent, s->intsetclr & s->intmskena); break; case MST_INTSETCLR: /* clear or set interrupt */ s->intsetclr = (value & 0xFEEFF); + qemu_set_irq(s->parent, s->intsetclr & s->intmskena); break; + /* For PCMCIAx allow the to change only power and reset */ case MST_PCMCIA0: - s->pcmcia0 = value; + s->pcmcia0 = (value & 0x1f) | (s->pcmcia0 & ~0x1f); break; case MST_PCMCIA1: - s->pcmcia1 = value; + s->pcmcia1 = (value & 0x1f) | (s->pcmcia1 & ~0x1f); break; default: printf("Mainstone - mst_fpga_writeb: Bad register offset " - REG_FMT " \n", addr); + "0x" TARGET_FMT_plx " \n", addr); } } @@ -175,66 +186,70 @@ mst_fpga_writeb, }; -static void -mst_fpga_save(QEMUFile *f, void *opaque) -{ - struct mst_irq_state *s = (mst_irq_state *) opaque; - - qemu_put_be32s(f, &s->prev_level); - qemu_put_be32s(f, &s->leddat1); - qemu_put_be32s(f, &s->leddat2); - qemu_put_be32s(f, &s->ledctrl); - qemu_put_be32s(f, &s->gpswr); - qemu_put_be32s(f, &s->mscwr1); - qemu_put_be32s(f, &s->mscwr2); - qemu_put_be32s(f, &s->mscwr3); - qemu_put_be32s(f, &s->mscrd); - qemu_put_be32s(f, &s->intmskena); - qemu_put_be32s(f, &s->intsetclr); - qemu_put_be32s(f, &s->pcmcia0); - qemu_put_be32s(f, &s->pcmcia1); -} -static int -mst_fpga_load(QEMUFile *f, void *opaque, int version_id) +static int mst_fpga_post_load(void *opaque, int version_id) { mst_irq_state *s = (mst_irq_state *) opaque; - qemu_get_be32s(f, &s->prev_level); - qemu_get_be32s(f, &s->leddat1); - qemu_get_be32s(f, &s->leddat2); - qemu_get_be32s(f, &s->ledctrl); - qemu_get_be32s(f, &s->gpswr); - qemu_get_be32s(f, &s->mscwr1); - qemu_get_be32s(f, &s->mscwr2); - qemu_get_be32s(f, &s->mscwr3); - qemu_get_be32s(f, &s->mscrd); - qemu_get_be32s(f, &s->intmskena); - qemu_get_be32s(f, &s->intsetclr); - qemu_get_be32s(f, &s->pcmcia0); - qemu_get_be32s(f, &s->pcmcia1); + qemu_set_irq(s->parent, s->intsetclr & s->intmskena); return 0; } -qemu_irq *mst_irq_init(PXA2xxState *cpu, uint32_t base, int irq) +static int mst_fpga_init(SysBusDevice *dev) { mst_irq_state *s; int iomemtype; - qemu_irq *qi; - s = (mst_irq_state *) - qemu_mallocz(sizeof(mst_irq_state)); + s = FROM_SYSBUS(mst_irq_state, dev); - s->parent = &cpu->pic[irq]; + s->pcmcia0 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; + s->pcmcia1 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; + + sysbus_init_irq(dev, &s->parent); /* alloc the external 16 irqs */ - qi = qemu_allocate_irqs(mst_fpga_set_irq, s, MST_NUM_IRQS); - s->pins = qi; + qdev_init_gpio_in(&dev->qdev, mst_fpga_set_irq, MST_NUM_IRQS); iomemtype = cpu_register_io_memory(mst_fpga_readfn, mst_fpga_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x00100000, iomemtype); - register_savevm(NULL, "mainstone_fpga", 0, 0, mst_fpga_save, - mst_fpga_load, s); - return qi; + sysbus_init_mmio(dev, 0x00100000, iomemtype); + return 0; +} + +static VMStateDescription vmstate_mst_fpga_regs = { + .name = "mainstone_fpga", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = mst_fpga_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT32(prev_level, mst_irq_state), + VMSTATE_UINT32(leddat1, mst_irq_state), + VMSTATE_UINT32(leddat2, mst_irq_state), + VMSTATE_UINT32(ledctrl, mst_irq_state), + VMSTATE_UINT32(gpswr, mst_irq_state), + VMSTATE_UINT32(mscwr1, mst_irq_state), + VMSTATE_UINT32(mscwr2, mst_irq_state), + VMSTATE_UINT32(mscwr3, mst_irq_state), + VMSTATE_UINT32(mscrd, mst_irq_state), + VMSTATE_UINT32(intmskena, mst_irq_state), + VMSTATE_UINT32(intsetclr, mst_irq_state), + VMSTATE_UINT32(pcmcia0, mst_irq_state), + VMSTATE_UINT32(pcmcia1, mst_irq_state), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo mst_fpga_info = { + .init = mst_fpga_init, + .qdev.name = "mainstone-fpga", + .qdev.desc = "Mainstone II FPGA", + .qdev.size = sizeof(mst_irq_state), + .qdev.vmsd = &vmstate_mst_fpga_regs, +}; + +static void mst_fpga_register(void) +{ + sysbus_register_withprop(&mst_fpga_info); } +device_init(mst_fpga_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/multiboot.c qemu-kvm-0.15.0+noroms/hw/multiboot.c --- qemu-kvm-0.14.1+noroms/hw/multiboot.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/multiboot.c 2011-08-09 07:40:29.000000000 -0500 @@ -97,11 +97,11 @@ static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline) { - int len = strlen(cmdline) + 1; target_phys_addr_t p = s->offset_cmdlines; + char *b = (char *)s->mb_buf + p; - pstrcpy((char *)s->mb_buf + p, len, cmdline); - s->offset_cmdlines += len; + get_opt_value(b, strlen(cmdline) + 1, cmdline); + s->offset_cmdlines += strlen(b) + 1; return s->mb_buf_phys + p; } @@ -238,7 +238,7 @@ const char *r = initrd_filename; mbs.mb_buf_size += strlen(r) + 1; mbs.mb_mods_avail = 1; - while ((r = strchr(r, ','))) { + while (*(r = get_opt_value(NULL, 0, r))) { mbs.mb_mods_avail++; r++; } @@ -252,7 +252,7 @@ mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; if (initrd_filename) { - char *next_initrd; + char *next_initrd, not_last; mbs.offset_mods = mbs.mb_buf_size; @@ -261,9 +261,9 @@ int mb_mod_length; uint32_t offs = mbs.mb_buf_size; - next_initrd = strchr(initrd_filename, ','); - if (next_initrd) - *next_initrd = '\0'; + next_initrd = (char *)get_opt_value(NULL, 0, initrd_filename); + not_last = *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); @@ -272,7 +272,7 @@ 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); + fprintf(stderr, "Failed to open file '%s'\n", initrd_filename); exit(1); } @@ -287,7 +287,7 @@ (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); initrd_filename = next_initrd+1; - } while (next_initrd); + } while (not_last); } /* Commandline support */ @@ -307,7 +307,7 @@ | 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_BOOT_DEVICE, 0x8000ffff); /* 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); diff -Nru qemu-kvm-0.14.1+noroms/hw/musicpal.c qemu-kvm-0.15.0+noroms/hw/musicpal.c --- qemu-kvm-0.14.1+noroms/hw/musicpal.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/musicpal.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" @@ -1597,11 +1597,11 @@ musicpal_misc_init(); dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]); - i2c_dev = sysbus_create_simple("gpio_i2c", 0, NULL); + i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c"); lcd_dev = sysbus_create_simple("musicpal_lcd", MP_LCD_BASE, NULL); - key_dev = sysbus_create_simple("musicpal_key", 0, NULL); + key_dev = sysbus_create_simple("musicpal_key", -1, NULL); /* I2C read data */ qdev_connect_gpio_out(i2c_dev, 0, diff -Nru qemu-kvm-0.14.1+noroms/hw/nand.c qemu-kvm-0.15.0+noroms/hw/nand.c --- qemu-kvm-0.14.1+noroms/hw/nand.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/nand.c 2011-08-09 07:40:29.000000000 -0500 @@ -52,7 +52,7 @@ BlockDriverState *bdrv; int mem_oob; - int cle, ale, ce, wp, gnd; + uint8_t cle, ale, ce, wp, gnd; uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; uint8_t *ioaddr; @@ -66,6 +66,8 @@ void (*blk_write)(NANDFlashState *s); void (*blk_erase)(NANDFlashState *s); void (*blk_load)(NANDFlashState *s, uint32_t addr, int offset); + + uint32_t ioaddr_vmstate; }; # define NAND_NO_AUTOINCR 0x00000001 @@ -281,56 +283,59 @@ } } -static void nand_save(QEMUFile *f, void *opaque) +static void nand_pre_save(void *opaque) { - NANDFlashState *s = (NANDFlashState *) opaque; - qemu_put_byte(f, s->cle); - qemu_put_byte(f, s->ale); - qemu_put_byte(f, s->ce); - qemu_put_byte(f, s->wp); - qemu_put_byte(f, s->gnd); - qemu_put_buffer(f, s->io, sizeof(s->io)); - qemu_put_be32(f, s->ioaddr - s->io); - qemu_put_be32(f, s->iolen); - - qemu_put_be32s(f, &s->cmd); - qemu_put_be32s(f, &s->addr); - qemu_put_be32(f, s->addrlen); - qemu_put_be32(f, s->status); - qemu_put_be32(f, s->offset); - /* XXX: do we want to save s->storage too? */ + NANDFlashState *s = opaque; + + s->ioaddr_vmstate = s->ioaddr - s->io; } -static int nand_load(QEMUFile *f, void *opaque, int version_id) +static int nand_post_load(void *opaque, int version_id) { - NANDFlashState *s = (NANDFlashState *) opaque; - s->cle = qemu_get_byte(f); - s->ale = qemu_get_byte(f); - s->ce = qemu_get_byte(f); - s->wp = qemu_get_byte(f); - s->gnd = qemu_get_byte(f); - qemu_get_buffer(f, s->io, sizeof(s->io)); - s->ioaddr = s->io + qemu_get_be32(f); - s->iolen = qemu_get_be32(f); - if (s->ioaddr >= s->io + sizeof(s->io) || s->ioaddr < s->io) + NANDFlashState *s = opaque; + + if (s->ioaddr_vmstate > sizeof(s->io)) { return -EINVAL; + } + s->ioaddr = s->io + s->ioaddr_vmstate; - qemu_get_be32s(f, &s->cmd); - qemu_get_be32s(f, &s->addr); - s->addrlen = qemu_get_be32(f); - s->status = qemu_get_be32(f); - s->offset = qemu_get_be32(f); return 0; } +static const VMStateDescription vmstate_nand = { + .name = "nand", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .pre_save = nand_pre_save, + .post_load = nand_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(cle, NANDFlashState), + VMSTATE_UINT8(ale, NANDFlashState), + VMSTATE_UINT8(ce, NANDFlashState), + VMSTATE_UINT8(wp, NANDFlashState), + VMSTATE_UINT8(gnd, NANDFlashState), + VMSTATE_BUFFER(io, NANDFlashState), + VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), + VMSTATE_INT32(iolen, NANDFlashState), + VMSTATE_UINT32(cmd, NANDFlashState), + VMSTATE_UINT32(addr, NANDFlashState), + VMSTATE_INT32(addrlen, NANDFlashState), + VMSTATE_INT32(status, NANDFlashState), + VMSTATE_INT32(offset, NANDFlashState), + /* XXX: do we want to save s->storage too? */ + VMSTATE_END_OF_LIST() + } +}; + /* * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip * outputs are R/B and eight I/O pins. * * CE, WP and R/B are active low. */ -void nand_setpins(NANDFlashState *s, - int cle, int ale, int ce, int wp, int gnd) +void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, + uint8_t ce, uint8_t wp, uint8_t gnd) { s->cle = cle; s->ale = ale; @@ -502,7 +507,7 @@ is used. */ s->ioaddr = s->io; - register_savevm(NULL, "nand", -1, 0, nand_save, nand_load, s); + vmstate_register(NULL, -1, &vmstate_nand, s); return s; } diff -Nru qemu-kvm-0.14.1+noroms/hw/ne2000.c qemu-kvm-0.15.0+noroms/hw/ne2000.c --- qemu-kvm-0.14.1+noroms/hw/ne2000.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ne2000.c 2011-08-09 07:40:29.000000000 -0500 @@ -721,9 +721,6 @@ uint8_t *pci_conf; pci_conf = d->dev.config; - 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); /* TODO: RST# value should be 0. PCI spec 6.2.4 */ pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0 @@ -742,7 +739,7 @@ if (!pci_dev->qdev.hotplugged) { static int loaded = 0; if (!loaded) { - rom_add_option("pxe-ne2k_pci.bin", -1); + rom_add_option("pxe-ne2k_pci.rom", -1); loaded = 1; } } @@ -767,6 +764,9 @@ .qdev.vmsd = &vmstate_pci_ne2000, .init = pci_ne2000_init, .exit = pci_ne2000_exit, + .vendor_id = PCI_VENDOR_ID_REALTEK, + .device_id = PCI_DEVICE_ID_REALTEK_8029, + .class_id = PCI_CLASS_NETWORK_ETHERNET, .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/ne2000-isa.c qemu-kvm-0.15.0+noroms/hw/ne2000-isa.c --- qemu-kvm-0.14.1+noroms/hw/ne2000-isa.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ne2000-isa.c 2011-08-09 07:40:29.000000000 -0500 @@ -92,19 +92,6 @@ return 0; } -void isa_ne2000_init(int base, int irq, NICInfo *nd) -{ - ISADevice *dev; - - qemu_check_nic_model(nd, "ne2k_isa"); - - dev = isa_create("ne2k_isa"); - qdev_prop_set_uint32(&dev->qdev, "iobase", base); - qdev_prop_set_uint32(&dev->qdev, "irq", irq); - qdev_set_nic_properties(&dev->qdev, nd); - qdev_init_nofail(&dev->qdev); -} - static ISADeviceInfo ne2000_isa_info = { .qdev.name = "ne2k_isa", .qdev.size = sizeof(ISANE2000State), diff -Nru qemu-kvm-0.14.1+noroms/hw/nseries.c qemu-kvm-0.15.0+noroms/hw/nseries.c --- qemu-kvm-0.14.1+noroms/hw/nseries.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/nseries.c 2011-08-09 07:40:29.000000000 -0500 @@ -1254,12 +1254,12 @@ return (void *) w - p; } -static int n800_atag_setup(struct arm_boot_info *info, void *p) +static int n800_atag_setup(const struct arm_boot_info *info, void *p) { return n8x0_atag_setup(p, 800); } -static int n810_atag_setup(struct arm_boot_info *info, void *p) +static int n810_atag_setup(const struct arm_boot_info *info, void *p) { return n8x0_atag_setup(p, 810); } diff -Nru qemu-kvm-0.14.1+noroms/hw/omap1.c qemu-kvm-0.15.0+noroms/hw/omap1.c --- qemu-kvm-0.14.1+noroms/hw/omap1.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/omap1.c 2011-08-09 07:40:29.000000000 -0500 @@ -101,7 +101,7 @@ static inline uint32_t omap_timer_read(struct omap_mpu_timer_s *timer) { - uint64_t distance = qemu_get_clock(vm_clock) - timer->time; + uint64_t distance = qemu_get_clock_ns(vm_clock) - timer->time; if (timer->st && timer->enable && timer->rate) return timer->val - muldiv64(distance >> (timer->ptv + 1), @@ -113,7 +113,7 @@ static inline void omap_timer_sync(struct omap_mpu_timer_s *timer) { timer->val = omap_timer_read(timer); - timer->time = qemu_get_clock(vm_clock); + timer->time = qemu_get_clock_ns(vm_clock); } static inline void omap_timer_update(struct omap_mpu_timer_s *timer) @@ -258,7 +258,7 @@ s->irq = irq; s->clk = clk; - s->timer = qemu_new_timer(vm_clock, omap_timer_tick, s); + s->timer = qemu_new_timer_ns(vm_clock, omap_timer_tick, s); s->tick = qemu_bh_new(omap_timer_fire, s); omap_mpu_timer_reset(s); omap_timer_clk_setup(s); @@ -382,7 +382,7 @@ s->timer.irq = irq; s->timer.clk = clk; - s->timer.timer = qemu_new_timer(vm_clock, omap_timer_tick, &s->timer); + s->timer.timer = qemu_new_timer_ns(vm_clock, omap_timer_tick, &s->timer); omap_wd_timer_reset(s); omap_timer_clk_setup(&s->timer); @@ -484,7 +484,7 @@ s->timer.irq = irq; s->timer.clk = clk; - s->timer.timer = qemu_new_timer(vm_clock, omap_timer_tick, &s->timer); + s->timer.timer = qemu_new_timer_ns(vm_clock, omap_timer_tick, &s->timer); omap_os_timer_reset(s); omap_timer_clk_setup(&s->timer); @@ -580,7 +580,7 @@ case 0x10: /* GAUGING_CTRL */ /* Bits 0 and 1 seem to be confused in the OMAP 310 TRM */ if ((s->ulpd_pm_regs[addr >> 2] ^ value) & 1) { - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); if (value & 1) s->ulpd_gauge_start = now; @@ -2802,7 +2802,7 @@ s->pm_am = 0; s->auto_comp = 0; s->round = 0; - s->tick = qemu_get_clock(rt_clock); + s->tick = qemu_get_clock_ms(rt_clock); memset(&s->alarm_tm, 0, sizeof(s->alarm_tm)); s->alarm_tm.tm_mday = 0x01; s->status = 1 << 7; @@ -2822,7 +2822,7 @@ s->irq = irq[0]; s->alarm = irq[1]; - s->clk = qemu_new_timer(rt_clock, omap_rtc_tick, s); + s->clk = qemu_new_timer_ms(rt_clock, omap_rtc_tick, s); omap_rtc_reset(s); @@ -2915,7 +2915,7 @@ s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7]; omap_mcbsp_rx_newdata(s); - qemu_mod_timer(s->source_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(s->source_timer, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec()); } @@ -2961,7 +2961,7 @@ s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7]; omap_mcbsp_tx_newdata(s); - qemu_mod_timer(s->sink_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(s->sink_timer, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec()); } @@ -3344,8 +3344,8 @@ s->rxirq = irq[1]; s->txdrq = dma[0]; s->rxdrq = dma[1]; - s->sink_timer = qemu_new_timer(vm_clock, omap_mcbsp_sink_tick, s); - s->source_timer = qemu_new_timer(vm_clock, omap_mcbsp_source_tick, s); + s->sink_timer = qemu_new_timer_ns(vm_clock, omap_mcbsp_sink_tick, s); + s->source_timer = qemu_new_timer_ns(vm_clock, omap_mcbsp_source_tick, s); omap_mcbsp_reset(s); iomemtype = cpu_register_io_memory(omap_mcbsp_readfn, @@ -3399,9 +3399,9 @@ struct omap_lpg_s *s = opaque; if (s->cycle) - qemu_mod_timer(s->tm, qemu_get_clock(rt_clock) + s->period - s->on); + qemu_mod_timer(s->tm, qemu_get_clock_ms(rt_clock) + s->period - s->on); else - qemu_mod_timer(s->tm, qemu_get_clock(rt_clock) + s->on); + qemu_mod_timer(s->tm, qemu_get_clock_ms(rt_clock) + s->on); s->cycle = !s->cycle; printf("%s: LED is %s\n", __FUNCTION__, s->cycle ? "on" : "off"); @@ -3516,7 +3516,7 @@ struct omap_lpg_s *s = (struct omap_lpg_s *) qemu_mallocz(sizeof(struct omap_lpg_s)); - s->tm = qemu_new_timer(rt_clock, omap_lpg_tick, s); + s->tm = qemu_new_timer_ms(rt_clock, omap_lpg_tick, s); omap_lpg_reset(s); diff -Nru qemu-kvm-0.14.1+noroms/hw/omap_gptimer.c qemu-kvm-0.15.0+noroms/hw/omap_gptimer.c --- qemu-kvm-0.14.1+noroms/hw/omap_gptimer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/omap_gptimer.c 2011-08-09 07:40:29.000000000 -0500 @@ -102,7 +102,7 @@ uint64_t distance; if (timer->st && timer->rate) { - distance = qemu_get_clock(vm_clock) - timer->time; + distance = qemu_get_clock_ns(vm_clock) - timer->time; distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); if (distance >= 0xffffffff - timer->val) @@ -117,7 +117,7 @@ { if (timer->st) { timer->val = omap_gp_timer_read(timer); - timer->time = qemu_get_clock(vm_clock); + timer->time = qemu_get_clock_ns(vm_clock); } } @@ -163,7 +163,7 @@ timer->val = 0; } else { timer->val = timer->load_val; - timer->time = qemu_get_clock(vm_clock); + timer->time = qemu_get_clock_ns(vm_clock); } if (timer->trigger == gpt_trigger_overflow || @@ -411,7 +411,7 @@ break; case 0x28: /* TCRR */ - s->time = qemu_get_clock(vm_clock); + s->time = qemu_get_clock_ns(vm_clock); s->val = value; omap_gp_timer_update(s); break; @@ -421,7 +421,7 @@ break; case 0x30: /* TTGR */ - s->time = qemu_get_clock(vm_clock); + s->time = qemu_get_clock_ns(vm_clock); s->val = s->load_val; omap_gp_timer_update(s); break; @@ -470,8 +470,8 @@ 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->timer = qemu_new_timer_ns(vm_clock, omap_gp_timer_tick, s); + s->match = qemu_new_timer_ns(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); diff -Nru qemu-kvm-0.14.1+noroms/hw/omap_sx1.c qemu-kvm-0.15.0+noroms/hw/omap_sx1.c --- qemu-kvm-0.14.1+noroms/hw/omap_sx1.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/omap_sx1.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,7 +26,6 @@ * with this program; if not, see . */ #include "hw.h" -#include "sysemu.h" #include "console.h" #include "omap.h" #include "boards.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/omap_synctimer.c qemu-kvm-0.15.0+noroms/hw/omap_synctimer.c --- qemu-kvm-0.14.1+noroms/hw/omap_synctimer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/omap_synctimer.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,7 @@ /* 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()); + return muldiv64(qemu_get_clock_ns(vm_clock), 0x8000, get_ticks_per_sec()); } void omap_synctimer_reset(struct omap_synctimer_s *s) diff -Nru qemu-kvm-0.14.1+noroms/hw/parallel.c qemu-kvm-0.15.0+noroms/hw/parallel.c --- qemu-kvm-0.14.1+noroms/hw/parallel.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/parallel.c 2011-08-09 07:40:29.000000000 -0500 @@ -64,7 +64,7 @@ #define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE) -struct ParallelState { +typedef struct ParallelState { uint8_t dataw; uint8_t datar; uint8_t status; @@ -77,7 +77,7 @@ uint32_t last_read_offset; /* For debugging */ /* Memory-mapped interface */ int it_shift; -}; +} ParallelState; typedef struct ISAParallelState { ISADevice dev; @@ -500,18 +500,6 @@ return 0; } -ParallelState *parallel_init(int index, CharDriverState *chr) -{ - ISADevice *dev; - - dev = isa_create("isa-parallel"); - qdev_prop_set_uint32(&dev->qdev, "index", index); - qdev_prop_set_chr(&dev->qdev, "chardev", chr); - if (qdev_init(&dev->qdev) < 0) - return NULL; - return &DO_UPCAST(ISAParallelState, dev, dev)->state; -} - /* Memory mapped interface */ static uint32_t parallel_mm_readb (void *opaque, target_phys_addr_t addr) { @@ -571,7 +559,8 @@ }; /* If fd is zero, it means that the parallel device uses the console */ -ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr) +bool parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, + CharDriverState *chr) { ParallelState *s; int io_sw; @@ -585,7 +574,7 @@ 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; + return true; } static ISADeviceInfo parallel_isa_info = { diff -Nru qemu-kvm-0.14.1+noroms/hw/pc.c qemu-kvm-0.15.0+noroms/hw/pc.c --- qemu-kvm-0.14.1+noroms/hw/pc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pc.c 2011-08-09 07:40:29.000000000 -0500 @@ -39,7 +39,6 @@ #include "msix.h" #include "sysbus.h" #include "sysemu.h" -#include "device-assignment.h" #include "kvm.h" #include "blockdev.h" #include "ui/qemu-spice.h" @@ -88,6 +87,7 @@ } __attribute((__packed__, __aligned__(4))); static struct e820_table e820_table; +struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; void isa_irq_handler(void *opaque, int n, int level) { @@ -195,23 +195,24 @@ #define REG_EQUIPMENT_BYTE 0x14 -static int cmos_get_fd_drive_type(int fd0) +static int cmos_get_fd_drive_type(FDriveType fd0) { int val; switch (fd0) { - case 0: + case FDRIVE_DRV_144: /* 1.44 Mb 3"5 drive */ val = 4; break; - case 1: + case FDRIVE_DRV_288: /* 2.88 Mb 3"5 drive */ val = 5; break; - case 2: + case FDRIVE_DRV_120: /* 1.2 Mb 5"5 drive */ val = 2; break; + case FDRIVE_DRV_NONE: default: val = 0; break; @@ -336,10 +337,11 @@ 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) + ISADevice *s) { - int val; - int fd0, fd1, nb; + int val, nb, nb_heads, max_track, last_sect, i; + FDriveType fd_type[2]; + DriveInfo *fd[2]; static pc_cmos_init_late_arg arg; /* various important CMOS locations needed by PC/Bochs bios */ @@ -381,19 +383,28 @@ } /* floppy type */ - - fd0 = fdctrl_get_drive_type(floppy_controller, 0); - fd1 = fdctrl_get_drive_type(floppy_controller, 1); - - val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1); + for (i = 0; i < 2; i++) { + fd[i] = drive_get(IF_FLOPPY, 0, i); + if (fd[i] && bdrv_is_inserted(fd[i]->bdrv)) { + bdrv_get_floppy_geometry_hint(fd[i]->bdrv, &nb_heads, &max_track, + &last_sect, FDRIVE_DRV_NONE, + &fd_type[i]); + } else { + fd_type[i] = FDRIVE_DRV_NONE; + } + } + val = (cmos_get_fd_drive_type(fd_type[0]) << 4) | + cmos_get_fd_drive_type(fd_type[1]); rtc_set_memory(s, 0x10, val); val = 0; nb = 0; - if (fd0 < 3) + if (fd_type[0] < FDRIVE_DRV_NONE) { nb++; - if (fd1 < 3) + } + if (fd_type[1] < FDRIVE_DRV_NONE) { nb++; + } switch (nb) { case 0: break; @@ -593,7 +604,7 @@ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, (uint8_t *)acpi_tables, acpi_tables_len); - fw_cfg_add_bytes(fw_cfg, FW_CFG_IRQ0_OVERRIDE, &irq0override, 1); + fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override()); smbios_table = smbios_get_table(&smbios_len); if (smbios_table) @@ -949,34 +960,18 @@ } } -void pc_memory_init(ram_addr_t ram_size, - const char *kernel_filename, +void pc_memory_init(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) + ram_addr_t below_4g_mem_size, + ram_addr_t above_4g_mem_size) { 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; void *fw_cfg; - if (ram_size >= 0xe0000000 ) { - above_4g_mem_size = ram_size - 0xe0000000; - below_4g_mem_size = 0xe0000000; - } 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; - -#if TARGET_PHYS_ADDR_BITS == 32 - if (above_4g_mem_size > 0) { - hw_error("To much RAM for 32-bit physical address"); - } -#endif linux_boot = (kernel_filename != NULL); /* allocate RAM */ @@ -986,12 +981,10 @@ cpu_register_physical_memory(0x100000, below_4g_mem_size - 0x100000, ram_addr + 0x100000); -#if TARGET_PHYS_ADDR_BITS > 32 if (above_4g_mem_size > 0) { cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, ram_addr + below_4g_mem_size); } -#endif /* BIOS load */ if (bios_name == NULL) @@ -1068,10 +1061,15 @@ isa_cirrus_vga_init(); } } else if (vmsvga_enabled) { - if (pci_bus) - pci_vmsvga_init(pci_bus); - else + if (pci_bus) { + if (!pci_vmsvga_init(pci_bus)) { + fprintf(stderr, "Warning: vmware_vga not available," + " using standard VGA instead\n"); + pci_vga_init(pci_bus); + } + } else { fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__); + } #ifdef CONFIG_SPICE } else if (qxl_enabled) { if (pci_bus) @@ -1086,6 +1084,15 @@ isa_vga_init(); } } + + /* + * sga does not suppress normal vga output. So a machine can have both a + * vga card and sga manually enabled. Output will be seen on both. + * For nographic case, sga is enabled at all times + */ + if (display_type == DT_NOGRAPHIC) { + isa_create_simple("sga"); + } } static void cpu_request_exit(void *opaque, int irq, int level) @@ -1098,15 +1105,14 @@ } void pc_basic_device_init(qemu_irq *isa_irq, - FDCtrl **floppy_controller, - ISADevice **rtc_state) + ISADevice **rtc_state, + bool no_vmport) { int i; DriveInfo *fd[MAX_FD]; - PITState *pit; qemu_irq rtc_irq = NULL; qemu_irq *a20_line; - ISADevice *i8042, *port92; + ISADevice *i8042, *port92, *vmmouse, *pit; qemu_irq *cpu_exit_irq; register_ioport_write(0x80, 1, 1, ioport80_write, NULL); @@ -1114,24 +1120,20 @@ register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); if (!no_hpet) { - DeviceState *hpet = sysbus_create_simple("hpet", HPET_BASE, NULL); + DeviceState *hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); - for (i = 0; i < 24; i++) { - sysbus_connect_irq(sysbus_from_qdev(hpet), i, isa_irq[i]); + if (hpet) { + 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_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() && kvm_pit_in_kernel()) - pit = kvm_pit_init(0x40, isa_get_irq(0)); - else -#endif - - pit = pit_init(0x40, isa_get_irq(0)); + pit = pit_init(0x40, 0); pcspk_init(pit); for(i = 0; i < MAX_SERIAL_PORTS; i++) { @@ -1149,7 +1151,16 @@ 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); + if (!no_vmport) { + vmport_init(); + vmmouse = isa_try_create("vmmouse"); + } else { + vmmouse = NULL; + } + if (vmmouse) { + qdev_prop_set_ptr(&vmmouse->qdev, "ps2_mouse", i8042); + qdev_init_nofail(&vmmouse->qdev); + } port92 = isa_create_simple("port92"); port92_init(port92, &a20_line[1]); @@ -1159,7 +1170,7 @@ 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); } void pc_pci_device_init(PCIBus *pci_bus) diff -Nru qemu-kvm-0.14.1+noroms/hw/pc.h qemu-kvm-0.15.0+noroms/hw/pc.h --- qemu-kvm-0.14.1+noroms/hw/pc.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pc.h 2011-08-09 07:40:29.000000000 -0500 @@ -5,6 +5,7 @@ #include "ioport.h" #include "isa.h" #include "fdc.h" +#include "net.h" /* PC-style peripherals (also used by other machines). */ @@ -16,14 +17,43 @@ qemu_irq irq, int baudbase, CharDriverState *chr, int ioregister, int be); -SerialState *serial_isa_init(int index, CharDriverState *chr); +static inline bool serial_isa_init(int index, CharDriverState *chr) +{ + ISADevice *dev; + + dev = isa_try_create("isa-serial"); + if (!dev) { + return false; + } + qdev_prop_set_uint32(&dev->qdev, "index", index); + qdev_prop_set_chr(&dev->qdev, "chardev", chr); + if (qdev_init(&dev->qdev) < 0) { + return false; + } + return true; +} + void serial_set_frequency(SerialState *s, uint32_t frequency); /* parallel.c */ +static inline bool parallel_init(int index, CharDriverState *chr) +{ + ISADevice *dev; + + dev = isa_try_create("isa-parallel"); + if (!dev) { + return false; + } + qdev_prop_set_uint32(&dev->qdev, "index", index); + qdev_prop_set_chr(&dev->qdev, "chardev", chr); + if (qdev_init(&dev->qdev) < 0) { + return false; + } + return true; +} -typedef struct ParallelState ParallelState; -ParallelState *parallel_init(int index, CharDriverState *chr); -ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr); +bool parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, + CharDriverState *chr); /* i8259.c */ @@ -53,28 +83,35 @@ #define PIT_FREQ 1193182 -typedef struct PITState PITState; - -PITState *pit_init(int base, qemu_irq irq); -void pit_set_gate(PITState *pit, int channel, int val); -int pit_get_gate(PITState *pit, int channel); -int pit_get_initial_count(PITState *pit, int channel); -int pit_get_mode(PITState *pit, int channel); -int pit_get_out(PITState *pit, int channel, int64_t current_time); - -/* i8254-kvm.c */ - -PITState *kvm_pit_init(int base, qemu_irq irq); +static inline ISADevice *pit_init(int base, int irq) +{ + ISADevice *dev; + + dev = isa_create("isa-pit"); + qdev_prop_set_uint32(&dev->qdev, "iobase", base); + qdev_prop_set_uint32(&dev->qdev, "irq", irq); + qdev_init_nofail(&dev->qdev); + + return dev; +} + +void pit_set_gate(ISADevice *dev, int channel, int val); +int pit_get_gate(ISADevice *dev, int channel); +int pit_get_initial_count(ISADevice *dev, int channel); +int pit_get_mode(ISADevice *dev, int channel); +int pit_get_out(ISADevice *dev, int channel, int64_t current_time); void hpet_pit_disable(void); void hpet_pit_enable(void); /* vmport.c */ -void vmport_init(void); +static inline void vmport_init(void) +{ + isa_create_simple("vmport"); +} void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque); - -/* vmmouse.c */ -void *vmmouse_init(void *m); +void vmmouse_get_data(uint32_t *data); +void vmmouse_set_data(const uint32_t *data); /* pckbd.c */ @@ -93,22 +130,21 @@ 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, +void pc_memory_init(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); + ram_addr_t below_4g_mem_size, + ram_addr_t above_4g_mem_size); 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); + ISADevice **rtc_state, + bool no_vmport); 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); + ISADevice *s); void pc_pci_device_init(PCIBus *pci_bus); typedef void (*cpu_set_smm_t)(int smm, void *arg); @@ -133,7 +169,7 @@ extern int no_hpet; /* pcspk.c */ -void pcspk_init(PITState *); +void pcspk_init(ISADevice *pit); int pcspk_audio_init(qemu_irq *pic); /* piix_pci.c */ @@ -162,7 +198,19 @@ extern enum vga_retrace_method vga_retrace_method; -int isa_vga_init(void); +static inline int isa_vga_init(void) +{ + ISADevice *dev; + + dev = isa_try_create("isa-vga"); + if (!dev) { + fprintf(stderr, "Warning: isa-vga not available\n"); + return 0; + } + qdev_init_nofail(&dev->qdev); + return 1; +} + 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); @@ -172,8 +220,22 @@ void isa_cirrus_vga_init(void); /* ne2000.c */ - -void isa_ne2000_init(int base, int irq, NICInfo *nd); +static inline bool isa_ne2000_init(int base, int irq, NICInfo *nd) +{ + ISADevice *dev; + + qemu_check_nic_model(nd, "ne2k_isa"); + + dev = isa_try_create("ne2k_isa"); + if (!dev) { + return false; + } + qdev_prop_set_uint32(&dev->qdev, "iobase", base); + qdev_prop_set_uint32(&dev->qdev, "irq", irq); + qdev_set_nic_properties(&dev->qdev, nd); + qdev_init_nofail(&dev->qdev); + return true; +} /* extboot.c */ diff -Nru qemu-kvm-0.14.1+noroms/hw/pci.c qemu-kvm-0.15.0+noroms/hw/pci.c --- qemu-kvm-0.14.1+noroms/hw/pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -34,6 +34,7 @@ #include "device-assignment.h" #include "qemu-objects.h" #include "range.h" +#include "msi.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -129,6 +130,13 @@ bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); } +int pci_bus_get_irq_level(PCIBus *bus, int irq_num) +{ + assert(irq_num >= 0); + assert(irq_num < bus->nirq); + return !!bus->irq_count[irq_num]; +} + /* Update interrupt status bit in config space on interrupt * state change. */ static void pci_update_irq_status(PCIDevice *dev) @@ -164,7 +172,7 @@ dev->irq_state = 0; pci_update_irq_status(dev); pci_device_deassert_intx(dev); - /* Clear all writeable bits */ + /* Clear all writable 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)); @@ -259,7 +267,7 @@ } void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, - const char *name, int devfn_min) + const char *name, uint8_t devfn_min) { qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name); assert(PCI_FUNC(devfn_min) == 0); @@ -272,7 +280,7 @@ vmstate_register(NULL, -1, &vmstate_pcibus, bus); } -PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min) +PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min) { PCIBus *bus; @@ -306,7 +314,7 @@ 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 *irq_opaque, uint8_t devfn_min, int nirq) { PCIBus *bus; @@ -342,6 +350,7 @@ memcpy(s->config, config, size); pci_update_mappings(s); + msi_post_load(s); qemu_free(config); return 0; @@ -638,7 +647,7 @@ return NULL; } - *devfnp = slot << 3; + *devfnp = PCI_DEVFN(slot, 0); return pci_find_bus(pci_find_root_bus(dom), bus); } @@ -799,10 +808,11 @@ /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn, - PCIConfigReadFunc *config_read, - PCIConfigWriteFunc *config_write, - bool is_bridge) + const PCIDeviceInfo *info) { + PCIConfigReadFunc *config_read = info->config_read; + PCIConfigWriteFunc *config_write = info->config_write; + if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); devfn += PCI_FUNC_MAX) { @@ -825,13 +835,29 @@ memset(pci_dev->config_map, 0xff, PCI_CONFIG_HEADER_SIZE); - if (!is_bridge) { - pci_set_default_subsystem_id(pci_dev); + pci_config_set_vendor_id(pci_dev->config, info->vendor_id); + pci_config_set_device_id(pci_dev->config, info->device_id); + pci_config_set_revision(pci_dev->config, info->revision); + pci_config_set_class(pci_dev->config, info->class_id); + + if (!info->is_bridge) { + if (info->subsystem_vendor_id || info->subsystem_id) { + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + info->subsystem_vendor_id); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + info->subsystem_id); + } else { + pci_set_default_subsystem_id(pci_dev); + } + } else { + /* subsystem_vendor_id/subsystem_id are only for header type 0 */ + assert(!info->subsystem_vendor_id); + assert(!info->subsystem_id); } pci_init_cmask(pci_dev); pci_init_wmask(pci_dev); pci_init_w1cmask(pci_dev); - if (is_bridge) { + if (info->is_bridge) { pci_init_wmask_bridge(pci_dev); } if (pci_init_multifunction(bus, pci_dev)) { @@ -858,17 +884,20 @@ pci_config_free(pci_dev); } +/* TODO: obsolete. eliminate this once all pci devices are qdevifed. */ PCIDevice *pci_register_device(PCIBus *bus, const char *name, int instance_size, int devfn, PCIConfigReadFunc *config_read, PCIConfigWriteFunc *config_write) { PCIDevice *pci_dev; + PCIDeviceInfo info = { + .config_read = config_read, + .config_write = config_write, + }; pci_dev = qemu_mallocz(instance_size); - pci_dev = do_pci_register_device(pci_dev, bus, name, devfn, - config_read, config_write, - PCI_HEADER_TYPE_NORMAL); + pci_dev = do_pci_register_device(pci_dev, bus, name, devfn, &info); if (pci_dev == NULL) { hw_error("PCI: can't register device\n"); } @@ -941,11 +970,12 @@ r->filtered_size = size; r->type = type; r->map_func = map_func; + r->ram_addr = IO_MEM_UNASSIGNED; wmask = ~(size - 1); addr = pci_bar(pci_dev, region_num); if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writeable */ + /* ROM enable bit is writable */ wmask |= PCI_ROM_ADDRESS_ENABLE; } pci_set_long(pci_dev->config + addr, type); @@ -959,6 +989,22 @@ } } +static void pci_simple_bar_mapfunc(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + cpu_register_physical_memory(addr, size, + pci_dev->io_regions[region_num].ram_addr); +} + +void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, + pcibus_t size, uint8_t attr, ram_addr_t ram_addr) +{ + pci_register_bar(pci_dev, region_num, size, + PCI_BASE_ADDRESS_SPACE_MEMORY | attr, + pci_simple_bar_mapfunc); + pci_dev->io_regions[region_num].ram_addr = ram_addr; +} + static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size, uint8_t type) { @@ -1244,6 +1290,7 @@ { 0x0400, "Video controller", "video"}, { 0x0401, "Audio controller", "sound"}, { 0x0402, "Phone"}, + { 0x0403, "Audio controller", "sound"}, { 0x0480, "Multimedia controller"}, { 0x0500, "RAM controller", "memory"}, { 0x0501, "Flash controller", "flash"}, @@ -1702,14 +1749,14 @@ return NULL; } -PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function) +PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) { bus = pci_find_bus(bus, bus_num); if (!bus) return NULL; - return bus->devices[PCI_DEVFN(slot, function)]; + return bus->devices[devfn]; } static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) @@ -1717,7 +1764,7 @@ PCIDevice *pci_dev = (PCIDevice *)qdev; PCIDeviceInfo *info = container_of(base, PCIDeviceInfo, qdev); PCIBus *bus; - int devfn, rc; + int rc; bool is_default_rom; /* initialize cap_present for pci_is_express() and pci_config_size() */ @@ -1726,10 +1773,8 @@ } bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev)); - devfn = pci_dev->devfn; - pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn, - info->config_read, info->config_write, - info->is_bridge); + pci_dev = do_pci_register_device(pci_dev, bus, base->name, + pci_dev->devfn, info); if (pci_dev == NULL) return -1; if (qdev->hotplugged && info->no_hotplug) { @@ -1737,10 +1782,12 @@ do_pci_unregister_device(pci_dev); return -1; } - rc = info->init(pci_dev); - if (rc != 0) { - do_pci_unregister_device(pci_dev); - return rc; + if (info->init) { + rc = info->init(pci_dev); + if (rc != 0) { + do_pci_unregister_device(pci_dev); + return rc; + } } /* rom loading */ @@ -1807,6 +1854,21 @@ return DO_UPCAST(PCIDevice, qdev, dev); } +PCIDevice *pci_try_create_multifunction(PCIBus *bus, int devfn, + bool multifunction, + const char *name) +{ + DeviceState *dev; + + dev = qdev_try_create(&bus->qbus, name); + if (!dev) { + return NULL; + } + qdev_prop_set_uint32(dev, "addr", devfn); + qdev_prop_set_bit(dev, "multifunction", multifunction); + return DO_UPCAST(PCIDevice, qdev, dev); +} + PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, bool multifunction, const char *name) @@ -1826,6 +1888,11 @@ return pci_create_simple_multifunction(bus, devfn, false, name); } +PCIDevice *pci_try_create(PCIBus *bus, int devfn, const char *name) +{ + return pci_try_create_multifunction(bus, devfn, false, name); +} + static int pci_find_space(PCIDevice *pdev, uint8_t size) { int config_size = pci_config_size(pdev); @@ -1954,6 +2021,7 @@ if (size < 0) { error_report("%s: failed to find romfile \"%s\"", __FUNCTION__, pdev->romfile); + qemu_free(path); return -1; } if (size & (size - 1)) { @@ -1975,6 +2043,8 @@ pci_patch_ids(pdev, ptr, size); } + qemu_put_ram_ptr(ptr); + pci_register_bar(pdev, PCI_ROM_SLOT, size, 0, pci_map_option_rom); @@ -2044,7 +2114,7 @@ if (!offset) return; pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; - /* Make capability writeable again */ + /* Make capability writable again */ memset(pdev->wmask + offset, 0xff, size); memset(pdev->w1cmask + offset, 0, size); /* Clear cmask as device-specific registers can't be checked */ diff -Nru qemu-kvm-0.14.1+noroms/hw/pcie_aer.c qemu-kvm-0.15.0+noroms/hw/pcie_aer.c --- qemu-kvm-0.14.1+noroms/hw/pcie_aer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pcie_aer.c 2011-08-09 07:40:29.000000000 -0500 @@ -38,6 +38,9 @@ #define PCIE_DEV_PRINTF(dev, fmt, ...) \ PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) +#define PCI_ERR_SRC_COR_OFFS 0 +#define PCI_ERR_SRC_UNCOR_OFFS 2 + /* 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) { @@ -320,7 +323,8 @@ 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); + pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_COR_OFFS, + msg->source_id); } root_status |= PCI_ERR_ROOT_COR_RCV; break; @@ -341,7 +345,8 @@ 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); + pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + + PCI_ERR_SRC_UNCOR_OFFS, msg->source_id); } root_status |= PCI_ERR_ROOT_UNCOR_RCV; } @@ -612,7 +617,7 @@ /* * 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 + * It is also caller's responsibility to determine which function should * report the rerror. * * 6.2.4 Error Logging @@ -785,16 +790,6 @@ } }; -#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, @@ -803,7 +798,7 @@ .fields = (VMStateField[]) { VMSTATE_UINT16(log_num, PCIEAERLog), VMSTATE_UINT16(log_max, PCIEAERLog), - VMSTATE_PCIE_AER_ERRS(log, PCIEAERLog, log_num, + VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num, vmstate_pcie_aer_err, PCIEAERErr), VMSTATE_END_OF_LIST() } diff -Nru qemu-kvm-0.14.1+noroms/hw/pcie.c qemu-kvm-0.15.0+noroms/hw/pcie.c --- qemu-kvm-0.14.1+noroms/hw/pcie.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pcie.c 2011-08-09 07:40:29.000000000 -0500 @@ -18,8 +18,7 @@ * with this program; if not, see . */ -#include "sysemu.h" -#include "range.h" +#include "qemu-common.h" #include "pci_bridge.h" #include "pcie.h" #include "msix.h" @@ -177,7 +176,7 @@ } /* - * A PCI Express Hot-Plug Event has occured, so update slot status register + * A PCI Express Hot-Plug Event has occurred, so update slot status register * and notify OS of the event if necessary. * * 6.7.3 PCI Express Hot-Plug Events diff -Nru qemu-kvm-0.14.1+noroms/hw/pcie.h qemu-kvm-0.15.0+noroms/hw/pcie.h --- qemu-kvm-0.14.1+noroms/hw/pcie.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pcie.h 2011-08-09 07:40:29.000000000 -0500 @@ -40,7 +40,7 @@ * * 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. + * show status, not to report event occurrence. * So such bits must be masked out when checking the software * notification condition. */ diff -Nru qemu-kvm-0.14.1+noroms/hw/pcie_host.c qemu-kvm-0.15.0+noroms/hw/pcie_host.c --- qemu-kvm-0.14.1+noroms/hw/pcie_host.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pcie_host.c 2011-08-09 07:40:29.000000000 -0500 @@ -49,8 +49,7 @@ uint32_t mmcfg_addr) { return pci_find_device(s, PCIE_MMCFG_BUS(mmcfg_addr), - PCI_SLOT(PCIE_MMCFG_DEVFN(mmcfg_addr)), - PCI_FUNC(PCIE_MMCFG_DEVFN(mmcfg_addr))); + PCIE_MMCFG_DEVFN(mmcfg_addr)); } static void pcie_mmcfg_data_write(PCIBus *s, diff -Nru qemu-kvm-0.14.1+noroms/hw/pci.h qemu-kvm-0.15.0+noroms/hw/pci.h --- qemu-kvm-0.14.1+noroms/hw/pci.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pci.h 2011-08-09 07:40:29.000000000 -0500 @@ -5,8 +5,7 @@ #include "qobject.h" #include "qdev.h" - -struct kvm_irq_routing_entry; +#include "kvm.h" /* PCI includes legacy ISA access. */ #include "isa.h" @@ -18,6 +17,7 @@ #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_SLOT_MAX 32 #define PCI_FUNC_MAX 8 /* Class, Vendor and Device IDs from Linux's pci_ids.h */ @@ -93,6 +93,7 @@ pcibus_t filtered_size; uint8_t type; PCIMapIORegionFunc *map_func; + ram_addr_t ram_addr; } PCIIORegion; #define PCI_ROM_SLOT 6 @@ -130,19 +131,12 @@ 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; /* PCI config space */ uint8_t *config; - /* Used to enable config checks on load. Note that writeable bits are + /* Used to enable config checks on load. Note that writable bits are * never checked even if set in cmask. */ uint8_t *cmask; @@ -202,6 +196,10 @@ ram_addr_t rom_offset; uint32_t rom_bar; + /* MSI entries */ + int msi_entries_nr; + struct KVMMsiMessage *msi_irq_entries; + /* How much space does an MSIX table need. */ /* The spec requires giving the table structure * a 4K aligned region all by itself. Align it to @@ -209,7 +207,7 @@ * on the rest of the region. */ target_phys_addr_t msix_page_size; - struct kvm_msix_message *msix_irq_entries; + KVMMsiMessage *msix_irq_entries; msix_mask_notifier_func msix_mask_notifier; }; @@ -222,6 +220,8 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pcibus_t size, uint8_t type, PCIMapIORegionFunc *map_func); +void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, + pcibus_t size, uint8_t attr, ram_addr_t ram_addr); void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr, pcibus_t size, int type); @@ -253,14 +253,15 @@ 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); + const char *name, uint8_t devfn_min); +PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t 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); +int pci_bus_get_irq_level(PCIBus *bus, int irq_num); 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 *irq_opaque, uint8_t devfn_min, int nirq); void pci_device_reset(PCIDevice *dev); void pci_bus_reset(PCIBus *bus); @@ -275,7 +276,7 @@ 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); +PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); int pci_qdev_find_device(const char *id, PCIDevice **pdev); PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr); @@ -455,6 +456,13 @@ PCIConfigReadFunc *config_read; PCIConfigWriteFunc *config_write; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + uint16_t class_id; + uint16_t subsystem_vendor_id; /* only for header type = 0 */ + uint16_t subsystem_id; /* only for header type = 0 */ + /* * pci-to-pci bridge or normal device. * This doesn't mean pci host switch. @@ -480,8 +488,12 @@ PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, bool multifunction, const char *name); +PCIDevice *pci_try_create_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); +PCIDevice *pci_try_create(PCIBus *bus, int devfn, const char *name); static inline int pci_is_express(const PCIDevice *d) { diff -Nru qemu-kvm-0.14.1+noroms/hw/pci_host.c qemu-kvm-0.15.0+noroms/hw/pci_host.c --- qemu-kvm-0.14.1+noroms/hw/pci_host.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pci_host.c 2011-08-09 07:40:29.000000000 -0500 @@ -44,7 +44,7 @@ uint8_t bus_num = addr >> 16; uint8_t devfn = addr >> 8; - return pci_find_device(bus, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn)); + return pci_find_device(bus, bus_num, devfn); } void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) diff -Nru qemu-kvm-0.14.1+noroms/hw/pci-hotplug.c qemu-kvm-0.15.0+noroms/hw/pci-hotplug.c --- qemu-kvm-0.14.1+noroms/hw/pci-hotplug.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pci-hotplug.c 2011-08-09 07:40:29.000000000 -0500 @@ -31,7 +31,6 @@ #include "scsi.h" #include "virtio-blk.h" #include "qemu-config.h" -#include "device-assignment.h" #include "blockdev.h" #if defined(TARGET_I386) @@ -128,7 +127,8 @@ if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { goto err; } - dev = pci_find_device(pci_find_root_bus(dom), pci_bus, slot, 0); + dev = pci_find_device(pci_find_root_bus(dom), pci_bus, + PCI_DEVFN(slot, 0)); if (!dev) { monitor_printf(mon, "no pci device with address %s\n", pci_addr); goto err; @@ -278,7 +278,7 @@ return -1; } - d = pci_find_device(pci_find_root_bus(dom), bus, slot, 0); + d = pci_find_device(pci_find_root_bus(dom), bus, PCI_DEVFN(slot, 0)); if (!d) { monitor_printf(mon, "slot %d empty\n", slot); return -1; diff -Nru qemu-kvm-0.14.1+noroms/hw/pci_ids.h qemu-kvm-0.15.0+noroms/hw/pci_ids.h --- qemu-kvm-0.14.1+noroms/hw/pci_ids.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pci_ids.h 2011-08-09 07:40:29.000000000 -0500 @@ -100,6 +100,7 @@ #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801D 0x24CD #define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 @@ -108,3 +109,14 @@ #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 +#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a +#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c + +#define PCI_VENDOR_ID_XEN 0x5853 +#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 diff -Nru qemu-kvm-0.14.1+noroms/hw/pci_internals.h qemu-kvm-0.15.0+noroms/hw/pci_internals.h --- qemu-kvm-0.14.1+noroms/hw/pci_internals.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pci_internals.h 2011-08-09 07:40:29.000000000 -0500 @@ -16,13 +16,13 @@ struct PCIBus { BusState qbus; - int devfn_min; + uint8_t 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 *devices[PCI_SLOT_MAX * PCI_FUNC_MAX]; PCIDevice *parent_dev; target_phys_addr_t mem_base; diff -Nru qemu-kvm-0.14.1+noroms/hw/pci_regs.h qemu-kvm-0.15.0+noroms/hw/pci_regs.h --- qemu-kvm-0.14.1+noroms/hw/pci_regs.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pci_regs.h 2011-08-09 07:40:29.000000000 -0500 @@ -230,7 +230,7 @@ #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_AUX_POWER 0x01C0 /* Auxiliary 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 */ @@ -307,12 +307,22 @@ #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) */ +/* MSI-X registers */ #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) +#define PCI_MSIX_TABLE 4 +#define PCI_MSIX_PBA 8 +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) + +/* MSI-X entry's format */ +#define PCI_MSIX_ENTRY_SIZE 16 +#define PCI_MSIX_ENTRY_LOWER_ADDR 0 +#define PCI_MSIX_ENTRY_UPPER_ADDR 4 +#define PCI_MSIX_ENTRY_DATA 8 +#define PCI_MSIX_ENTRY_VECTOR_CTRL 12 +#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1 /* CompactPCI Hotswap Register */ @@ -372,6 +382,11 @@ #define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ #define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ +/* PCI Bridge Subsystem ID registers */ + +#define PCI_SSVID_VENDOR_ID 4 /* PCI-Bridge subsystem vendor id register */ +#define PCI_SSVID_DEVICE_ID 6 /* PCI-Bridge subsystem device id register */ + /* PCI Express capability registers */ #define PCI_EXP_FLAGS 2 /* Capabilities register */ @@ -427,7 +442,7 @@ #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_SDERC 0x00080000 /* Surprise 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 */ @@ -444,7 +459,10 @@ #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_CLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ +#define PCI_EXP_LNKSTA_CLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */ #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 */ @@ -493,10 +511,22 @@ #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_RTSTA_PME 0x10000 /* PME status */ +#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ +#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ +#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ +#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ +#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ +#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ +#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ +#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ +#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ @@ -509,9 +539,12 @@ #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_VNDR 11 +#define PCI_EXT_CAP_ID_ACS 13 #define PCI_EXT_CAP_ID_ARI 14 #define PCI_EXT_CAP_ID_ATS 15 #define PCI_EXT_CAP_ID_SRIOV 16 +#define PCI_EXT_CAP_ID_LTR 24 /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ @@ -563,8 +596,7 @@ #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 +#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ /* Virtual Channel */ #define PCI_VC_PORT_REG1 4 @@ -669,4 +701,22 @@ #define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ #define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ +#define PCI_LTR_MAX_SNOOP_LAT 0x4 +#define PCI_LTR_MAX_NOSNOOP_LAT 0x6 +#define PCI_LTR_VALUE_MASK 0x000003ff +#define PCI_LTR_SCALE_MASK 0x00001c00 +#define PCI_LTR_SCALE_SHIFT 10 + +/* Access Control Service */ +#define PCI_ACS_CAP 0x04 /* ACS Capability Register */ +#define PCI_ACS_SV 0x01 /* Source Validation */ +#define PCI_ACS_TB 0x02 /* Translation Blocking */ +#define PCI_ACS_RR 0x04 /* P2P Request Redirect */ +#define PCI_ACS_CR 0x08 /* P2P Completion Redirect */ +#define PCI_ACS_UF 0x10 /* Upstream Forwarding */ +#define PCI_ACS_EC 0x20 /* P2P Egress Control */ +#define PCI_ACS_DT 0x40 /* Direct Translated P2P */ +#define PCI_ACS_CTRL 0x06 /* ACS Control Register */ +#define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ + #endif /* LINUX_PCI_REGS_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/pcnet.c qemu-kvm-0.15.0+noroms/hw/pcnet.c --- qemu-kvm-0.14.1+noroms/hw/pcnet.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pcnet.c 2011-08-09 07:40:29.000000000 -0500 @@ -113,23 +113,23 @@ #define CSR_XMTRL(S) ((S)->csr[78]) #define CSR_MISSC(S) ((S)->csr[112]) -#define CSR_IADR(S) ((S)->csr[ 1] | ((S)->csr[ 2] << 16)) -#define CSR_CRBA(S) ((S)->csr[18] | ((S)->csr[19] << 16)) -#define CSR_CXBA(S) ((S)->csr[20] | ((S)->csr[21] << 16)) -#define CSR_NRBA(S) ((S)->csr[22] | ((S)->csr[23] << 16)) -#define CSR_BADR(S) ((S)->csr[24] | ((S)->csr[25] << 16)) -#define CSR_NRDA(S) ((S)->csr[26] | ((S)->csr[27] << 16)) -#define CSR_CRDA(S) ((S)->csr[28] | ((S)->csr[29] << 16)) -#define CSR_BADX(S) ((S)->csr[30] | ((S)->csr[31] << 16)) -#define CSR_NXDA(S) ((S)->csr[32] | ((S)->csr[33] << 16)) -#define CSR_CXDA(S) ((S)->csr[34] | ((S)->csr[35] << 16)) -#define CSR_NNRD(S) ((S)->csr[36] | ((S)->csr[37] << 16)) -#define CSR_NNXD(S) ((S)->csr[38] | ((S)->csr[39] << 16)) -#define CSR_PXDA(S) ((S)->csr[60] | ((S)->csr[61] << 16)) -#define CSR_NXBA(S) ((S)->csr[64] | ((S)->csr[65] << 16)) +#define CSR_IADR(S) ((S)->csr[ 1] | ((uint32_t)(S)->csr[ 2] << 16)) +#define CSR_CRBA(S) ((S)->csr[18] | ((uint32_t)(S)->csr[19] << 16)) +#define CSR_CXBA(S) ((S)->csr[20] | ((uint32_t)(S)->csr[21] << 16)) +#define CSR_NRBA(S) ((S)->csr[22] | ((uint32_t)(S)->csr[23] << 16)) +#define CSR_BADR(S) ((S)->csr[24] | ((uint32_t)(S)->csr[25] << 16)) +#define CSR_NRDA(S) ((S)->csr[26] | ((uint32_t)(S)->csr[27] << 16)) +#define CSR_CRDA(S) ((S)->csr[28] | ((uint32_t)(S)->csr[29] << 16)) +#define CSR_BADX(S) ((S)->csr[30] | ((uint32_t)(S)->csr[31] << 16)) +#define CSR_NXDA(S) ((S)->csr[32] | ((uint32_t)(S)->csr[33] << 16)) +#define CSR_CXDA(S) ((S)->csr[34] | ((uint32_t)(S)->csr[35] << 16)) +#define CSR_NNRD(S) ((S)->csr[36] | ((uint32_t)(S)->csr[37] << 16)) +#define CSR_NNXD(S) ((S)->csr[38] | ((uint32_t)(S)->csr[39] << 16)) +#define CSR_PXDA(S) ((S)->csr[60] | ((uint32_t)(S)->csr[61] << 16)) +#define CSR_NXBA(S) ((S)->csr[64] | ((uint32_t)(S)->csr[65] << 16)) #define PHYSADDR(S,A) \ - (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(s)->csr[2])<<16)) + (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(S)->csr[2])<<16)) struct pcnet_initblk16 { uint16_t mode; @@ -1336,7 +1336,7 @@ pcnet_update_irq(s); if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) { - uint64_t now = qemu_get_clock(vm_clock) * 33; + uint64_t now = qemu_get_clock_ns(vm_clock) * 33; if (!s->timer || !now) s->timer = now; else { @@ -1348,7 +1348,7 @@ CSR_POLL(s) = t; } qemu_mod_timer(s->poll_timer, - pcnet_get_next_poll_time(s,qemu_get_clock(vm_clock))); + pcnet_get_next_poll_time(s,qemu_get_clock_ns(vm_clock))); } } @@ -1557,19 +1557,6 @@ void pcnet_h_reset(void *opaque) { PCNetState *s = opaque; - int i; - uint16_t checksum; - - /* Initialize the PROM */ - - memcpy(s->prom, s->conf.macaddr.a, 6); - s->prom[12] = s->prom[13] = 0x00; - s->prom[14] = s->prom[15] = 0x57; - - for (i = 0,checksum = 0; i < 16; i++) - checksum += s->prom[i]; - *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum); - s->bcr[BCR_MSRDA] = 0x0005; s->bcr[BCR_MSWRA] = 0x0005; @@ -1736,7 +1723,10 @@ int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) { - s->poll_timer = qemu_new_timer(vm_clock, pcnet_poll_timer, s); + int i; + uint16_t checksum; + + s->poll_timer = qemu_new_timer_ns(vm_clock, pcnet_poll_timer, s); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(info, &s->conf, dev->info->name, dev->id, s); @@ -1744,5 +1734,32 @@ add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0"); + /* Initialize the PROM */ + + /* + Datasheet: http://pdfdata.datasheetsite.com/web/24528/AM79C970A.pdf + page 95 + */ + memcpy(s->prom, s->conf.macaddr.a, 6); + /* Reserved Location: must be 00h */ + s->prom[6] = s->prom[7] = 0x00; + /* Reserved Location: must be 00h */ + s->prom[8] = 0x00; + /* Hardware ID: must be 11h if compatibility to AMD drivers is desired */ + s->prom[9] = 0x11; + /* User programmable space, init with 0 */ + s->prom[10] = s->prom[11] = 0x00; + /* LSByte of two-byte checksum, which is the sum of bytes 00h-0Bh + and bytes 0Eh and 0Fh, must therefore be initialized with 0! */ + s->prom[12] = s->prom[13] = 0x00; + /* Must be ASCII W (57h) if compatibility to AMD + driver software is desired */ + s->prom[14] = s->prom[15] = 0x57; + + for (i = 0, checksum = 0; i < 16; i++) { + checksum += s->prom[i]; + } + *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum); + return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pcnet-pci.c qemu-kvm-0.15.0+noroms/hw/pcnet-pci.c --- qemu-kvm-0.14.1+noroms/hw/pcnet-pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pcnet-pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -214,19 +214,6 @@ &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) { @@ -278,12 +265,8 @@ 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); @@ -300,8 +283,7 @@ 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); + pci_register_bar_simple(pci_dev, 1, PCNET_PNPMMIO_SIZE, 0, s->mmio_index); s->irq = pci_dev->irq[0]; s->phys_mem_read = pci_physical_memory_read; @@ -310,7 +292,7 @@ if (!pci_dev->qdev.hotplugged) { static int loaded = 0; if (!loaded) { - rom_add_option("pxe-pcnet.bin", -1); + rom_add_option("pxe-pcnet.rom", -1); loaded = 1; } } @@ -332,6 +314,10 @@ .qdev.vmsd = &vmstate_pci_pcnet, .init = pci_pcnet_init, .exit = pci_pcnet_uninit, + .vendor_id = PCI_VENDOR_ID_AMD, + .device_id = PCI_DEVICE_ID_AMD_LANCE, + .revision = 0x10, + .class_id = PCI_CLASS_NETWORK_ETHERNET, .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/pc_piix.c qemu-kvm-0.15.0+noroms/hw/pc_piix.c --- qemu-kvm-0.14.1+noroms/hw/pc_piix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pc_piix.c 2011-08-09 07:40:29.000000000 -0500 @@ -32,10 +32,16 @@ #include "boards.h" #include "ide.h" #include "kvm.h" +#include "kvmclock.h" #include "sysemu.h" #include "sysbus.h" #include "arch_init.h" #include "blockdev.h" +#include "smbus.h" +#include "xen.h" +#ifdef CONFIG_XEN +# include +#endif qemu_irq *ioapic_irq_hack; @@ -70,7 +76,8 @@ const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model, - int pci_enabled) + int pci_enabled, + int kvmclock_enabled) { int i; ram_addr_t below_4g_mem_size, above_4g_mem_size; @@ -84,7 +91,6 @@ 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; @@ -92,31 +98,43 @@ pc_cpus_init(cpu_model); - vmport_init(); + if (kvmclock_enabled) { + kvmclock_create(); + } + + if (ram_size >= 0xe0000000 ) { + above_4g_mem_size = ram_size - 0xe0000000; + below_4g_mem_size = 0xe0000000; + } else { + above_4g_mem_size = 0; + below_4g_mem_size = ram_size; + } /* 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); + if (!xen_enabled()) { + pc_memory_init(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); + if (!xen_enabled()) { + cpu_irq = pc_allocate_cpu_irq(); + if (!(kvm_enabled() && kvm_irqchip_in_kernel())) { + i8259 = i8259_init(cpu_irq[0]); + } else { + i8259 = kvm_i8259_init(cpu_irq[0]); } + } else { + i8259 = xen_interrupt_controller_init(); + } + isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); + isa_irq_state->i8259 = i8259; + if (pci_enabled) { + ioapic_init(isa_irq_state); + } + if (!(kvm_enabled() && kvm_irqchip_in_kernel())) { isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); + } else { + isa_irq = i8259; } if (pci_enabled) { @@ -132,8 +150,12 @@ pc_vga_init(pci_enabled? pci_bus: NULL); + if (xen_enabled()) { + pci_create_simple(pci_bus, -1, "xen-platform"); + } + /* init basic PC hardware */ - pc_basic_device_init(isa_irq, &floppy_controller, &rtc_state); + pc_basic_device_init(isa_irq, &rtc_state, xen_enabled()); for(i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -144,15 +166,7 @@ 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); - } - + ide_drive_get(hd, MAX_IDE_BUS); if (pci_enabled) { PCIDevice *dev; dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); @@ -170,29 +184,26 @@ 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); + idebus[0], idebus[1], 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); + if (!xen_enabled()) { + cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1); + } else { + cmos_s3 = qemu_allocate_irqs(xen_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); - } + smbus_eeprom_init(smbus, 8, NULL, 0); } if (i440fx_state) { @@ -213,7 +224,19 @@ { pc_init1(ram_size, boot_device, kernel_filename, kernel_cmdline, - initrd_filename, cpu_model, 1); + initrd_filename, cpu_model, 1, 1); +} + +static void pc_init_pci_no_kvmclock(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, 0); } static void pc_init_isa(ram_addr_t ram_size, @@ -227,9 +250,27 @@ cpu_model = "486"; pc_init1(ram_size, boot_device, kernel_filename, kernel_cmdline, - initrd_filename, cpu_model, 0); + initrd_filename, cpu_model, 0, 1); } +#ifdef CONFIG_XEN +static void pc_xen_hvm_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) +{ + if (xen_hvm_init() != 0) { + hw_error("xen hardware virtual machine initialisation failed"); + } + pc_init_pci_no_kvmclock(ram_size, boot_device, + kernel_filename, kernel_cmdline, + initrd_filename, cpu_model); + xen_vcpu_init(); +} +#endif + static QEMUMachine pc_machine = { .name = "pc-0.14", .alias = "pc", @@ -242,7 +283,7 @@ static QEMUMachine pc_machine_v0_13 = { .name = "pc-0.13", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_no_kvmclock, .max_cpus = 255, .compat_props = (GlobalProperty[]) { { @@ -261,6 +302,18 @@ .driver = "PCI", .property = "command_serr_enable", .value = "off", + },{ + .driver = "virtio-blk-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-serial-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-net-pci", + .property = "event_idx", + .value = "off", }, { /* end of list */ } }, @@ -269,7 +322,7 @@ static QEMUMachine pc_machine_v0_12 = { .name = "pc-0.12", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_no_kvmclock, .max_cpus = 255, .compat_props = (GlobalProperty[]) { { @@ -292,6 +345,18 @@ .driver = "PCI", .property = "command_serr_enable", .value = "off", + },{ + .driver = "virtio-blk-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-serial-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-net-pci", + .property = "event_idx", + .value = "off", }, { /* end of list */ } } @@ -300,7 +365,7 @@ static QEMUMachine pc_machine_v0_11 = { .name = "pc-0.11", .desc = "Standard PC, qemu 0.11", - .init = pc_init_pci, + .init = pc_init_pci_no_kvmclock, .max_cpus = 255, .compat_props = (GlobalProperty[]) { { @@ -331,6 +396,18 @@ .driver = "PCI", .property = "command_serr_enable", .value = "off", + },{ + .driver = "virtio-blk-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-serial-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-net-pci", + .property = "event_idx", + .value = "off", }, { /* end of list */ } } @@ -339,7 +416,7 @@ static QEMUMachine pc_machine_v0_10 = { .name = "pc-0.10", .desc = "Standard PC, qemu 0.10", - .init = pc_init_pci, + .init = pc_init_pci_no_kvmclock, .max_cpus = 255, .compat_props = (GlobalProperty[]) { { @@ -382,6 +459,18 @@ .driver = "PCI", .property = "command_serr_enable", .value = "off", + },{ + .driver = "virtio-blk-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-serial-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-net-pci", + .property = "event_idx", + .value = "off", }, { /* end of list */ } }, @@ -394,6 +483,16 @@ .max_cpus = 1, }; +#ifdef CONFIG_XEN +static QEMUMachine xenfv_machine = { + .name = "xenfv", + .desc = "Xen Fully-virtualized PC", + .init = pc_xen_hvm_init, + .max_cpus = HVM_MAX_VCPUS, + .default_machine_opts = "accel=xen", +}; +#endif + static void pc_machine_init(void) { qemu_register_machine(&pc_machine); @@ -402,6 +501,9 @@ qemu_register_machine(&pc_machine_v0_11); qemu_register_machine(&pc_machine_v0_10); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenfv_machine); +#endif } machine_init(pc_machine_init); diff -Nru qemu-kvm-0.14.1+noroms/hw/pcspk.c qemu-kvm-0.15.0+noroms/hw/pcspk.c --- qemu-kvm-0.14.1+noroms/hw/pcspk.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pcspk.c 2011-08-09 07:40:29.000000000 -0500 @@ -39,7 +39,7 @@ uint8_t sample_buf[PCSPK_BUF_LEN]; QEMUSoundCard card; SWVoiceOut *voice; - PITState *pit; + ISADevice *pit; unsigned int pit_count; unsigned int samples; unsigned int play_pos; @@ -51,13 +51,14 @@ static PCSpkState pcspk_state; #ifdef CONFIG_KVM_PIT -static void kvm_get_pit_ch2(PITState *pit, +static void kvm_get_pit_ch2(ISADevice *dev, struct kvm_pit_state *inkernel_state) { + struct PITState *pit = DO_UPCAST(struct PITState, dev, dev); struct kvm_pit_state pit_state; if (kvm_enabled() && kvm_pit_in_kernel()) { - kvm_get_pit(kvm_context, &pit_state); + kvm_get_pit(kvm_state, &pit_state); pit->channels[2].mode = pit_state.channels[2].mode; pit->channels[2].count = pit_state.channels[2].count; pit->channels[2].count_load_time = pit_state.channels[2].count_load_time; @@ -68,22 +69,24 @@ } } -static void kvm_set_pit_ch2(PITState *pit, +static void kvm_set_pit_ch2(ISADevice *dev, struct kvm_pit_state *inkernel_state) { + struct PITState *pit = DO_UPCAST(struct PITState, dev, dev); + 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 = pit->channels[2].count_load_time; inkernel_state->channels[2].gate = pit->channels[2].gate; - kvm_set_pit(kvm_context, inkernel_state); + kvm_set_pit(kvm_state, inkernel_state); } } #else -static inline void kvm_get_pit_ch2(PITState *pit, +static inline void kvm_get_pit_ch2(ISADevice *dev, struct kvm_pit_state *inkernel_state) { } -static inline void kvm_set_pit_ch2(PITState *pit, +static inline void kvm_set_pit_ch2(ISADevice *dev, struct kvm_pit_state *inkernel_state) { } #endif @@ -161,7 +164,7 @@ kvm_get_pit_ch2(s->pit, NULL); s->dummy_refresh_clock ^= (1 << 4); - out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5; + out = pit_get_out(s->pit, 2, qemu_get_clock_ns(vm_clock)) << 5; return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out; } @@ -185,7 +188,7 @@ kvm_set_pit_ch2(s->pit, &inkernel_state); } -void pcspk_init(PITState *pit) +void pcspk_init(ISADevice *pit) { PCSpkState *s = &pcspk_state; diff -Nru qemu-kvm-0.14.1+noroms/hw/petalogix_ml605_mmu.c qemu-kvm-0.15.0+noroms/hw/petalogix_ml605_mmu.c --- qemu-kvm-0.14.1+noroms/hw/petalogix_ml605_mmu.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/petalogix_ml605_mmu.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,266 @@ +/* + * Model of Petalogix linux reference design targeting Xilinx Spartan ml605 + * board. + * + * Copyright (c) 2011 Michal Simek + * Copyright (c) 2011 PetaLogix + * Copyright (c) 2009 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 "net.h" +#include "flash.h" +#include "sysemu.h" +#include "devices.h" +#include "boards.h" +#include "device_tree.h" +#include "xilinx.h" +#include "loader.h" +#include "elf.h" +#include "blockdev.h" +#include "pc.h" + +#include "xilinx_axidma.h" + +#define LMB_BRAM_SIZE (128 * 1024) +#define FLASH_SIZE (32 * 1024 * 1024) + +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->regs[5] = boot_info.cmdline; + env->regs[7] = boot_info.fdt; + env->sregs[SR_PC] = boot_info.bootstrap_pc; + env->pvr.regs[10] = 0x0e000000; /* virtex 6 */ + /* setup pvr to match kernel setting */ + env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK; + env->pvr.regs[0] |= PVR0_USE_FPU_MASK | PVR0_ENDI; + env->pvr.regs[0] = (env->pvr.regs[0] & ~PVR0_VERSION_MASK) | (0x14 << 8); + env->pvr.regs[2] ^= PVR2_USE_FPU2_MASK; + env->pvr.regs[4] = 0xc56b8000; + env->pvr.regs[5] = 0xc56be000; +} + +#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" +static int petalogix_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 "mb.dtb" override. */ + fdt = load_device_tree("mb.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("mb.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 uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return addr - 0x30000000LL; +} + +#define MEMORY_BASEADDR 0x50000000 +#define FLASH_BASEADDR 0x86000000 +#define INTC_BASEADDR 0x81800000 +#define TIMER_BASEADDR 0x83c00000 +#define UART16550_BASEADDR 0x83e00000 +#define AXIENET_BASEADDR 0x82780000 +#define AXIDMA_BASEADDR 0x84600000 + +static void +petalogix_ml605_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; + int kernel_size; + DriveInfo *dinfo; + int i; + target_phys_addr_t ddr_base = MEMORY_BASEADDR; + ram_addr_t phys_lmb_bram; + ram_addr_t phys_ram; + ram_addr_t phys_flash; + qemu_irq irq[32], *cpu_irq; + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "microblaze"; + } + env = cpu_init(cpu_model); + + qemu_register_reset(main_cpu_reset, env); + + /* Attach emulated BRAM through the LMB. */ + phys_lmb_bram = qemu_ram_alloc(NULL, "petalogix_ml605.lmb_bram", + LMB_BRAM_SIZE); + cpu_register_physical_memory(0x00000000, LMB_BRAM_SIZE, + phys_lmb_bram | IO_MEM_RAM); + + phys_ram = qemu_ram_alloc(NULL, "petalogix_ml605.ram", ram_size); + cpu_register_physical_memory(ddr_base, ram_size, phys_ram | IO_MEM_RAM); + + phys_flash = qemu_ram_alloc(NULL, "petalogix_ml605.flash", FLASH_SIZE); + dinfo = drive_get(IF_PFLASH, 0, 0); + /* 5th parameter 2 means bank-width + * 10th paremeter 0 means little-endian */ + pflash_cfi01_register(FLASH_BASEADDR, phys_flash, + dinfo ? dinfo->bdrv : NULL, (64 * 1024), + FLASH_SIZE >> 16, + 2, 0x89, 0x18, 0x0000, 0x0, 0); + + + cpu_irq = microblaze_pic_init_cpu(env); + dev = xilinx_intc_create(INTC_BASEADDR, cpu_irq[0], 4); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(dev, i); + } + + serial_mm_init(UART16550_BASEADDR + 0x1000, 2, irq[5], 115200, + serial_hds[0], 1, 0); + + /* 2 timers at irq 2 @ 100 Mhz. */ + xilinx_timer_create(TIMER_BASEADDR, irq[2], 2, 100 * 1000000); + + /* axi ethernet and dma initialization. TODO: Dynamically connect them. */ + { + static struct XilinxDMAConnection dmach; + + xilinx_axiethernet_create(&dmach, &nd_table[0], 0x82780000, + irq[3], 0x1000, 0x1000); + xilinx_axiethernetdma_create(&dmach, 0x84600000, + irq[1], irq[0], 100 * 1000000); + } + + if (kernel_filename) { + uint64_t entry, low, high; + uint32_t base32; + int big_endian = 0; + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#endif + + /* Boots a kernel elf binary. */ + kernel_size = load_elf(kernel_filename, NULL, NULL, + &entry, &low, &high, + big_endian, ELF_MACHINE, 0); + base32 = entry; + if (base32 == 0xc0000000) { + kernel_size = load_elf(kernel_filename, translate_kernel_address, + NULL, &entry, NULL, NULL, + big_endian, ELF_MACHINE, 0); + } + /* Always boot into physical ram. */ + 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) { + kernel_size = load_image_targphys(kernel_filename, ddr_base, + ram_size); + boot_info.bootstrap_pc = ddr_base; + high = (ddr_base + kernel_size + 3) & ~3; + } + + boot_info.cmdline = high + 4096; + if (kernel_cmdline && strlen(kernel_cmdline)) { + pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); + } + /* Provide a device-tree. */ + boot_info.fdt = boot_info.cmdline + 4096; + petalogix_load_device_tree(boot_info.fdt, ram_size, + 0, 0, + kernel_cmdline); + } +} + +static QEMUMachine petalogix_ml605_machine = { + .name = "petalogix-ml605", + .desc = "PetaLogix linux refdesign for xilinx ml605 little endian", + .init = petalogix_ml605_init, + .is_default = 0 +}; + +static void petalogix_ml605_machine_init(void) +{ + qemu_register_machine(&petalogix_ml605_machine); +} + +machine_init(petalogix_ml605_machine_init); diff -Nru qemu-kvm-0.14.1+noroms/hw/petalogix_s3adsp1800_mmu.c qemu-kvm-0.15.0+noroms/hw/petalogix_s3adsp1800_mmu.c --- qemu-kvm-0.14.1+noroms/hw/petalogix_s3adsp1800_mmu.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/petalogix_s3adsp1800_mmu.c 2011-08-09 07:40:29.000000000 -0500 @@ -167,16 +167,21 @@ if (kernel_filename) { uint64_t entry, low, high; uint32_t base32; + int big_endian = 0; + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#endif /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, &low, &high, - 1, ELF_MACHINE, 0); + big_endian, ELF_MACHINE, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, &entry, NULL, NULL, - 1, ELF_MACHINE, 0); + big_endian, ELF_MACHINE, 0); } /* Always boot into physical ram. */ boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); @@ -212,7 +217,7 @@ static QEMUMachine petalogix_s3adsp1800_machine = { .name = "petalogix-s3adsp1800", - .desc = "Petalogix linux refdesign for xilinx Spartan 3ADSP1800", + .desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800", .init = petalogix_s3adsp1800_init, .is_default = 1 }; diff -Nru qemu-kvm-0.14.1+noroms/hw/pflash_cfi01.c qemu-kvm-0.15.0+noroms/hw/pflash_cfi01.c --- qemu-kvm-0.14.1+noroms/hw/pflash_cfi01.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pflash_cfi01.c 2011-08-09 07:40:29.000000000 -0500 @@ -628,7 +628,7 @@ #else pfl->ro = 0; #endif - pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl); + pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl); pfl->base = base; pfl->sector_len = sector_len; pfl->total_len = total_len; diff -Nru qemu-kvm-0.14.1+noroms/hw/pflash_cfi02.c qemu-kvm-0.15.0+noroms/hw/pflash_cfi02.c --- qemu-kvm-0.14.1+noroms/hw/pflash_cfi02.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pflash_cfi02.c 2011-08-09 07:40:29.000000000 -0500 @@ -50,6 +50,8 @@ #define DPRINTF(fmt, ...) do { } while (0) #endif +#define PFLASH_LAZY_ROMD_THRESHOLD 42 + struct pflash_t { BlockDriverState *bs; target_phys_addr_t base; @@ -70,6 +72,7 @@ ram_addr_t off; int fl_mem; int rom_mode; + int read_counter; /* used for lazy switch-back to rom mode */ void *storage; }; @@ -112,10 +115,10 @@ DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); ret = -1; - if (pfl->rom_mode) { - /* Lazy reset of to ROMD mode */ - if (pfl->wcycle == 0) - pflash_register_memory(pfl, 1); + /* Lazy reset to ROMD mode after a certain amount of read accesses */ + if (!pfl->rom_mode && pfl->wcycle == 0 && + ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { + pflash_register_memory(pfl, 1); } offset &= pfl->chip_len - 1; boff = offset & 0xFF; @@ -185,7 +188,7 @@ default: goto flash_read; } - DPRINTF("%s: ID " TARGET_FMT_pld " %x\n", __func__, boff, ret); + DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret); break; case 0xA0: case 0x10: @@ -254,6 +257,7 @@ /* Set the device in I/O access mode if required */ if (pfl->rom_mode) pflash_register_memory(pfl, 0); + pfl->read_counter = 0; /* We're in read mode */ check_unlock0: if (boff == 0x55 && cmd == 0x98) { @@ -363,7 +367,7 @@ case 4: switch (pfl->cmd) { case 0xA0: - /* Ignore writes while flash data write is occuring */ + /* Ignore writes while flash data write is occurring */ /* As we suppose write is immediate, this should never happen */ return; case 0x80: @@ -390,7 +394,7 @@ pflash_update(pfl, 0, pfl->chip_len); /* Let's wait 5 seconds before chip erase is done */ qemu_mod_timer(pfl->timer, - qemu_get_clock(vm_clock) + (get_ticks_per_sec() * 5)); + qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 5)); break; case 0x30: /* Sector erase */ @@ -403,7 +407,7 @@ pfl->status = 0x00; /* Let's wait 1/2 second before sector erase is done */ qemu_mod_timer(pfl->timer, - qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 2)); + qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 2)); break; default: DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd); @@ -647,7 +651,7 @@ #else pfl->ro = 0; #endif - pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl); + pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl); pfl->sector_len = sector_len; pfl->width = width; pfl->wcycle = 0; diff -Nru qemu-kvm-0.14.1+noroms/hw/piix4.c qemu-kvm-0.15.0+noroms/hw/piix4.c --- qemu-kvm-0.14.1+noroms/hw/piix4.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/piix4.c 2011-08-09 07:40:29.000000000 -0500 @@ -30,10 +30,14 @@ PCIDevice *piix4_dev; +typedef struct PIIX4State { + PCIDevice dev; +} PIIX4State; + static void piix4_reset(void *opaque) { - PCIDevice *d = opaque; - uint8_t *pci_conf = d->config; + PIIX4State *d = opaque; + uint8_t *pci_conf = d->dev.config; pci_conf[0x04] = 0x07; // master, memory and I/O pci_conf[0x05] = 0x00; @@ -68,33 +72,23 @@ pci_conf[0xae] = 0x00; } -static void piix_save(QEMUFile* f, void *opaque) -{ - PCIDevice *d = opaque; - pci_device_save(d, f); -} - -static int piix_load(QEMUFile* f, void *opaque, int version_id) -{ - PCIDevice *d = opaque; - if (version_id != 2) - return -EINVAL; - return pci_device_load(d, f); -} +static const VMStateDescription vmstate_piix4 = { + .name = "PIIX4", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PIIX4State), + VMSTATE_END_OF_LIST() + } +}; -static int piix4_initfn(PCIDevice *d) +static int piix4_initfn(PCIDevice *dev) { - uint8_t *pci_conf; - - isa_bus_new(&d->qdev); - 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); + PIIX4State *d = DO_UPCAST(PIIX4State, dev, dev); - piix4_dev = d; + isa_bus_new(&d->dev.qdev); + piix4_dev = &d->dev; qemu_register_reset(piix4_reset, d); return 0; } @@ -111,10 +105,14 @@ { .qdev.name = "PIIX4", .qdev.desc = "ISA bridge", - .qdev.size = sizeof(PCIDevice), + .qdev.size = sizeof(PIIX4State), + .qdev.vmsd = &vmstate_piix4, .qdev.no_user = 1, .no_hotplug = 1, .init = piix4_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371AB_0, // 82371AB/EB/MB PIIX4 PCI-to-ISA bridge + .class_id = PCI_CLASS_BRIDGE_ISA, },{ /* end of list */ } diff -Nru qemu-kvm-0.14.1+noroms/hw/piix_pci.c qemu-kvm-0.15.0+noroms/hw/piix_pci.c --- qemu-kvm-0.14.1+noroms/hw/piix_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/piix_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,6 +29,7 @@ #include "isa.h" #include "sysbus.h" #include "range.h" +#include "xen.h" #include "kvm.h" /* @@ -38,10 +39,32 @@ typedef PCIHostState I440FXState; +#define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ +#define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ +#define XEN_PIIX_NUM_PIRQS 128ULL +#define PIIX_PIRQC 0x60 + typedef struct PIIX3State { PCIDevice dev; - int pci_irq_levels[4]; + + /* + * bitmap to track pic levels. + * The pic level is the logical OR of all the PCI irqs mapped to it + * So one PIC level is tracked by PIIX_NUM_PIRQS bits. + * + * PIRQ is mapped to PIC pins, we track it by + * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with + * pic_irq * PIIX_NUM_PIRQS + pirq + */ +#if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 +#error "unable to encode pic state in 64bit in pic_levels." +#endif + uint64_t pic_levels; + qemu_irq *pic; + + /* This member isn't used. Just for save/load compatibility */ + int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; } PIIX3State; struct PCII440FXState { @@ -56,16 +79,18 @@ #define I440FX_PAM_SIZE 7 #define I440FX_SMRAM 0x72 -static void piix3_set_irq(void *opaque, int irq_num, int level); +static void piix3_set_irq(void *opaque, int pirq, int level); +static void piix3_write_config_xen(PCIDevice *dev, + uint32_t address, uint32_t val, int len); /* return the global irq number corresponding to a given device irq pin. We could also use the bus number to have a more precise mapping. */ -static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) { int slot_addend; slot_addend = (pci_dev->devfn >> 3) - 1; - return (irq_num + slot_addend) & 3; + return (pci_intx + slot_addend) & 3; } static void update_pam(PCII440FXState *d, uint32_t start, uint32_t end, int r) @@ -163,9 +188,11 @@ i440fx_update_memory_mappings(d); qemu_get_8s(f, &d->smm_enabled); - if (version_id == 2) - for (i = 0; i < 4; i++) - d->piix3->pci_irq_levels[i] = qemu_get_be32(f); + if (version_id == 2) { + for (i = 0; i < PIIX_NUM_PIRQS; i++) { + qemu_get_be32(f); /* dummy load for compatibility */ + } + } return 0; } @@ -206,11 +233,6 @@ { PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev); - pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL); - 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[I440FX_SMRAM] = 0x02; cpu_smm_register(&i440fx_set_smm, d); @@ -219,7 +241,10 @@ static PIIX3State *piix3_dev; -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic, ram_addr_t ram_size) +static PCIBus *i440fx_common_init(const char *device_name, + PCII440FXState **pi440fx_state, + int *piix3_devfn, + qemu_irq *pic, ram_addr_t ram_size) { DeviceState *dev; PCIBus *b; @@ -233,13 +258,26 @@ s->bus = b; qdev_init_nofail(dev); - d = pci_create_simple(b, 0, "i440FX"); + d = pci_create_simple(b, 0, device_name); *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d); - piix3 = DO_UPCAST(PIIX3State, dev, - pci_create_simple_multifunction(b, -1, true, "PIIX3")); + /* Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. */ + if (xen_enabled()) { + piix3 = DO_UPCAST(PIIX3State, dev, + pci_create_simple_multifunction(b, -1, true, "PIIX3-xen")); + pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, + piix3, XEN_PIIX_NUM_PIRQS); + } else { + piix3 = DO_UPCAST(PIIX3State, dev, + pci_create_simple_multifunction(b, -1, true, "PIIX3")); + pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, + PIIX_NUM_PIRQS); + } 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; @@ -254,27 +292,70 @@ return b; } +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, + qemu_irq *pic, ram_addr_t ram_size) +{ + PCIBus *b; + + b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, ram_size); + return b; +} + /* PIIX3 PCI to ISA bridge */ +static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) +{ + qemu_set_irq(piix3->pic[pic_irq], + !!(piix3->pic_levels & + (((1ULL << PIIX_NUM_PIRQS) - 1) << + (pic_irq * PIIX_NUM_PIRQS)))); +} + +static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) +{ + int pic_irq; + uint64_t mask; + + pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; + if (pic_irq >= PIIX_NUM_PIC_IRQS) { + return; + } + + mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); + piix3->pic_levels &= ~mask; + piix3->pic_levels |= mask * !!level; + + piix3_set_irq_pic(piix3, pic_irq); +} -static void piix3_set_irq(void *opaque, int irq_num, int level) +static void piix3_set_irq(void *opaque, int pirq, int level) { - int i, pic_irq, pic_level; PIIX3State *piix3 = opaque; + piix3_set_irq_level(piix3, pirq, level); +} + +/* irq routing is changed. so rebuild bitmap */ +static void piix3_update_irq_levels(PIIX3State *piix3) +{ + int pirq; - piix3->pci_irq_levels[irq_num] = level; + piix3->pic_levels = 0; + for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { + piix3_set_irq_level(piix3, pirq, + pci_bus_get_irq_level(piix3->dev.bus, pirq)); + } +} - /* now we change the pic irq level according to the piix irq mappings */ - /* XXX: optimize */ - pic_irq = piix3->dev.config[0x60 + irq_num]; - if (pic_irq < 16) { - /* The pic level is the logical OR of all the PCI irqs mapped - to it */ - pic_level = 0; - for (i = 0; i < 4; i++) { - if (pic_irq == piix3->dev.config[0x60 + i]) - pic_level |= piix3->pci_irq_levels[i]; +static void piix3_write_config(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + pci_default_write_config(dev, address, val, len); + if (ranges_overlap(address, len, PIIX_PIRQC, 4)) { + PIIX3State *piix3 = DO_UPCAST(PIIX3State, dev, dev); + int pic_irq; + piix3_update_irq_levels(piix3); + for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { + piix3_set_irq_pic(piix3, pic_irq); } - qemu_set_irq(piix3->pic[pic_irq], pic_level); } } @@ -285,6 +366,13 @@ return 0; } +static void piix3_write_config_xen(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + xen_piix_pci_write_config_client(address, val, len); + piix3_write_config(dev, address, val, len); +} + static void piix3_reset(void *opaque) { PIIX3State *d = opaque; @@ -322,7 +410,25 @@ pci_conf[0xac] = 0x00; pci_conf[0xae] = 0x00; - memset(d->pci_irq_levels, 0, sizeof(d->pci_irq_levels)); + d->pic_levels = 0; +} + +static int piix3_post_load(void *opaque, int version_id) +{ + PIIX3State *piix3 = opaque; + piix3_update_irq_levels(piix3); + return 0; +} + +static void piix3_pre_save(void *opaque) +{ + int i; + PIIX3State *piix3 = opaque; + + for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { + piix3->pci_irq_levels_vmstate[i] = + pci_bus_get_irq_level(piix3->dev.bus, i); + } } static const VMStateDescription vmstate_piix3 = { @@ -330,9 +436,12 @@ .version_id = 3, .minimum_version_id = 2, .minimum_version_id_old = 2, + .post_load = piix3_post_load, + .pre_save = piix3_pre_save, .fields = (VMStateField []) { VMSTATE_PCI_DEVICE(dev, PIIX3State), - VMSTATE_INT32_ARRAY_V(pci_irq_levels, PIIX3State, 4, 3), + VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, + PIIX_NUM_PIRQS, 3), VMSTATE_END_OF_LIST() } }; @@ -340,15 +449,8 @@ static int piix3_initfn(PCIDevice *dev) { PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev); - uint8_t *pci_conf; isa_bus_new(&d->dev.qdev); - - 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_82371SB_0); // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) - pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA); - qemu_register_reset(piix3_reset, d); return 0; } @@ -363,6 +465,10 @@ .no_hotplug = 1, .init = i440fx_initfn, .config_write = i440fx_write_config, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82441, + .revision = 0x02, + .class_id = PCI_CLASS_BRIDGE_HOST, },{ .qdev.name = "PIIX3", .qdev.desc = "ISA bridge", @@ -371,6 +477,22 @@ .qdev.no_user = 1, .no_hotplug = 1, .init = piix3_initfn, + .config_write = piix3_write_config, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_0, // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) + .class_id = PCI_CLASS_BRIDGE_ISA, + },{ + .qdev.name = "PIIX3-xen", + .qdev.desc = "ISA bridge", + .qdev.size = sizeof(PIIX3State), + .qdev.vmsd = &vmstate_piix3, + .qdev.no_user = 1, + .no_hotplug = 1, + .init = piix3_initfn, + .config_write = piix3_write_config_xen, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_0, // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) + .class_id = PCI_CLASS_BRIDGE_ISA, },{ /* end of list */ } diff -Nru qemu-kvm-0.14.1+noroms/hw/pl011.c qemu-kvm-0.15.0+noroms/hw/pl011.c --- qemu-kvm-0.14.1+noroms/hw/pl011.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl011.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -235,56 +235,30 @@ pl011_write }; -static void pl011_save(QEMUFile *f, void *opaque) -{ - pl011_state *s = (pl011_state *)opaque; - int i; - - qemu_put_be32(f, s->readbuff); - qemu_put_be32(f, s->flags); - qemu_put_be32(f, s->lcr); - qemu_put_be32(f, s->cr); - qemu_put_be32(f, s->dmacr); - qemu_put_be32(f, s->int_enabled); - qemu_put_be32(f, s->int_level); - for (i = 0; i < 16; i++) - qemu_put_be32(f, s->read_fifo[i]); - qemu_put_be32(f, s->ilpr); - qemu_put_be32(f, s->ibrd); - qemu_put_be32(f, s->fbrd); - qemu_put_be32(f, s->ifl); - qemu_put_be32(f, s->read_pos); - qemu_put_be32(f, s->read_count); - qemu_put_be32(f, s->read_trigger); -} - -static int pl011_load(QEMUFile *f, void *opaque, int version_id) -{ - pl011_state *s = (pl011_state *)opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - s->readbuff = qemu_get_be32(f); - s->flags = qemu_get_be32(f); - s->lcr = qemu_get_be32(f); - s->cr = qemu_get_be32(f); - s->dmacr = qemu_get_be32(f); - s->int_enabled = qemu_get_be32(f); - s->int_level = qemu_get_be32(f); - for (i = 0; i < 16; i++) - s->read_fifo[i] = qemu_get_be32(f); - s->ilpr = qemu_get_be32(f); - s->ibrd = qemu_get_be32(f); - s->fbrd = qemu_get_be32(f); - s->ifl = qemu_get_be32(f); - s->read_pos = qemu_get_be32(f); - s->read_count = qemu_get_be32(f); - s->read_trigger = qemu_get_be32(f); - - return 0; -} +static const VMStateDescription vmstate_pl011 = { + .name = "pl011", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(readbuff, pl011_state), + VMSTATE_UINT32(flags, pl011_state), + VMSTATE_UINT32(lcr, pl011_state), + VMSTATE_UINT32(cr, pl011_state), + VMSTATE_UINT32(dmacr, pl011_state), + VMSTATE_UINT32(int_enabled, pl011_state), + VMSTATE_UINT32(int_level, pl011_state), + VMSTATE_UINT32_ARRAY(read_fifo, pl011_state, 16), + VMSTATE_UINT32(ilpr, pl011_state), + VMSTATE_UINT32(ibrd, pl011_state), + VMSTATE_UINT32(fbrd, pl011_state), + VMSTATE_UINT32(ifl, pl011_state), + VMSTATE_INT32(read_pos, pl011_state), + VMSTATE_INT32(read_count, pl011_state), + VMSTATE_INT32(read_trigger, pl011_state), + VMSTATE_END_OF_LIST() + } +}; static int pl011_init(SysBusDevice *dev, const unsigned char *id) { @@ -307,7 +281,7 @@ qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive, pl011_event, s); } - register_savevm(&dev->qdev, "pl011_uart", -1, 1, pl011_save, pl011_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_pl011, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pl022.c qemu-kvm-0.15.0+noroms/hw/pl022.c --- qemu-kvm-0.14.1+noroms/hw/pl022.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl022.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -239,54 +239,42 @@ pl022_write }; -static void pl022_save(QEMUFile *f, void *opaque) -{ - pl022_state *s = (pl022_state *)opaque; - int i; - - qemu_put_be32(f, s->cr0); - qemu_put_be32(f, s->cr1); - qemu_put_be32(f, s->bitmask); - qemu_put_be32(f, s->sr); - qemu_put_be32(f, s->cpsr); - qemu_put_be32(f, s->is); - qemu_put_be32(f, s->im); - qemu_put_be32(f, s->tx_fifo_head); - qemu_put_be32(f, s->rx_fifo_head); - qemu_put_be32(f, s->tx_fifo_len); - qemu_put_be32(f, s->rx_fifo_len); - for (i = 0; i < 8; i++) { - qemu_put_be16(f, s->tx_fifo[i]); - qemu_put_be16(f, s->rx_fifo[i]); - } -} - -static int pl022_load(QEMUFile *f, void *opaque, int version_id) -{ - pl022_state *s = (pl022_state *)opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - s->cr0 = qemu_get_be32(f); - s->cr1 = qemu_get_be32(f); - s->bitmask = qemu_get_be32(f); - s->sr = qemu_get_be32(f); - s->cpsr = qemu_get_be32(f); - s->is = qemu_get_be32(f); - s->im = qemu_get_be32(f); - s->tx_fifo_head = qemu_get_be32(f); - s->rx_fifo_head = qemu_get_be32(f); - s->tx_fifo_len = qemu_get_be32(f); - s->rx_fifo_len = qemu_get_be32(f); - for (i = 0; i < 8; i++) { - s->tx_fifo[i] = qemu_get_be16(f); - s->rx_fifo[i] = qemu_get_be16(f); +static const VMStateDescription vmstate_pl022 = { + .name = "pl022_ssp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr0, pl022_state), + VMSTATE_UINT32(cr1, pl022_state), + VMSTATE_UINT32(bitmask, pl022_state), + VMSTATE_UINT32(sr, pl022_state), + VMSTATE_UINT32(cpsr, pl022_state), + VMSTATE_UINT32(is, pl022_state), + VMSTATE_UINT32(im, pl022_state), + VMSTATE_INT32(tx_fifo_head, pl022_state), + VMSTATE_INT32(rx_fifo_head, pl022_state), + VMSTATE_INT32(tx_fifo_len, pl022_state), + VMSTATE_INT32(rx_fifo_len, pl022_state), + VMSTATE_UINT16(tx_fifo[0], pl022_state), + VMSTATE_UINT16(rx_fifo[0], pl022_state), + VMSTATE_UINT16(tx_fifo[1], pl022_state), + VMSTATE_UINT16(rx_fifo[1], pl022_state), + VMSTATE_UINT16(tx_fifo[2], pl022_state), + VMSTATE_UINT16(rx_fifo[2], pl022_state), + VMSTATE_UINT16(tx_fifo[3], pl022_state), + VMSTATE_UINT16(rx_fifo[3], pl022_state), + VMSTATE_UINT16(tx_fifo[4], pl022_state), + VMSTATE_UINT16(rx_fifo[4], pl022_state), + VMSTATE_UINT16(tx_fifo[5], pl022_state), + VMSTATE_UINT16(rx_fifo[5], pl022_state), + VMSTATE_UINT16(tx_fifo[6], pl022_state), + VMSTATE_UINT16(rx_fifo[6], pl022_state), + VMSTATE_UINT16(tx_fifo[7], pl022_state), + VMSTATE_UINT16(rx_fifo[7], pl022_state), + VMSTATE_END_OF_LIST() } - - return 0; -} +}; static int pl022_init(SysBusDevice *dev) { @@ -300,7 +288,7 @@ sysbus_init_irq(dev, &s->irq); s->ssi = ssi_create_bus(&dev->qdev, "ssi"); pl022_reset(s); - register_savevm(&dev->qdev, "pl022_ssp", -1, 1, pl022_save, pl022_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_pl022, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pl031.c qemu-kvm-0.15.0+noroms/hw/pl031.c --- qemu-kvm-0.14.1+noroms/hw/pl031.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl031.c 2011-08-09 07:40:29.000000000 -0500 @@ -80,9 +80,9 @@ static uint32_t pl031_get_count(pl031_state *s) { - /* This assumes qemu_get_clock returns the time since the machine was + /* This assumes qemu_get_clock_ns returns the time since the machine was created. */ - return s->tick_offset + qemu_get_clock(vm_clock) / get_ticks_per_sec(); + return s->tick_offset + qemu_get_clock_ns(vm_clock) / get_ticks_per_sec(); } static void pl031_set_alarm(pl031_state *s) @@ -90,7 +90,7 @@ int64_t now; uint32_t ticks; - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); ticks = s->tick_offset + now / get_ticks_per_sec(); /* The timer wraps around. This subtraction also wraps in the same way, @@ -161,7 +161,7 @@ pl031_update(s); break; case RTC_ICR: - /* The PL031 documentation (DDI0224B) states that the interupt is + /* The PL031 documentation (DDI0224B) states that the interrupt is cleared when bit 0 of the written value is set. However the arm926e documentation (DDI0287B) states that the interrupt is cleared when any value is written. */ @@ -217,7 +217,7 @@ qemu_get_timedate(&tm, 0); s->tick_offset = mktimegm(&tm); - s->timer = qemu_new_timer(vm_clock, pl031_interrupt, s); + s->timer = qemu_new_timer_ns(vm_clock, pl031_interrupt, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pl050.c qemu-kvm-0.15.0+noroms/hw/pl050.c --- qemu-kvm-0.14.1+noroms/hw/pl050.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl050.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/pl061.c qemu-kvm-0.15.0+noroms/hw/pl061.c --- qemu-kvm-0.14.1+noroms/hw/pl061.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl061.c 2011-08-09 07:40:29.000000000 -0500 @@ -5,7 +5,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -24,6 +24,8 @@ #endif static const uint8_t pl061_id[12] = + { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; +static const uint8_t pl061_id_luminary[12] = { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; typedef struct { @@ -50,6 +52,7 @@ uint8_t float_high; qemu_irq irq; qemu_irq out[8]; + const unsigned char *id; } pl061_state; static void pl061_update(pl061_state *s) @@ -83,7 +86,7 @@ pl061_state *s = (pl061_state *)opaque; if (offset >= 0xfd0 && offset < 0x1000) { - return pl061_id[(offset - 0xfd0) >> 2]; + return s->id[(offset - 0xfd0) >> 2]; } if (offset < 0x400) { return s->data & (offset >> 2); @@ -95,7 +98,7 @@ return s->isense; case 0x408: /* Interrupt both edges */ return s->ibe; - case 0x40c: /* Interupt event */ + case 0x40c: /* Interrupt event */ return s->iev; case 0x410: /* Interrupt mask */ return s->im; @@ -153,7 +156,7 @@ case 0x408: /* Interrupt both edges */ s->ibe = value; break; - case 0x40c: /* Interupt event */ + case 0x40c: /* Interrupt event */ s->iev = value; break; case 0x410: /* Interrupt mask */ @@ -291,11 +294,11 @@ return 0; } -static int pl061_init(SysBusDevice *dev) +static int pl061_init(SysBusDevice *dev, const unsigned char *id) { int iomemtype; pl061_state *s = FROM_SYSBUS(pl061_state, dev); - + s->id = id; iomemtype = cpu_register_io_memory(pl061_readfn, pl061_writefn, s, DEVICE_NATIVE_ENDIAN); @@ -308,10 +311,22 @@ return 0; } +static int pl061_init_luminary(SysBusDevice *dev) +{ + return pl061_init(dev, pl061_id_luminary); +} + +static int pl061_init_arm(SysBusDevice *dev) +{ + return pl061_init(dev, pl061_id); +} + static void pl061_register_devices(void) { sysbus_register_dev("pl061", sizeof(pl061_state), - pl061_init); + pl061_init_arm); + sysbus_register_dev("pl061_luminary", sizeof(pl061_state), + pl061_init_luminary); } device_init(pl061_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/pl080.c qemu-kvm-0.15.0+noroms/hw/pl080.c --- qemu-kvm-0.14.1+noroms/hw/pl080.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl080.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -199,10 +199,10 @@ if (size == 0) { /* Transfer complete. */ if (ch->lli) { - ch->src = ldl_phys(ch->lli); - ch->dest = ldl_phys(ch->lli + 4); - ch->ctrl = ldl_phys(ch->lli + 12); - ch->lli = ldl_phys(ch->lli + 8); + ch->src = ldl_le_phys(ch->lli); + ch->dest = ldl_le_phys(ch->lli + 4); + ch->ctrl = ldl_le_phys(ch->lli + 12); + ch->lli = ldl_le_phys(ch->lli + 8); } else { ch->conf &= ~PL080_CCONF_E; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pl110.c qemu-kvm-0.15.0+noroms/hw/pl110.c --- qemu-kvm-0.14.1+noroms/hw/pl110.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl110.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2005-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL */ #include "sysbus.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/pl110_template.h qemu-kvm-0.15.0+noroms/hw/pl110_template.h --- qemu-kvm-0.14.1+noroms/hw/pl110_template.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl110_template.h 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL * * Framebuffer format conversion routines. */ diff -Nru qemu-kvm-0.14.1+noroms/hw/pl181.c qemu-kvm-0.15.0+noroms/hw/pl181.c --- qemu-kvm-0.14.1+noroms/hw/pl181.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl181.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "blockdev.h" @@ -47,6 +47,8 @@ int linux_hack; uint32_t fifo[PL181_FIFO_LEN]; qemu_irq irq[2]; + /* GPIO outputs for 'card is readonly' and 'card inserted' */ + qemu_irq cardstatus[2]; } pl181_state; #define PL181_CMD_INDEX 0x3f @@ -444,6 +446,9 @@ s->linux_hack = 0; s->mask[0] = 0; s->mask[1] = 0; + + /* We can assume our GPIO outputs have been wired up now */ + sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]); } static int pl181_init(SysBusDevice *dev) @@ -457,6 +462,7 @@ sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq[0]); sysbus_init_irq(dev, &s->irq[1]); + qdev_init_gpio_out(&s->busdev.qdev, s->cardstatus, 2); dinfo = drive_get_next(IF_SD); s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0); qemu_register_reset(pl181_reset, s); diff -Nru qemu-kvm-0.14.1+noroms/hw/pl190.c qemu-kvm-0.15.0+noroms/hw/pl190.c --- qemu-kvm-0.14.1+noroms/hw/pl190.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pl190.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc405_boards.c qemu-kvm-0.15.0+noroms/hw/ppc405_boards.c --- qemu-kvm-0.14.1+noroms/hw/ppc405_boards.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc405_boards.c 2011-08-09 07:40:29.000000000 -0500 @@ -560,6 +560,7 @@ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { bios_size = load_image(filename, qemu_get_ram_ptr(bios_offset)); + qemu_free(filename); } else { bios_size = -1; } diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc405_uc.c qemu-kvm-0.15.0+noroms/hw/ppc405_uc.c --- qemu-kvm-0.14.1+noroms/hw/ppc405_uc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc405_uc.c 2011-08-09 07:40:29.000000000 -0500 @@ -51,39 +51,42 @@ bdloc = 0x01000000UL - sizeof(struct ppc4xx_bd_info_t); else bdloc = bd->bi_memsize - sizeof(struct ppc4xx_bd_info_t); - stl_phys(bdloc + 0x00, bd->bi_memstart); - stl_phys(bdloc + 0x04, bd->bi_memsize); - stl_phys(bdloc + 0x08, bd->bi_flashstart); - stl_phys(bdloc + 0x0C, bd->bi_flashsize); - stl_phys(bdloc + 0x10, bd->bi_flashoffset); - stl_phys(bdloc + 0x14, bd->bi_sramstart); - stl_phys(bdloc + 0x18, bd->bi_sramsize); - stl_phys(bdloc + 0x1C, bd->bi_bootflags); - stl_phys(bdloc + 0x20, bd->bi_ipaddr); - for (i = 0; i < 6; i++) + stl_be_phys(bdloc + 0x00, bd->bi_memstart); + stl_be_phys(bdloc + 0x04, bd->bi_memsize); + stl_be_phys(bdloc + 0x08, bd->bi_flashstart); + stl_be_phys(bdloc + 0x0C, bd->bi_flashsize); + stl_be_phys(bdloc + 0x10, bd->bi_flashoffset); + stl_be_phys(bdloc + 0x14, bd->bi_sramstart); + stl_be_phys(bdloc + 0x18, bd->bi_sramsize); + stl_be_phys(bdloc + 0x1C, bd->bi_bootflags); + stl_be_phys(bdloc + 0x20, bd->bi_ipaddr); + for (i = 0; i < 6; i++) { stb_phys(bdloc + 0x24 + i, bd->bi_enetaddr[i]); - stw_phys(bdloc + 0x2A, bd->bi_ethspeed); - stl_phys(bdloc + 0x2C, bd->bi_intfreq); - stl_phys(bdloc + 0x30, bd->bi_busfreq); - stl_phys(bdloc + 0x34, bd->bi_baudrate); - for (i = 0; i < 4; i++) + } + stw_be_phys(bdloc + 0x2A, bd->bi_ethspeed); + stl_be_phys(bdloc + 0x2C, bd->bi_intfreq); + stl_be_phys(bdloc + 0x30, bd->bi_busfreq); + stl_be_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_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++) + stl_be_phys(bdloc + 0x5C, bd->bi_plb_busfreq); + stl_be_phys(bdloc + 0x60, bd->bi_pci_busfreq); + for (i = 0; i < 6; i++) { stb_phys(bdloc + 0x64 + i, bd->bi_pci_enetaddr[i]); + } n = 0x6A; if (flags & 0x00000001) { for (i = 0; i < 6; i++) stb_phys(bdloc + n++, bd->bi_pci_enetaddr2[i]); } - stl_phys(bdloc + n, bd->bi_opbfreq); + stl_be_phys(bdloc + n, bd->bi_opbfreq); n += 4; for (i = 0; i < 2; i++) { - stl_phys(bdloc + n, bd->bi_iic_fast[i]); + stl_be_phys(bdloc + n, bd->bi_iic_fast[i]); n += 4; } @@ -1347,7 +1350,7 @@ switch (addr) { case 0x00: /* Time base counter */ - ret = muldiv64(qemu_get_clock(vm_clock) + gpt->tb_offset, + ret = muldiv64(qemu_get_clock_ns(vm_clock) + gpt->tb_offset, gpt->tb_freq, get_ticks_per_sec()); break; case 0x10: @@ -1404,7 +1407,7 @@ case 0x00: /* Time base counter */ gpt->tb_offset = muldiv64(value, get_ticks_per_sec(), gpt->tb_freq) - - qemu_get_clock(vm_clock); + - qemu_get_clock_ns(vm_clock); ppc4xx_gpt_compute_timer(gpt); break; case 0x10: @@ -1501,7 +1504,7 @@ for (i = 0; i < 5; i++) { gpt->irqs[i] = irqs[i]; } - gpt->timer = qemu_new_timer(vm_clock, &ppc4xx_gpt_cb, gpt); + gpt->timer = qemu_new_timer_ns(vm_clock, &ppc4xx_gpt_cb, gpt); #ifdef DEBUG_GPT printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc440_bamboo.c qemu-kvm-0.15.0+noroms/hw/ppc440_bamboo.c --- qemu-kvm-0.14.1+noroms/hw/ppc440_bamboo.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc440_bamboo.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,14 +17,12 @@ #include "hw.h" #include "pci.h" #include "boards.h" -#include "sysemu.h" #include "ppc440.h" #include "kvm.h" #include "kvm_ppc.h" #include "device_tree.h" #include "loader.h" #include "elf.h" -#include "qemu-kvm.h" #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" @@ -157,8 +155,6 @@ exit(1); } - cpu_synchronize_state(env); - /* Set initial guest state. */ env->gpr[1] = (16<<20) - 8; env->gpr[3] = FDT_ADDR; diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc440.c qemu-kvm-0.15.0+noroms/hw/ppc440.c --- qemu-kvm-0.14.1+noroms/hw/ppc440.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc440.c 2011-08-09 07:40:29.000000000 -0500 @@ -20,7 +20,6 @@ #include "ppc405.h" #include "sysemu.h" #include "kvm.h" -#include "qemu-kvm.h" #define PPC440EP_PCI_CONFIG 0xeec00000 #define PPC440EP_PCI_INTACK 0xeed00000 @@ -46,8 +45,9 @@ qemu_irq *irqs; qemu_irq *pci_irqs; - if (cpu_model == NULL) - cpu_model = "405"; // XXX: should be 440EP + if (cpu_model == NULL) { + cpu_model = "440-Xilinx"; // XXX: should be 440EP + } env = cpu_init(cpu_model); if (!env) { fprintf(stderr, "Unable to initialize CPU!\n"); diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc4xx_devs.c qemu-kvm-0.15.0+noroms/hw/ppc4xx_devs.c --- qemu-kvm-0.14.1+noroms/hw/ppc4xx_devs.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc4xx_devs.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include "hw.h" #include "ppc.h" #include "ppc4xx.h" -#include "sysemu.h" #include "qemu-log.h" //#define DEBUG_MMIO @@ -39,7 +38,7 @@ #endif /*****************************************************************************/ -/* Generic PowerPC 4xx processor instanciation */ +/* Generic PowerPC 4xx processor instantiation */ CPUState *ppc4xx_init (const char *cpu_model, clk_setup_t *cpu_clk, clk_setup_t *tb_clk, uint32_t sysclk) diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc4xx_pci.c qemu-kvm-0.15.0+noroms/hw/ppc4xx_pci.c --- qemu-kvm-0.14.1+noroms/hw/ppc4xx_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc4xx_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -285,50 +285,48 @@ qemu_set_irq(pci_irqs[irq_num], level); } -static void ppc4xx_pci_save(QEMUFile *f, void *opaque) -{ - PPC4xxPCIState *controller = opaque; - int i; - - pci_device_save(controller->pci_dev, f); - - for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) { - qemu_put_be32s(f, &controller->pmm[i].la); - qemu_put_be32s(f, &controller->pmm[i].ma); - qemu_put_be32s(f, &controller->pmm[i].pcila); - qemu_put_be32s(f, &controller->pmm[i].pciha); +static const VMStateDescription vmstate_pci_master_map = { + .name = "pci_master_map", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(la, struct PCIMasterMap), + VMSTATE_UINT32(ma, struct PCIMasterMap), + VMSTATE_UINT32(pcila, struct PCIMasterMap), + VMSTATE_UINT32(pciha, struct PCIMasterMap), + VMSTATE_END_OF_LIST() } +}; - for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) { - qemu_put_be32s(f, &controller->ptm[i].ms); - qemu_put_be32s(f, &controller->ptm[i].la); - } -} - -static int ppc4xx_pci_load(QEMUFile *f, void *opaque, int version_id) -{ - PPC4xxPCIState *controller = opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - pci_device_load(controller->pci_dev, f); - - for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) { - qemu_get_be32s(f, &controller->pmm[i].la); - qemu_get_be32s(f, &controller->pmm[i].ma); - qemu_get_be32s(f, &controller->pmm[i].pcila); - qemu_get_be32s(f, &controller->pmm[i].pciha); +static const VMStateDescription vmstate_pci_target_map = { + .name = "pci_target_map", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ms, struct PCITargetMap), + VMSTATE_UINT32(la, struct PCITargetMap), + VMSTATE_END_OF_LIST() } +}; - for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) { - qemu_get_be32s(f, &controller->ptm[i].ms); - qemu_get_be32s(f, &controller->ptm[i].la); +static const VMStateDescription vmstate_ppc4xx_pci = { + .name = "ppc4xx_pci", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE_POINTER(pci_dev, PPC4xxPCIState), + VMSTATE_STRUCT_ARRAY(pmm, PPC4xxPCIState, PPC4xx_PCI_NR_PMMS, 1, + vmstate_pci_master_map, + struct PCIMasterMap), + VMSTATE_STRUCT_ARRAY(ptm, PPC4xxPCIState, PPC4xx_PCI_NR_PTMS, 1, + vmstate_pci_target_map, + struct PCITargetMap), + VMSTATE_END_OF_LIST() } - - return 0; -} +}; /* XXX Interrupt acknowledge cycles not supported. */ PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4], @@ -381,8 +379,8 @@ qemu_register_reset(ppc4xx_pci_reset, controller); /* XXX load/save code not tested. */ - register_savevm(&controller->pci_dev->qdev, "ppc4xx_pci", ppc4xx_pci_id++, - 1, ppc4xx_pci_save, ppc4xx_pci_load, controller); + vmstate_register(&controller->pci_dev->qdev, ppc4xx_pci_id++, + &vmstate_ppc4xx_pci, controller); return controller->pci_state.bus; diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc.c qemu-kvm-0.15.0+noroms/hw/ppc.c --- qemu-kvm-0.14.1+noroms/hw/ppc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc.c 2011-08-09 07:40:29.000000000 -0500 @@ -208,6 +208,7 @@ } else { LOG_IRQ("%s: restart the CPU\n", __func__); env->halted = 0; + qemu_cpu_kick(env); } break; case PPC970_INPUT_HRESET: @@ -246,6 +247,39 @@ env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, env, PPC970_INPUT_NB); } + +/* POWER7 internal IRQ controller */ +static void power7_set_irq (void *opaque, int pin, int level) +{ + CPUState *env = opaque; + + LOG_IRQ("%s: env %p pin %d level %d\n", __func__, + env, pin, level); + + switch (pin) { + case POWER7_INPUT_INT: + /* Level sensitive - active high */ + LOG_IRQ("%s: set the external IRQ state to %d\n", + __func__, level); + ppc_set_irq(env, PPC_INTERRUPT_EXT, level); + break; + default: + /* Unknown pin - do nothing */ + LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); + return; + } + if (level) { + env->irq_input_state |= 1 << pin; + } else { + env->irq_input_state &= ~(1 << pin); + } +} + +void ppcPOWER7_irq_init (CPUState *env) +{ + env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, env, + POWER7_INPUT_NB); +} #endif /* defined(TARGET_PPC64) */ /* PowerPC 40x internal IRQ controller */ @@ -300,6 +334,7 @@ } else { LOG_IRQ("%s: restart the CPU\n", __func__); env->halted = 0; + qemu_cpu_kick(env); } break; case PPC40x_INPUT_DEBUG: @@ -417,7 +452,11 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); + if (kvm_enabled()) { + return env->spr[SPR_TBL]; + } + + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb; @@ -428,7 +467,7 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb >> 32; @@ -436,6 +475,10 @@ uint32_t cpu_ppc_load_tbu (CPUState *env) { + if (kvm_enabled()) { + return env->spr[SPR_TBU]; + } + return _cpu_ppc_load_tbu(env); } @@ -452,9 +495,9 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), &tb_env->tb_offset, tb | (uint64_t)value); } @@ -463,9 +506,9 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), &tb_env->tb_offset, ((uint64_t)value << 32) | tb); } @@ -479,7 +522,7 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb; @@ -490,7 +533,7 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb >> 32; @@ -501,9 +544,9 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), &tb_env->atb_offset, tb | (uint64_t)value); } @@ -512,9 +555,9 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), &tb_env->atb_offset, ((uint64_t)value << 32) | tb); } @@ -525,7 +568,7 @@ /* If the time base is already frozen, do nothing */ if (tb_env->tb_freq != 0) { - vmclk = qemu_get_clock(vm_clock); + vmclk = qemu_get_clock_ns(vm_clock); /* Get the time base */ tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset); /* Get the alternate time base */ @@ -547,7 +590,7 @@ /* If the time base is not frozen, do nothing */ if (tb_env->tb_freq == 0) { - vmclk = qemu_get_clock(vm_clock); + vmclk = qemu_get_clock_ns(vm_clock); /* Get the time base from tb_offset */ tb = tb_env->tb_offset; /* Get the alternate time base from atb_offset */ @@ -567,7 +610,7 @@ uint32_t decr; int64_t diff; - diff = next - qemu_get_clock(vm_clock); + diff = next - qemu_get_clock_ns(vm_clock); if (diff >= 0) decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec()); else @@ -581,6 +624,10 @@ { ppc_tb_t *tb_env = env->tb_env; + if (kvm_enabled()) { + return env->spr[SPR_DECR]; + } + return _cpu_ppc_load_decr(env, tb_env->decr_next); } @@ -596,7 +643,7 @@ ppc_tb_t *tb_env = env->tb_env; uint64_t diff; - diff = qemu_get_clock(vm_clock) - tb_env->purr_start; + diff = qemu_get_clock_ns(vm_clock) - tb_env->purr_start; return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec()); } @@ -629,7 +676,7 @@ LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__, decr, value); - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); if (is_excp) next += *nextp - now; @@ -690,7 +737,7 @@ ppc_tb_t *tb_env = env->tb_env; tb_env->purr_load = value; - tb_env->purr_start = qemu_get_clock(vm_clock); + tb_env->purr_start = qemu_get_clock_ns(vm_clock); } static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) @@ -717,11 +764,11 @@ tb_env = qemu_mallocz(sizeof(ppc_tb_t)); env->tb_env = tb_env; /* Create new timer */ - tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env); + tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env); if (0) { /* XXX: find a suitable condition to enable the hypervisor decrementer */ - tb_env->hdecr_timer = qemu_new_timer(vm_clock, &cpu_ppc_hdecr_cb, env); + tb_env->hdecr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_hdecr_cb, env); } else { tb_env->hdecr_timer = NULL; } @@ -785,7 +832,7 @@ env = opaque; tb_env = env->tb_env; ppcemb_timer = tb_env->opaque; - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { case 0: next = 1 << 9; @@ -831,7 +878,7 @@ } else { LOG_TB("%s: start PIT %016" PRIx64 "\n", __func__, ppcemb_timer->pit_reload); - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); next = now + muldiv64(ppcemb_timer->pit_reload, get_ticks_per_sec(), tb_env->decr_freq); if (is_excp) @@ -875,7 +922,7 @@ env = opaque; tb_env = env->tb_env; ppcemb_timer = tb_env->opaque; - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { case 0: next = 1 << 17; @@ -1000,11 +1047,11 @@ LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); if (ppcemb_timer != NULL) { /* We use decr timer for PIT */ - tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env); + tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env); ppcemb_timer->fit_timer = - qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env); + qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env); ppcemb_timer->wdt_timer = - qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env); + qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env); ppcemb_timer->decr_excp = decr_excp; } diff -Nru qemu-kvm-0.14.1+noroms/hw/ppce500.h qemu-kvm-0.15.0+noroms/hw/ppce500.h --- qemu-kvm-0.14.1+noroms/hw/ppce500.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppce500.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,22 +0,0 @@ -/* - * QEMU PowerPC E500 emulation shared definitions - * - * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, - * - * This file is derived from hw/ppc440.h - * the copyright for that material belongs to the original owners. - * - * This 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. - */ - -#if !defined(PPC_E500_H) -#define PPC_E500_H - -PCIBus *ppce500_pci_init(qemu_irq *pic, target_phys_addr_t registers); - -#endif /* !defined(PPC_E500_H) */ diff -Nru qemu-kvm-0.14.1+noroms/hw/ppce500_mpc8544ds.c qemu-kvm-0.15.0+noroms/hw/ppce500_mpc8544ds.c --- qemu-kvm-0.14.1+noroms/hw/ppce500_mpc8544ds.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppce500_mpc8544ds.c 2011-08-09 07:40:29.000000000 -0500 @@ -28,10 +28,10 @@ #include "kvm_ppc.h" #include "device_tree.h" #include "openpic.h" -#include "ppce500.h" +#include "ppc.h" #include "loader.h" #include "elf.h" -#include "qemu-kvm.h" +#include "sysbus.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 @@ -50,6 +50,13 @@ #define MPC8544_PCI_REGS_SIZE 0x1000 #define MPC8544_PCI_IO 0xE1000000 #define MPC8544_PCI_IOLEN 0x10000 +#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000) + +struct boot_info +{ + uint32_t dt_base; + uint32_t entry; +}; #ifdef CONFIG_FDT static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop) @@ -75,18 +82,20 @@ } #endif -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) +static int mpc8544_load_device_tree(CPUState *env, + target_phys_addr_t addr, + uint32_t ramsize, + target_phys_addr_t initrd_base, + target_phys_addr_t initrd_size, + const char *kernel_cmdline) { int ret = -1; #ifdef CONFIG_FDT - uint32_t mem_reg_property[] = {0, ramsize}; + uint32_t mem_reg_property[] = {0, cpu_to_be32(ramsize)}; char *filename; int fdt_size; void *fdt; + uint8_t hypercall[16]; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -104,15 +113,19 @@ if (ret < 0) fprintf(stderr, "couldn't set /memory/reg\n"); - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); + if (initrd_size) { + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (ret < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); + } - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (ret < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); + } + } ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); @@ -146,6 +159,20 @@ mpc8544_copy_soc_cell(fdt, buf, "clock-frequency"); mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency"); + + /* indicate KVM hypercall interface */ + qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", + "linux,kvm"); + kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); + qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", + hypercall, sizeof(hypercall)); + } else { + const uint32_t freq = 400000000; + + qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", + "clock-frequency", freq); + qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", + "timebase-frequency", freq); } ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); @@ -157,6 +184,40 @@ return ret; } +/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ +static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) +{ + return (ffs(size >> 10) - 1) >> 1; +} + +static void mmubooke_create_initial_mapping(CPUState *env, + target_ulong va, + target_phys_addr_t pa) +{ + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); + target_phys_addr_t size; + + size = (booke206_page_size_to_tlb(256 * 1024 * 1024) << MAS1_TSIZE_SHIFT); + tlb->mas1 = MAS1_VALID | size; + tlb->mas2 = va & TARGET_PAGE_MASK; + tlb->mas7_3 = pa & TARGET_PAGE_MASK; + tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; +} + +static void mpc8544ds_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + struct boot_info *bi = env->load_info; + + cpu_reset(env); + + /* Set initial guest state. */ + env->gpr[1] = (16<<20) - 8; + env->gpr[3] = bi->dt_base; + env->nip = bi->entry; + mmubooke_create_initial_mapping(env, 0, 0); +} + static void mpc8544ds_init(ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, @@ -176,15 +237,28 @@ target_long initrd_size=0; int i=0; unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; - qemu_irq *irqs, *mpic, *pci_irqs; + qemu_irq *irqs, *mpic; + DeviceState *dev; + struct boot_info *boot_info; /* Setup CPU */ - env = cpu_ppc_init("e500v2_v30"); + if (cpu_model == NULL) { + cpu_model = "e500v2_v30"; + } + + env = cpu_ppc_init(cpu_model); if (!env) { fprintf(stderr, "Unable to initialize CPU!\n"); exit(1); } + /* XXX register timer? */ + ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR); + ppc_dcr_init(env, NULL, NULL); + + /* Register reset handler */ + qemu_register_reset(mpc8544ds_cpu_reset, env); + /* Fixup Memory size on a alignment boundary */ ram_size &= ~(RAM_SIZES_ALIGN - 1); @@ -211,13 +285,15 @@ serial_hds[0], 1, 1); } + /* General Utility device */ + sysbus_create_simple("mpc8544-guts", MPC8544_UTIL_BASE, NULL); + /* PCI */ - pci_irqs = qemu_malloc(sizeof(qemu_irq) * 4); - pci_irqs[0] = mpic[pci_irq_nrs[0]]; - pci_irqs[1] = mpic[pci_irq_nrs[1]]; - pci_irqs[2] = mpic[pci_irq_nrs[2]]; - pci_irqs[3] = mpic[pci_irq_nrs[3]]; - pci_bus = ppce500_pci_init(pci_irqs, MPC8544_PCI_REGS_BASE); + dev = sysbus_create_varargs("e500-pcihost", MPC8544_PCI_REGS_BASE, + mpic[pci_irq_nrs[0]], mpic[pci_irq_nrs[1]], + mpic[pci_irq_nrs[2]], mpic[pci_irq_nrs[3]], + NULL); + pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); if (!pci_bus) printf("couldn't create PCI controller!\n"); @@ -260,28 +336,28 @@ } } + boot_info = qemu_mallocz(sizeof(struct boot_info)); + /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { +#ifndef CONFIG_FDT + cpu_abort(env, "Compiled without FDT support - can't load kernel\n"); +#endif dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - if (mpc8544_load_device_tree(dt_base, ram_size, + if (mpc8544_load_device_tree(env, 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; - env->nip = entry; - /* XXX we currently depend on KVM to create some initial TLB entries. */ + boot_info->entry = entry; + boot_info->dt_base = dt_base; } + env->load_info = boot_info; - if (kvm_enabled()) + if (kvm_enabled()) { kvmppc_init(); - - return; + } } static QEMUMachine mpc8544ds_machine = { diff -Nru qemu-kvm-0.14.1+noroms/hw/ppce500_pci.c qemu-kvm-0.15.0+noroms/hw/ppce500_pci.c --- qemu-kvm-0.14.1+noroms/hw/ppce500_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppce500_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,7 +15,6 @@ */ #include "hw.h" -#include "ppce500.h" #include "pci.h" #include "pci_host.h" #include "bswap.h" @@ -29,7 +28,8 @@ #define PCIE500_CFGADDR 0x0 #define PCIE500_CFGDATA 0x4 #define PCIE500_REG_BASE 0xC00 -#define PCIE500_REG_SIZE (0x1000 - PCIE500_REG_BASE) +#define PCIE500_ALL_SIZE 0x1000 +#define PCIE500_REG_SIZE (PCIE500_ALL_SIZE - PCIE500_REG_BASE) #define PPCE500_PCI_CONFIG_ADDR 0x0 #define PPCE500_PCI_CONFIG_DATA 0x4 @@ -73,11 +73,15 @@ }; struct PPCE500PCIState { + PCIHostState pci_state; struct pci_outbound pob[PPCE500_PCI_NR_POBS]; struct pci_inbound pib[PPCE500_PCI_NR_PIBS]; uint32_t gasket_time; - PCIHostState pci_state; - PCIDevice *pci_dev; + qemu_irq irq[4]; + /* mmio maps */ + int cfgaddr; + int cfgdata; + int reg; }; typedef struct PPCE500PCIState PPCE500PCIState; @@ -198,7 +202,7 @@ ret = (irq_num + devno - 0x10) % 4; break; default: - printf("Error:%s:unknow dev number\n", __func__); + printf("Error:%s:unknown dev number\n", __func__); } pci_debug("%s: devfn %x irq %d -> %d devno:%x\n", __func__, @@ -216,111 +220,109 @@ qemu_set_irq(pic[irq_num], level); } -static void ppce500_pci_save(QEMUFile *f, void *opaque) -{ - PPCE500PCIState *controller = opaque; - int i; - - pci_device_save(controller->pci_dev, f); +static const VMStateDescription vmstate_pci_outbound = { + .name = "pci_outbound", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(potar, struct pci_outbound), + VMSTATE_UINT32(potear, struct pci_outbound), + VMSTATE_UINT32(powbar, struct pci_outbound), + VMSTATE_UINT32(powar, struct pci_outbound), + VMSTATE_END_OF_LIST() + } +}; - for (i = 0; i < PPCE500_PCI_NR_POBS; i++) { - qemu_put_be32s(f, &controller->pob[i].potar); - qemu_put_be32s(f, &controller->pob[i].potear); - qemu_put_be32s(f, &controller->pob[i].powbar); - qemu_put_be32s(f, &controller->pob[i].powar); +static const VMStateDescription vmstate_pci_inbound = { + .name = "pci_inbound", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(pitar, struct pci_inbound), + VMSTATE_UINT32(piwbar, struct pci_inbound), + VMSTATE_UINT32(piwbear, struct pci_inbound), + VMSTATE_UINT32(piwar, struct pci_inbound), + VMSTATE_END_OF_LIST() } +}; - for (i = 0; i < PPCE500_PCI_NR_PIBS; i++) { - qemu_put_be32s(f, &controller->pib[i].pitar); - qemu_put_be32s(f, &controller->pib[i].piwbar); - qemu_put_be32s(f, &controller->pib[i].piwbear); - qemu_put_be32s(f, &controller->pib[i].piwar); +static const VMStateDescription vmstate_ppce500_pci = { + .name = "ppce500_pci", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1, + vmstate_pci_outbound, struct pci_outbound), + VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1, + vmstate_pci_outbound, struct pci_inbound), + VMSTATE_UINT32(gasket_time, PPCE500PCIState), + VMSTATE_END_OF_LIST() } - qemu_put_be32s(f, &controller->gasket_time); +}; + +static void e500_pci_map(SysBusDevice *dev, target_phys_addr_t base) +{ + PCIHostState *h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); + PPCE500PCIState *s = DO_UPCAST(PPCE500PCIState, pci_state, h); + + cpu_register_physical_memory(base + PCIE500_CFGADDR, 4, s->cfgaddr); + cpu_register_physical_memory(base + PCIE500_CFGDATA, 4, s->cfgdata); + cpu_register_physical_memory(base + PCIE500_REG_BASE, PCIE500_REG_SIZE, + s->reg); } -static int ppce500_pci_load(QEMUFile *f, void *opaque, int version_id) +static int e500_pcihost_initfn(SysBusDevice *dev) { - PPCE500PCIState *controller = opaque; + PCIHostState *h; + PPCE500PCIState *s; + PCIBus *b; int i; - if (version_id != 1) - return -EINVAL; + h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); + s = DO_UPCAST(PPCE500PCIState, pci_state, h); - pci_device_load(controller->pci_dev, f); - - for (i = 0; i < PPCE500_PCI_NR_POBS; i++) { - qemu_get_be32s(f, &controller->pob[i].potar); - qemu_get_be32s(f, &controller->pob[i].potear); - qemu_get_be32s(f, &controller->pob[i].powbar); - qemu_get_be32s(f, &controller->pob[i].powar); + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); } - for (i = 0; i < PPCE500_PCI_NR_PIBS; i++) { - qemu_get_be32s(f, &controller->pib[i].pitar); - qemu_get_be32s(f, &controller->pib[i].piwbar); - qemu_get_be32s(f, &controller->pib[i].piwbear); - qemu_get_be32s(f, &controller->pib[i].piwar); - } - qemu_get_be32s(f, &controller->gasket_time); + b = pci_register_bus(&s->pci_state.busdev.qdev, NULL, mpc85xx_pci_set_irq, + mpc85xx_pci_map_irq, s->irq, PCI_DEVFN(0x11, 0), 4); + s->pci_state.bus = b; + + pci_create_simple(b, 0, "e500-host-bridge"); + + s->cfgaddr = pci_host_conf_register_mmio(&s->pci_state, DEVICE_BIG_ENDIAN); + s->cfgdata = pci_host_data_register_mmio(&s->pci_state, + DEVICE_LITTLE_ENDIAN); + s->reg = cpu_register_io_memory(e500_pci_reg_read, e500_pci_reg_write, s, + DEVICE_BIG_ENDIAN); + sysbus_init_mmio_cb(dev, PCIE500_ALL_SIZE, e500_pci_map); return 0; } -PCIBus *ppce500_pci_init(qemu_irq pci_irqs[4], target_phys_addr_t registers) +static PCIDeviceInfo e500_host_bridge_info = { + .qdev.name = "e500-host-bridge", + .qdev.desc = "Host bridge", + .qdev.size = sizeof(PCIDevice), + .vendor_id = PCI_VENDOR_ID_FREESCALE, + .device_id = PCI_DEVICE_ID_MPC8533E, + .class_id = PCI_CLASS_PROCESSOR_POWERPC, +}; + +static SysBusDeviceInfo e500_pcihost_info = { + .init = e500_pcihost_initfn, + .qdev.name = "e500-pcihost", + .qdev.size = sizeof(PPCE500PCIState), + .qdev.vmsd = &vmstate_ppce500_pci, +}; + +static void e500_pci_register(void) { - PPCE500PCIState *controller; - PCIDevice *d; - int index; - static int ppce500_pci_id; - - controller = qemu_mallocz(sizeof(PPCE500PCIState)); - - controller->pci_state.bus = pci_register_bus(NULL, "pci", - mpc85xx_pci_set_irq, - mpc85xx_pci_map_irq, - pci_irqs, PCI_DEVFN(0x11, 0), - 4); - d = pci_register_device(controller->pci_state.bus, - "host bridge", sizeof(PCIDevice), - 0, NULL, NULL); - - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_FREESCALE); - pci_config_set_device_id(d->config, PCI_DEVICE_ID_MPC8533E); - pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_POWERPC); - - controller->pci_dev = d; - - /* CFGADDR */ - 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, - 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, - 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(&d->qdev, "ppce500_pci", ppce500_pci_id++, - 1, ppce500_pci_save, ppce500_pci_load, controller); - - return controller->pci_state.bus; - -free: - printf("%s error\n", __func__); - qemu_free(controller); - return NULL; + sysbus_register_withprop(&e500_pcihost_info); + pci_qdev_register(&e500_host_bridge_info); } +device_init(e500_pci_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc.h qemu-kvm-0.15.0+noroms/hw/ppc.h --- qemu-kvm-0.14.1+noroms/hw/ppc.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc.h 2011-08-09 07:40:29.000000000 -0500 @@ -36,6 +36,7 @@ void ppce500_irq_init (CPUState *env); void ppc6xx_irq_init (CPUState *env); void ppc970_irq_init (CPUState *env); +void ppcPOWER7_irq_init (CPUState *env); /* PPC machines for OpenBIOS */ enum { diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc_mac.h qemu-kvm-0.15.0+noroms/hw/ppc_mac.h --- qemu-kvm-0.14.1+noroms/hw/ppc_mac.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc_mac.h 2011-08-09 07:40:29.000000000 -0500 @@ -35,8 +35,7 @@ #define PROM_ADDR 0xfff00000 #define KERNEL_LOAD_ADDR 0x01000000 -#define CMDLINE_ADDR 0x027ff000 -#define INITRD_LOAD_ADDR 0x02800000 +#define KERNEL_GAP 0x00100000 #define ESCC_CLOCK 3686400 diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc_newworld.c qemu-kvm-0.15.0+noroms/hw/ppc_newworld.c --- qemu-kvm-0.14.1+noroms/hw/ppc_newworld.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc_newworld.c 2011-08-09 07:40:29.000000000 -0500 @@ -120,6 +120,11 @@ return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; } +static target_phys_addr_t round_page(target_phys_addr_t addr) +{ + return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; +} + /* PowerPC Mac99 hardware initialisation */ static void ppc_core99_init (ram_addr_t ram_size, const char *boot_device, @@ -134,7 +139,7 @@ int unin_memory; int linux_boot, i; ram_addr_t ram_offset, bios_offset; - uint32_t kernel_base, initrd_base; + target_phys_addr_t kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; PCIBus *pci_bus; MacIONVRAMState *nvr; @@ -220,7 +225,7 @@ } /* load initrd */ if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; + initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { @@ -228,9 +233,11 @@ initrd_filename); exit(1); } + cmdline_base = round_page(initrd_base + initrd_size); } else { initrd_base = 0; initrd_size = 0; + cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); } ppc_boot_device = 'm'; } else { @@ -325,20 +332,13 @@ for(i = 0; i < nb_nics; i++) pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } + ide_drive_get(hd, MAX_IDE_BUS); dbdma = DBDMA_init(&dbdma_mem_index); /* 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]); + ide_mem_index[2] = pmac_ide_init(&hd[MAX_IDE_DEVS], pic[0x0e], dbdma, 0x1a, pic[0x02]); /* cuda also initialize ADB */ if (machine_arch == ARCH_MAC99_U3) { @@ -380,8 +380,8 @@ 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) { - 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_KERNEL_CMDLINE, cmdline_base); + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc_oldworld.c qemu-kvm-0.15.0+noroms/hw/ppc_oldworld.c --- qemu-kvm-0.14.1+noroms/hw/ppc_oldworld.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc_oldworld.c 2011-08-09 07:40:29.000000000 -0500 @@ -59,6 +59,11 @@ return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; } +static target_phys_addr_t round_page(target_phys_addr_t addr) +{ + return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; +} + static void ppc_heathrow_init (ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, @@ -71,7 +76,7 @@ qemu_irq *pic, **heathrow_irqs; int linux_boot, i; ram_addr_t ram_offset, bios_offset; - uint32_t kernel_base, initrd_base; + uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; MacIONVRAMState *nvr; @@ -157,7 +162,7 @@ } /* load initrd */ if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; + initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { @@ -165,9 +170,11 @@ initrd_filename); exit(1); } + cmdline_base = round_page(initrd_base + initrd_size); } else { initrd_base = 0; initrd_size = 0; + cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); } ppc_boot_device = 'm'; } else { @@ -236,21 +243,16 @@ pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } + ide_drive_get(hd, MAX_IDE_BUS); /* First IDE channel is a MAC IDE on the MacIO bus */ - hd[0] = drive_get(IF_IDE, 0, 0); - hd[1] = drive_get(IF_IDE, 0, 1); dbdma = DBDMA_init(&dbdma_mem_index); ide_mem_index[0] = -1; ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16, pic[0x02]); /* Second IDE channel is a CMD646 on the PCI bus */ - hd[0] = drive_get(IF_IDE, 1, 0); - hd[1] = drive_get(IF_IDE, 1, 1); + hd[0] = hd[MAX_IDE_DEVS]; + hd[1] = hd[MAX_IDE_DEVS + 1]; hd[3] = hd[2] = NULL; pci_cmd646_ide_init(pci_bus, hd, 0); @@ -283,8 +285,8 @@ 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) { - 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_KERNEL_CMDLINE, cmdline_base); + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc_prep.c qemu-kvm-0.15.0+noroms/hw/ppc_prep.c --- qemu-kvm-0.14.1+noroms/hw/ppc_prep.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ppc_prep.c 2011-08-09 07:40:29.000000000 -0500 @@ -82,7 +82,7 @@ static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; -//static PITState *pit; +//static ISADevice *pit; /* ISA IO ports bridge */ #define PPC_IO_BASE 0x80000000 @@ -105,7 +105,7 @@ { #if 0 int out; - out = pit_get_out(pit, 2, qemu_get_clock(vm_clock)); + out = pit_get_out(pit, 2, qemu_get_clock_ns(vm_clock)); dummy_refresh_clock ^= 1; return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) | (dummy_refresh_clock << 4); @@ -662,7 +662,7 @@ /* init basic PC hardware */ pci_vga_init(pci_bus); // openpic = openpic_init(0x00000000, 0xF0000000, 1); - // pit = pit_init(0x40, i8259[0]); + // pit = pit_init(0x40, 0); rtc_init(2000, NULL); if (serial_hds[0]) @@ -681,15 +681,7 @@ } } - 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); - } - + ide_drive_get(hd, MAX_IDE_BUS); for(i = 0; i < MAX_IDE_BUS; i++) { isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[2 * i], diff -Nru qemu-kvm-0.14.1+noroms/hw/ppc-viosrp.h qemu-kvm-0.15.0+noroms/hw/ppc-viosrp.h --- qemu-kvm-0.14.1+noroms/hw/ppc-viosrp.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/ppc-viosrp.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,216 @@ +/*****************************************************************************/ +/* srp.h -- SCSI RDMA Protocol definitions */ +/* */ +/* Written By: Colin Devilbis, IBM Corporation */ +/* */ +/* Copyright (C) 2003 IBM 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 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* */ +/* This file contains structures and definitions for IBM RPA (RS/6000 */ +/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */ +/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */ +/* commands between logical partitions. */ +/* */ +/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */ +/* between partitions. The definitions in this file are architected, */ +/* and cannot be changed without breaking compatibility with other versions */ +/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/ +/* between logical partitions */ +/*****************************************************************************/ +#ifndef PPC_VIOSRP_H +#define PPC_VIOSRP_H + +#define SRP_VERSION "16.a" +#define SRP_MAX_IU_LEN 256 +#define SRP_MAX_LOC_LEN 32 + +union srp_iu { + struct srp_login_req login_req; + struct srp_login_rsp login_rsp; + struct srp_login_rej login_rej; + struct srp_i_logout i_logout; + struct srp_t_logout t_logout; + struct srp_tsk_mgmt tsk_mgmt; + struct srp_cmd cmd; + struct srp_rsp rsp; + uint8_t reserved[SRP_MAX_IU_LEN]; +}; + +enum viosrp_crq_formats { + VIOSRP_SRP_FORMAT = 0x01, + VIOSRP_MAD_FORMAT = 0x02, + VIOSRP_OS400_FORMAT = 0x03, + VIOSRP_AIX_FORMAT = 0x04, + VIOSRP_LINUX_FORMAT = 0x06, + VIOSRP_INLINE_FORMAT = 0x07 +}; + +enum viosrp_crq_status { + VIOSRP_OK = 0x0, + VIOSRP_NONRECOVERABLE_ERR = 0x1, + VIOSRP_VIOLATES_MAX_XFER = 0x2, + VIOSRP_PARTNER_PANIC = 0x3, + VIOSRP_DEVICE_BUSY = 0x8, + VIOSRP_ADAPTER_FAIL = 0x10, + VIOSRP_OK2 = 0x99, +}; + +struct viosrp_crq { + uint8_t valid; /* used by RPA */ + uint8_t format; /* SCSI vs out-of-band */ + uint8_t reserved; + uint8_t status; /* non-scsi failure? (e.g. DMA failure) */ + uint16_t timeout; /* in seconds */ + uint16_t IU_length; /* in bytes */ + uint64_t IU_data_ptr; /* the TCE for transferring data */ +}; + +/* MADs are Management requests above and beyond the IUs defined in the SRP + * standard. + */ +enum viosrp_mad_types { + VIOSRP_EMPTY_IU_TYPE = 0x01, + VIOSRP_ERROR_LOG_TYPE = 0x02, + VIOSRP_ADAPTER_INFO_TYPE = 0x03, + VIOSRP_HOST_CONFIG_TYPE = 0x04, + VIOSRP_CAPABILITIES_TYPE = 0x05, + VIOSRP_ENABLE_FAST_FAIL = 0x08, +}; + +enum viosrp_mad_status { + VIOSRP_MAD_SUCCESS = 0x00, + VIOSRP_MAD_NOT_SUPPORTED = 0xF1, + VIOSRP_MAD_FAILED = 0xF7, +}; + +enum viosrp_capability_type { + MIGRATION_CAPABILITIES = 0x01, + RESERVATION_CAPABILITIES = 0x02, +}; + +enum viosrp_capability_support { + SERVER_DOES_NOT_SUPPORTS_CAP = 0x0, + SERVER_SUPPORTS_CAP = 0x01, + SERVER_CAP_DATA = 0x02, +}; + +enum viosrp_reserve_type { + CLIENT_RESERVE_SCSI_2 = 0x01, +}; + +enum viosrp_capability_flag { + CLIENT_MIGRATED = 0x01, + CLIENT_RECONNECT = 0x02, + CAP_LIST_SUPPORTED = 0x04, + CAP_LIST_DATA = 0x08, +}; + +/* + * Common MAD header + */ +struct mad_common { + uint32_t type; + uint16_t status; + uint16_t length; + uint64_t tag; +}; + +/* + * All SRP (and MAD) requests normally flow from the + * client to the server. There is no way for the server to send + * an asynchronous message back to the client. The Empty IU is used + * to hang out a meaningless request to the server so that it can respond + * asynchrouously with something like a SCSI AER + */ +struct viosrp_empty_iu { + struct mad_common common; + uint64_t buffer; + uint32_t port; +}; + +struct viosrp_error_log { + struct mad_common common; + uint64_t buffer; +}; + +struct viosrp_adapter_info { + struct mad_common common; + uint64_t buffer; +}; + +struct viosrp_host_config { + struct mad_common common; + uint64_t buffer; +}; + +struct viosrp_fast_fail { + struct mad_common common; +}; + +struct viosrp_capabilities { + struct mad_common common; + uint64_t buffer; +}; + +struct mad_capability_common { + uint32_t cap_type; + uint16_t length; + uint16_t server_support; +}; + +struct mad_reserve_cap { + struct mad_capability_common common; + uint32_t type; +}; + +struct mad_migration_cap { + struct mad_capability_common common; + uint32_t ecl; +}; + +struct capabilities { + uint32_t flags; + char name[SRP_MAX_LOC_LEN]; + char loc[SRP_MAX_LOC_LEN]; + struct mad_migration_cap migration; + struct mad_reserve_cap reserve; +}; + +union mad_iu { + struct viosrp_empty_iu empty_iu; + struct viosrp_error_log error_log; + struct viosrp_adapter_info adapter_info; + struct viosrp_host_config host_config; + struct viosrp_fast_fail fast_fail; + struct viosrp_capabilities capabilities; +}; + +union viosrp_iu { + union srp_iu srp; + union mad_iu mad; +}; + +struct mad_adapter_info_data { + char srp_version[8]; + char partition_name[96]; + uint32_t partition_number; + uint32_t mad_version; + uint32_t os_type; + uint32_t port_max_txu[8]; /* per-port maximum transfer */ +}; + +#endif diff -Nru qemu-kvm-0.14.1+noroms/hw/primecell.h qemu-kvm-0.15.0+noroms/hw/primecell.h --- qemu-kvm-0.14.1+noroms/hw/primecell.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/primecell.h 2011-08-09 07:40:29.000000000 -0500 @@ -11,4 +11,8 @@ /* arm_sysctl.c */ void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id); +/* arm_sysctl GPIO lines */ +#define ARM_SYSCTL_GPIO_MMC_WPROT 0 +#define ARM_SYSCTL_GPIO_MMC_CARDIN 1 + #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/ps2.c qemu-kvm-0.15.0+noroms/hw/ps2.c --- qemu-kvm-0.14.1+noroms/hw/ps2.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ps2.c 2011-08-09 07:40:29.000000000 -0500 @@ -110,14 +110,24 @@ /* Table to convert from PC scancodes to raw scancodes. */ static const unsigned char ps2_raw_keycode[128] = { - 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, - 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, - 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, - 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + 0, 118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89, 124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105, +114, 122, 112, 113, 127, 96, 97, 120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111, + 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110 +}; +static const unsigned char ps2_raw_keycode_set3[128] = { + 0, 8, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 17, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 92, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89, 126, 25, 41, 20, 7, 15, 23, 31, 39, + 47, 2, 63, 71, 79, 118, 95, 108, 117, 125, 132, 107, 115, 116, 124, 105, +114, 122, 112, 113, 127, 96, 97, 86, 94, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111, + 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110 }; void ps2_queue(void *opaque, int b) @@ -143,12 +153,16 @@ { PS2KbdState *s = opaque; - /* XXX: add support for scancode sets 1 and 3 */ - if (!s->translate && keycode < 0xe0 && s->scancode_set == 2) - { - if (keycode & 0x80) + /* XXX: add support for scancode set 1 */ + if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) { + if (keycode & 0x80) { ps2_queue(&s->common, 0xf0); - keycode = ps2_raw_keycode[keycode & 0x7f]; + } + if (s->scancode_set == 2) { + keycode = ps2_raw_keycode[keycode & 0x7f]; + } else if (s->scancode_set == 3) { + keycode = ps2_raw_keycode_set3[keycode & 0x7f]; + } } ps2_queue(&s->common, keycode); } diff -Nru qemu-kvm-0.14.1+noroms/hw/ptimer.c qemu-kvm-0.15.0+noroms/hw/ptimer.c --- qemu-kvm-0.14.1+noroms/hw/ptimer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ptimer.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GNU LGPL. + * This code is licensed under the GNU LGPL. */ #include "hw.h" #include "qemu-timer.h" @@ -11,7 +11,7 @@ struct ptimer_state { - int enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */ + uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */ uint64_t limit; uint64_t delta; uint32_t period_frac; @@ -68,7 +68,7 @@ uint64_t counter; if (s->enabled) { - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); /* Figure out the current counter value. */ if (now - s->next_event > 0 || s->period == 0) { @@ -122,7 +122,7 @@ { s->delta = count; if (s->enabled) { - s->next_event = qemu_get_clock(vm_clock); + s->next_event = qemu_get_clock_ns(vm_clock); ptimer_reload(s); } } @@ -137,7 +137,7 @@ return; } s->enabled = oneshot ? 2 : 1; - s->next_event = qemu_get_clock(vm_clock); + s->next_event = qemu_get_clock_ns(vm_clock); ptimer_reload(s); } @@ -159,7 +159,7 @@ s->period = period; s->period_frac = 0; if (s->enabled) { - s->next_event = qemu_get_clock(vm_clock); + s->next_event = qemu_get_clock_ns(vm_clock); ptimer_reload(s); } } @@ -170,7 +170,7 @@ s->period = 1000000000ll / freq; s->period_frac = (1000000000ll << 32) / freq; if (s->enabled) { - s->next_event = qemu_get_clock(vm_clock); + s->next_event = qemu_get_clock_ns(vm_clock); ptimer_reload(s); } } @@ -183,54 +183,27 @@ if (reload) s->delta = limit; if (s->enabled && reload) { - s->next_event = qemu_get_clock(vm_clock); + s->next_event = qemu_get_clock_ns(vm_clock); ptimer_reload(s); } } -void qemu_put_ptimer(QEMUFile *f, ptimer_state *s) -{ - qemu_put_byte(f, s->enabled); - qemu_put_be64s(f, &s->limit); - qemu_put_be64s(f, &s->delta); - qemu_put_be32s(f, &s->period_frac); - qemu_put_sbe64s(f, &s->period); - qemu_put_sbe64s(f, &s->last_event); - qemu_put_sbe64s(f, &s->next_event); - qemu_put_timer(f, s->timer); -} - -void qemu_get_ptimer(QEMUFile *f, ptimer_state *s) -{ - s->enabled = qemu_get_byte(f); - qemu_get_be64s(f, &s->limit); - qemu_get_be64s(f, &s->delta); - qemu_get_be32s(f, &s->period_frac); - qemu_get_sbe64s(f, &s->period); - qemu_get_sbe64s(f, &s->last_event); - qemu_get_sbe64s(f, &s->next_event); - qemu_get_timer(f, s->timer); -} - -static int get_ptimer(QEMUFile *f, void *pv, size_t size) -{ - ptimer_state *v = pv; - - qemu_get_ptimer(f, v); - return 0; -} - -static void put_ptimer(QEMUFile *f, void *pv, size_t size) -{ - ptimer_state *v = pv; - - qemu_put_ptimer(f, v); -} - -const VMStateInfo vmstate_info_ptimer = { +const VMStateDescription vmstate_ptimer = { .name = "ptimer", - .get = get_ptimer, - .put = put_ptimer, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(enabled, ptimer_state), + VMSTATE_UINT64(limit, ptimer_state), + VMSTATE_UINT64(delta, ptimer_state), + VMSTATE_UINT32(period_frac, ptimer_state), + VMSTATE_INT64(period, ptimer_state), + VMSTATE_INT64(last_event, ptimer_state), + VMSTATE_INT64(next_event, ptimer_state), + VMSTATE_TIMER(timer, ptimer_state), + VMSTATE_END_OF_LIST() + } }; ptimer_state *ptimer_init(QEMUBH *bh) @@ -239,6 +212,6 @@ s = (ptimer_state *)qemu_mallocz(sizeof(ptimer_state)); s->bh = bh; - s->timer = qemu_new_timer(vm_clock, ptimer_tick, s); + s->timer = qemu_new_timer_ns(vm_clock, ptimer_tick, s); return s; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx.c qemu-kvm-0.15.0+noroms/hw/pxa2xx.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -13,7 +13,6 @@ #include "pc.h" #include "i2c.h" #include "ssi.h" -#include "qemu-timer.h" #include "qemu-char.h" #include "blockdev.h" @@ -147,25 +146,16 @@ pxa2xx_pm_write, }; -static void pxa2xx_pm_save(QEMUFile *f, void *opaque) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - int i; - - for (i = 0; i < 0x40; i ++) - qemu_put_be32s(f, &s->pm_regs[i]); -} - -static int pxa2xx_pm_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - int i; - - for (i = 0; i < 0x40; i ++) - qemu_get_be32s(f, &s->pm_regs[i]); - - return 0; -} +static const VMStateDescription vmstate_pxa2xx_pm = { + .name = "pxa2xx_pm", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(pm_regs, PXA2xxState, 0x40), + VMSTATE_END_OF_LIST() + } +}; #define CCCR 0x00 /* Core Clock Configuration register */ #define CKEN 0x04 /* Clock Enable register */ @@ -228,29 +218,18 @@ pxa2xx_cm_write, }; -static void pxa2xx_cm_save(QEMUFile *f, void *opaque) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - int i; - - for (i = 0; i < 4; i ++) - qemu_put_be32s(f, &s->cm_regs[i]); - qemu_put_be32s(f, &s->clkcfg); - qemu_put_be32s(f, &s->pmnc); -} - -static int pxa2xx_cm_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - int i; - - for (i = 0; i < 4; i ++) - qemu_get_be32s(f, &s->cm_regs[i]); - qemu_get_be32s(f, &s->clkcfg); - qemu_get_be32s(f, &s->pmnc); - - return 0; -} +static const VMStateDescription vmstate_pxa2xx_cm = { + .name = "pxa2xx_cm", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(cm_regs, PXA2xxState, 4), + VMSTATE_UINT32(clkcfg, PXA2xxState), + VMSTATE_UINT32(pmnc, PXA2xxState), + VMSTATE_END_OF_LIST() + } +}; static uint32_t pxa2xx_clkpwr_read(void *opaque, int op2, int reg, int crm) { @@ -373,7 +352,7 @@ return s->pmnc; case CPCCNT: if (s->pmnc & 1) - return qemu_get_clock(vm_clock); + return qemu_get_clock_ns(vm_clock); else return 0; case CPINTEN: @@ -528,25 +507,16 @@ pxa2xx_mm_write, }; -static void pxa2xx_mm_save(QEMUFile *f, void *opaque) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - int i; - - for (i = 0; i < 0x1a; i ++) - qemu_put_be32s(f, &s->mm_regs[i]); -} - -static int pxa2xx_mm_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - int i; - - for (i = 0; i < 0x1a; i ++) - qemu_get_be32s(f, &s->mm_regs[i]); - - return 0; -} +static const VMStateDescription vmstate_pxa2xx_mm = { + .name = "pxa2xx_mm", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(mm_regs, PXA2xxState, 0x1a), + VMSTATE_END_OF_LIST() + } +}; /* Synchronous Serial Ports */ typedef struct { @@ -886,14 +856,43 @@ #define RTCPICR 0x34 /* RTC Periodic Interrupt Counter register */ #define PIAR 0x38 /* RTC Periodic Interrupt Alarm register */ -static inline void pxa2xx_rtc_int_update(PXA2xxState *s) +typedef struct { + SysBusDevice busdev; + uint32_t rttr; + uint32_t rtsr; + uint32_t rtar; + uint32_t rdar1; + uint32_t rdar2; + uint32_t ryar1; + uint32_t ryar2; + uint32_t swar1; + uint32_t swar2; + uint32_t piar; + uint32_t last_rcnr; + uint32_t last_rdcr; + uint32_t last_rycr; + uint32_t last_swcr; + uint32_t last_rtcpicr; + int64_t last_hz; + int64_t last_sw; + int64_t last_pi; + QEMUTimer *rtc_hz; + QEMUTimer *rtc_rdal1; + QEMUTimer *rtc_rdal2; + QEMUTimer *rtc_swal1; + QEMUTimer *rtc_swal2; + QEMUTimer *rtc_pi; + qemu_irq rtc_irq; +} PXA2xxRTCState; + +static inline void pxa2xx_rtc_int_update(PXA2xxRTCState *s) { - qemu_set_irq(s->pic[PXA2XX_PIC_RTCALARM], !!(s->rtsr & 0x2553)); + qemu_set_irq(s->rtc_irq, !!(s->rtsr & 0x2553)); } -static void pxa2xx_rtc_hzupdate(PXA2xxState *s) +static void pxa2xx_rtc_hzupdate(PXA2xxRTCState *s) { - int64_t rt = qemu_get_clock(rt_clock); + int64_t rt = qemu_get_clock_ms(rt_clock); s->last_rcnr += ((rt - s->last_hz) << 15) / (1000 * ((s->rttr & 0xffff) + 1)); s->last_rdcr += ((rt - s->last_hz) << 15) / @@ -901,23 +900,23 @@ s->last_hz = rt; } -static void pxa2xx_rtc_swupdate(PXA2xxState *s) +static void pxa2xx_rtc_swupdate(PXA2xxRTCState *s) { - int64_t rt = qemu_get_clock(rt_clock); + int64_t rt = qemu_get_clock_ms(rt_clock); if (s->rtsr & (1 << 12)) s->last_swcr += (rt - s->last_sw) / 10; s->last_sw = rt; } -static void pxa2xx_rtc_piupdate(PXA2xxState *s) +static void pxa2xx_rtc_piupdate(PXA2xxRTCState *s) { - int64_t rt = qemu_get_clock(rt_clock); + int64_t rt = qemu_get_clock_ms(rt_clock); if (s->rtsr & (1 << 15)) s->last_swcr += rt - s->last_pi; s->last_pi = rt; } -static inline void pxa2xx_rtc_alarm_update(PXA2xxState *s, +static inline void pxa2xx_rtc_alarm_update(PXA2xxRTCState *s, uint32_t rtsr) { if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) @@ -962,7 +961,7 @@ static inline void pxa2xx_rtc_hz_tick(void *opaque) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; s->rtsr |= (1 << 0); pxa2xx_rtc_alarm_update(s, s->rtsr); pxa2xx_rtc_int_update(s); @@ -970,7 +969,7 @@ static inline void pxa2xx_rtc_rdal1_tick(void *opaque) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; s->rtsr |= (1 << 4); pxa2xx_rtc_alarm_update(s, s->rtsr); pxa2xx_rtc_int_update(s); @@ -978,7 +977,7 @@ static inline void pxa2xx_rtc_rdal2_tick(void *opaque) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; s->rtsr |= (1 << 6); pxa2xx_rtc_alarm_update(s, s->rtsr); pxa2xx_rtc_int_update(s); @@ -986,7 +985,7 @@ static inline void pxa2xx_rtc_swal1_tick(void *opaque) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; s->rtsr |= (1 << 8); pxa2xx_rtc_alarm_update(s, s->rtsr); pxa2xx_rtc_int_update(s); @@ -994,7 +993,7 @@ static inline void pxa2xx_rtc_swal2_tick(void *opaque) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; s->rtsr |= (1 << 10); pxa2xx_rtc_alarm_update(s, s->rtsr); pxa2xx_rtc_int_update(s); @@ -1002,7 +1001,7 @@ static inline void pxa2xx_rtc_pi_tick(void *opaque) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; s->rtsr |= (1 << 13); pxa2xx_rtc_piupdate(s); s->last_rtcpicr = 0; @@ -1012,7 +1011,7 @@ static uint32_t pxa2xx_rtc_read(void *opaque, target_phys_addr_t addr) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; switch (addr) { case RTTR: @@ -1036,16 +1035,16 @@ case PIAR: return s->piar; case RCNR: - return s->last_rcnr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) / + return s->last_rcnr + ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) / (1000 * ((s->rttr & 0xffff) + 1)); case RDCR: - return s->last_rdcr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) / + return s->last_rdcr + ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) / (1000 * ((s->rttr & 0xffff) + 1)); case RYCR: return s->last_rycr; case SWCR: if (s->rtsr & (1 << 12)) - return s->last_swcr + (qemu_get_clock(rt_clock) - s->last_sw) / 10; + return s->last_swcr + (qemu_get_clock_ms(rt_clock) - s->last_sw) / 10; else return s->last_swcr; default: @@ -1058,7 +1057,7 @@ static void pxa2xx_rtc_write(void *opaque, target_phys_addr_t addr, uint32_t value) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; switch (addr) { case RTTR: @@ -1170,10 +1169,12 @@ pxa2xx_rtc_write, }; -static void pxa2xx_rtc_init(PXA2xxState *s) +static int pxa2xx_rtc_init(SysBusDevice *dev) { + PXA2xxRTCState *s = FROM_SYSBUS(PXA2xxRTCState, dev); struct tm tm; int wom; + int iomemtype; s->rttr = 0x7fff; s->rtsr = 0; @@ -1189,72 +1190,80 @@ s->last_swcr = (tm.tm_hour << 19) | (tm.tm_min << 13) | (tm.tm_sec << 7); s->last_rtcpicr = 0; - s->last_hz = s->last_sw = s->last_pi = qemu_get_clock(rt_clock); + s->last_hz = s->last_sw = s->last_pi = qemu_get_clock_ms(rt_clock); + + s->rtc_hz = qemu_new_timer_ms(rt_clock, pxa2xx_rtc_hz_tick, s); + s->rtc_rdal1 = qemu_new_timer_ms(rt_clock, pxa2xx_rtc_rdal1_tick, s); + s->rtc_rdal2 = qemu_new_timer_ms(rt_clock, pxa2xx_rtc_rdal2_tick, s); + s->rtc_swal1 = qemu_new_timer_ms(rt_clock, pxa2xx_rtc_swal1_tick, s); + s->rtc_swal2 = qemu_new_timer_ms(rt_clock, pxa2xx_rtc_swal2_tick, s); + s->rtc_pi = qemu_new_timer_ms(rt_clock, pxa2xx_rtc_pi_tick, s); + + sysbus_init_irq(dev, &s->rtc_irq); + + iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn, + pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x10000, iomemtype); - s->rtc_hz = qemu_new_timer(rt_clock, pxa2xx_rtc_hz_tick, s); - s->rtc_rdal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal1_tick, s); - s->rtc_rdal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal2_tick, s); - s->rtc_swal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal1_tick, s); - s->rtc_swal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal2_tick, s); - s->rtc_pi = qemu_new_timer(rt_clock, pxa2xx_rtc_pi_tick, s); + return 0; } -static void pxa2xx_rtc_save(QEMUFile *f, void *opaque) +static void pxa2xx_rtc_pre_save(void *opaque) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; pxa2xx_rtc_hzupdate(s); pxa2xx_rtc_piupdate(s); pxa2xx_rtc_swupdate(s); - - qemu_put_be32s(f, &s->rttr); - qemu_put_be32s(f, &s->rtsr); - qemu_put_be32s(f, &s->rtar); - qemu_put_be32s(f, &s->rdar1); - qemu_put_be32s(f, &s->rdar2); - qemu_put_be32s(f, &s->ryar1); - qemu_put_be32s(f, &s->ryar2); - qemu_put_be32s(f, &s->swar1); - qemu_put_be32s(f, &s->swar2); - qemu_put_be32s(f, &s->piar); - qemu_put_be32s(f, &s->last_rcnr); - qemu_put_be32s(f, &s->last_rdcr); - qemu_put_be32s(f, &s->last_rycr); - qemu_put_be32s(f, &s->last_swcr); - qemu_put_be32s(f, &s->last_rtcpicr); - qemu_put_sbe64s(f, &s->last_hz); - qemu_put_sbe64s(f, &s->last_sw); - qemu_put_sbe64s(f, &s->last_pi); } -static int pxa2xx_rtc_load(QEMUFile *f, void *opaque, int version_id) +static int pxa2xx_rtc_post_load(void *opaque, int version_id) { - PXA2xxState *s = (PXA2xxState *) opaque; - - qemu_get_be32s(f, &s->rttr); - qemu_get_be32s(f, &s->rtsr); - qemu_get_be32s(f, &s->rtar); - qemu_get_be32s(f, &s->rdar1); - qemu_get_be32s(f, &s->rdar2); - qemu_get_be32s(f, &s->ryar1); - qemu_get_be32s(f, &s->ryar2); - qemu_get_be32s(f, &s->swar1); - qemu_get_be32s(f, &s->swar2); - qemu_get_be32s(f, &s->piar); - qemu_get_be32s(f, &s->last_rcnr); - qemu_get_be32s(f, &s->last_rdcr); - qemu_get_be32s(f, &s->last_rycr); - qemu_get_be32s(f, &s->last_swcr); - qemu_get_be32s(f, &s->last_rtcpicr); - qemu_get_sbe64s(f, &s->last_hz); - qemu_get_sbe64s(f, &s->last_sw); - qemu_get_sbe64s(f, &s->last_pi); + PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; pxa2xx_rtc_alarm_update(s, s->rtsr); return 0; } +static const VMStateDescription vmstate_pxa2xx_rtc_regs = { + .name = "pxa2xx_rtc", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .pre_save = pxa2xx_rtc_pre_save, + .post_load = pxa2xx_rtc_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(rttr, PXA2xxRTCState), + VMSTATE_UINT32(rtsr, PXA2xxRTCState), + VMSTATE_UINT32(rtar, PXA2xxRTCState), + VMSTATE_UINT32(rdar1, PXA2xxRTCState), + VMSTATE_UINT32(rdar2, PXA2xxRTCState), + VMSTATE_UINT32(ryar1, PXA2xxRTCState), + VMSTATE_UINT32(ryar2, PXA2xxRTCState), + VMSTATE_UINT32(swar1, PXA2xxRTCState), + VMSTATE_UINT32(swar2, PXA2xxRTCState), + VMSTATE_UINT32(piar, PXA2xxRTCState), + VMSTATE_UINT32(last_rcnr, PXA2xxRTCState), + VMSTATE_UINT32(last_rdcr, PXA2xxRTCState), + VMSTATE_UINT32(last_rycr, PXA2xxRTCState), + VMSTATE_UINT32(last_swcr, PXA2xxRTCState), + VMSTATE_UINT32(last_rtcpicr, PXA2xxRTCState), + VMSTATE_INT64(last_hz, PXA2xxRTCState), + VMSTATE_INT64(last_sw, PXA2xxRTCState), + VMSTATE_INT64(last_pi, PXA2xxRTCState), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo pxa2xx_rtc_sysbus_info = { + .init = pxa2xx_rtc_init, + .qdev.name = "pxa2xx_rtc", + .qdev.desc = "PXA2xx RTC Controller", + .qdev.size = sizeof(PXA2xxRTCState), + .qdev.vmsd = &vmstate_pxa2xx_rtc_regs, +}; + /* I2C Interface */ typedef struct { i2c_slave i2c; @@ -1262,10 +1271,12 @@ } PXA2xxI2CSlaveState; struct PXA2xxI2CState { + SysBusDevice busdev; PXA2xxI2CSlaveState *slave; i2c_bus *bus; qemu_irq irq; - target_phys_addr_t offset; + uint32_t offset; + uint32_t region_size; uint16_t control; uint16_t status; @@ -1499,27 +1510,42 @@ PXA2xxI2CState *pxa2xx_i2c_init(target_phys_addr_t base, qemu_irq irq, uint32_t region_size) { - int iomemtype; DeviceState *dev; - PXA2xxI2CState *s = qemu_mallocz(sizeof(PXA2xxI2CState)); + SysBusDevice *i2c_dev; + PXA2xxI2CState *s; + + i2c_dev = sysbus_from_qdev(qdev_create(NULL, "pxa2xx_i2c")); + qdev_prop_set_uint32(&i2c_dev->qdev, "size", region_size + 1); + qdev_prop_set_uint32(&i2c_dev->qdev, "offset", + base - (base & (~region_size) & TARGET_PAGE_MASK)); + + qdev_init_nofail(&i2c_dev->qdev); + sysbus_mmio_map(i2c_dev, 0, base & ~region_size); + sysbus_connect_irq(i2c_dev, 0, irq); + + s = FROM_SYSBUS(PXA2xxI2CState, i2c_dev); /* FIXME: Should the slave device really be on a separate bus? */ dev = i2c_create_slave(i2c_init_bus(NULL, "dummy"), "pxa2xx-i2c-slave", 0); s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE_FROM_QDEV(dev)); s->slave->host = s; - s->irq = irq; - s->bus = i2c_init_bus(NULL, "i2c"); - s->offset = base - (base & (~region_size) & TARGET_PAGE_MASK); + return s; +} + +static int pxa2xx_i2c_initfn(SysBusDevice *dev) +{ + PXA2xxI2CState *s = FROM_SYSBUS(PXA2xxI2CState, dev); + int iomemtype; + + s->bus = i2c_init_bus(&dev->qdev, "i2c"); iomemtype = cpu_register_io_memory(pxa2xx_i2c_readfn, pxa2xx_i2c_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base & ~region_size, - region_size + 1, iomemtype); - - vmstate_register(NULL, base, &vmstate_pxa2xx_i2c, s); + sysbus_init_mmio(dev, s->region_size, iomemtype); + sysbus_init_irq(dev, &s->irq); - return s; + return 0; } i2c_bus *pxa2xx_i2c_bus(PXA2xxI2CState *s) @@ -1527,6 +1553,19 @@ return s->bus; } +static SysBusDeviceInfo pxa2xx_i2c_info = { + .init = pxa2xx_i2c_initfn, + .qdev.name = "pxa2xx_i2c", + .qdev.desc = "PXA2xx I2C Bus Controller", + .qdev.size = sizeof(PXA2xxI2CState), + .qdev.vmsd = &vmstate_pxa2xx_i2c, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("size", PXA2xxI2CState, region_size, 0x10000), + DEFINE_PROP_UINT32("offset", PXA2xxI2CState, offset, 0), + DEFINE_PROP_END_OF_LIST(), + }, +}; + /* PXA Inter-IC Sound Controller */ static void pxa2xx_i2s_reset(PXA2xxI2SState *i2s) { @@ -1553,8 +1592,8 @@ tfs = (i2s->tx_len || i2s->fifo_len < SACR_TFTH(i2s->control[0])) && i2s->enable && !SACR_DPRL(i2s->control[1]); - pxa2xx_dma_request(i2s->dma, PXA2XX_RX_RQ_I2S, rfs); - pxa2xx_dma_request(i2s->dma, PXA2XX_TX_RQ_I2S, tfs); + qemu_set_irq(i2s->rx_dma, rfs); + qemu_set_irq(i2s->tx_dma, tfs); i2s->status &= 0xe0; if (i2s->fifo_len < 16 || !i2s->enable) @@ -1631,7 +1670,7 @@ } if (value & (1 << 4)) /* EFWR */ printf("%s: Attempt to use special function\n", __FUNCTION__); - s->enable = ((value ^ 4) & 5) == 5; /* ENB && !RST*/ + s->enable = (value & 9) == 1; /* ENB && !RST*/ pxa2xx_i2s_update(s); break; case SACR1: @@ -1680,39 +1719,23 @@ pxa2xx_i2s_write, }; -static void pxa2xx_i2s_save(QEMUFile *f, void *opaque) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - - qemu_put_be32s(f, &s->control[0]); - qemu_put_be32s(f, &s->control[1]); - qemu_put_be32s(f, &s->status); - qemu_put_be32s(f, &s->mask); - qemu_put_be32s(f, &s->clk); - - qemu_put_be32(f, s->enable); - qemu_put_be32(f, s->rx_len); - qemu_put_be32(f, s->tx_len); - qemu_put_be32(f, s->fifo_len); -} - -static int pxa2xx_i2s_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - - qemu_get_be32s(f, &s->control[0]); - qemu_get_be32s(f, &s->control[1]); - qemu_get_be32s(f, &s->status); - qemu_get_be32s(f, &s->mask); - qemu_get_be32s(f, &s->clk); - - s->enable = qemu_get_be32(f); - s->rx_len = qemu_get_be32(f); - s->tx_len = qemu_get_be32(f); - s->fifo_len = qemu_get_be32(f); - - return 0; -} +static const VMStateDescription vmstate_pxa2xx_i2s = { + .name = "pxa2xx_i2s", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(control, PXA2xxI2SState, 2), + VMSTATE_UINT32(status, PXA2xxI2SState), + VMSTATE_UINT32(mask, PXA2xxI2SState), + VMSTATE_UINT32(clk, PXA2xxI2SState), + VMSTATE_INT32(enable, PXA2xxI2SState), + VMSTATE_INT32(rx_len, PXA2xxI2SState), + VMSTATE_INT32(tx_len, PXA2xxI2SState), + VMSTATE_INT32(fifo_len, PXA2xxI2SState), + VMSTATE_END_OF_LIST() + } +}; static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx) { @@ -1737,14 +1760,15 @@ } static PXA2xxI2SState *pxa2xx_i2s_init(target_phys_addr_t base, - qemu_irq irq, PXA2xxDMAState *dma) + qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) { int iomemtype; PXA2xxI2SState *s = (PXA2xxI2SState *) qemu_mallocz(sizeof(PXA2xxI2SState)); s->irq = irq; - s->dma = dma; + s->rx_dma = rx_dma; + s->tx_dma = tx_dma; s->data_req = pxa2xx_i2s_data_req; pxa2xx_i2s_reset(s); @@ -1753,8 +1777,7 @@ pxa2xx_i2s_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100000, iomemtype); - register_savevm(NULL, "pxa2xx_i2s", base, 0, - pxa2xx_i2s_save, pxa2xx_i2s_load, s); + vmstate_register(NULL, base, &vmstate_pxa2xx_i2s, s); return s; } @@ -1762,7 +1785,8 @@ /* PXA Fast Infra-red Communications Port */ struct PXA2xxFIrState { qemu_irq irq; - PXA2xxDMAState *dma; + qemu_irq rx_dma; + qemu_irq tx_dma; int enable; CharDriverState *chr; @@ -1816,8 +1840,8 @@ (s->status[0] & (1 << 1)); /* TUR */ intr |= s->status[0] & 0x25; /* FRE, RAB, EIF */ - pxa2xx_dma_request(s->dma, PXA2XX_RX_RQ_ICP, (s->status[0] >> 4) & 1); - pxa2xx_dma_request(s->dma, PXA2XX_TX_RQ_ICP, (s->status[0] >> 3) & 1); + qemu_set_irq(s->rx_dma, (s->status[0] >> 4) & 1); + qemu_set_irq(s->tx_dma, (s->status[0] >> 3) & 1); qemu_set_irq(s->irq, intr && s->enable); } @@ -1996,7 +2020,7 @@ } static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base, - qemu_irq irq, PXA2xxDMAState *dma, + qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma, CharDriverState *chr) { int iomemtype; @@ -2004,7 +2028,8 @@ qemu_mallocz(sizeof(PXA2xxFIrState)); s->irq = irq; - s->dma = dma; + s->rx_dma = rx_dma; + s->tx_dma = tx_dma; s->chr = chr; pxa2xx_fir_reset(s); @@ -2065,10 +2090,16 @@ s->pic = pxa2xx_pic_init(0x40d00000, s->env); - s->dma = pxa27x_dma_init(0x40000000, s->pic[PXA2XX_PIC_DMA]); + s->dma = pxa27x_dma_init(0x40000000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA)); - pxa27x_timer_init(0x40a00000, &s->pic[PXA2XX_PIC_OST_0], - s->pic[PXA27X_PIC_OST_4_11]); + sysbus_create_varargs("pxa27x-timer", 0x40a00000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0), + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1), + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2), + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3), + qdev_get_gpio_in(s->pic, PXA27X_PIC_OST_4_11), + NULL); s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 121); @@ -2078,26 +2109,32 @@ exit(1); } s->mmc = pxa2xx_mmci_init(0x41100000, dinfo->bdrv, - s->pic[PXA2XX_PIC_MMC], s->dma); + qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), + qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), + qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); 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); + qdev_get_gpio_in(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, 0); + qdev_get_gpio_in(s->pic, pxa270_serial[i].irqn), + 14857000 / 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]); + s->fir = pxa2xx_fir_init(0x40800000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), + qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), + qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), + serial_hds[i]); - s->lcd = pxa2xx_lcdc_init(0x44000000, s->pic[PXA2XX_PIC_LCD]); + s->lcd = pxa2xx_lcdc_init(0x44000000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); s->cm_base = 0x41300000; s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ @@ -2105,7 +2142,7 @@ iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn, pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype); - register_savevm(NULL, "pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); @@ -2116,45 +2153,46 @@ iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn, pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype); - register_savevm(NULL, "pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); s->pm_base = 0x40f00000; iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn, pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->pm_base, 0x100, iomemtype); - register_savevm(NULL, "pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa27x_ssp[i].io_base; i ++); s->ssp = (SSIBus **)qemu_mallocz(sizeof(SSIBus *) * i); for (i = 0; pxa27x_ssp[i].io_base; i ++) { DeviceState *dev; dev = sysbus_create_simple("pxa2xx-ssp", pxa27x_ssp[i].io_base, - s->pic[pxa27x_ssp[i].irqn]); + qdev_get_gpio_in(s->pic, pxa27x_ssp[i].irqn)); s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } if (usb_enabled) { sysbus_create_simple("sysbus-ohci", 0x4c000000, - s->pic[PXA2XX_PIC_USBH1]); + qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); } s->pcmcia[0] = pxa2xx_pcmcia_init(0x20000000); s->pcmcia[1] = pxa2xx_pcmcia_init(0x30000000); - s->rtc_base = 0x40900000; - iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn, - pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->rtc_base, 0x1000, iomemtype); - pxa2xx_rtc_init(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); + sysbus_create_simple("pxa2xx_rtc", 0x40900000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); - s->i2s = pxa2xx_i2s_init(0x40400000, s->pic[PXA2XX_PIC_I2S], s->dma); + s->i2c[0] = pxa2xx_i2c_init(0x40301600, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff); + s->i2c[1] = pxa2xx_i2c_init(0x40f00100, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); + + s->i2s = pxa2xx_i2s_init(0x40400000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), + qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), + qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); - s->kp = pxa27x_keypad_init(0x41500000, s->pic[PXA2XX_PIC_KEYPAD]); + s->kp = pxa27x_keypad_init(0x41500000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_KEYPAD)); /* GPIO1 resets the processor */ /* The handler can be overridden by board-specific code */ @@ -2188,9 +2226,15 @@ s->pic = pxa2xx_pic_init(0x40d00000, s->env); - s->dma = pxa255_dma_init(0x40000000, s->pic[PXA2XX_PIC_DMA]); + s->dma = pxa255_dma_init(0x40000000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA)); - pxa25x_timer_init(0x40a00000, &s->pic[PXA2XX_PIC_OST_0]); + sysbus_create_varargs("pxa25x-timer", 0x40a00000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0), + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1), + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2), + qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3), + NULL); s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 85); @@ -2200,27 +2244,33 @@ exit(1); } s->mmc = pxa2xx_mmci_init(0x41100000, dinfo->bdrv, - s->pic[PXA2XX_PIC_MMC], s->dma); + qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), + qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), + qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); for (i = 0; pxa255_serial[i].io_base; 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, 1); + qdev_get_gpio_in(s->pic, pxa255_serial[i].irqn), + 14745600 / 16, 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); + qdev_get_gpio_in(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]); + s->fir = pxa2xx_fir_init(0x40800000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), + qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), + qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), + serial_hds[i]); - s->lcd = pxa2xx_lcdc_init(0x44000000, s->pic[PXA2XX_PIC_LCD]); + s->lcd = pxa2xx_lcdc_init(0x44000000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); s->cm_base = 0x41300000; s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ @@ -2228,7 +2278,7 @@ iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn, pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype); - register_savevm(NULL, "pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); @@ -2239,43 +2289,43 @@ iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn, pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype); - register_savevm(NULL, "pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); s->pm_base = 0x40f00000; iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn, pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->pm_base, 0x100, iomemtype); - register_savevm(NULL, "pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa255_ssp[i].io_base; i ++); s->ssp = (SSIBus **)qemu_mallocz(sizeof(SSIBus *) * i); for (i = 0; pxa255_ssp[i].io_base; i ++) { DeviceState *dev; dev = sysbus_create_simple("pxa2xx-ssp", pxa255_ssp[i].io_base, - s->pic[pxa255_ssp[i].irqn]); + qdev_get_gpio_in(s->pic, pxa255_ssp[i].irqn)); s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } if (usb_enabled) { sysbus_create_simple("sysbus-ohci", 0x4c000000, - s->pic[PXA2XX_PIC_USBH1]); + qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); } s->pcmcia[0] = pxa2xx_pcmcia_init(0x20000000); s->pcmcia[1] = pxa2xx_pcmcia_init(0x30000000); - s->rtc_base = 0x40900000; - iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn, - pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->rtc_base, 0x1000, iomemtype); - pxa2xx_rtc_init(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); + sysbus_create_simple("pxa2xx_rtc", 0x40900000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); - s->i2s = pxa2xx_i2s_init(0x40400000, s->pic[PXA2XX_PIC_I2S], s->dma); + s->i2c[0] = pxa2xx_i2c_init(0x40301600, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff); + s->i2c[1] = pxa2xx_i2c_init(0x40f00100, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); + + s->i2s = pxa2xx_i2s_init(0x40400000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), + qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), + qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); /* GPIO1 resets the processor */ /* The handler can be overridden by board-specific code */ @@ -2287,6 +2337,8 @@ { i2c_register_slave(&pxa2xx_i2c_slave_info); sysbus_register_dev("pxa2xx-ssp", sizeof(PXA2xxSSPState), pxa2xx_ssp_init); + sysbus_register_withprop(&pxa2xx_i2c_info); + sysbus_register_withprop(&pxa2xx_rtc_sysbus_info); } device_init(pxa2xx_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx_dma.c qemu-kvm-0.15.0+noroms/hw/pxa2xx_dma.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx_dma.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx_dma.c 2011-08-09 07:40:29.000000000 -0500 @@ -5,11 +5,17 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" #include "pxa.h" +#include "sysbus.h" + +#define PXA255_DMA_NUM_CHANNELS 16 +#define PXA27X_DMA_NUM_CHANNELS 32 + +#define PXA2XX_DMA_NUM_REQUESTS 75 typedef struct { target_phys_addr_t descr; @@ -20,11 +26,8 @@ int request; } PXA2xxDMAChannel; -/* Allow the DMA to be used as a PIC. */ -typedef void (*pxa2xx_dma_handler_t)(void *opaque, int irq, int level); - -struct PXA2xxDMAState { - pxa2xx_dma_handler_t handler; +typedef struct PXA2xxDMAState { + SysBusDevice busdev; qemu_irq irq; uint32_t stopintr; @@ -39,16 +42,11 @@ int channels; PXA2xxDMAChannel *chan; - uint8_t *req; + uint8_t req[PXA2XX_DMA_NUM_REQUESTS]; /* Flag to avoid recursive DMA invocations. */ int running; -}; - -#define PXA255_DMA_NUM_CHANNELS 16 -#define PXA27X_DMA_NUM_CHANNELS 32 - -#define PXA2XX_DMA_NUM_REQUESTS 75 +} PXA2xxDMAState; #define DCSR0 0x0000 /* DMA Control / Status register for Channel 0 */ #define DCSR31 0x007c /* DMA Control / Status register for Channel 31 */ @@ -428,74 +426,42 @@ pxa2xx_dma_write }; -static void pxa2xx_dma_save(QEMUFile *f, void *opaque) +static void pxa2xx_dma_request(void *opaque, int req_num, int on) { - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - int i; - - qemu_put_be32(f, s->channels); - - qemu_put_be32s(f, &s->stopintr); - qemu_put_be32s(f, &s->eorintr); - qemu_put_be32s(f, &s->rasintr); - qemu_put_be32s(f, &s->startintr); - qemu_put_be32s(f, &s->endintr); - qemu_put_be32s(f, &s->align); - qemu_put_be32s(f, &s->pio); - - qemu_put_buffer(f, s->req, PXA2XX_DMA_NUM_REQUESTS); - for (i = 0; i < s->channels; i ++) { - qemu_put_betl(f, s->chan[i].descr); - qemu_put_betl(f, s->chan[i].src); - qemu_put_betl(f, s->chan[i].dest); - qemu_put_be32s(f, &s->chan[i].cmd); - qemu_put_be32s(f, &s->chan[i].state); - qemu_put_be32(f, s->chan[i].request); - }; -} - -static int pxa2xx_dma_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - int i; + PXA2xxDMAState *s = opaque; + int ch; + if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS) + hw_error("%s: Bad DMA request %i\n", __FUNCTION__, req_num); - if (qemu_get_be32(f) != s->channels) - return -EINVAL; + if (!(s->req[req_num] & DRCMR_MAPVLD)) + return; + ch = s->req[req_num] & DRCMR_CHLNUM; - qemu_get_be32s(f, &s->stopintr); - qemu_get_be32s(f, &s->eorintr); - qemu_get_be32s(f, &s->rasintr); - qemu_get_be32s(f, &s->startintr); - qemu_get_be32s(f, &s->endintr); - qemu_get_be32s(f, &s->align); - qemu_get_be32s(f, &s->pio); - - qemu_get_buffer(f, s->req, PXA2XX_DMA_NUM_REQUESTS); - for (i = 0; i < s->channels; i ++) { - s->chan[i].descr = qemu_get_betl(f); - s->chan[i].src = qemu_get_betl(f); - s->chan[i].dest = qemu_get_betl(f); - qemu_get_be32s(f, &s->chan[i].cmd); - qemu_get_be32s(f, &s->chan[i].state); - s->chan[i].request = qemu_get_be32(f); - }; + if (!s->chan[ch].request && on) + s->chan[ch].state |= DCSR_RASINTR; + else + s->chan[ch].state &= ~DCSR_RASINTR; + if (s->chan[ch].request && !on) + s->chan[ch].state |= DCSR_EORINT; - return 0; + s->chan[ch].request = on; + if (on) { + pxa2xx_dma_run(s); + pxa2xx_dma_update(s, ch); + } } -static PXA2xxDMAState *pxa2xx_dma_init(target_phys_addr_t base, - qemu_irq irq, int channels) +static int pxa2xx_dma_init(SysBusDevice *dev) { int i, iomemtype; PXA2xxDMAState *s; - s = (PXA2xxDMAState *) - qemu_mallocz(sizeof(PXA2xxDMAState)); + s = FROM_SYSBUS(PXA2xxDMAState, dev); + + if (s->channels <= 0) { + return -1; + } - s->channels = channels; s->chan = qemu_mallocz(sizeof(PXA2xxDMAChannel) * s->channels); - s->irq = irq; - s->handler = (pxa2xx_dma_handler_t) pxa2xx_dma_request; - s->req = qemu_mallocz(sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); memset(s->chan, 0, sizeof(PXA2xxDMAChannel) * s->channels); for (i = 0; i < s->channels; i ++) @@ -503,47 +469,100 @@ memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); + qdev_init_gpio_in(&dev->qdev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS); + iomemtype = cpu_register_io_memory(pxa2xx_dma_readfn, pxa2xx_dma_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x00010000, iomemtype); - - register_savevm(NULL, "pxa2xx_dma", 0, 0, pxa2xx_dma_save, pxa2xx_dma_load, s); + sysbus_init_mmio(dev, 0x00010000, iomemtype); + sysbus_init_irq(dev, &s->irq); - return s; + return 0; } -PXA2xxDMAState *pxa27x_dma_init(target_phys_addr_t base, - qemu_irq irq) +DeviceState *pxa27x_dma_init(target_phys_addr_t base, qemu_irq irq) { - return pxa2xx_dma_init(base, irq, PXA27X_DMA_NUM_CHANNELS); + DeviceState *dev; + + dev = qdev_create(NULL, "pxa2xx-dma"); + qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); + qdev_init_nofail(dev); + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + + return dev; } -PXA2xxDMAState *pxa255_dma_init(target_phys_addr_t base, - qemu_irq irq) +DeviceState *pxa255_dma_init(target_phys_addr_t base, qemu_irq irq) { - return pxa2xx_dma_init(base, irq, PXA255_DMA_NUM_CHANNELS); + DeviceState *dev; + + dev = qdev_create(NULL, "pxa2xx-dma"); + qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); + qdev_init_nofail(dev); + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + + return dev; } -void pxa2xx_dma_request(PXA2xxDMAState *s, int req_num, int on) +static bool is_version_0(void *opaque, int version_id) { - int ch; - if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS) - hw_error("%s: Bad DMA request %i\n", __FUNCTION__, req_num); + return version_id == 0; +} - if (!(s->req[req_num] & DRCMR_MAPVLD)) - return; - ch = s->req[req_num] & DRCMR_CHLNUM; +static VMStateDescription vmstate_pxa2xx_dma_chan = { + .name = "pxa2xx_dma_chan", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(descr, PXA2xxDMAChannel), + VMSTATE_UINTTL(src, PXA2xxDMAChannel), + VMSTATE_UINTTL(dest, PXA2xxDMAChannel), + VMSTATE_UINT32(cmd, PXA2xxDMAChannel), + VMSTATE_UINT32(state, PXA2xxDMAChannel), + VMSTATE_INT32(request, PXA2xxDMAChannel), + VMSTATE_END_OF_LIST(), + }, +}; - if (!s->chan[ch].request && on) - s->chan[ch].state |= DCSR_RASINTR; - else - s->chan[ch].state &= ~DCSR_RASINTR; - if (s->chan[ch].request && !on) - s->chan[ch].state |= DCSR_EORINT; +static VMStateDescription vmstate_pxa2xx_dma = { + .name = "pxa2xx_dma", + .version_id = 1, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UNUSED_TEST(is_version_0, 4), + VMSTATE_UINT32(stopintr, PXA2xxDMAState), + VMSTATE_UINT32(eorintr, PXA2xxDMAState), + VMSTATE_UINT32(rasintr, PXA2xxDMAState), + VMSTATE_UINT32(startintr, PXA2xxDMAState), + VMSTATE_UINT32(endintr, PXA2xxDMAState), + VMSTATE_UINT32(align, PXA2xxDMAState), + VMSTATE_UINT32(pio, PXA2xxDMAState), + VMSTATE_BUFFER(req, PXA2xxDMAState), + VMSTATE_STRUCT_VARRAY_POINTER_INT32(chan, PXA2xxDMAState, channels, + vmstate_pxa2xx_dma_chan, PXA2xxDMAChannel), + VMSTATE_END_OF_LIST(), + }, +}; - s->chan[ch].request = on; - if (on) { - pxa2xx_dma_run(s); - pxa2xx_dma_update(s, ch); - } +static SysBusDeviceInfo pxa2xx_dma_info = { + .init = pxa2xx_dma_init, + .qdev.name = "pxa2xx-dma", + .qdev.desc = "PXA2xx DMA controller", + .qdev.size = sizeof(PXA2xxDMAState), + .qdev.vmsd = &vmstate_pxa2xx_dma, + .qdev.props = (Property[]) { + DEFINE_PROP_INT32("channels", PXA2xxDMAState, channels, -1), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void pxa2xx_dma_register(void) +{ + sysbus_register_withprop(&pxa2xx_dma_info); } +device_init(pxa2xx_dma_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx_gpio.c qemu-kvm-0.15.0+noroms/hw/pxa2xx_gpio.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx_gpio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx_gpio.c 2011-08-09 07:40:29.000000000 -0500 @@ -253,7 +253,7 @@ }; DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, - CPUState *env, qemu_irq *pic, int lines) + CPUState *env, DeviceState *pic, int lines) { DeviceState *dev; @@ -263,9 +263,12 @@ qdev_init_nofail(dev); 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]); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, + qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_0)); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, + qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_1)); + sysbus_connect_irq(sysbus_from_qdev(dev), 2, + qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_X)); return dev; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx_keypad.c qemu-kvm-0.15.0+noroms/hw/pxa2xx_keypad.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx_keypad.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx_keypad.c 2011-08-09 07:40:29.000000000 -0500 @@ -82,22 +82,45 @@ struct PXA2xxKeyPadState { qemu_irq irq; struct keymap *map; + int pressed_cnt; + int alt_code; uint32_t kpc; uint32_t kpdk; uint32_t kprec; uint32_t kpmk; uint32_t kpas; - uint32_t kpasmkp0; - uint32_t kpasmkp1; - uint32_t kpasmkp2; - uint32_t kpasmkp3; + uint32_t kpasmkp[4]; uint32_t kpkdi; }; +static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col) +{ + int i; + for (i = 0; i < 4; i++) + { + *col = i * 2; + for (*row = 0; *row < 8; (*row)++) { + if (kp->kpasmkp[i] & (1 << *row)) + return; + } + *col = i * 2 + 1; + for (*row = 0; *row < 8; (*row)++) { + if (kp->kpasmkp[i] & (1 << (*row + 16))) + return; + } + } +} + static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode) { - int row, col,rel; + int row, col, rel, assert_irq = 0; + uint32_t val; + + if (keycode == 0xe0) { + kp->alt_code = 1; + return; + } if(!(kp->kpc & KPC_ME)) /* skip if not enabled */ return; @@ -108,46 +131,43 @@ rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */ keycode &= ~(0x80); /* strip qemu key release bit */ + if (kp->alt_code) { + keycode |= 0x80; + kp->alt_code = 0; + } + row = kp->map[keycode].row; col = kp->map[keycode].column; if(row == -1 || col == -1) return; - switch (col) { - case 0: - case 1: - if(rel) - kp->kpasmkp0 = ~(0xffffffff); - else - kp->kpasmkp0 |= KPASMKPx_MKC(row,col); - break; - case 2: - case 3: - if(rel) - kp->kpasmkp1 = ~(0xffffffff); - else - kp->kpasmkp1 |= KPASMKPx_MKC(row,col); - break; - case 4: - case 5: - if(rel) - kp->kpasmkp2 = ~(0xffffffff); - else - kp->kpasmkp2 |= KPASMKPx_MKC(row,col); - break; - case 6: - case 7: - if(rel) - kp->kpasmkp3 = ~(0xffffffff); - else - kp->kpasmkp3 |= KPASMKPx_MKC(row,col); - break; - } /* switch */ + + val = KPASMKPx_MKC(row, col); + if (rel) { + if (kp->kpasmkp[col / 2] & val) { + kp->kpasmkp[col / 2] &= ~val; + kp->pressed_cnt--; + assert_irq = 1; + } + } else { + if (!(kp->kpasmkp[col / 2] & val)) { + kp->kpasmkp[col / 2] |= val; + kp->pressed_cnt++; + assert_irq = 1; + } + } + kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf; + if (kp->pressed_cnt == 1) { + kp->kpas &= ~((0xf << 4) | 0xf); + if (rel) + pxa27x_keypad_find_pressed_key(kp, &row, &col); + kp->kpas |= ((row & 0xf) << 4) | (col & 0xf); + } goto out; } return; out: - if(kp->kpc & KPC_MIE) { + if (assert_irq && (kp->kpc & KPC_MIE)) { kp->kpc |= KPC_MI; qemu_irq_raise(kp->irq); } @@ -194,16 +214,16 @@ return s->kpas; break; case KPASMKP0: - return s->kpasmkp0; + return s->kpasmkp[0]; break; case KPASMKP1: - return s->kpasmkp1; + return s->kpasmkp[1]; break; case KPASMKP2: - return s->kpasmkp2; + return s->kpasmkp[2]; break; case KPASMKP3: - return s->kpasmkp3; + return s->kpasmkp[3]; break; case KPKDI: return s->kpkdi; @@ -237,16 +257,16 @@ s->kpas = value; break; case KPASMKP0: - s->kpasmkp0 = value; + s->kpasmkp[0] = value; break; case KPASMKP1: - s->kpasmkp1 = value; + s->kpasmkp[1] = value; break; case KPASMKP2: - s->kpasmkp2 = value; + s->kpasmkp[2] = value; break; case KPASMKP3: - s->kpasmkp3 = value; + s->kpasmkp[3] = value; break; case KPKDI: s->kpkdi = value; @@ -269,40 +289,22 @@ pxa2xx_keypad_write }; -static void pxa2xx_keypad_save(QEMUFile *f, void *opaque) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - - qemu_put_be32s(f, &s->kpc); - qemu_put_be32s(f, &s->kpdk); - qemu_put_be32s(f, &s->kprec); - qemu_put_be32s(f, &s->kpmk); - qemu_put_be32s(f, &s->kpas); - qemu_put_be32s(f, &s->kpasmkp0); - qemu_put_be32s(f, &s->kpasmkp1); - qemu_put_be32s(f, &s->kpasmkp2); - qemu_put_be32s(f, &s->kpasmkp3); - qemu_put_be32s(f, &s->kpkdi); - -} - -static int pxa2xx_keypad_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - - qemu_get_be32s(f, &s->kpc); - qemu_get_be32s(f, &s->kpdk); - qemu_get_be32s(f, &s->kprec); - qemu_get_be32s(f, &s->kpmk); - qemu_get_be32s(f, &s->kpas); - qemu_get_be32s(f, &s->kpasmkp0); - qemu_get_be32s(f, &s->kpasmkp1); - qemu_get_be32s(f, &s->kpasmkp2); - qemu_get_be32s(f, &s->kpasmkp3); - qemu_get_be32s(f, &s->kpkdi); - - return 0; -} +static const VMStateDescription vmstate_pxa2xx_keypad = { + .name = "pxa2xx_keypad", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(kpc, PXA2xxKeyPadState), + VMSTATE_UINT32(kpdk, PXA2xxKeyPadState), + VMSTATE_UINT32(kprec, PXA2xxKeyPadState), + VMSTATE_UINT32(kpmk, PXA2xxKeyPadState), + VMSTATE_UINT32(kpas, PXA2xxKeyPadState), + VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4), + VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState), + VMSTATE_END_OF_LIST() + } +}; PXA2xxKeyPadState *pxa27x_keypad_init(target_phys_addr_t base, qemu_irq irq) @@ -317,8 +319,7 @@ pxa2xx_keypad_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00100000, iomemtype); - register_savevm(NULL, "pxa2xx_keypad", 0, 0, - pxa2xx_keypad_save, pxa2xx_keypad_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s); return s; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx_lcd.c qemu-kvm-0.15.0+noroms/hw/pxa2xx_lcd.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx_lcd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx_lcd.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,6 +15,20 @@ #include "sysemu.h" #include "framebuffer.h" +struct DMAChannel { + target_phys_addr_t branch; + uint8_t up; + uint8_t palette[1024]; + uint8_t pbuffer[1024]; + void (*redraw)(PXA2xxLCDState *s, target_phys_addr_t addr, + int *miny, int *maxy); + + target_phys_addr_t descriptor; + target_phys_addr_t source; + uint32_t id; + uint32_t command; +}; + struct PXA2xxLCDState { qemu_irq irq; int irqlevel; @@ -50,19 +64,7 @@ uint32_t liidr; uint8_t bscntr; - struct { - target_phys_addr_t branch; - int up; - uint8_t palette[1024]; - uint8_t pbuffer[1024]; - void (*redraw)(PXA2xxLCDState *s, target_phys_addr_t addr, - int *miny, int *maxy); - - target_phys_addr_t descriptor; - target_phys_addr_t source; - uint32_t id; - uint32_t command; - } dma_ch[7]; + struct DMAChannel dma_ch[7]; qemu_irq vsync_cb; int orientation; @@ -663,7 +665,7 @@ } } -static void pxa2xx_lcdc_dma0_redraw_horiz(PXA2xxLCDState *s, +static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, target_phys_addr_t addr, int *miny, int *maxy) { int src_width, dest_width; @@ -690,7 +692,7 @@ fn, s->dma_ch[0].palette, miny, maxy); } -static void pxa2xx_lcdc_dma0_redraw_vert(PXA2xxLCDState *s, +static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, target_phys_addr_t addr, int *miny, int *maxy) { int src_width, dest_width; @@ -718,6 +720,67 @@ miny, maxy); } +static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, + target_phys_addr_t addr, int *miny, int *maxy) +{ + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) { + fn = s->line_fn[s->transp][s->bpp]; + } + if (!fn) { + return; + } + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { + src_width *= 3; + } else if (s->bpp > pxa_lcdc_16bpp) { + src_width *= 4; + } else if (s->bpp > pxa_lcdc_8bpp) { + src_width *= 2; + } + + dest_width = s->xres * s->dest_width; + *miny = 0; + framebuffer_update_display(s->ds, + addr, s->xres, s->yres, + src_width, -dest_width, -s->dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, miny, maxy); +} + +static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, + target_phys_addr_t addr, int *miny, int *maxy) +{ + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) { + fn = s->line_fn[s->transp][s->bpp]; + } + if (!fn) { + return; + } + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { + src_width *= 3; + } else if (s->bpp > pxa_lcdc_16bpp) { + src_width *= 4; + } else if (s->bpp > pxa_lcdc_8bpp) { + src_width *= 2; + } + + dest_width = s->yres * s->dest_width; + *miny = 0; + framebuffer_update_display(s->ds, + addr, s->xres, s->yres, + src_width, -s->dest_width, dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, + miny, maxy); +} + static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) { int width, height; @@ -728,10 +791,11 @@ height = LCCR2_LPP(s->control[2]) + 1; if (width != s->xres || height != s->yres) { - if (s->orientation) + if (s->orientation == 90 || s->orientation == 270) { qemu_console_resize(s->ds, height, width); - else + } else { qemu_console_resize(s->ds, width, height); + } s->invalidated = 1; s->xres = width; s->yres = height; @@ -795,10 +859,24 @@ } if (miny >= 0) { - if (s->orientation) - dpy_update(s->ds, miny, 0, maxy - miny, s->xres); - else - dpy_update(s->ds, 0, miny, s->xres, maxy - miny); + switch (s->orientation) { + case 0: + dpy_update(s->ds, 0, miny, s->xres, maxy - miny + 1); + break; + case 90: + dpy_update(s->ds, miny, 0, maxy - miny + 1, s->xres); + break; + case 180: + maxy = s->yres - maxy - 1; + miny = s->yres - miny - 1; + dpy_update(s->ds, 0, maxy, s->xres, miny - maxy + 1); + break; + case 270: + maxy = s->yres - maxy - 1; + miny = s->yres - miny - 1; + dpy_update(s->ds, maxy, 0, miny - maxy + 1, s->xres); + break; + } } pxa2xx_lcdc_int_update(s); @@ -820,10 +898,19 @@ { PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - if (angle) { - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_vert; - } else { - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_horiz; + switch (angle) { + case 0: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot0; + break; + case 90: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot90; + break; + case 180: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot180; + break; + case 270: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot270; + break; } s->orientation = angle; @@ -831,74 +918,26 @@ pxa2xx_lcdc_resize(s); } -static void pxa2xx_lcdc_save(QEMUFile *f, void *opaque) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int i; - - qemu_put_be32(f, s->irqlevel); - qemu_put_be32(f, s->transp); - - for (i = 0; i < 6; i ++) - qemu_put_be32s(f, &s->control[i]); - for (i = 0; i < 2; i ++) - qemu_put_be32s(f, &s->status[i]); - for (i = 0; i < 2; i ++) - qemu_put_be32s(f, &s->ovl1c[i]); - for (i = 0; i < 2; i ++) - qemu_put_be32s(f, &s->ovl2c[i]); - qemu_put_be32s(f, &s->ccr); - qemu_put_be32s(f, &s->cmdcr); - qemu_put_be32s(f, &s->trgbr); - qemu_put_be32s(f, &s->tcr); - qemu_put_be32s(f, &s->liidr); - qemu_put_8s(f, &s->bscntr); - - for (i = 0; i < 7; i ++) { - qemu_put_betl(f, s->dma_ch[i].branch); - qemu_put_byte(f, s->dma_ch[i].up); - qemu_put_buffer(f, s->dma_ch[i].pbuffer, sizeof(s->dma_ch[i].pbuffer)); - - qemu_put_betl(f, s->dma_ch[i].descriptor); - qemu_put_betl(f, s->dma_ch[i].source); - qemu_put_be32s(f, &s->dma_ch[i].id); - qemu_put_be32s(f, &s->dma_ch[i].command); +static const VMStateDescription vmstate_dma_channel = { + .name = "dma_channel", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(branch, struct DMAChannel), + VMSTATE_UINT8(up, struct DMAChannel), + VMSTATE_BUFFER(pbuffer, struct DMAChannel), + VMSTATE_UINTTL(descriptor, struct DMAChannel), + VMSTATE_UINTTL(source, struct DMAChannel), + VMSTATE_UINT32(id, struct DMAChannel), + VMSTATE_UINT32(command, struct DMAChannel), + VMSTATE_END_OF_LIST() } -} +}; -static int pxa2xx_lcdc_load(QEMUFile *f, void *opaque, int version_id) +static int pxa2xx_lcdc_post_load(void *opaque, int version_id) { - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int i; - - s->irqlevel = qemu_get_be32(f); - s->transp = qemu_get_be32(f); - - for (i = 0; i < 6; i ++) - qemu_get_be32s(f, &s->control[i]); - for (i = 0; i < 2; i ++) - qemu_get_be32s(f, &s->status[i]); - for (i = 0; i < 2; i ++) - qemu_get_be32s(f, &s->ovl1c[i]); - for (i = 0; i < 2; i ++) - qemu_get_be32s(f, &s->ovl2c[i]); - qemu_get_be32s(f, &s->ccr); - qemu_get_be32s(f, &s->cmdcr); - qemu_get_be32s(f, &s->trgbr); - qemu_get_be32s(f, &s->tcr); - qemu_get_be32s(f, &s->liidr); - qemu_get_8s(f, &s->bscntr); - - for (i = 0; i < 7; i ++) { - s->dma_ch[i].branch = qemu_get_betl(f); - s->dma_ch[i].up = qemu_get_byte(f); - qemu_get_buffer(f, s->dma_ch[i].pbuffer, sizeof(s->dma_ch[i].pbuffer)); - - s->dma_ch[i].descriptor = qemu_get_betl(f); - s->dma_ch[i].source = qemu_get_betl(f); - qemu_get_be32s(f, &s->dma_ch[i].id); - qemu_get_be32s(f, &s->dma_ch[i].command); - } + PXA2xxLCDState *s = opaque; s->bpp = LCCR3_BPP(s->control[3]); s->xres = s->yres = s->pal_for = -1; @@ -906,6 +945,31 @@ return 0; } +static const VMStateDescription vmstate_pxa2xx_lcdc = { + .name = "pxa2xx_lcdc", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = pxa2xx_lcdc_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32(irqlevel, PXA2xxLCDState), + VMSTATE_INT32(transp, PXA2xxLCDState), + VMSTATE_UINT32_ARRAY(control, PXA2xxLCDState, 6), + VMSTATE_UINT32_ARRAY(status, PXA2xxLCDState, 2), + VMSTATE_UINT32_ARRAY(ovl1c, PXA2xxLCDState, 2), + VMSTATE_UINT32_ARRAY(ovl2c, PXA2xxLCDState, 2), + VMSTATE_UINT32(ccr, PXA2xxLCDState), + VMSTATE_UINT32(cmdcr, PXA2xxLCDState), + VMSTATE_UINT32(trgbr, PXA2xxLCDState), + VMSTATE_UINT32(tcr, PXA2xxLCDState), + VMSTATE_UINT32(liidr, PXA2xxLCDState), + VMSTATE_UINT8(bscntr, PXA2xxLCDState), + VMSTATE_STRUCT_ARRAY(dma_ch, PXA2xxLCDState, 7, 0, + vmstate_dma_channel, struct DMAChannel), + VMSTATE_END_OF_LIST() + } +}; + #define BITS 8 #include "pxa2xx_template.h" #define BITS 15 @@ -970,8 +1034,7 @@ exit(1); } - register_savevm(NULL, "pxa2xx_lcdc", 0, 0, - pxa2xx_lcdc_save, pxa2xx_lcdc_load, s); + vmstate_register(NULL, 0, &vmstate_pxa2xx_lcdc, s); return s; } diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx_mmci.c qemu-kvm-0.15.0+noroms/hw/pxa2xx_mmci.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx_mmci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx_mmci.c 2011-08-09 07:40:29.000000000 -0500 @@ -10,10 +10,12 @@ #include "hw.h" #include "pxa.h" #include "sd.h" +#include "qdev.h" struct PXA2xxMMCIState { qemu_irq irq; - void *dma; + qemu_irq rx_dma; + qemu_irq tx_dma; SDState *card; @@ -102,10 +104,8 @@ if (s->cmdat & CMDAT_DMA_EN) { mask |= INT_RXFIFO_REQ | INT_TXFIFO_REQ; - pxa2xx_dma_request(s->dma, - PXA2XX_RX_RQ_MMCI, !!(s->intreq & INT_RXFIFO_REQ)); - pxa2xx_dma_request(s->dma, - PXA2XX_TX_RQ_MMCI, !!(s->intreq & INT_TXFIFO_REQ)); + qemu_set_irq(s->rx_dma, !!(s->intreq & INT_RXFIFO_REQ)); + qemu_set_irq(s->tx_dma, !!(s->intreq & INT_TXFIFO_REQ)); } qemu_set_irq(s->irq, !!(s->intreq & ~mask)); @@ -518,14 +518,16 @@ } PXA2xxMMCIState *pxa2xx_mmci_init(target_phys_addr_t base, - BlockDriverState *bd, qemu_irq irq, void *dma) + BlockDriverState *bd, qemu_irq irq, + qemu_irq rx_dma, qemu_irq tx_dma) { int iomemtype; PXA2xxMMCIState *s; s = (PXA2xxMMCIState *) qemu_mallocz(sizeof(PXA2xxMMCIState)); s->irq = irq; - s->dma = dma; + s->rx_dma = rx_dma; + s->tx_dma = tx_dma; iomemtype = cpu_register_io_memory(pxa2xx_mmci_readfn, pxa2xx_mmci_writefn, s, DEVICE_NATIVE_ENDIAN); diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx_pic.c qemu-kvm-0.15.0+noroms/hw/pxa2xx_pic.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx_pic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx_pic.c 2011-08-09 07:40:29.000000000 -0500 @@ -5,11 +5,12 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" #include "pxa.h" +#include "sysbus.h" #define ICIP 0x00 /* Interrupt Controller IRQ Pending register */ #define ICMR 0x04 /* Interrupt Controller Mask register */ @@ -31,6 +32,7 @@ #define PXA2XX_PIC_SRCS 40 typedef struct { + SysBusDevice busdev; CPUState *cpu_env; uint32_t int_enabled[2]; uint32_t int_pending[2]; @@ -241,51 +243,17 @@ pxa2xx_pic_mem_write, }; -static void pxa2xx_pic_save(QEMUFile *f, void *opaque) +static int pxa2xx_pic_post_load(void *opaque, int version_id) { - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - int i; - - for (i = 0; i < 2; i ++) - qemu_put_be32s(f, &s->int_enabled[i]); - for (i = 0; i < 2; i ++) - qemu_put_be32s(f, &s->int_pending[i]); - for (i = 0; i < 2; i ++) - qemu_put_be32s(f, &s->is_fiq[i]); - qemu_put_be32s(f, &s->int_idle); - for (i = 0; i < PXA2XX_PIC_SRCS; i ++) - qemu_put_be32s(f, &s->priority[i]); -} - -static int pxa2xx_pic_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - int i; - - for (i = 0; i < 2; i ++) - qemu_get_be32s(f, &s->int_enabled[i]); - for (i = 0; i < 2; i ++) - qemu_get_be32s(f, &s->int_pending[i]); - for (i = 0; i < 2; i ++) - qemu_get_be32s(f, &s->is_fiq[i]); - qemu_get_be32s(f, &s->int_idle); - for (i = 0; i < PXA2XX_PIC_SRCS; i ++) - qemu_get_be32s(f, &s->priority[i]); - pxa2xx_pic_update(opaque); return 0; } -qemu_irq *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env) +DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env) { - PXA2xxPICState *s; + DeviceState *dev = qdev_create(NULL, "pxa2xx_pic"); int iomemtype; - qemu_irq *qi; - - s = (PXA2xxPICState *) - qemu_mallocz(sizeof(PXA2xxPICState)); - if (!s) - return NULL; + PXA2xxPICState *s = FROM_SYSBUS(PXA2xxPICState, sysbus_from_qdev(dev)); s->cpu_env = env; @@ -296,18 +264,53 @@ s->is_fiq[0] = 0; s->is_fiq[1] = 0; - qi = qemu_allocate_irqs(pxa2xx_pic_set_irq, s, PXA2XX_PIC_SRCS); + qdev_init_nofail(dev); + + qdev_init_gpio_in(dev, pxa2xx_pic_set_irq, PXA2XX_PIC_SRCS); /* Enable IC memory-mapped registers access. */ iomemtype = cpu_register_io_memory(pxa2xx_pic_readfn, pxa2xx_pic_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x00100000, iomemtype); + sysbus_init_mmio(sysbus_from_qdev(dev), 0x00100000, iomemtype); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); /* Enable IC coprocessor access. */ cpu_arm_set_cp_io(env, 6, pxa2xx_pic_cp_read, pxa2xx_pic_cp_write, s); - register_savevm(NULL, "pxa2xx_pic", 0, 0, pxa2xx_pic_save, - pxa2xx_pic_load, s); + return dev; +} + +static VMStateDescription vmstate_pxa2xx_pic_regs = { + .name = "pxa2xx_pic", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = pxa2xx_pic_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(int_enabled, PXA2xxPICState, 2), + VMSTATE_UINT32_ARRAY(int_pending, PXA2xxPICState, 2), + VMSTATE_UINT32_ARRAY(is_fiq, PXA2xxPICState, 2), + VMSTATE_UINT32(int_idle, PXA2xxPICState), + VMSTATE_UINT32_ARRAY(priority, PXA2xxPICState, PXA2XX_PIC_SRCS), + VMSTATE_END_OF_LIST(), + }, +}; + +static int pxa2xx_pic_initfn(SysBusDevice *dev) +{ + return 0; +} - return qi; +static SysBusDeviceInfo pxa2xx_pic_info = { + .init = pxa2xx_pic_initfn, + .qdev.name = "pxa2xx_pic", + .qdev.desc = "PXA2xx PIC", + .qdev.size = sizeof(PXA2xxPICState), + .qdev.vmsd = &vmstate_pxa2xx_pic_regs, +}; + +static void pxa2xx_pic_register(void) +{ + sysbus_register_withprop(&pxa2xx_pic_info); } +device_init(pxa2xx_pic_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa2xx_timer.c qemu-kvm-0.15.0+noroms/hw/pxa2xx_timer.c --- qemu-kvm-0.14.1+noroms/hw/pxa2xx_timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa2xx_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,13 +4,14 @@ * Copyright (c) 2006 Openedhand Ltd. * Copyright (c) 2006 Thorsten Zitterell * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" #include "qemu-timer.h" #include "sysemu.h" #include "pxa.h" +#include "sysbus.h" #define OSMR0 0x00 #define OSMR1 0x04 @@ -59,13 +60,14 @@ [5 ... 7] = 0, }; +typedef struct PXA2xxTimerInfo PXA2xxTimerInfo; + typedef struct { uint32_t value; - int level; qemu_irq irq; QEMUTimer *qtimer; int num; - void *info; + PXA2xxTimerInfo *info; } PXA2xxTimer0; typedef struct { @@ -77,22 +79,34 @@ uint32_t control; } PXA2xxTimer4; -typedef struct { +struct PXA2xxTimerInfo { + SysBusDevice busdev; + uint32_t flags; + int32_t clock; int32_t oldclock; uint64_t lastload; uint32_t freq; PXA2xxTimer0 timer[4]; - PXA2xxTimer4 *tm4; uint32_t events; uint32_t irq_enabled; uint32_t reset3; uint32_t snapshot; -} pxa2xx_timer_info; + + qemu_irq irq4; + PXA2xxTimer4 tm4[8]; +}; + +#define PXA2XX_TIMER_HAVE_TM4 0 + +static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s) +{ + return s->flags & (1 << PXA2XX_TIMER_HAVE_TM4); +} static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu) { - pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; int i; uint32_t now_vm; uint64_t new_qemu; @@ -109,7 +123,7 @@ static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n) { - pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; uint32_t now_vm; uint64_t new_qemu; static const int counters[8] = { 0, 0, 0, 0, 4, 4, 6, 6 }; @@ -136,7 +150,7 @@ static uint32_t pxa2xx_timer_read(void *opaque, target_phys_addr_t offset) { - pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; int tm = 0; switch (offset) { @@ -153,11 +167,11 @@ case OSMR6: tm ++; case OSMR5: tm ++; case OSMR4: - if (!s->tm4) + if (!pxa2xx_timer_has_tm4(s)) goto badreg; return s->tm4[tm].tm.value; case OSCR: - return s->clock + muldiv64(qemu_get_clock(vm_clock) - + return s->clock + muldiv64(qemu_get_clock_ns(vm_clock) - s->lastload, s->freq, get_ticks_per_sec()); case OSCR11: tm ++; case OSCR10: tm ++; @@ -167,13 +181,13 @@ case OSCR6: tm ++; case OSCR5: tm ++; case OSCR4: - if (!s->tm4) + if (!pxa2xx_timer_has_tm4(s)) goto badreg; if ((tm == 9 - 4 || tm == 11 - 4) && (s->tm4[tm].control & (1 << 9))) { if (s->tm4[tm - 1].freq) s->snapshot = s->tm4[tm - 1].clock + muldiv64( - qemu_get_clock(vm_clock) - + qemu_get_clock_ns(vm_clock) - s->tm4[tm - 1].lastload, s->tm4[tm - 1].freq, get_ticks_per_sec()); else @@ -182,7 +196,7 @@ if (!s->tm4[tm].freq) return s->tm4[tm].clock; - return s->tm4[tm].clock + muldiv64(qemu_get_clock(vm_clock) - + return s->tm4[tm].clock + muldiv64(qemu_get_clock_ns(vm_clock) - s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec()); case OIER: return s->irq_enabled; @@ -198,7 +212,7 @@ case OMCR6: tm ++; case OMCR5: tm ++; case OMCR4: - if (!s->tm4) + if (!pxa2xx_timer_has_tm4(s)) goto badreg; return s->tm4[tm].control; case OSNR: @@ -215,7 +229,7 @@ uint32_t value) { int i, tm = 0; - pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; switch (offset) { case OSMR3: tm ++; @@ -223,7 +237,7 @@ case OSMR1: tm ++; case OSMR0: s->timer[tm].value = value; - pxa2xx_timer_update(s, qemu_get_clock(vm_clock)); + pxa2xx_timer_update(s, qemu_get_clock_ns(vm_clock)); break; case OSMR11: tm ++; case OSMR10: tm ++; @@ -233,14 +247,14 @@ case OSMR6: tm ++; case OSMR5: tm ++; case OSMR4: - if (!s->tm4) + if (!pxa2xx_timer_has_tm4(s)) goto badreg; s->tm4[tm].tm.value = value; - pxa2xx_timer_update4(s, qemu_get_clock(vm_clock), tm); + pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); break; case OSCR: s->oldclock = s->clock; - s->lastload = qemu_get_clock(vm_clock); + s->lastload = qemu_get_clock_ns(vm_clock); s->clock = value; pxa2xx_timer_update(s, s->lastload); break; @@ -252,10 +266,10 @@ case OSCR6: tm ++; case OSCR5: tm ++; case OSCR4: - if (!s->tm4) + if (!pxa2xx_timer_has_tm4(s)) goto badreg; s->tm4[tm].oldclock = s->tm4[tm].clock; - s->tm4[tm].lastload = qemu_get_clock(vm_clock); + s->tm4[tm].lastload = qemu_get_clock_ns(vm_clock); s->tm4[tm].clock = value; pxa2xx_timer_update4(s, s->tm4[tm].lastload, tm); break; @@ -263,20 +277,13 @@ s->irq_enabled = value & 0xfff; break; case OSSR: /* Status register */ + value &= s->events; s->events &= ~value; - for (i = 0; i < 4; i ++, value >>= 1) { - if (s->timer[i].level && (value & 1)) { - s->timer[i].level = 0; + for (i = 0; i < 4; i ++, value >>= 1) + if (value & 1) qemu_irq_lower(s->timer[i].irq); - } - } - if (s->tm4) { - for (i = 0; i < 8; i ++, value >>= 1) - if (s->tm4[i].tm.level && (value & 1)) - s->tm4[i].tm.level = 0; - if (!(s->events & 0xff0)) - qemu_irq_lower(s->tm4->tm.irq); - } + if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value) + qemu_irq_lower(s->irq4); break; case OWER: /* XXX: Reset on OSMR3 match? */ s->reset3 = value; @@ -285,7 +292,7 @@ case OMCR6: tm ++; case OMCR5: tm ++; case OMCR4: - if (!s->tm4) + if (!pxa2xx_timer_has_tm4(s)) goto badreg; s->tm4[tm].control = value & 0x0ff; /* XXX Stop if running (shouldn't happen) */ @@ -293,14 +300,14 @@ s->tm4[tm].freq = pxa2xx_timer4_freq[value & 7]; else { s->tm4[tm].freq = 0; - pxa2xx_timer_update4(s, qemu_get_clock(vm_clock), tm); + pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); } break; case OMCR11: tm ++; case OMCR10: tm ++; case OMCR9: tm ++; case OMCR8: tm += 4; - if (!s->tm4) + if (!pxa2xx_timer_has_tm4(s)) goto badreg; s->tm4[tm].control = value & 0x3ff; /* XXX Stop if running (shouldn't happen) */ @@ -309,7 +316,7 @@ pxa2xx_timer4_freq[(value & (1 << 8)) ? 0 : (value & 7)]; else { s->tm4[tm].freq = 0; - pxa2xx_timer_update4(s, qemu_get_clock(vm_clock), tm); + pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); } break; default: @@ -333,10 +340,9 @@ static void pxa2xx_timer_tick(void *opaque) { PXA2xxTimer0 *t = (PXA2xxTimer0 *) opaque; - pxa2xx_timer_info *i = (pxa2xx_timer_info *) t->info; + PXA2xxTimerInfo *i = t->info; if (i->irq_enabled & (1 << t->num)) { - t->level = 1; i->events |= 1 << t->num; qemu_irq_raise(t->irq); } @@ -351,140 +357,162 @@ static void pxa2xx_timer_tick4(void *opaque) { PXA2xxTimer4 *t = (PXA2xxTimer4 *) opaque; - pxa2xx_timer_info *i = (pxa2xx_timer_info *) t->tm.info; + PXA2xxTimerInfo *i = (PXA2xxTimerInfo *) t->tm.info; pxa2xx_timer_tick(&t->tm); if (t->control & (1 << 3)) t->clock = 0; if (t->control & (1 << 6)) - pxa2xx_timer_update4(i, qemu_get_clock(vm_clock), t->tm.num - 4); -} - -static void pxa2xx_timer_save(QEMUFile *f, void *opaque) -{ - pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; - int i; - - qemu_put_be32s(f, (uint32_t *) &s->clock); - qemu_put_be32s(f, (uint32_t *) &s->oldclock); - qemu_put_be64s(f, &s->lastload); - - for (i = 0; i < 4; i ++) { - qemu_put_be32s(f, &s->timer[i].value); - qemu_put_be32(f, s->timer[i].level); - } - if (s->tm4) - for (i = 0; i < 8; i ++) { - qemu_put_be32s(f, &s->tm4[i].tm.value); - qemu_put_be32(f, s->tm4[i].tm.level); - qemu_put_sbe32s(f, &s->tm4[i].oldclock); - qemu_put_sbe32s(f, &s->tm4[i].clock); - qemu_put_be64s(f, &s->tm4[i].lastload); - qemu_put_be32s(f, &s->tm4[i].freq); - qemu_put_be32s(f, &s->tm4[i].control); - } - - qemu_put_be32s(f, &s->events); - qemu_put_be32s(f, &s->irq_enabled); - qemu_put_be32s(f, &s->reset3); - qemu_put_be32s(f, &s->snapshot); + pxa2xx_timer_update4(i, qemu_get_clock_ns(vm_clock), t->tm.num - 4); + if (i->events & 0xff0) + qemu_irq_raise(i->irq4); } -static int pxa2xx_timer_load(QEMUFile *f, void *opaque, int version_id) +static int pxa25x_timer_post_load(void *opaque, int version_id) { - pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque; + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; int64_t now; int i; - qemu_get_be32s(f, (uint32_t *) &s->clock); - qemu_get_be32s(f, (uint32_t *) &s->oldclock); - qemu_get_be64s(f, &s->lastload); - - now = qemu_get_clock(vm_clock); - for (i = 0; i < 4; i ++) { - qemu_get_be32s(f, &s->timer[i].value); - s->timer[i].level = qemu_get_be32(f); - } + now = qemu_get_clock_ns(vm_clock); pxa2xx_timer_update(s, now); - if (s->tm4) - for (i = 0; i < 8; i ++) { - qemu_get_be32s(f, &s->tm4[i].tm.value); - s->tm4[i].tm.level = qemu_get_be32(f); - qemu_get_sbe32s(f, &s->tm4[i].oldclock); - qemu_get_sbe32s(f, &s->tm4[i].clock); - qemu_get_be64s(f, &s->tm4[i].lastload); - qemu_get_be32s(f, &s->tm4[i].freq); - qemu_get_be32s(f, &s->tm4[i].control); + if (pxa2xx_timer_has_tm4(s)) + for (i = 0; i < 8; i ++) pxa2xx_timer_update4(s, now, i); - } - - qemu_get_be32s(f, &s->events); - qemu_get_be32s(f, &s->irq_enabled); - qemu_get_be32s(f, &s->reset3); - qemu_get_be32s(f, &s->snapshot); return 0; } -static pxa2xx_timer_info *pxa2xx_timer_init(target_phys_addr_t base, - qemu_irq *irqs) +static int pxa2xx_timer_init(SysBusDevice *dev) { int i; int iomemtype; - pxa2xx_timer_info *s; + PXA2xxTimerInfo *s; - s = (pxa2xx_timer_info *) qemu_mallocz(sizeof(pxa2xx_timer_info)); + s = FROM_SYSBUS(PXA2xxTimerInfo, dev); s->irq_enabled = 0; s->oldclock = 0; s->clock = 0; - s->lastload = qemu_get_clock(vm_clock); + s->lastload = qemu_get_clock_ns(vm_clock); s->reset3 = 0; for (i = 0; i < 4; i ++) { s->timer[i].value = 0; - s->timer[i].irq = irqs[i]; + sysbus_init_irq(dev, &s->timer[i].irq); s->timer[i].info = s; s->timer[i].num = i; - s->timer[i].level = 0; - s->timer[i].qtimer = qemu_new_timer(vm_clock, + s->timer[i].qtimer = qemu_new_timer_ns(vm_clock, pxa2xx_timer_tick, &s->timer[i]); } + if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) { + sysbus_init_irq(dev, &s->irq4); + + for (i = 0; i < 8; i ++) { + s->tm4[i].tm.value = 0; + s->tm4[i].tm.info = s; + s->tm4[i].tm.num = i + 4; + s->tm4[i].freq = 0; + s->tm4[i].control = 0x0; + s->tm4[i].tm.qtimer = qemu_new_timer_ns(vm_clock, + pxa2xx_timer_tick4, &s->tm4[i]); + } + } iomemtype = cpu_register_io_memory(pxa2xx_timer_readfn, pxa2xx_timer_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x00001000, iomemtype); + sysbus_init_mmio(dev, 0x00001000, iomemtype); - register_savevm(NULL, "pxa2xx_timer", 0, 0, - pxa2xx_timer_save, pxa2xx_timer_load, s); - - return s; + return 0; } -void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs) +static const VMStateDescription vmstate_pxa2xx_timer0_regs = { + .name = "pxa2xx_timer0", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32(value, PXA2xxTimer0), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_pxa2xx_timer4_regs = { + .name = "pxa2xx_timer4", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(tm, PXA2xxTimer4, 1, + vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), + VMSTATE_INT32(oldclock, PXA2xxTimer4), + VMSTATE_INT32(clock, PXA2xxTimer4), + VMSTATE_UINT64(lastload, PXA2xxTimer4), + VMSTATE_UINT32(freq, PXA2xxTimer4), + VMSTATE_UINT32(control, PXA2xxTimer4), + VMSTATE_END_OF_LIST(), + }, +}; + +static bool pxa2xx_timer_has_tm4_test(void *opaque, int version_id) { - pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs); - s->freq = PXA25X_FREQ; - s->tm4 = NULL; + return pxa2xx_timer_has_tm4(opaque); } -void pxa27x_timer_init(target_phys_addr_t base, - qemu_irq *irqs, qemu_irq irq4) -{ - pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs); - int i; - s->freq = PXA27X_FREQ; - s->tm4 = (PXA2xxTimer4 *) qemu_mallocz(8 * - sizeof(PXA2xxTimer4)); - for (i = 0; i < 8; i ++) { - s->tm4[i].tm.value = 0; - s->tm4[i].tm.irq = irq4; - s->tm4[i].tm.info = s; - s->tm4[i].tm.num = i + 4; - s->tm4[i].tm.level = 0; - s->tm4[i].freq = 0; - s->tm4[i].control = 0x0; - s->tm4[i].tm.qtimer = qemu_new_timer(vm_clock, - pxa2xx_timer_tick4, &s->tm4[i]); +static const VMStateDescription vmstate_pxa2xx_timer_regs = { + .name = "pxa2xx_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = pxa25x_timer_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32(clock, PXA2xxTimerInfo), + VMSTATE_INT32(oldclock, PXA2xxTimerInfo), + VMSTATE_UINT64(lastload, PXA2xxTimerInfo), + VMSTATE_STRUCT_ARRAY(timer, PXA2xxTimerInfo, 4, 1, + vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), + VMSTATE_UINT32(events, PXA2xxTimerInfo), + VMSTATE_UINT32(irq_enabled, PXA2xxTimerInfo), + VMSTATE_UINT32(reset3, PXA2xxTimerInfo), + VMSTATE_UINT32(snapshot, PXA2xxTimerInfo), + VMSTATE_STRUCT_ARRAY_TEST(tm4, PXA2xxTimerInfo, 8, + pxa2xx_timer_has_tm4_test, 0, + vmstate_pxa2xx_timer4_regs, PXA2xxTimer4), + VMSTATE_END_OF_LIST(), } -} +}; + +static SysBusDeviceInfo pxa25x_timer_dev_info = { + .init = pxa2xx_timer_init, + .qdev.name = "pxa25x-timer", + .qdev.desc = "PXA25x timer", + .qdev.size = sizeof(PXA2xxTimerInfo), + .qdev.vmsd = &vmstate_pxa2xx_timer_regs, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ), + DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, + PXA2XX_TIMER_HAVE_TM4, false), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo pxa27x_timer_dev_info = { + .init = pxa2xx_timer_init, + .qdev.name = "pxa27x-timer", + .qdev.desc = "PXA27x timer", + .qdev.size = sizeof(PXA2xxTimerInfo), + .qdev.vmsd = &vmstate_pxa2xx_timer_regs, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA27X_FREQ), + DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, + PXA2XX_TIMER_HAVE_TM4, true), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void pxa2xx_timer_register(void) +{ + sysbus_register_withprop(&pxa25x_timer_dev_info); + sysbus_register_withprop(&pxa27x_timer_dev_info); +}; +device_init(pxa2xx_timer_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/pxa.h qemu-kvm-0.15.0+noroms/hw/pxa.h --- qemu-kvm-0.14.1+noroms/hw/pxa.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/pxa.h 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #ifndef PXA_H # define PXA_H "pxa.h" @@ -63,24 +63,16 @@ # define PXA2XX_INTERNAL_SIZE 0x40000 /* pxa2xx_pic.c */ -qemu_irq *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env); - -/* pxa2xx_timer.c */ -void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs); -void pxa27x_timer_init(target_phys_addr_t base, qemu_irq *irqs, qemu_irq irq4); +DeviceState *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env); /* pxa2xx_gpio.c */ DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, - CPUState *env, qemu_irq *pic, int lines); + CPUState *env, DeviceState *pic, int lines); void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler); /* pxa2xx_dma.c */ -typedef struct PXA2xxDMAState PXA2xxDMAState; -PXA2xxDMAState *pxa255_dma_init(target_phys_addr_t base, - qemu_irq irq); -PXA2xxDMAState *pxa27x_dma_init(target_phys_addr_t base, - qemu_irq irq); -void pxa2xx_dma_request(PXA2xxDMAState *s, int req_num, int on); +DeviceState *pxa255_dma_init(target_phys_addr_t base, qemu_irq irq); +DeviceState *pxa27x_dma_init(target_phys_addr_t base, qemu_irq irq); /* pxa2xx_lcd.c */ typedef struct PXA2xxLCDState PXA2xxLCDState; @@ -92,7 +84,8 @@ /* pxa2xx_mmci.c */ typedef struct PXA2xxMMCIState PXA2xxMMCIState; PXA2xxMMCIState *pxa2xx_mmci_init(target_phys_addr_t base, - BlockDriverState *bd, qemu_irq irq, void *dma); + BlockDriverState *bd, qemu_irq irq, + qemu_irq rx_dma, qemu_irq tx_dma); void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, qemu_irq coverswitch); @@ -125,9 +118,9 @@ typedef struct { CPUState *env; - qemu_irq *pic; + DeviceState *pic; qemu_irq reset; - PXA2xxDMAState *dma; + DeviceState *dma; DeviceState *gpio; PXA2xxLCDState *lcd; SSIBus **ssp; @@ -153,38 +146,12 @@ /* Performance monitoring */ uint32_t pmnc; - - /* Real-Time clock */ - target_phys_addr_t rtc_base; - uint32_t rttr; - uint32_t rtsr; - uint32_t rtar; - uint32_t rdar1; - uint32_t rdar2; - uint32_t ryar1; - uint32_t ryar2; - uint32_t swar1; - uint32_t swar2; - uint32_t piar; - uint32_t last_rcnr; - uint32_t last_rdcr; - uint32_t last_rycr; - uint32_t last_swcr; - uint32_t last_rtcpicr; - int64_t last_hz; - int64_t last_sw; - int64_t last_pi; - QEMUTimer *rtc_hz; - QEMUTimer *rtc_rdal1; - QEMUTimer *rtc_rdal2; - QEMUTimer *rtc_swal1; - QEMUTimer *rtc_swal2; - QEMUTimer *rtc_pi; } PXA2xxState; struct PXA2xxI2SState { qemu_irq irq; - PXA2xxDMAState *dma; + qemu_irq rx_dma; + qemu_irq tx_dma; void (*data_req)(void *, int, int); uint32_t control[2]; diff -Nru qemu-kvm-0.14.1+noroms/hw/qdev.c qemu-kvm-0.15.0+noroms/hw/qdev.c --- qemu-kvm-0.14.1+noroms/hw/qdev.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/qdev.c 2011-08-09 07:40:29.000000000 -0500 @@ -106,6 +106,18 @@ initialize the actual device emulation. */ DeviceState *qdev_create(BusState *bus, const char *name) { + DeviceState *dev; + + dev = qdev_try_create(bus, name); + if (!dev) { + hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name); + } + + return dev; +} + +DeviceState *qdev_try_create(BusState *bus, const char *name) +{ DeviceInfo *info; if (!bus) { @@ -114,7 +126,7 @@ info = qdev_find_info(bus->info, name); if (!info) { - hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name); + return NULL; } return qdev_create_from_info(bus, info); @@ -346,7 +358,8 @@ return 0; } -/* Like qdev_init(), but terminate program via hw_error() instead of + +/* Like qdev_init(), but terminate program via error_report() instead of returning an error value. This is okay during machine creation. Don't use for hotplug, because there callers need to recover from failure. Exception: if you know the device's init() callback can't @@ -358,7 +371,7 @@ DeviceInfo *info = dev->info; if (qdev_init(dev) < 0) { - error_report("Initialization of device %s failed\n", info->name); + error_report("Initialization of device %s failed", info->name); exit(1); } } @@ -446,7 +459,7 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) { - qdev_prop_set_macaddr(dev, "mac", nd->macaddr); + qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); if (nd->vlan) qdev_prop_set_vlan(dev, "vlan", nd->vlan); if (nd->netdev) @@ -455,6 +468,7 @@ qdev_prop_exists(dev, "vectors")) { qdev_prop_set_uint32(dev, "vectors", nd->nvectors); } + nd->instantiated = 1; } BusState *qdev_get_child_bus(DeviceState *dev, const char *name) diff -Nru qemu-kvm-0.14.1+noroms/hw/qdev.h qemu-kvm-0.15.0+noroms/hw/qdev.h --- qemu-kvm-0.14.1+noroms/hw/qdev.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/qdev.h 2011-08-09 07:40:29.000000000 -0500 @@ -122,6 +122,7 @@ /*** Board API. This should go away once we have a machine config file. ***/ DeviceState *qdev_create(BusState *bus, const char *name); +DeviceState *qdev_try_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; diff -Nru qemu-kvm-0.14.1+noroms/hw/qdev-properties.c qemu-kvm-0.15.0+noroms/hw/qdev-properties.c --- qemu-kvm-0.14.1+noroms/hw/qdev-properties.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/qdev-properties.c 2011-08-09 07:40:29.000000000 -0500 @@ -51,7 +51,7 @@ static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len) { - uint8_t *p = qdev_get_prop_ptr(dev, prop); + uint32_t *p = qdev_get_prop_ptr(dev, prop); return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off"); } @@ -351,8 +351,13 @@ CharDriverState **ptr = qdev_get_prop_ptr(dev, prop); *ptr = qemu_chr_find(str); - if (*ptr == NULL) + if (*ptr == NULL) { return -ENOENT; + } + if ((*ptr)->avail_connections < 1) { + return -EEXIST; + } + --(*ptr)->avail_connections; return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/qxl.c qemu-kvm-0.15.0+noroms/hw/qxl.c --- qemu-kvm-0.14.1+noroms/hw/qxl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/qxl.c 2011-08-09 07:40:29.000000000 -0500 @@ -336,6 +336,21 @@ info->n_surfaces = NUM_SURFACES; } +static const char *qxl_mode_to_string(int mode) +{ + switch (mode) { + case QXL_MODE_COMPAT: + return "compat"; + case QXL_MODE_NATIVE: + return "native"; + case QXL_MODE_UNDEFINED: + return "undefined"; + case QXL_MODE_VGA: + return "vga"; + } + return "INVALID"; +} + /* called from spice server thread context only */ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) { @@ -343,27 +358,34 @@ SimpleSpiceUpdate *update; QXLCommandRing *ring; QXLCommand *cmd; - int notify; + int notify, ret; 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; + ret = false; + qemu_mutex_lock(&qxl->ssd.lock); + if (qxl->ssd.update != NULL) { + update = qxl->ssd.update; + qxl->ssd.update = NULL; + *ext = update->ext; + ret = true; } - *ext = update->ext; - qxl_log_command(qxl, "vga", ext); - return true; + qemu_mutex_unlock(&qxl->ssd.lock); + if (ret) { + dprint(qxl, 2, "%s %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); + qxl_log_command(qxl, "vga", ext); + } + return ret; case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: case QXL_MODE_UNDEFINED: - dprint(qxl, 2, "%s: %s\n", __FUNCTION__, - qxl->cmdflags ? "compat" : "native"); + dprint(qxl, 4, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); ring = &qxl->ram->cmd_ring; if (SPICE_RING_IS_EMPTY(ring)) { return false; } + dprint(qxl, 2, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); SPICE_RING_CONS_ITEM(ring, cmd); ext->cmd = *cmd; ext->group_id = MEMSLOT_GROUP_GUEST; @@ -634,8 +656,8 @@ QXLRam *ram = d->ram; QXLRom *rom = d->rom; - assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); - assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); + assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); + assert(!d->ssd.running || 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); @@ -662,10 +684,8 @@ 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); @@ -795,9 +815,7 @@ { 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)); } @@ -866,9 +884,7 @@ 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) @@ -926,6 +942,8 @@ case QXL_IO_MEMSLOT_ADD: case QXL_IO_MEMSLOT_DEL: case QXL_IO_CREATE_PRIMARY: + case QXL_IO_UPDATE_IRQ: + case QXL_IO_LOG: break; default: if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT) @@ -938,10 +956,8 @@ 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: @@ -971,7 +987,8 @@ break; case QXL_IO_LOG: if (d->guestdebug) { - fprintf(stderr, "qxl/guest: %s", d->ram->log_buf); + fprintf(stderr, "qxl/guest-%d: %ld: %s", d->id, + qemu_get_clock_ns(vm_clock), d->ram->log_buf); } break; case QXL_IO_RESET: @@ -995,7 +1012,7 @@ break; case QXL_IO_DESTROY_PRIMARY: PANIC_ON(val != 0); - dprint(d, 1, "QXL_IO_DESTROY_PRIMARY\n"); + dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (%s)\n", qxl_mode_to_string(d->mode)); qxl_destroy_primary(d); break; case QXL_IO_DESTROY_SURFACE_WAIT: @@ -1169,11 +1186,14 @@ 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 */ + /* dirty all vram (which holds surfaces) and devram (primary surface) + * to make sure they are 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); + ram_addr_t vram_addr = qxl->vram_offset; + ram_addr_t surface0_addr = qxl->vga.vram_offset + qxl->shadow_rom.draw_area_offset; + qxl_set_dirty(vram_addr, vram_addr + qxl->vram_size); + qxl_set_dirty(surface0_addr, surface0_addr + qxl->shadow_rom.surface0_area_size); } } @@ -1209,7 +1229,6 @@ 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; @@ -1220,21 +1239,14 @@ 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; + default: 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); @@ -1304,13 +1316,15 @@ 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; + qemu_mutex_init(&qxl->ssd.lock); + qxl->ssd.mouse_x = -1; + qxl->ssd.mouse_y = -1; 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); } @@ -1330,7 +1344,6 @@ 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); } @@ -1377,7 +1390,8 @@ d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); - dprint(d, 1, "%s: restore mode\n", __FUNCTION__); + dprint(d, 1, "%s: restore mode (%s)\n", __FUNCTION__, + qxl_mode_to_string(d->mode)); newmode = d->mode; d->mode = QXL_MODE_UNDEFINED; switch (newmode) { @@ -1493,6 +1507,9 @@ .init = qxl_init_primary, .config_write = qxl_write_config, .romfile = "vgabios-qxl.bin", + .vendor_id = REDHAT_PCI_VENDOR_ID, + .device_id = QXL_DEVICE_ID_STABLE, + .class_id = PCI_CLASS_DISPLAY_VGA, .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), @@ -1511,6 +1528,9 @@ .qdev.reset = qxl_reset_handler, .qdev.vmsd = &qxl_vmstate, .init = qxl_init_secondary, + .vendor_id = REDHAT_PCI_VENDOR_ID, + .device_id = QXL_DEVICE_ID_STABLE, + .class_id = PCI_CLASS_DISPLAY_OTHER, .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), diff -Nru qemu-kvm-0.14.1+noroms/hw/qxl-logger.c qemu-kvm-0.15.0+noroms/hw/qxl-logger.c --- qemu-kvm-0.14.1+noroms/hw/qxl-logger.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/qxl-logger.c 2011-08-09 07:40:29.000000000 -0500 @@ -19,6 +19,7 @@ * along with this program; if not, see . */ +#include "qemu-timer.h" #include "qxl.h" static const char *qxl_type[] = { @@ -223,7 +224,8 @@ if (!qxl->cmdlog) { return; } - fprintf(stderr, "qxl-%d/%s:", qxl->id, ring); + fprintf(stderr, "%ld qxl-%d/%s:", qemu_get_clock_ns(vm_clock), + qxl->id, ring); fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data, qxl_name(qxl_type, ext->cmd.type), compat ? "(compat)" : ""); diff -Nru qemu-kvm-0.14.1+noroms/hw/qxl-render.c qemu-kvm-0.15.0+noroms/hw/qxl-render.c --- qemu-kvm-0.14.1+noroms/hw/qxl-render.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/qxl-render.c 2011-08-09 07:40:29.000000000 -0500 @@ -185,7 +185,6 @@ 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; @@ -198,8 +197,6 @@ } 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__); @@ -209,18 +206,20 @@ 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); + qemu_mutex_lock(&qxl->ssd.lock); + if (qxl->ssd.cursor) { + cursor_put(qxl->ssd.cursor); + } + qxl->ssd.cursor = c; + qxl->ssd.mouse_x = cmd->u.set.position.x; + qxl->ssd.mouse_y = cmd->u.set.position.y; + qemu_mutex_unlock(&qxl->ssd.lock); 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(); + qemu_mutex_lock(&qxl->ssd.lock); + qxl->ssd.mouse_x = cmd->u.position.x; + qxl->ssd.mouse_y = cmd->u.position.y; + qemu_mutex_unlock(&qxl->ssd.lock); break; } } diff -Nru qemu-kvm-0.14.1+noroms/hw/rc4030.c qemu-kvm-0.15.0+noroms/hw/rc4030.c --- qemu-kvm-0.14.1+noroms/hw/rc4030.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/rc4030.c 2011-08-09 07:40:29.000000000 -0500 @@ -104,7 +104,7 @@ tm_hz = 1000 / (s->itr + 1); - qemu_mod_timer(s->periodic_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(s->periodic_timer, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / tm_hz); } @@ -307,7 +307,7 @@ if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) { target_phys_addr_t dest = s->cache_ptag & ~0x1; dest += (s->cache_maint & 0x3) << 3; - cpu_physical_memory_rw(dest, (uint8_t*)&val, 4, 1); + cpu_physical_memory_write(dest, &val, 4); } break; /* Remote Speed Registers */ @@ -704,7 +704,7 @@ entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry); /* XXX: not sure. should we really use only lowest bits? */ entry_addr &= 0x7fffffff; - cpu_physical_memory_rw(entry_addr, (uint8_t *)&entry, sizeof(entry), 0); + cpu_physical_memory_read(entry_addr, &entry, sizeof(entry)); /* Read/write data at right place */ phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1)); @@ -811,7 +811,7 @@ *irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16); *dmas = rc4030_allocate_dmas(s, 4); - s->periodic_timer = qemu_new_timer(vm_clock, rc4030_periodic_timer, s); + s->periodic_timer = qemu_new_timer_ns(vm_clock, rc4030_periodic_timer, s); s->timer_irq = timer; s->jazz_bus_irq = jazz_bus; diff -Nru qemu-kvm-0.14.1+noroms/hw/realview.c qemu-kvm-0.15.0+noroms/hw/realview.c --- qemu-kvm-0.14.1+noroms/hw/realview.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/realview.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -17,7 +17,6 @@ #include "sysemu.h" #include "boards.h" #include "bitbang_i2c.h" -#include "sysbus.h" #include "blockdev.h" #define SMP_BOOT_ADDR 0xe0000000 @@ -104,17 +103,6 @@ .smp_loader_start = SMP_BOOT_ADDR, }; -static void secondary_cpu_reset(void *opaque) -{ - CPUState *env = opaque; - - cpu_reset(env); - /* Set entry point for secondary CPUs. This assumes we're using - the init code from arm_boot.c. Real hardware resets all CPUs - the same. */ - env->regs[15] = SMP_BOOT_ADDR; -} - /* The following two lists must be consistent. */ enum realview_board_type { BOARD_EB, @@ -138,10 +126,11 @@ { CPUState *env = NULL; ram_addr_t ram_offset; - DeviceState *dev; + DeviceState *dev, *sysctl, *gpio2; SysBusDevice *busdev; qemu_irq *irqp; qemu_irq pic[64]; + qemu_irq mmc_irq[2]; PCIBus *pci_bus; NICInfo *nd; i2c_bus *i2c; @@ -176,9 +165,6 @@ } irqp = arm_pic_init_cpu(env); cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ]; - if (n > 0) { - qemu_register_reset(secondary_cpu_reset, env); - } } if (arm_feature(env, ARM_FEATURE_V7)) { if (is_mpcore) { @@ -218,7 +204,11 @@ } sys_id = is_pb ? 0x01780500 : 0xc1400400; - arm_sysctl_init(0x10000000, sys_id, proc_id); + sysctl = qdev_create(NULL, "realview_sysctl"); + qdev_prop_set_uint32(sysctl, "sys_id", sys_id); + qdev_init_nofail(sysctl); + qdev_prop_set_uint32(sysctl, "proc_id", proc_id); + sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000); if (is_mpcore) { dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore"); @@ -257,9 +247,27 @@ sysbus_create_simple("sp804", 0x10011000, pic[4]); sysbus_create_simple("sp804", 0x10012000, pic[5]); + sysbus_create_simple("pl061", 0x10013000, pic[6]); + sysbus_create_simple("pl061", 0x10014000, pic[7]); + gpio2 = sysbus_create_simple("pl061", 0x10015000, pic[8]); + sysbus_create_simple("pl110_versatile", 0x10020000, pic[23]); - sysbus_create_varargs("pl181", 0x10005000, pic[17], pic[18], NULL); + dev = sysbus_create_varargs("pl181", 0x10005000, pic[17], pic[18], NULL); + /* Wire up MMC card detect and read-only signals. These have + * to go to both the PL061 GPIO and the sysctl register. + * Note that the PL181 orders these lines (readonly,inserted) + * and the PL061 has them the other way about. Also the card + * detect line is inverted. + */ + mmc_irq[0] = qemu_irq_split( + qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT), + qdev_get_gpio_in(gpio2, 1)); + mmc_irq[1] = qemu_irq_split( + qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN), + qemu_irq_invert(qdev_get_gpio_in(gpio2, 0))); + qdev_connect_gpio_out(dev, 0, mmc_irq[0]); + qdev_connect_gpio_out(dev, 1, mmc_irq[1]); sysbus_create_simple("pl031", 0x10017000, pic[10]); @@ -279,8 +287,8 @@ for(n = 0; n < nb_nics; n++) { nd = &nd_table[n]; - if ((!nd->model && !done_nic) - || strcmp(nd->model, is_pb ? "lan9118" : "smc91c111") == 0) { + if (!done_nic && (!nd->model || + strcmp(nd->model, is_pb ? "lan9118" : "smc91c111") == 0)) { if (is_pb) { lan9118_init(nd, 0x4e000000, pic[28]); } else { diff -Nru qemu-kvm-0.14.1+noroms/hw/realview_gic.c qemu-kvm-0.15.0+noroms/hw/realview_gic.c --- qemu-kvm-0.14.1+noroms/hw/realview_gic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/realview_gic.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/rtl8139.c qemu-kvm-0.15.0+noroms/hw/rtl8139.c --- qemu-kvm-0.14.1+noroms/hw/rtl8139.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/rtl8139.c 2011-08-09 07:40:29.000000000 -0500 @@ -45,14 +45,19 @@ * 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only * when strictly needed (required for for * Darwin) + * 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading */ +/* For crc32 */ +#include + #include "hw.h" #include "pci.h" #include "qemu-timer.h" #include "net.h" #include "loader.h" #include "sysemu.h" +#include "iov.h" /* debug RTL8139 card */ //#define DEBUG_RTL8139 1 @@ -62,14 +67,6 @@ /* debug RTL8139 card C+ mode only */ //#define DEBUG_RTL8139CP 1 -/* Calculate CRCs properly on Rx packets */ -#define RTL8139_CALCULATE_RXCRC 1 - -#if defined(RTL8139_CALCULATE_RXCRC) -/* For crc32 */ -#include -#endif - #define SET_MASKED(input, mask, curr) \ ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) ) @@ -77,10 +74,24 @@ #define MOD2(input, size) \ ( ( input ) & ( size - 1 ) ) +#define ETHER_ADDR_LEN 6 +#define ETHER_TYPE_LEN 2 +#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_MTU 1500 + +#define VLAN_TCI_LEN 2 +#define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN) + #if defined (DEBUG_RTL8139) -# define DEBUG_PRINT(x) do { printf x ; } while (0) +# define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "RTL8139: " fmt, ## __VA_ARGS__); } while (0) #else -# define DEBUG_PRINT(x) +static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...) +{ + return 0; +} #endif /* Symbolic offsets to registers. */ @@ -503,7 +514,7 @@ static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command) { - DEBUG_PRINT(("RTL8139: eeprom command 0x%02x\n", command)); + DPRINTF("eeprom command 0x%02x\n", command); switch (command & Chip9346_op_mask) { @@ -514,8 +525,8 @@ eeprom->eedo = 0; eeprom->tick = 0; eeprom->mode = Chip9346_data_read; - DEBUG_PRINT(("RTL8139: eeprom read from address 0x%02x data=0x%04x\n", - eeprom->address, eeprom->output)); + DPRINTF("eeprom read from address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->output); } break; @@ -525,8 +536,8 @@ eeprom->input = 0; eeprom->tick = 0; eeprom->mode = Chip9346_none; /* Chip9346_data_write */ - DEBUG_PRINT(("RTL8139: eeprom begin write to address 0x%02x\n", - eeprom->address)); + DPRINTF("eeprom begin write to address 0x%02x\n", + eeprom->address); } break; default: @@ -534,13 +545,13 @@ switch (command & Chip9346_op_ext_mask) { case Chip9346_op_write_enable: - DEBUG_PRINT(("RTL8139: eeprom write enabled\n")); + DPRINTF("eeprom write enabled\n"); break; case Chip9346_op_write_all: - DEBUG_PRINT(("RTL8139: eeprom begin write all\n")); + DPRINTF("eeprom begin write all\n"); break; case Chip9346_op_write_disable: - DEBUG_PRINT(("RTL8139: eeprom write disabled\n")); + DPRINTF("eeprom write disabled\n"); break; } break; @@ -553,7 +564,8 @@ ++ eeprom->tick; - DEBUG_PRINT(("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, eeprom->eedo)); + DPRINTF("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, + eeprom->eedo); switch (eeprom->mode) { @@ -563,7 +575,7 @@ eeprom->mode = Chip9346_read_command; eeprom->tick = 0; eeprom->input = 0; - DEBUG_PRINT(("eeprom: +++ synchronized, begin command read\n")); + DPRINTF("eeprom: +++ synchronized, begin command read\n"); } break; @@ -588,7 +600,7 @@ eeprom->input = 0; eeprom->tick = 0; - DEBUG_PRINT(("eeprom: +++ end of read, awaiting next command\n")); + DPRINTF("eeprom: +++ end of read, awaiting next command\n"); #else // original behaviour ++eeprom->address; @@ -596,8 +608,8 @@ eeprom->output = eeprom->contents[eeprom->address]; eeprom->tick = 0; - DEBUG_PRINT(("eeprom: +++ read next address 0x%02x data=0x%04x\n", - eeprom->address, eeprom->output)); + DPRINTF("eeprom: +++ read next address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->output); #endif } break; @@ -606,8 +618,8 @@ eeprom->input = (eeprom->input << 1) | (bit & 1); if (eeprom->tick == 16) { - DEBUG_PRINT(("RTL8139: eeprom write to address 0x%02x data=0x%04x\n", - eeprom->address, eeprom->input)); + DPRINTF("eeprom write to address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->input); eeprom->contents[eeprom->address] = eeprom->input; eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */ @@ -625,8 +637,7 @@ { eeprom->contents[i] = eeprom->input; } - DEBUG_PRINT(("RTL8139: eeprom filled with data=0x%04x\n", - eeprom->input)); + DPRINTF("eeprom filled with data=0x%04x\n", eeprom->input); eeprom->mode = Chip9346_enter_command_mode; eeprom->tick = 0; @@ -659,8 +670,8 @@ eeprom->eesk = eesk; eeprom->eedi = eedi; - DEBUG_PRINT(("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", - eeprom->eecs, eeprom->eesk, eeprom->eedi, eeprom->eedo)); + DPRINTF("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", eeprom->eecs, + eeprom->eesk, eeprom->eedi, eeprom->eedo); if (!old_eecs && eecs) { @@ -670,12 +681,12 @@ eeprom->output = 0; eeprom->mode = Chip9346_enter_command_mode; - DEBUG_PRINT(("=== eeprom: begin access, enter command mode\n")); + DPRINTF("=== eeprom: begin access, enter command mode\n"); } if (!eecs) { - DEBUG_PRINT(("=== eeprom: end access\n")); + DPRINTF("=== eeprom: end access\n"); return; } @@ -691,8 +702,8 @@ int isr; isr = (s->IntrStatus & s->IntrMask) & 0xffff; - DEBUG_PRINT(("RTL8139: Set IRQ to %d (%04x %04x)\n", - isr ? 1 : 0, s->IntrStatus, s->IntrMask)); + DPRINTF("Set IRQ to %d (%04x %04x)\n", isr ? 1 : 0, s->IntrStatus, + s->IntrMask); qemu_set_irq(s->dev.irq[0], (isr != 0)); } @@ -756,7 +767,7 @@ /* write packet data */ if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s))) { - DEBUG_PRINT((">>> RTL8139: rx packet wrapped in buffer at %d\n", size-wrapped)); + DPRINTF(">>> rx packet wrapped in buffer at %d\n", size - wrapped); if (size > wrapped) { @@ -817,20 +828,22 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) { RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + /* size is the length of the buffer passed to the driver */ int size = size_; + const uint8_t *dot1q_buf = NULL; uint32_t packet_header = 0; - uint8_t buf1[60]; + uint8_t buf1[MIN_BUF_SIZE + VLAN_HLEN]; static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - DEBUG_PRINT((">>> RTL8139: received len=%d\n", size)); + DPRINTF(">>> received len=%d\n", size); /* test if board clock is stopped */ if (!s->clock_enabled) { - DEBUG_PRINT(("RTL8139: stopped ==========================\n")); + DPRINTF("stopped ==========================\n"); return -1; } @@ -838,21 +851,21 @@ if (!rtl8139_receiver_enabled(s)) { - DEBUG_PRINT(("RTL8139: receiver disabled ================\n")); + DPRINTF("receiver disabled ================\n"); return -1; } /* XXX: check this */ if (s->RxConfig & AcceptAllPhys) { /* promiscuous: receive all */ - DEBUG_PRINT((">>> RTL8139: packet received in promiscuous mode\n")); + DPRINTF(">>> packet received in promiscuous mode\n"); } else { if (!memcmp(buf, broadcast_macaddr, 6)) { /* broadcast address */ if (!(s->RxConfig & AcceptBroadcast)) { - DEBUG_PRINT((">>> RTL8139: broadcast packet rejected\n")); + DPRINTF(">>> broadcast packet rejected\n"); /* update tally counter */ ++s->tally_counters.RxERR; @@ -862,7 +875,7 @@ packet_header |= RxBroadcast; - DEBUG_PRINT((">>> RTL8139: broadcast packet received\n")); + DPRINTF(">>> broadcast packet received\n"); /* update tally counter */ ++s->tally_counters.RxOkBrd; @@ -871,7 +884,7 @@ /* multicast */ if (!(s->RxConfig & AcceptMulticast)) { - DEBUG_PRINT((">>> RTL8139: multicast packet rejected\n")); + DPRINTF(">>> multicast packet rejected\n"); /* update tally counter */ ++s->tally_counters.RxERR; @@ -883,7 +896,7 @@ if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { - DEBUG_PRINT((">>> RTL8139: multicast address mismatch\n")); + DPRINTF(">>> multicast address mismatch\n"); /* update tally counter */ ++s->tally_counters.RxERR; @@ -893,7 +906,7 @@ packet_header |= RxMulticast; - DEBUG_PRINT((">>> RTL8139: multicast packet received\n")); + DPRINTF(">>> multicast packet received\n"); /* update tally counter */ ++s->tally_counters.RxOkMul; @@ -907,7 +920,7 @@ /* match */ if (!(s->RxConfig & AcceptMyPhys)) { - DEBUG_PRINT((">>> RTL8139: rejecting physical address matching packet\n")); + DPRINTF(">>> rejecting physical address matching packet\n"); /* update tally counter */ ++s->tally_counters.RxERR; @@ -917,14 +930,14 @@ packet_header |= RxPhysical; - DEBUG_PRINT((">>> RTL8139: physical address matching packet received\n")); + DPRINTF(">>> physical address matching packet received\n"); /* update tally counter */ ++s->tally_counters.RxOkPhy; } else { - DEBUG_PRINT((">>> RTL8139: unknown packet\n")); + DPRINTF(">>> unknown packet\n"); /* update tally counter */ ++s->tally_counters.RxERR; @@ -933,17 +946,20 @@ } } - /* if too small buffer, then expand it */ - if (size < MIN_BUF_SIZE) { + /* if too small buffer, then expand it + * Include some tailroom in case a vlan tag is later removed. */ + if (size < MIN_BUF_SIZE + VLAN_HLEN) { memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE - size); + memset(buf1 + size, 0, MIN_BUF_SIZE + VLAN_HLEN - size); buf = buf1; - size = MIN_BUF_SIZE; + if (size < MIN_BUF_SIZE) { + size = MIN_BUF_SIZE; + } } if (rtl8139_cp_receiver_enabled(s)) { - DEBUG_PRINT(("RTL8139: in C+ Rx mode ================\n")); + DPRINTF("in C+ Rx mode ================\n"); /* begin C+ receiver mode */ @@ -966,8 +982,9 @@ cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI); cplus_rx_ring_desc += 16 * descriptor; - DEBUG_PRINT(("RTL8139: +++ C+ mode reading RX descriptor %d from host memory at %08x %08x = %016" PRIx64 "\n", - descriptor, s->RxRingAddrHI, s->RxRingAddrLO, (uint64_t)cplus_rx_ring_desc)); + DPRINTF("+++ C+ mode reading RX descriptor %d from host memory at " + "%08x %08x = "TARGET_FMT_plx"\n", descriptor, s->RxRingAddrHI, + s->RxRingAddrLO, cplus_rx_ring_desc); uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI; @@ -980,13 +997,13 @@ cpu_physical_memory_read(cplus_rx_ring_desc+12, (uint8_t *)&val, 4); rxbufHI = le32_to_cpu(val); - DEBUG_PRINT(("RTL8139: +++ C+ mode RX descriptor %d %08x %08x %08x %08x\n", - descriptor, - rxdw0, rxdw1, rxbufLO, rxbufHI)); + DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n", + descriptor, rxdw0, rxdw1, rxbufLO, rxbufHI); if (!(rxdw0 & CP_RX_OWN)) { - DEBUG_PRINT(("RTL8139: C+ Rx mode : descriptor %d is owned by host\n", descriptor)); + DPRINTF("C+ Rx mode : descriptor %d is owned by host\n", + descriptor); s->IntrStatus |= RxOverflow; ++s->RxMissed; @@ -1001,12 +1018,34 @@ uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK; + /* write VLAN info to descriptor variables. */ + if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *) + &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) { + dot1q_buf = &buf[ETHER_ADDR_LEN * 2]; + size -= VLAN_HLEN; + /* if too small buffer, use the tailroom added duing expansion */ + if (size < MIN_BUF_SIZE) { + size = MIN_BUF_SIZE; + } + + rxdw1 &= ~CP_RX_VLAN_TAG_MASK; + /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */ + rxdw1 |= CP_RX_TAVA | le16_to_cpup((uint16_t *) + &dot1q_buf[ETHER_TYPE_LEN]); + + DPRINTF("C+ Rx mode : extracted vlan tag with tci: ""%u\n", + be16_to_cpup((uint16_t *)&dot1q_buf[ETHER_TYPE_LEN])); + } else { + /* reset VLAN tag flag */ + rxdw1 &= ~CP_RX_TAVA; + } + /* TODO: scatter the packet over available receive ring descriptors space */ if (size+4 > rx_space) { - DEBUG_PRINT(("RTL8139: C+ Rx mode : descriptor %d size %d received %d + 4\n", - descriptor, rx_space, size)); + DPRINTF("C+ Rx mode : descriptor %d size %d received %d + 4\n", + descriptor, rx_space, size); s->IntrStatus |= RxOverflow; ++s->RxMissed; @@ -1022,7 +1061,14 @@ target_phys_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI); /* receive/copy to target memory */ - cpu_physical_memory_write( rx_addr, buf, size ); + if (dot1q_buf) { + cpu_physical_memory_write(rx_addr, buf, 2 * ETHER_ADDR_LEN); + cpu_physical_memory_write(rx_addr + 2 * ETHER_ADDR_LEN, + buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN, + size - 2 * ETHER_ADDR_LEN); + } else { + cpu_physical_memory_write(rx_addr, buf, size); + } if (s->CpCmd & CPlusRxChkSum) { @@ -1030,11 +1076,7 @@ } /* write checksum */ -#if defined (RTL8139_CALCULATE_RXCRC) - val = cpu_to_le32(crc32(0, buf, size)); -#else - val = 0; -#endif + val = cpu_to_le32(crc32(0, buf, size_)); cpu_physical_memory_write( rx_addr+size, (uint8_t *)&val, 4); /* first segment of received packet flag */ @@ -1079,9 +1121,6 @@ rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK; rxdw0 |= (size+4); - /* reset VLAN tag flag */ - rxdw1 &= ~CP_RX_TAVA; - /* update ring data */ val = cpu_to_le32(rxdw0); cpu_physical_memory_write(cplus_rx_ring_desc, (uint8_t *)&val, 4); @@ -1101,12 +1140,12 @@ ++s->currCPlusRxDesc; } - DEBUG_PRINT(("RTL8139: done C+ Rx mode ----------------\n")); + DPRINTF("done C+ Rx mode ----------------\n"); } else { - DEBUG_PRINT(("RTL8139: in ring Rx mode ================\n")); + DPRINTF("in ring Rx mode ================\n"); /* begin ring receiver mode */ int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize); @@ -1115,8 +1154,9 @@ if (avail != 0 && size + 8 >= avail) { - DEBUG_PRINT(("rx overflow: rx buffer length %d head 0x%04x read 0x%04x === available 0x%04x need 0x%04x\n", - s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8)); + DPRINTF("rx overflow: rx buffer length %d head 0x%04x " + "read 0x%04x === available 0x%04x need 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8); s->IntrStatus |= RxOverflow; ++s->RxMissed; @@ -1136,12 +1176,7 @@ rtl8139_write_buffer(s, buf, size); /* write checksum */ -#if defined (RTL8139_CALCULATE_RXCRC) val = cpu_to_le32(crc32(0, buf, size)); -#else - val = 0; -#endif - rtl8139_write_buffer(s, (uint8_t *)&val, 4); /* correct buffer write pointer */ @@ -1149,8 +1184,8 @@ /* now we can signal we have received something */ - DEBUG_PRINT((" received: rx buffer length %d head 0x%04x read 0x%04x\n", - s->RxBufferSize, s->RxBufAddr, s->RxBufPtr)); + DPRINTF("received: rx buffer length %d head 0x%04x read 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); } s->IntrStatus |= RxOK; @@ -1189,18 +1224,6 @@ rtl8139_update_irq(s); - /* prepare eeprom */ - s->eeprom.contents[0] = 0x8129; -#if 1 - // PCI vendor and device ID should be mirrored here - s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK; - s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139; -#endif - - s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8; - s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8; - s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8; - /* mark all status registers as owned by host */ for (i = 0; i < 4; ++i) { @@ -1356,27 +1379,27 @@ { val &= 0xff; - DEBUG_PRINT(("RTL8139: ChipCmd write val=0x%08x\n", val)); + DPRINTF("ChipCmd write val=0x%08x\n", val); if (val & CmdReset) { - DEBUG_PRINT(("RTL8139: ChipCmd reset\n")); + DPRINTF("ChipCmd reset\n"); rtl8139_reset(&s->dev.qdev); } if (val & CmdRxEnb) { - DEBUG_PRINT(("RTL8139: ChipCmd enable receiver\n")); + DPRINTF("ChipCmd enable receiver\n"); s->currCPlusRxDesc = 0; } if (val & CmdTxEnb) { - DEBUG_PRINT(("RTL8139: ChipCmd enable transmitter\n")); + DPRINTF("ChipCmd enable transmitter\n"); s->currCPlusTxDesc = 0; } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xe3, s->bChipCmdState); /* Deassert reset pin before next read */ @@ -1391,11 +1414,11 @@ if (unread != 0) { - DEBUG_PRINT(("RTL8139: receiver buffer data available 0x%04x\n", unread)); + DPRINTF("receiver buffer data available 0x%04x\n", unread); return 0; } - DEBUG_PRINT(("RTL8139: receiver buffer is empty\n")); + DPRINTF("receiver buffer is empty\n"); return 1; } @@ -1407,7 +1430,7 @@ if (rtl8139_RxBufferEmpty(s)) ret |= RxBufEmpty; - DEBUG_PRINT(("RTL8139: ChipCmd read val=0x%04x\n", ret)); + DPRINTF("ChipCmd read val=0x%04x\n", ret); return ret; } @@ -1416,11 +1439,11 @@ { val &= 0xffff; - DEBUG_PRINT(("RTL8139C+ command register write(w) val=0x%04x\n", val)); + DPRINTF("C+ command register write(w) val=0x%04x\n", val); s->cplus_enabled = 1; - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xff84, s->CpCmd); s->CpCmd = val; @@ -1430,33 +1453,33 @@ { uint32_t ret = s->CpCmd; - DEBUG_PRINT(("RTL8139C+ command register read(w) val=0x%04x\n", ret)); + DPRINTF("C+ command register read(w) val=0x%04x\n", ret); return ret; } static void rtl8139_IntrMitigate_write(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139C+ IntrMitigate register write(w) val=0x%04x\n", val)); + DPRINTF("C+ IntrMitigate register write(w) val=0x%04x\n", val); } static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s) { uint32_t ret = 0; - DEBUG_PRINT(("RTL8139C+ IntrMitigate register read(w) val=0x%04x\n", ret)); + DPRINTF("C+ IntrMitigate register read(w) val=0x%04x\n", ret); return ret; } -static int rtl8139_config_writeable(RTL8139State *s) +static int rtl8139_config_writable(RTL8139State *s) { if (s->Cfg9346 & Cfg9346_Unlock) { return 1; } - DEBUG_PRINT(("RTL8139: Configuration registers are write-protected\n")); + DPRINTF("Configuration registers are write-protected\n"); return 0; } @@ -1465,12 +1488,12 @@ { val &= 0xffff; - DEBUG_PRINT(("RTL8139: BasicModeCtrl register write(w) val=0x%04x\n", val)); + DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ uint32_t mask = 0x4cff; - if (1 || !rtl8139_config_writeable(s)) + if (1 || !rtl8139_config_writable(s)) { /* Speed setting and autonegotiation enable bits are read-only */ mask |= 0x3000; @@ -1487,7 +1510,7 @@ { uint32_t ret = s->BasicModeCtrl; - DEBUG_PRINT(("RTL8139: BasicModeCtrl register read(w) val=0x%04x\n", ret)); + DPRINTF("BasicModeCtrl register read(w) val=0x%04x\n", ret); return ret; } @@ -1496,9 +1519,9 @@ { val &= 0xffff; - DEBUG_PRINT(("RTL8139: BasicModeStatus register write(w) val=0x%04x\n", val)); + DPRINTF("BasicModeStatus register write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xff3f, s->BasicModeStatus); s->BasicModeStatus = val; @@ -1508,7 +1531,7 @@ { uint32_t ret = s->BasicModeStatus; - DEBUG_PRINT(("RTL8139: BasicModeStatus register read(w) val=0x%04x\n", ret)); + DPRINTF("BasicModeStatus register read(w) val=0x%04x\n", ret); return ret; } @@ -1517,9 +1540,9 @@ { val &= 0xff; - DEBUG_PRINT(("RTL8139: Cfg9346 write val=0x%02x\n", val)); + DPRINTF("Cfg9346 write val=0x%02x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x31, s->Cfg9346); uint32_t opmode = val & 0xc0; @@ -1560,7 +1583,7 @@ } } - DEBUG_PRINT(("RTL8139: Cfg9346 read val=0x%02x\n", ret)); + DPRINTF("Cfg9346 read val=0x%02x\n", ret); return ret; } @@ -1569,12 +1592,13 @@ { val &= 0xff; - DEBUG_PRINT(("RTL8139: Config0 write val=0x%02x\n", val)); + DPRINTF("Config0 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xf8, s->Config0); s->Config0 = val; @@ -1584,7 +1608,7 @@ { uint32_t ret = s->Config0; - DEBUG_PRINT(("RTL8139: Config0 read val=0x%02x\n", ret)); + DPRINTF("Config0 read val=0x%02x\n", ret); return ret; } @@ -1593,12 +1617,13 @@ { val &= 0xff; - DEBUG_PRINT(("RTL8139: Config1 write val=0x%02x\n", val)); + DPRINTF("Config1 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xC, s->Config1); s->Config1 = val; @@ -1608,7 +1633,7 @@ { uint32_t ret = s->Config1; - DEBUG_PRINT(("RTL8139: Config1 read val=0x%02x\n", ret)); + DPRINTF("Config1 read val=0x%02x\n", ret); return ret; } @@ -1617,12 +1642,13 @@ { val &= 0xff; - DEBUG_PRINT(("RTL8139: Config3 write val=0x%02x\n", val)); + DPRINTF("Config3 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x8F, s->Config3); s->Config3 = val; @@ -1632,7 +1658,7 @@ { uint32_t ret = s->Config3; - DEBUG_PRINT(("RTL8139: Config3 read val=0x%02x\n", ret)); + DPRINTF("Config3 read val=0x%02x\n", ret); return ret; } @@ -1641,12 +1667,13 @@ { val &= 0xff; - DEBUG_PRINT(("RTL8139: Config4 write val=0x%02x\n", val)); + DPRINTF("Config4 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x0a, s->Config4); s->Config4 = val; @@ -1656,7 +1683,7 @@ { uint32_t ret = s->Config4; - DEBUG_PRINT(("RTL8139: Config4 read val=0x%02x\n", ret)); + DPRINTF("Config4 read val=0x%02x\n", ret); return ret; } @@ -1665,9 +1692,9 @@ { val &= 0xff; - DEBUG_PRINT(("RTL8139: Config5 write val=0x%02x\n", val)); + DPRINTF("Config5 write val=0x%02x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x80, s->Config5); s->Config5 = val; @@ -1677,7 +1704,7 @@ { uint32_t ret = s->Config5; - DEBUG_PRINT(("RTL8139: Config5 read val=0x%02x\n", ret)); + DPRINTF("Config5 read val=0x%02x\n", ret); return ret; } @@ -1686,11 +1713,11 @@ { if (!rtl8139_transmitter_enabled(s)) { - DEBUG_PRINT(("RTL8139: transmitter disabled; no TxConfig write val=0x%08x\n", val)); + DPRINTF("transmitter disabled; no TxConfig write val=0x%08x\n", val); return; } - DEBUG_PRINT(("RTL8139: TxConfig write val=0x%08x\n", val)); + DPRINTF("TxConfig write val=0x%08x\n", val); val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig); @@ -1699,7 +1726,7 @@ static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139C TxConfig via write(b) val=0x%02x\n", val)); + DPRINTF("RTL8139C TxConfig via write(b) val=0x%02x\n", val); uint32_t tc = s->TxConfig; tc &= 0xFFFFFF00; @@ -1711,16 +1738,16 @@ { uint32_t ret = s->TxConfig; - DEBUG_PRINT(("RTL8139: TxConfig read val=0x%04x\n", ret)); + DPRINTF("TxConfig read val=0x%04x\n", ret); return ret; } static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139: RxConfig write val=0x%08x\n", val)); + DPRINTF("RxConfig write val=0x%08x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xf0fc0040, s->RxConfig); s->RxConfig = val; @@ -1728,34 +1755,64 @@ /* reset buffer size and read/write pointers */ rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3)); - DEBUG_PRINT(("RTL8139: RxConfig write reset buffer size to %d\n", s->RxBufferSize)); + DPRINTF("RxConfig write reset buffer size to %d\n", s->RxBufferSize); } static uint32_t rtl8139_RxConfig_read(RTL8139State *s) { uint32_t ret = s->RxConfig; - DEBUG_PRINT(("RTL8139: RxConfig read val=0x%08x\n", ret)); + DPRINTF("RxConfig read val=0x%08x\n", ret); return ret; } -static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size, int do_interrupt) +static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, + int do_interrupt, const uint8_t *dot1q_buf) { + struct iovec *iov = NULL; + if (!size) { - DEBUG_PRINT(("RTL8139: +++ empty ethernet frame\n")); + DPRINTF("+++ empty ethernet frame\n"); return; } + if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) { + iov = (struct iovec[3]) { + { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 }, + { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN }, + { .iov_base = buf + ETHER_ADDR_LEN * 2, + .iov_len = size - ETHER_ADDR_LEN * 2 }, + }; + } + if (TxLoopBack == (s->TxConfig & TxLoopBack)) { - DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n")); + size_t buf2_size; + uint8_t *buf2; + + if (iov) { + buf2_size = iov_size(iov, 3); + buf2 = qemu_malloc(buf2_size); + iov_to_buf(iov, 3, buf2, 0, buf2_size); + buf = buf2; + } + + DPRINTF("+++ transmit loopback mode\n"); rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt); + + if (iov) { + qemu_free(buf2); + } } else { - qemu_send_packet(&s->nic->nc, buf, size); + if (iov) { + qemu_sendv_packet(&s->nic->nc, iov, 3); + } else { + qemu_send_packet(&s->nic->nc, buf, size); + } } } @@ -1763,25 +1820,25 @@ { if (!rtl8139_transmitter_enabled(s)) { - DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: transmitter disabled\n", - descriptor)); + DPRINTF("+++ cannot transmit from descriptor %d: transmitter " + "disabled\n", descriptor); return 0; } if (s->TxStatus[descriptor] & TxHostOwns) { - DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: owned by host (%08x)\n", - descriptor, s->TxStatus[descriptor])); + DPRINTF("+++ cannot transmit from descriptor %d: owned by host " + "(%08x)\n", descriptor, s->TxStatus[descriptor]); return 0; } - DEBUG_PRINT(("RTL8139: +++ transmitting from descriptor %d\n", descriptor)); + DPRINTF("+++ transmitting from descriptor %d\n", descriptor); int txsize = s->TxStatus[descriptor] & 0x1fff; uint8_t txbuffer[0x2000]; - DEBUG_PRINT(("RTL8139: +++ transmit reading %d bytes from host memory at 0x%08x\n", - txsize, s->TxAddr[descriptor])); + DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n", + txsize, s->TxAddr[descriptor]); cpu_physical_memory_read(s->TxAddr[descriptor], txbuffer, txsize); @@ -1789,9 +1846,10 @@ s->TxStatus[descriptor] |= TxHostOwns; s->TxStatus[descriptor] |= TxStatOK; - rtl8139_transfer_frame(s, txbuffer, txsize, 0); + rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL); - DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor)); + DPRINTF("+++ transmitted %d bytes from descriptor %d\n", txsize, + descriptor); /* update interrupt */ s->IntrStatus |= TxOK; @@ -1891,13 +1949,13 @@ { if (!rtl8139_transmitter_enabled(s)) { - DEBUG_PRINT(("RTL8139: +++ C+ mode: transmitter disabled\n")); + DPRINTF("+++ C+ mode: transmitter disabled\n"); return 0; } if (!rtl8139_cp_transmitter_enabled(s)) { - DEBUG_PRINT(("RTL8139: +++ C+ mode: C+ transmitter disabled\n")); + DPRINTF("+++ C+ mode: C+ transmitter disabled\n"); return 0 ; } @@ -1909,14 +1967,14 @@ /* Normal priority ring */ cplus_tx_ring_desc += 16 * descriptor; - DEBUG_PRINT(("RTL8139: +++ C+ mode reading TX descriptor %d from host memory at %08x0x%08x = 0x%8lx\n", - descriptor, s->TxAddr[1], s->TxAddr[0], cplus_tx_ring_desc)); + DPRINTF("+++ C+ mode reading TX descriptor %d from host memory at " + "%08x0x%08x = 0x"TARGET_FMT_plx"\n", descriptor, s->TxAddr[1], + s->TxAddr[0], cplus_tx_ring_desc); uint32_t val, txdw0,txdw1,txbufLO,txbufHI; 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); @@ -1924,12 +1982,8 @@ cpu_physical_memory_read(cplus_tx_ring_desc+12, (uint8_t *)&val, 4); txbufHI = le32_to_cpu(val); - DEBUG_PRINT(("RTL8139: +++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", - descriptor, - txdw0, txdw1, txbufLO, txbufHI)); - - /* TODO: the following discard cast should clean clang analyzer output */ - (void)txdw1; + DPRINTF("+++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor, + txdw0, txdw1, txbufLO, txbufHI); /* w0 ownership flag */ #define CP_TX_OWN (1<<31) @@ -1954,9 +2008,9 @@ /* w0 bits 0...15 : buffer size */ #define CP_TX_BUFFER_SIZE (1<<16) #define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1) -/* w1 tag available flag */ -#define CP_RX_TAGC (1<<17) -/* w1 bits 0...15 : VLAN tag */ +/* w1 add tag flag */ +#define CP_TX_TAGC (1<<17) +/* w1 bits 0...15 : VLAN tag (big endian) */ #define CP_TX_VLAN_TAG_MASK ((1<<16) - 1) /* w2 low 32bit of Rx buffer ptr */ /* w3 high 32bit of Rx buffer ptr */ @@ -1975,15 +2029,16 @@ if (!(txdw0 & CP_TX_OWN)) { - DEBUG_PRINT(("RTL8139: C+ Tx mode : descriptor %d is owned by host\n", descriptor)); + DPRINTF("C+ Tx mode : descriptor %d is owned by host\n", descriptor); return 0 ; } - DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : transmitting from descriptor %d\n", descriptor)); + DPRINTF("+++ C+ Tx mode : transmitting from descriptor %d\n", descriptor); if (txdw0 & CP_TX_FS) { - DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is first segment descriptor\n", descriptor)); + DPRINTF("+++ C+ Tx mode : descriptor %d is first segment " + "descriptor\n", descriptor); /* reset internal buffer offset */ s->cplus_txbuffer_offset = 0; @@ -1999,7 +2054,8 @@ s->cplus_txbuffer = qemu_malloc(s->cplus_txbuffer_len); s->cplus_txbuffer_offset = 0; - DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer allocated space %d\n", s->cplus_txbuffer_len)); + DPRINTF("+++ C+ mode transmission buffer allocated space %d\n", + s->cplus_txbuffer_len); } while (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) @@ -2007,14 +2063,16 @@ s->cplus_txbuffer_len += CP_TX_BUFFER_SIZE; s->cplus_txbuffer = qemu_realloc(s->cplus_txbuffer, s->cplus_txbuffer_len); - DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space changed to %d\n", s->cplus_txbuffer_len)); + DPRINTF("+++ C+ mode transmission buffer space changed to %d\n", + s->cplus_txbuffer_len); } if (!s->cplus_txbuffer) { /* out of memory */ - DEBUG_PRINT(("RTL8139: +++ C+ mode transmiter failed to reallocate %d bytes\n", s->cplus_txbuffer_len)); + DPRINTF("+++ C+ mode transmiter failed to reallocate %d bytes\n", + s->cplus_txbuffer_len); /* update tally counter */ ++s->tally_counters.TxERR; @@ -2025,8 +2083,9 @@ /* append more data to the packet */ - DEBUG_PRINT(("RTL8139: +++ C+ mode transmit reading %d bytes from host memory at %016" PRIx64 " to offset %d\n", - txsize, (uint64_t)tx_addr, s->cplus_txbuffer_offset)); + DPRINTF("+++ C+ mode transmit reading %d bytes from host memory at " + TARGET_FMT_plx" to offset %d\n", txsize, tx_addr, + s->cplus_txbuffer_offset); cpu_physical_memory_read(tx_addr, s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize); s->cplus_txbuffer_offset += txsize; @@ -2056,14 +2115,15 @@ /* 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); /* Now decide if descriptor being processed is holding the last segment of packet */ if (txdw0 & CP_TX_LS) { - DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is last segment descriptor\n", descriptor)); + uint8_t dot1q_buffer_space[VLAN_HLEN]; + uint16_t *dot1q_buffer; + + DPRINTF("+++ C+ Tx mode : descriptor %d is last segment descriptor\n", + descriptor); /* can transfer fully assembled packet */ @@ -2071,6 +2131,21 @@ int saved_size = s->cplus_txbuffer_offset; int saved_buffer_len = s->cplus_txbuffer_len; + /* create vlan tag */ + if (txdw1 & CP_TX_TAGC) { + /* the vlan tag is in BE byte order in the descriptor + * BE + le_to_cpu() + ~swap()~ = cpu */ + DPRINTF("+++ C+ Tx mode : inserting vlan tag with ""tci: %u\n", + bswap16(txdw1 & CP_TX_VLAN_TAG_MASK)); + + dot1q_buffer = (uint16_t *) dot1q_buffer_space; + dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q); + /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */ + dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK); + } else { + dot1q_buffer = NULL; + } + /* reset the card space to protect from recursive call */ s->cplus_txbuffer = NULL; s->cplus_txbuffer_offset = 0; @@ -2078,11 +2153,7 @@ if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN)) { - DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task checksum\n")); - - #define ETH_P_IP 0x0800 /* Internet Protocol packet */ - #define ETH_HLEN 14 - #define ETH_MTU 1500 + DPRINTF("+++ C+ mode offloaded task checksum\n"); /* ip packet header */ ip_header *ip = NULL; @@ -2096,7 +2167,7 @@ int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12)); if (proto == ETH_P_IP) { - DEBUG_PRINT(("RTL8139: +++ C+ mode has IP packet\n")); + DPRINTF("+++ C+ mode has IP packet\n"); /* not aligned */ eth_payload_data = saved_buffer + ETH_HLEN; @@ -2105,7 +2176,9 @@ ip = (ip_header*)eth_payload_data; if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { - DEBUG_PRINT(("RTL8139: +++ C+ mode packet has bad IP version %d expected %d\n", IP_HEADER_VERSION(ip), IP_HEADER_VERSION_4)); + DPRINTF("+++ C+ mode packet has bad IP version %d " + "expected %d\n", IP_HEADER_VERSION(ip), + IP_HEADER_VERSION_4); ip = NULL; } else { hlen = IP_HEADER_LENGTH(ip); @@ -2118,7 +2191,7 @@ { if (txdw0 & CP_TX_IPCS) { - DEBUG_PRINT(("RTL8139: +++ C+ mode need IP checksum\n")); + DPRINTF("+++ C+ mode need IP checksum\n"); if (hleneth_payload_len) {/* min header length */ /* bad packet header len */ @@ -2128,17 +2201,18 @@ { ip->ip_sum = 0; ip->ip_sum = ip_checksum(ip, hlen); - DEBUG_PRINT(("RTL8139: +++ C+ mode IP header len=%d checksum=%04x\n", hlen, ip->ip_sum)); + DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n", + hlen, ip->ip_sum); } } if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP) { -#if defined (DEBUG_RTL8139) int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK; -#endif - DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task TSO MTU=%d IP data %d frame data %d specified MSS=%d\n", - ETH_MTU, ip_data_len, saved_size - ETH_HLEN, large_send_mss)); + + DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d " + "frame data %d specified MSS=%d\n", ETH_MTU, + ip_data_len, saved_size - ETH_HLEN, large_send_mss); int tcp_send_offset = 0; int send_count = 0; @@ -2162,8 +2236,9 @@ int tcp_data_len = ip_data_len - tcp_hlen; int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen; - DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP data len %d TCP hlen %d TCP data len %d TCP chunk size %d\n", - ip_data_len, tcp_hlen, tcp_data_len, tcp_chunk_size)); + DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP " + "data len %d TCP chunk size %d\n", ip_data_len, + tcp_hlen, tcp_data_len, tcp_chunk_size); /* note the cycle below overwrites IP header data, but restores it from saved_ip_header before sending packet */ @@ -2181,13 +2256,16 @@ chunk_size = tcp_data_len - tcp_send_offset; } - DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP seqno %08x\n", be32_to_cpu(p_tcp_hdr->th_seq))); + DPRINTF("+++ C+ mode TSO TCP seqno %08x\n", + be32_to_cpu(p_tcp_hdr->th_seq)); /* add 4 TCP pseudoheader fields */ /* copy IP source and destination fields */ memcpy(data_to_checksum, saved_ip_header + 12, 8); - DEBUG_PRINT(("RTL8139: +++ C+ mode TSO calculating TCP checksum for packet with %d bytes data\n", tcp_hlen + chunk_size)); + DPRINTF("+++ C+ mode TSO calculating TCP checksum for " + "packet with %d bytes data\n", tcp_hlen + + chunk_size); if (tcp_send_offset) { @@ -2209,7 +2287,8 @@ p_tcp_hdr->th_sum = 0; int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12); - DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP checksum %04x\n", tcp_checksum)); + DPRINTF("+++ C+ mode TSO TCP checksum %04x\n", + tcp_checksum); p_tcp_hdr->th_sum = tcp_checksum; @@ -2224,11 +2303,14 @@ ip->ip_sum = 0; ip->ip_sum = ip_checksum(eth_payload_data, hlen); - DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP header len=%d checksum=%04x\n", hlen, ip->ip_sum)); + DPRINTF("+++ C+ mode TSO IP header len=%d " + "checksum=%04x\n", hlen, ip->ip_sum); int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size; - DEBUG_PRINT(("RTL8139: +++ C+ mode TSO transferring packet size %d\n", tso_send_size)); - rtl8139_transfer_frame(s, saved_buffer, tso_send_size, 0); + DPRINTF("+++ C+ mode TSO transferring packet size " + "%d\n", tso_send_size); + rtl8139_transfer_frame(s, saved_buffer, tso_send_size, + 0, (uint8_t *) dot1q_buffer); /* add transferred count to TCP sequence number */ p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); @@ -2240,7 +2322,7 @@ } else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS)) { - DEBUG_PRINT(("RTL8139: +++ C+ mode need TCP or UDP checksum\n")); + DPRINTF("+++ C+ mode need TCP or UDP checksum\n"); /* maximum IP header length is 60 bytes */ uint8_t saved_ip_header[60]; @@ -2255,7 +2337,8 @@ if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP) { - DEBUG_PRINT(("RTL8139: +++ C+ mode calculating TCP checksum for packet with %d bytes data\n", ip_data_len)); + DPRINTF("+++ C+ mode calculating TCP checksum for " + "packet with %d bytes data\n", ip_data_len); ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum; p_tcpip_hdr->zeros = 0; @@ -2267,13 +2350,15 @@ p_tcp_hdr->th_sum = 0; int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); - DEBUG_PRINT(("RTL8139: +++ C+ mode TCP checksum %04x\n", tcp_checksum)); + DPRINTF("+++ C+ mode TCP checksum %04x\n", + tcp_checksum); p_tcp_hdr->th_sum = tcp_checksum; } else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP) { - DEBUG_PRINT(("RTL8139: +++ C+ mode calculating UDP checksum for packet with %d bytes data\n", ip_data_len)); + DPRINTF("+++ C+ mode calculating UDP checksum for " + "packet with %d bytes data\n", ip_data_len); ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum; p_udpip_hdr->zeros = 0; @@ -2285,7 +2370,8 @@ p_udp_hdr->uh_sum = 0; int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); - DEBUG_PRINT(("RTL8139: +++ C+ mode UDP checksum %04x\n", udp_checksum)); + DPRINTF("+++ C+ mode UDP checksum %04x\n", + udp_checksum); p_udp_hdr->uh_sum = udp_checksum; } @@ -2299,9 +2385,10 @@ /* update tally counter */ ++s->tally_counters.TxOk; - DEBUG_PRINT(("RTL8139: +++ C+ mode transmitting %d bytes packet\n", saved_size)); + DPRINTF("+++ C+ mode transmitting %d bytes packet\n", saved_size); - rtl8139_transfer_frame(s, saved_buffer, saved_size, 1); + rtl8139_transfer_frame(s, saved_buffer, saved_size, 1, + (uint8_t *) dot1q_buffer); /* restore card space if there was no recursion and reset offset */ if (!s->cplus_txbuffer) @@ -2317,7 +2404,7 @@ } else { - DEBUG_PRINT(("RTL8139: +++ C+ mode transmission continue to next descriptor\n")); + DPRINTF("+++ C+ mode transmission continue to next descriptor\n"); } return 1; @@ -2335,8 +2422,8 @@ /* Mark transfer completed */ if (!txcount) { - DEBUG_PRINT(("RTL8139: C+ mode : transmitter queue stalled, current TxDesc = %d\n", - s->currCPlusTxDesc)); + DPRINTF("C+ mode : transmitter queue stalled, current TxDesc = %d\n", + s->currCPlusTxDesc); } else { @@ -2361,7 +2448,8 @@ /* Mark transfer completed */ if (!txcount) { - DEBUG_PRINT(("RTL8139: transmitter queue stalled, current TxDesc = %d\n", s->currTxDesc)); + DPRINTF("transmitter queue stalled, current TxDesc = %d\n", + s->currTxDesc); } } @@ -2374,7 +2462,8 @@ if (s->cplus_enabled) { - DEBUG_PRINT(("RTL8139C+ DTCCR write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor)); + DPRINTF("RTL8139C+ DTCCR write offset=0x%x val=0x%08x " + "descriptor=%d\n", txRegOffset, val, descriptor); /* handle Dump Tally Counters command */ s->TxStatus[descriptor] = val; @@ -2393,7 +2482,8 @@ return; } - DEBUG_PRINT(("RTL8139: TxStatus write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor)); + DPRINTF("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n", + txRegOffset, val, descriptor); /* mask only reserved bits */ val &= ~0xff00c000; /* these bits are reset on write */ @@ -2409,7 +2499,7 @@ { uint32_t ret = s->TxStatus[txRegOffset/4]; - DEBUG_PRINT(("RTL8139: TxStatus read offset=0x%x val=0x%08x\n", txRegOffset, ret)); + DPRINTF("TxStatus read offset=0x%x val=0x%08x\n", txRegOffset, ret); return ret; } @@ -2441,7 +2531,7 @@ |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ; - DEBUG_PRINT(("RTL8139: TSAD read val=0x%04x\n", ret)); + DPRINTF("TSAD read val=0x%04x\n", ret); return ret; } @@ -2450,14 +2540,14 @@ { uint16_t ret = s->CSCR; - DEBUG_PRINT(("RTL8139: CSCR read val=0x%04x\n", ret)); + DPRINTF("CSCR read val=0x%04x\n", ret); return ret; } static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val) { - DEBUG_PRINT(("RTL8139: TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val)); + DPRINTF("TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val); s->TxAddr[txAddrOffset/4] = val; } @@ -2466,20 +2556,20 @@ { uint32_t ret = s->TxAddr[txAddrOffset/4]; - DEBUG_PRINT(("RTL8139: TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret)); + DPRINTF("TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret); return ret; } static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139: RxBufPtr write val=0x%04x\n", val)); + DPRINTF("RxBufPtr write val=0x%04x\n", val); /* this value is off by 16 */ s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize); - DEBUG_PRINT((" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n", - s->RxBufferSize, s->RxBufAddr, s->RxBufPtr)); + DPRINTF(" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); } static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s) @@ -2487,7 +2577,7 @@ /* this value is off by 16 */ uint32_t ret = s->RxBufPtr - 0x10; - DEBUG_PRINT(("RTL8139: RxBufPtr read val=0x%04x\n", ret)); + DPRINTF("RxBufPtr read val=0x%04x\n", ret); return ret; } @@ -2497,14 +2587,14 @@ /* this value is NOT off by 16 */ uint32_t ret = s->RxBufAddr; - DEBUG_PRINT(("RTL8139: RxBufAddr read val=0x%04x\n", ret)); + DPRINTF("RxBufAddr read val=0x%04x\n", ret); return ret; } static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139: RxBuf write val=0x%08x\n", val)); + DPRINTF("RxBuf write val=0x%08x\n", val); s->RxBuf = val; @@ -2515,21 +2605,21 @@ { uint32_t ret = s->RxBuf; - DEBUG_PRINT(("RTL8139: RxBuf read val=0x%08x\n", ret)); + DPRINTF("RxBuf read val=0x%08x\n", ret); return ret; } static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139: IntrMask write(w) val=0x%04x\n", val)); + DPRINTF("IntrMask write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x1e00, s->IntrMask); s->IntrMask = val; - rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock)); rtl8139_update_irq(s); } @@ -2538,14 +2628,14 @@ { uint32_t ret = s->IntrMask; - DEBUG_PRINT(("RTL8139: IntrMask read(w) val=0x%04x\n", ret)); + DPRINTF("IntrMask read(w) val=0x%04x\n", ret); return ret; } static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139: IntrStatus write(w) val=0x%04x\n", val)); + DPRINTF("IntrStatus write(w) val=0x%04x\n", val); #if 0 @@ -2556,7 +2646,7 @@ #else uint16_t newStatus = s->IntrStatus & ~val; - /* mask unwriteable bits */ + /* mask unwritable bits */ newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus); /* writing 1 to interrupt status register bit clears it */ @@ -2570,7 +2660,7 @@ * 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_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock)); rtl8139_update_irq(s); #endif @@ -2578,11 +2668,11 @@ static uint32_t rtl8139_IntrStatus_read(RTL8139State *s) { - rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock)); uint32_t ret = s->IntrStatus; - DEBUG_PRINT(("RTL8139: IntrStatus read(w) val=0x%04x\n", ret)); + DPRINTF("IntrStatus read(w) val=0x%04x\n", ret); #if 0 @@ -2598,9 +2688,9 @@ static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val) { - DEBUG_PRINT(("RTL8139: MultiIntr write(w) val=0x%04x\n", val)); + DPRINTF("MultiIntr write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xf000, s->MultiIntr); s->MultiIntr = val; @@ -2610,7 +2700,7 @@ { uint32_t ret = s->MultiIntr; - DEBUG_PRINT(("RTL8139: MultiIntr read(w) val=0x%04x\n", ret)); + DPRINTF("MultiIntr read(w) val=0x%04x\n", ret); return ret; } @@ -2658,11 +2748,12 @@ break; case MediaStatus: /* ignore */ - DEBUG_PRINT(("RTL8139: not implemented write(b) to MediaStatus val=0x%02x\n", val)); + DPRINTF("not implemented write(b) to MediaStatus val=0x%02x\n", + val); break; case HltClk: - DEBUG_PRINT(("RTL8139: HltClk write val=0x%08x\n", val)); + DPRINTF("HltClk write val=0x%08x\n", val); if (val == 'R') { s->clock_enabled = 1; @@ -2674,27 +2765,29 @@ break; case TxThresh: - DEBUG_PRINT(("RTL8139C+ TxThresh write(b) val=0x%02x\n", val)); + DPRINTF("C+ TxThresh write(b) val=0x%02x\n", val); s->TxThresh = val; break; case TxPoll: - DEBUG_PRINT(("RTL8139C+ TxPoll write(b) val=0x%02x\n", val)); + DPRINTF("C+ TxPoll write(b) val=0x%02x\n", val); if (val & (1 << 7)) { - DEBUG_PRINT(("RTL8139C+ TxPoll high priority transmission (not implemented)\n")); + DPRINTF("C+ TxPoll high priority transmission (not " + "implemented)\n"); //rtl8139_cplus_transmit(s); } if (val & (1 << 6)) { - DEBUG_PRINT(("RTL8139C+ TxPoll normal priority transmission\n")); + DPRINTF("C+ TxPoll normal priority transmission\n"); rtl8139_cplus_transmit(s); } break; default: - DEBUG_PRINT(("RTL8139: not implemented write(b) addr=0x%x val=0x%02x\n", addr, val)); + DPRINTF("not implemented write(b) addr=0x%x val=0x%02x\n", addr, + val); break; } } @@ -2730,14 +2823,14 @@ rtl8139_BasicModeStatus_write(s, val); break; case NWayAdvert: - DEBUG_PRINT(("RTL8139: NWayAdvert write(w) val=0x%04x\n", val)); + DPRINTF("NWayAdvert write(w) val=0x%04x\n", val); s->NWayAdvert = val; break; case NWayLPAR: - DEBUG_PRINT(("RTL8139: forbidden NWayLPAR write(w) val=0x%04x\n", val)); + DPRINTF("forbidden NWayLPAR write(w) val=0x%04x\n", val); break; case NWayExpansion: - DEBUG_PRINT(("RTL8139: NWayExpansion write(w) val=0x%04x\n", val)); + DPRINTF("NWayExpansion write(w) val=0x%04x\n", val); s->NWayExpansion = val; break; @@ -2750,7 +2843,8 @@ break; default: - DEBUG_PRINT(("RTL8139: ioport write(w) addr=0x%x val=0x%04x via write(b)\n", addr, val)); + DPRINTF("ioport write(w) addr=0x%x val=0x%04x via write(b)\n", + addr, val); rtl8139_io_writeb(opaque, addr, val & 0xff); rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff); @@ -2763,7 +2857,7 @@ int64_t pci_time, next_time; uint32_t low_pci; - DEBUG_PRINT(("RTL8139: entered rtl8139_set_next_tctr_time\n")); + DPRINTF("entered rtl8139_set_next_tctr_time\n"); if (s->TimerExpire && current_time >= s->TimerExpire) { s->IntrStatus |= PCSTimeout; @@ -2807,7 +2901,7 @@ switch (addr) { case RxMissed: - DEBUG_PRINT(("RTL8139: RxMissed clearing on write\n")); + DPRINTF("RxMissed clearing on write\n"); s->RxMissed = 0; break; @@ -2832,31 +2926,32 @@ break; case RxRingAddrLO: - DEBUG_PRINT(("RTL8139: C+ RxRing low bits write val=0x%08x\n", val)); + DPRINTF("C+ RxRing low bits write val=0x%08x\n", val); s->RxRingAddrLO = val; break; case RxRingAddrHI: - DEBUG_PRINT(("RTL8139: C+ RxRing high bits write val=0x%08x\n", val)); + DPRINTF("C+ RxRing high bits write val=0x%08x\n", val); s->RxRingAddrHI = val; break; case Timer: - DEBUG_PRINT(("RTL8139: TCTR Timer reset on write\n")); - s->TCTR_base = qemu_get_clock(vm_clock); + DPRINTF("TCTR Timer reset on write\n"); + s->TCTR_base = qemu_get_clock_ns(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)); + DPRINTF("FlashReg TimerInt write val=0x%08x\n", val); if (s->TimerInt != val) { s->TimerInt = val; - rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock)); } break; default: - DEBUG_PRINT(("RTL8139: ioport write(l) addr=0x%x val=0x%08x via write(b)\n", addr, val)); + DPRINTF("ioport write(l) addr=0x%x val=0x%08x via write(b)\n", + addr, val); rtl8139_io_writeb(opaque, addr, val & 0xff); rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff); rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff); @@ -2907,31 +3002,31 @@ case MediaStatus: ret = 0xd0; - DEBUG_PRINT(("RTL8139: MediaStatus read 0x%x\n", ret)); + DPRINTF("MediaStatus read 0x%x\n", ret); break; case HltClk: ret = s->clock_enabled; - DEBUG_PRINT(("RTL8139: HltClk read 0x%x\n", ret)); + DPRINTF("HltClk read 0x%x\n", ret); break; case PCIRevisionID: ret = RTL8139_PCI_REVID; - DEBUG_PRINT(("RTL8139: PCI Revision ID read 0x%x\n", ret)); + DPRINTF("PCI Revision ID read 0x%x\n", ret); break; case TxThresh: ret = s->TxThresh; - DEBUG_PRINT(("RTL8139C+ TxThresh read(b) val=0x%02x\n", ret)); + DPRINTF("C+ TxThresh read(b) val=0x%02x\n", ret); break; case 0x43: /* Part of TxConfig register. Windows driver tries to read it */ ret = s->TxConfig >> 24; - DEBUG_PRINT(("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret)); + DPRINTF("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret); break; default: - DEBUG_PRINT(("RTL8139: not implemented read(b) addr=0x%x\n", addr)); + DPRINTF("not implemented read(b) addr=0x%x\n", addr); ret = 0; break; } @@ -2976,15 +3071,15 @@ break; case NWayAdvert: ret = s->NWayAdvert; - DEBUG_PRINT(("RTL8139: NWayAdvert read(w) val=0x%04x\n", ret)); + DPRINTF("NWayAdvert read(w) val=0x%04x\n", ret); break; case NWayLPAR: ret = s->NWayLPAR; - DEBUG_PRINT(("RTL8139: NWayLPAR read(w) val=0x%04x\n", ret)); + DPRINTF("NWayLPAR read(w) val=0x%04x\n", ret); break; case NWayExpansion: ret = s->NWayExpansion; - DEBUG_PRINT(("RTL8139: NWayExpansion read(w) val=0x%04x\n", ret)); + DPRINTF("NWayExpansion read(w) val=0x%04x\n", ret); break; case CpCmd: @@ -3004,12 +3099,12 @@ break; default: - DEBUG_PRINT(("RTL8139: ioport read(w) addr=0x%x via read(b)\n", addr)); + DPRINTF("ioport read(w) addr=0x%x via read(b)\n", addr); ret = rtl8139_io_readb(opaque, addr); ret |= rtl8139_io_readb(opaque, addr + 1) << 8; - DEBUG_PRINT(("RTL8139: ioport read(w) addr=0x%x val=0x%04x\n", addr, ret)); + DPRINTF("ioport read(w) addr=0x%x val=0x%04x\n", addr, ret); break; } @@ -3028,7 +3123,7 @@ case RxMissed: ret = s->RxMissed; - DEBUG_PRINT(("RTL8139: RxMissed read val=0x%08x\n", ret)); + DPRINTF("RxMissed read val=0x%08x\n", ret); break; case TxConfig: @@ -3053,34 +3148,34 @@ case RxRingAddrLO: ret = s->RxRingAddrLO; - DEBUG_PRINT(("RTL8139: C+ RxRing low bits read val=0x%08x\n", ret)); + DPRINTF("C+ RxRing low bits read val=0x%08x\n", ret); break; case RxRingAddrHI: ret = s->RxRingAddrHI; - DEBUG_PRINT(("RTL8139: C+ RxRing high bits read val=0x%08x\n", ret)); + DPRINTF("C+ RxRing high bits read val=0x%08x\n", ret); break; case Timer: - ret = muldiv64(qemu_get_clock(vm_clock) - s->TCTR_base, + ret = muldiv64(qemu_get_clock_ns(vm_clock) - s->TCTR_base, PCI_FREQUENCY, get_ticks_per_sec()); - DEBUG_PRINT(("RTL8139: TCTR Timer read val=0x%08x\n", ret)); + DPRINTF("TCTR Timer read val=0x%08x\n", ret); break; case FlashReg: ret = s->TimerInt; - DEBUG_PRINT(("RTL8139: FlashReg TimerInt read val=0x%08x\n", ret)); + DPRINTF("FlashReg TimerInt read val=0x%08x\n", ret); break; default: - DEBUG_PRINT(("RTL8139: ioport read(l) addr=0x%x via read(b)\n", addr)); + DPRINTF("ioport read(l) addr=0x%x via read(b)\n", addr); ret = rtl8139_io_readb(opaque, addr); ret |= rtl8139_io_readb(opaque, addr + 1) << 8; ret |= rtl8139_io_readb(opaque, addr + 2) << 16; ret |= rtl8139_io_readb(opaque, addr + 3) << 24; - DEBUG_PRINT(("RTL8139: read(l) addr=0x%x val=%08x\n", addr, ret)); + DPRINTF("read(l) addr=0x%x val=%08x\n", addr, ret); break; } @@ -3156,7 +3251,7 @@ static int rtl8139_post_load(void *opaque, int version_id) { RTL8139State* s = opaque; - rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock)); if (version_id < 4) { s->cplus_enabled = s->CpCmd != 0; } @@ -3182,7 +3277,7 @@ static void rtl8139_pre_save(void *opaque) { RTL8139State* s = opaque; - int64_t current_time = qemu_get_clock(vm_clock); + int64_t current_time = qemu_get_clock_ns(vm_clock); /* set IntrStatus correctly */ rtl8139_set_next_tctr_time(s, current_time); @@ -3284,14 +3379,6 @@ /***********************************************************/ /* PCI RTL8139 definitions */ -static void rtl8139_mmio_map(PCIDevice *pci_dev, int region_num, - pcibus_t addr, pcibus_t size, int type) -{ - RTL8139State *s = DO_UPCAST(RTL8139State, dev, pci_dev); - - cpu_register_physical_memory(addr + 0, 0x100, s->rtl8139_mmio_io_addr); -} - static void rtl8139_ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type) { @@ -3325,13 +3412,13 @@ if (!s->clock_enabled) { - DEBUG_PRINT(("RTL8139: >>> timer: clock is not running\n")); + DPRINTF(">>> timer: clock is not running\n"); return; } s->IntrStatus |= PCSTimeout; rtl8139_update_irq(s); - rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock)); } static void rtl8139_cleanup(VLANClientState *nc) @@ -3370,10 +3457,6 @@ uint8_t *pci_conf; 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[PCI_REVISION_ID] = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ - pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); 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. */ @@ -3387,11 +3470,21 @@ pci_register_bar(&s->dev, 0, 0x100, PCI_BASE_ADDRESS_SPACE_IO, rtl8139_ioport_map); - pci_register_bar(&s->dev, 1, 0x100, - PCI_BASE_ADDRESS_SPACE_MEMORY, rtl8139_mmio_map); + pci_register_bar_simple(&s->dev, 1, 0x100, 0, s->rtl8139_mmio_io_addr); qemu_macaddr_default_if_unset(&s->conf.macaddr); + /* prepare eeprom */ + s->eeprom.contents[0] = 0x8129; +#if 1 + /* PCI vendor and device ID should be mirrored here */ + s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK; + s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139; +#endif + s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8; + s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8; + s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8; + s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf, dev->qdev.info->name, dev->qdev.id, s); qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); @@ -3401,8 +3494,8 @@ s->cplus_txbuffer_offset = 0; s->TimerExpire = 0; - s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s); - rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + s->timer = qemu_new_timer_ns(vm_clock, rtl8139_timer, s); + rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock)); add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet-phy@0"); @@ -3416,7 +3509,11 @@ .qdev.vmsd = &vmstate_rtl8139, .init = pci_rtl8139_init, .exit = pci_rtl8139_uninit, - .romfile = "pxe-rtl8139.bin", + .romfile = "pxe-rtl8139.rom", + .vendor_id = PCI_VENDOR_ID_REALTEK, + .device_id = PCI_DEVICE_ID_REALTEK_8139, + .revision = RTL8139_PCI_REVID, /* >=0x20 is for 8139C+ */ + .class_id = PCI_CLASS_NETWORK_ETHERNET, .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(RTL8139State, conf), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/s390-virtio-bus.c qemu-kvm-0.15.0+noroms/hw/s390-virtio-bus.c --- qemu-kvm-0.14.1+noroms/hw/s390-virtio-bus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/s390-virtio-bus.c 2011-08-09 07:40:29.000000000 -0500 @@ -43,6 +43,8 @@ do { } while (0) #endif +#define VIRTIO_EXT_CODE 0x2603 + struct BusInfo s390_virtio_bus_info = { .name = "s390-virtio", .size = sizeof(VirtIOS390Bus), @@ -58,6 +60,9 @@ static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev); +/* length of VirtIO device pages */ +const target_phys_addr_t virtio_size = S390_DEVICE_PAGES * TARGET_PAGE_SIZE; + VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) { VirtIOS390Bus *bus; @@ -123,7 +128,8 @@ { VirtIODevice *vdev; - vdev = virtio_blk_init((DeviceState *)dev, &dev->block); + vdev = virtio_blk_init((DeviceState *)dev, &dev->block, + &dev->block_serial); if (!vdev) { return -1; } @@ -139,7 +145,7 @@ bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus); - vdev = virtio_serial_init((DeviceState *)dev, dev->max_virtserial_ports); + vdev = virtio_serial_init((DeviceState *)dev, &dev->serial); if (!vdev) { return -1; } @@ -160,7 +166,7 @@ (vq * VIRTIO_VQCONFIG_LEN) + VIRTIO_VQCONFIG_OFFS_TOKEN; - return ldq_phys(token_off); + return ldq_be_phys(token_off); } static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev) @@ -214,8 +220,8 @@ vring = s390_virtio_next_ring(bus); virtio_queue_set_addr(dev->vdev, i, vring); virtio_queue_set_vector(dev->vdev, i, i); - stq_phys(vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring); - stw_phys(vq + VIRTIO_VQCONFIG_OFFS_NUM, virtio_queue_get_num(dev->vdev, i)); + stq_be_phys(vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring); + stw_be_phys(vq + VIRTIO_VQCONFIG_OFFS_NUM, virtio_queue_get_num(dev->vdev, i)); } cur_offs = dev->dev_offs; @@ -223,7 +229,7 @@ cur_offs += num_vq * VIRTIO_VQCONFIG_LEN; /* Sync feature bitmap */ - stl_phys(cur_offs, dev->host_features); + stl_le_phys(cur_offs, dev->host_features); dev->feat_offs = cur_offs + dev->feat_len; cur_offs += dev->feat_len * 2; @@ -233,7 +239,8 @@ dev->vdev->get_config(dev->vdev, dev->vdev->config); } - cpu_physical_memory_rw(cur_offs, dev->vdev->config, dev->vdev->config_len, 1); + cpu_physical_memory_write(cur_offs, + dev->vdev->config, dev->vdev->config_len); cur_offs += dev->vdev->config_len; } @@ -246,7 +253,7 @@ /* Update guest supported feature bitmap */ - features = ldl_phys(dev->feat_offs); + features = bswap32(ldl_be_phys(dev->feat_offs)); if (vdev->set_features) { vdev->set_features(vdev, features); } @@ -304,9 +311,13 @@ { VirtIOS390Device *dev = (VirtIOS390Device*)opaque; uint64_t token = s390_virtio_device_vq_token(dev, vector); + CPUState *env = s390_cpu_addr2state(0); - /* XXX kvm dependency! */ - kvm_s390_virtio_irq(s390_cpu_addr2state(0), 0, token); + if (kvm_enabled()) { + kvm_s390_virtio_irq(env, 0, token); + } else { + cpu_inject_ext(env, VIRTIO_EXT_CODE, 0, token); + } } static unsigned virtio_s390_get_features(void *opaque) @@ -325,6 +336,7 @@ static VirtIOS390DeviceInfo s390_virtio_net = { .init = s390_virtio_net_init, .qdev.name = "virtio-net-s390", + .qdev.alias = "virtio-net", .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(VirtIOS390Device, nic), @@ -340,9 +352,11 @@ static VirtIOS390DeviceInfo s390_virtio_blk = { .init = s390_virtio_blk_init, .qdev.name = "virtio-blk-s390", + .qdev.alias = "virtio-blk", .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(VirtIOS390Device, block), + DEFINE_PROP_STRING("serial", VirtIOS390Device, block_serial), DEFINE_PROP_END_OF_LIST(), }, }; @@ -353,8 +367,8 @@ .qdev.alias = "virtio-serial", .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("max_ports", VirtIOS390Device, max_virtserial_ports, - 31), + DEFINE_PROP_UINT32("max_ports", VirtIOS390Device, + serial.max_virtserial_ports, 31), DEFINE_PROP_END_OF_LIST(), }, }; diff -Nru qemu-kvm-0.14.1+noroms/hw/s390-virtio-bus.h qemu-kvm-0.15.0+noroms/hw/s390-virtio-bus.h --- qemu-kvm-0.14.1+noroms/hw/s390-virtio-bus.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/s390-virtio-bus.h 2011-08-09 07:40:29.000000000 -0500 @@ -18,6 +18,7 @@ */ #include "virtio-net.h" +#include "virtio-serial.h" #define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */ #define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */ @@ -32,7 +33,7 @@ #define VIRTIO_VQCONFIG_LEN 24 #define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3) -#define S390_DEVICE_PAGES 256 +#define S390_DEVICE_PAGES 512 typedef struct VirtIOS390Device { DeviceState qdev; @@ -41,10 +42,10 @@ uint8_t feat_len; VirtIODevice *vdev; BlockConf block; + char *block_serial; 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_serial_conf serial; virtio_net_conf net; } VirtIOS390Device; diff -Nru qemu-kvm-0.14.1+noroms/hw/s390-virtio.c qemu-kvm-0.15.0+noroms/hw/s390-virtio.c --- qemu-kvm-0.14.1+noroms/hw/s390-virtio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/s390-virtio.c 2011-08-09 07:40:29.000000000 -0500 @@ -82,13 +82,12 @@ return ipi_states[cpu_addr]; } -int s390_virtio_hypercall(CPUState *env) +int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall) { int r = 0, i; - target_ulong mem = env->regs[2]; - dprintf("KVM hypercall: %ld\n", env->regs[1]); - switch (env->regs[1]) { + dprintf("KVM hypercall: %ld\n", hypercall); + switch (hypercall) { case KVM_S390_VIRTIO_NOTIFY: if (mem > ram_size) { VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, @@ -128,12 +127,11 @@ break; } - env->regs[2] = r; - return 0; + return r; } /* PC hardware initialisation */ -static void s390_init(ram_addr_t ram_size, +static void s390_init(ram_addr_t my_ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, @@ -145,21 +143,29 @@ ram_addr_t kernel_size = 0; ram_addr_t initrd_offset; ram_addr_t initrd_size = 0; + int shift = 0; + uint8_t *storage_keys; int i; - /* XXX we only work on KVM for now */ - - if (!kvm_enabled()) { - fprintf(stderr, "The S390 target only works with KVM enabled\n"); - exit(1); + /* s390x ram size detection needs a 16bit multiplier + an increment. So + guests > 64GB can be specified in 2MB steps etc. */ + while ((my_ram_size >> (20 + shift)) > 65535) { + shift++; } + my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); + + /* lets propagate the changed ram size into the global variable. */ + ram_size = my_ram_size; /* get a BUS */ - s390_bus = s390_virtio_bus_init(&ram_size); + s390_bus = s390_virtio_bus_init(&my_ram_size); /* allocate RAM */ - ram_addr = qemu_ram_alloc(NULL, "s390.ram", ram_size); - cpu_register_physical_memory(0, ram_size, ram_addr); + ram_addr = qemu_ram_alloc(NULL, "s390.ram", my_ram_size); + cpu_register_physical_memory(0, my_ram_size, ram_addr); + + /* allocate storage keys */ + storage_keys = qemu_mallocz(my_ram_size / TARGET_PAGE_SIZE); /* init CPUs */ if (cpu_model == NULL) { @@ -178,6 +184,7 @@ ipi_states[i] = tmp_env; tmp_env->halted = 1; tmp_env->exception_index = EXCP_HLT; + tmp_env->storage_keys = storage_keys; } env->halted = 0; @@ -186,7 +193,7 @@ if (kernel_filename) { kernel_size = load_image(kernel_filename, qemu_get_ram_ptr(0)); - if (lduw_phys(KERN_IMAGE_START) != 0x0dd0) { + if (lduw_be_phys(KERN_IMAGE_START) != 0x0dd0) { fprintf(stderr, "Specified image is not an s390 boot image\n"); exit(1); } @@ -204,6 +211,7 @@ bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); bios_size = load_image(bios_filename, qemu_get_ram_ptr(ZIPL_LOAD_ADDR)); + qemu_free(bios_filename); if ((long)bios_size < 0) { hw_error("could not load bootloader '%s'\n", bios_name); @@ -224,13 +232,13 @@ } initrd_size = load_image(initrd_filename, qemu_get_ram_ptr(initrd_offset)); - stq_phys(INITRD_PARM_START, initrd_offset); - stq_phys(INITRD_PARM_SIZE, initrd_size); + stq_be_phys(INITRD_PARM_START, initrd_offset); + stq_be_phys(INITRD_PARM_SIZE, initrd_size); } if (kernel_cmdline) { - cpu_physical_memory_rw(KERN_PARM_AREA, (uint8_t *)kernel_cmdline, - strlen(kernel_cmdline), 1); + cpu_physical_memory_write(KERN_PARM_AREA, kernel_cmdline, + strlen(kernel_cmdline)); } /* Create VirtIO network adapters */ diff -Nru qemu-kvm-0.14.1+noroms/hw/sb16.c qemu-kvm-0.15.0+noroms/hw/sb16.c --- qemu-kvm-0.14.1+noroms/hw/sb16.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sb16.c 2011-08-09 07:40:29.000000000 -0500 @@ -766,7 +766,7 @@ if (s->aux_ts) { qemu_mod_timer ( s->aux_ts, - qemu_get_clock (vm_clock) + ticks + qemu_get_clock_ns (vm_clock) + ticks ); } } @@ -1361,7 +1361,7 @@ s->csp_regs[9] = 0xf8; reset_mixer (s); - s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s); + s->aux_ts = qemu_new_timer_ns (vm_clock, aux_timer, s); if (!s->aux_ts) { dolog ("warning: Could not create auxiliary timer\n"); } diff -Nru qemu-kvm-0.14.1+noroms/hw/scsi-bus.c qemu-kvm-0.15.0+noroms/hw/scsi-bus.c --- qemu-kvm-0.14.1+noroms/hw/scsi-bus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/scsi-bus.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,6 +4,7 @@ #include "scsi-defs.h" #include "qdev.h" #include "blockdev.h" +#include "trace.h" static char *scsibus_get_fw_dev_path(DeviceState *dev); @@ -20,13 +21,13 @@ /* Create a scsi bus, and attach devices to it. */ void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - scsi_completionfn complete) + const SCSIBusOps *ops) { qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL); bus->busnr = next_scsi_bus++; bus->tcq = tcq; bus->ndev = ndev; - bus->complete = complete; + bus->ops = ops; bus->qbus.allow_hotplug = 1; } @@ -130,47 +131,68 @@ return res; } -SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun) +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, + uint32_t lun, void *hba_private) { SCSIRequest *req; req = qemu_mallocz(size); + req->refcount = 1; req->bus = scsi_bus_from_device(d); req->dev = d; req->tag = tag; req->lun = lun; + req->hba_private = hba_private; req->status = -1; - req->enqueued = true; - QTAILQ_INSERT_TAIL(&d->requests, req, next); + trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); return req; } -SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag) +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private) { - SCSIRequest *req; + return d->info->alloc_req(d, tag, lun, hba_private); +} - QTAILQ_FOREACH(req, &d->requests, next) { - if (req->tag == tag) { - return req; - } +uint8_t *scsi_req_get_buf(SCSIRequest *req) +{ + return req->dev->info->get_buf(req); +} + +int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) +{ + if (req->dev->info->get_sense) { + return req->dev->info->get_sense(req, buf, len); + } else { + return 0; } - return NULL; +} + +int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf) +{ + int32_t rc; + + assert(!req->enqueued); + scsi_req_ref(req); + req->enqueued = true; + QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + + scsi_req_ref(req); + rc = req->dev->info->send_command(req, buf); + scsi_req_unref(req); + return rc; } static void scsi_req_dequeue(SCSIRequest *req) { + trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); if (req->enqueued) { QTAILQ_REMOVE(&req->dev->requests, req, next); req->enqueued = false; + scsi_req_unref(req); } } -void scsi_req_free(SCSIRequest *req) -{ - scsi_req_dequeue(req); - qemu_free(req); -} - static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) { switch (cmd[0] >> 5) { @@ -195,6 +217,7 @@ req->cmd.len = 12; break; default: + trace_scsi_req_parse_bad(req->dev->id, req->lun, req->tag, cmd[0]); return -1; } @@ -392,9 +415,104 @@ memcpy(req->cmd.buf, buf, req->cmd.len); scsi_req_xfer_mode(req); req->cmd.lba = scsi_req_lba(req); + trace_scsi_req_parsed(req->dev->id, req->lun, req->tag, buf[0], + req->cmd.mode, req->cmd.xfer); + if (req->cmd.lba != -1) { + trace_scsi_req_parsed_lba(req->dev->id, req->lun, req->tag, buf[0], + req->cmd.lba); + } return 0; } +/* + * Predefined sense codes + */ + +/* No sense data available */ +const struct SCSISense sense_code_NO_SENSE = { + .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 +}; + +/* LUN not ready, Manual intervention required */ +const struct SCSISense sense_code_LUN_NOT_READY = { + .key = NOT_READY, .asc = 0x04, .ascq = 0x03 +}; + +/* LUN not ready, Medium not present */ +const struct SCSISense sense_code_NO_MEDIUM = { + .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 +}; + +/* Hardware error, internal target failure */ +const struct SCSISense sense_code_TARGET_FAILURE = { + .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 +}; + +/* Illegal request, invalid command operation code */ +const struct SCSISense sense_code_INVALID_OPCODE = { + .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 +}; + +/* Illegal request, LBA out of range */ +const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { + .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 +}; + +/* Illegal request, Invalid field in CDB */ +const struct SCSISense sense_code_INVALID_FIELD = { + .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 +}; + +/* Illegal request, LUN not supported */ +const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { + .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 +}; + +/* Command aborted, I/O process terminated */ +const struct SCSISense sense_code_IO_ERROR = { + .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 +}; + +/* Command aborted, I_T Nexus loss occurred */ +const struct SCSISense sense_code_I_T_NEXUS_LOSS = { + .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 +}; + +/* Command aborted, Logical Unit failure */ +const struct SCSISense sense_code_LUN_FAILURE = { + .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 +}; + +/* + * scsi_build_sense + * + * Build a sense buffer + */ +int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed) +{ + if (!fixed && len < 8) { + return 0; + } + + memset(buf, 0, len); + if (fixed) { + /* Return fixed format sense buffer */ + buf[0] = 0xf0; + buf[2] = sense.key; + buf[7] = 7; + buf[12] = sense.asc; + buf[13] = sense.ascq; + return MIN(len, 18); + } else { + /* Return descriptor format sense buffer */ + buf[0] = 0x72; + buf[1] = sense.key; + buf[2] = sense.asc; + buf[3] = sense.ascq; + return 8; + } +} + static const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { @@ -489,6 +607,43 @@ return names[cmd]; } +SCSIRequest *scsi_req_ref(SCSIRequest *req) +{ + req->refcount++; + return req; +} + +void scsi_req_unref(SCSIRequest *req) +{ + if (--req->refcount == 0) { + if (req->dev->info->free_req) { + req->dev->info->free_req(req); + } + qemu_free(req); + } +} + +/* Tell the device that we finished processing this chunk of I/O. It + will start the next chunk or complete the command. */ +void scsi_req_continue(SCSIRequest *req) +{ + trace_scsi_req_continue(req->dev->id, req->lun, req->tag); + if (req->cmd.mode == SCSI_XFER_TO_DEV) { + req->dev->info->write_data(req); + } else { + req->dev->info->read_data(req); + } +} + +/* Called by the devices when data is ready for the HBA. The HBA should + start a DMA operation to read or fill the device's data buffer. + Once it completes, calling scsi_req_continue will restart I/O. */ +void scsi_req_data(SCSIRequest *req, int len) +{ + trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); + req->bus->ops->transfer_data(req, len); +} + void scsi_req_print(SCSIRequest *req) { FILE *fp = stderr; @@ -520,10 +675,42 @@ void scsi_req_complete(SCSIRequest *req) { assert(req->status != -1); + scsi_req_ref(req); + scsi_req_dequeue(req); + req->bus->ops->complete(req, req->status); + scsi_req_unref(req); +} + +void scsi_req_cancel(SCSIRequest *req) +{ + if (req->dev && req->dev->info->cancel_io) { + req->dev->info->cancel_io(req); + } + scsi_req_ref(req); scsi_req_dequeue(req); - req->bus->complete(req->bus, SCSI_REASON_DONE, - req->tag, - req->status); + if (req->bus->ops->cancel) { + req->bus->ops->cancel(req); + } + scsi_req_unref(req); +} + +void scsi_req_abort(SCSIRequest *req, int status) +{ + req->status = status; + if (req->dev && req->dev->info->cancel_io) { + req->dev->info->cancel_io(req); + } + scsi_req_complete(req); +} + +void scsi_device_purge_requests(SCSIDevice *sdev) +{ + SCSIRequest *req; + + while (!QTAILQ_EMPTY(&sdev->requests)) { + req = QTAILQ_FIRST(&sdev->requests); + scsi_req_cancel(req); + } } static char *scsibus_get_fw_dev_path(DeviceState *dev) diff -Nru qemu-kvm-0.14.1+noroms/hw/scsi-disk.c qemu-kvm-0.15.0+noroms/hw/scsi-disk.c --- qemu-kvm-0.14.1+noroms/hw/scsi-disk.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/scsi-disk.c 2011-08-09 07:40:29.000000000 -0500 @@ -12,7 +12,7 @@ * 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the * MODE SENSE response. * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * * Note that this file only handles the SCSI architecture model and device * commands. Emulation of interface/link layer protocols is handled by @@ -49,14 +49,8 @@ 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 - a read or a write. Currently we rely on the host getting it right. */ /* Both sector and sector_count are in terms of qemu 512 byte blocks. */ uint64_t sector; uint32_t sector_count; @@ -65,6 +59,8 @@ uint32_t status; } SCSIDiskReq; +typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind; + struct SCSIDiskState { SCSIDevice qdev; @@ -78,32 +74,30 @@ char *version; char *serial; SCSISense sense; + SCSIDriveKind drive_kind; }; 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) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, + uint32_t lun, void *hba_private) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; SCSIDiskReq *r; - req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); + req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun, hba_private); r = DO_UPCAST(SCSIDiskReq, req, req); r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); - return r; + return req; } -static void scsi_remove_request(SCSIDiskReq *r) +static void scsi_free_request(SCSIRequest *req) { - qemu_vfree(r->iov.iov_base); - scsi_req_free(&r->req); -} + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); -static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag) -{ - return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag)); + qemu_vfree(r->iov.iov_base); } static void scsi_disk_clear_sense(SCSIDiskState *s) @@ -111,42 +105,33 @@ memset(&s->sense, 0, sizeof(s->sense)); } -static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key) -{ - s->sense.key = key; -} - -static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code) +static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); r->req.status = status; - scsi_disk_set_sense(s, sense_code); + s->sense = sense; } /* Helper function for command completion. */ -static void scsi_command_complete(SCSIDiskReq *r, int status, int sense) +static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense) { - DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", - r->req.tag, status, sense); + DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n", + r->req.tag, status, sense.key, sense.asc, sense.ascq); scsi_req_set_status(r, status, sense); scsi_req_complete(&r->req); - scsi_remove_request(r); } /* Cancel a pending data transfer. */ -static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) +static void scsi_cancel_io(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - DPRINTF("Cancel tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (r) { - if (r->req.aiocb) - bdrv_aio_cancel(r->req.aiocb); - r->req.aiocb = NULL; - scsi_remove_request(r); + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + + DPRINTF("Cancel tag=0x%x\n", req->tag); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); } + r->req.aiocb = NULL; } static void scsi_read_complete(void * opaque, int ret) @@ -167,30 +152,38 @@ 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); + scsi_req_data(&r->req, r->iov.iov_len); } -static void scsi_read_request(SCSIDiskReq *r) +/* Read more data from scsi device into buffer. */ +static void scsi_read_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; if (r->sector_count == (uint32_t)-1) { 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); + scsi_req_data(&r->req, r->iov.iov_len); return; } DPRINTF("Read sector_count=%d\n", r->sector_count); if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); return; } /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + DPRINTF("Data transfer direction invalid\n"); + scsi_read_complete(r, -EINVAL); + return; + } + n = r->sector_count; if (n > SCSI_DMA_BUF_SIZE / 512) n = SCSI_DMA_BUF_SIZE / 512; @@ -204,23 +197,6 @@ } } -/* 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); - return; - } - - scsi_read_request(r); -} - static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); @@ -239,16 +215,27 @@ r->status |= SCSI_REQ_STATUS_RETRY | type; bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); - vm_stop(0); + vm_stop(VMSTOP_DISKFULL); } else { if (type == SCSI_REQ_STATUS_RETRY_READ) { - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); + scsi_req_data(&r->req, 0); + } + switch (error) { + case ENOMEM: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(TARGET_FAILURE)); + break; + case EINVAL: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(INVALID_FIELD)); + break; + default: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(IO_ERROR)); + break; } - scsi_command_complete(r, CHECK_CONDITION, - HARDWARE_ERROR); bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } - return 1; } @@ -270,7 +257,7 @@ r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } else { len = r->sector_count * 512; if (len > SCSI_DMA_BUF_SIZE) { @@ -278,25 +265,32 @@ } r->iov.iov_len = len; DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); + scsi_req_data(&r->req, len); } } -static void scsi_write_request(SCSIDiskReq *r) +static void scsi_write_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); 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); + if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { + DPRINTF("Data transfer direction invalid\n"); + scsi_write_complete(r, -EINVAL); + return; + } + n = r->iov.iov_len / 512; if (n) { qemu_iovec_init_external(&r->qiov, &r->iov, 1); r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, scsi_write_complete, r); if (r->req.aiocb == NULL) { - scsi_write_complete(r, -EIO); + scsi_write_complete(r, -ENOMEM); } } else { /* Invoke completion routine to fetch data from host. */ @@ -304,26 +298,6 @@ } } -/* Write data to a scsi device. Returns nonzero on failure. - The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIDevice *d, uint32_t tag) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - - DPRINTF("Write data tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad write tag 0x%x\n", tag); - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return 1; - } - - scsi_write_request(r); - - return 0; -} - static void scsi_dma_restart_bh(void *opaque) { SCSIDiskState *s = opaque; @@ -344,15 +318,15 @@ switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { case SCSI_REQ_STATUS_RETRY_READ: - scsi_read_request(r); + scsi_read_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_WRITE: - scsi_write_request(r); + scsi_write_data(&r->req); 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); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } } } @@ -373,19 +347,21 @@ } /* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) +static uint8_t *scsi_get_buf(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad buffer tag 0x%x\n", tag); - return NULL; - } return (uint8_t *)r->iov.iov_base; } +/* Copy sense information into the provided buffer */ +static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + return scsi_build_sense(s->sense, outbuf, len, len > 14); +} + static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); @@ -406,7 +382,7 @@ return -1; } - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { outbuf[buflen++] = 5; } else { outbuf[buflen++] = 0; @@ -422,9 +398,10 @@ "buffer size %zd\n", req->cmd.xfer); pages = buflen++; outbuf[buflen++] = 0x00; // list of supported pages (this page) - outbuf[buflen++] = 0x80; // unit serial number + if (s->serial) + outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x83; // device identification - if (bdrv_get_type_hint(s->bs) != BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_HD) { outbuf[buflen++] = 0xb0; // block limits outbuf[buflen++] = 0xb2; // thin provisioning } @@ -433,8 +410,14 @@ } case 0x80: /* Device serial number, optional */ { - int l = strlen(s->serial); + int l; + + if (!s->serial) { + DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); + return -1; + } + l = strlen(s->serial); if (l > req->cmd.xfer) l = req->cmd.xfer; if (l > 20) @@ -477,7 +460,7 @@ unsigned int opt_io_size = s->qdev.conf.opt_io_size / s->qdev.blocksize; - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", page_code); return -1; @@ -542,12 +525,12 @@ memset(outbuf, 0, buflen); - if (req->lun || req->cmd.buf[1] >> 5) { + if (req->lun) { outbuf[0] = 0x7f; /* LUN not supported */ return buflen; } - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { outbuf[0] = 5; outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); @@ -678,7 +661,7 @@ return p[1] + 2; case 0x2a: /* CD Capabilities and Mechanical Status page. */ - if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM) + if (s->drive_kind != SCSI_CD) return 0; p[0] = 0x2a; p[1] = 0x14; @@ -857,19 +840,8 @@ case REQUEST_SENSE: if (req->cmd.xfer < 4) goto illegal_request; - memset(outbuf, 0, 4); - buflen = 4; - if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) { - memset(outbuf, 0, 18); - buflen = 18; - outbuf[7] = 10; - /* asc 0x3a, ascq 0: Medium not present */ - outbuf[12] = 0x3a; - outbuf[13] = 0; - } - outbuf[0] = 0xf0; - outbuf[1] = 0; - outbuf[2] = s->sense.key; + buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer, + req->cmd.xfer > 13); scsi_disk_clear_sense(s); break; case INQUIRY: @@ -905,7 +877,7 @@ goto illegal_request; break; case START_STOP: - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) { + if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) { /* load/eject medium */ bdrv_eject(s->bs, !(req->cmd.buf[4] & 1)); } @@ -1007,17 +979,22 @@ } break; default: - goto illegal_request; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return -1; } - scsi_req_set_status(r, GOOD, NO_SENSE); + scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE)); return buflen; not_ready: - scsi_command_complete(r, CHECK_CONDITION, NOT_READY); + if (!bdrv_is_inserted(s->bs)) { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM)); + } else { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY)); + } return -1; illegal_request: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return -1; } @@ -1026,33 +1003,23 @@ (eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, - uint8_t *buf, int lun) +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - uint32_t len; - int is_write; + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + int32_t len; uint8_t command; uint8_t *outbuf; - SCSIDiskReq *r; int rc; command = buf[0]; - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use\n", tag); - scsi_cancel_io(d, tag); - } - /* ??? Tags are not unique for different luns. We only implement a - single lun, so this should not matter. */ - 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]); + DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", req->lun, req->tag, buf[0]); if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); - goto fail; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; } #ifdef DEBUG_SCSI { @@ -1064,11 +1031,14 @@ } #endif - if (lun || buf[1] >> 5) { + if (req->lun) { /* Only LUN 0 supported. */ - DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5); - if (command != REQUEST_SENSE && command != INQUIRY) - goto fail; + DPRINTF("Unimplemented LUN %d\n", req->lun); + if (command != REQUEST_SENSE && command != INQUIRY) { + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(LUN_NOT_SUPPORTED)); + return 0; + } } switch (command) { case TEST_UNIT_READY: @@ -1101,7 +1071,7 @@ case READ_10: case READ_12: case READ_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); if (r->req.cmd.lba > s->max_lba) goto illegal_lba; @@ -1115,7 +1085,7 @@ case WRITE_VERIFY: case WRITE_VERIFY_12: case WRITE_VERIFY_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); @@ -1123,7 +1093,6 @@ goto illegal_lba; 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); @@ -1150,7 +1119,7 @@ } break; case WRITE_SAME_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); @@ -1176,18 +1145,20 @@ break; default: DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; fail: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return 0; illegal_lba: - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } len = r->sector_count * 512 + r->iov.iov_len; - if (is_write) { + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { return -len; } else { if (!r->sector_count) @@ -1196,25 +1167,12 @@ } } -static void scsi_disk_purge_requests(SCSIDiskState *s) -{ - 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); - } -} - static void scsi_disk_reset(DeviceState *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); uint64_t nb_sectors; - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); bdrv_get_geometry(s->bs, &nb_sectors); nb_sectors /= s->cluster_size; @@ -1228,14 +1186,13 @@ { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); blockdev_mark_auto_del(s->qdev.conf.bs); } -static int scsi_disk_initfn(SCSIDevice *dev) +static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - int is_cd; DriveInfo *dinfo; if (!s->qdev.conf.bs) { @@ -1243,9 +1200,9 @@ return -1; } s->bs = s->qdev.conf.bs; - is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM; + s->drive_kind = kind; - if (!is_cd && !bdrv_is_inserted(s->bs)) { + if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) { error_report("Device needs media, but drive is empty"); return -1; } @@ -1253,7 +1210,9 @@ 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 (*dinfo->serial) { + s->serial = qemu_strdup(dinfo->serial); + } } if (!s->version) { @@ -1265,7 +1224,7 @@ return -1; } - if (is_cd) { + if (kind == SCSI_CD) { s->qdev.blocksize = 2048; } else { s->qdev.blocksize = s->qdev.conf.logical_block_size; @@ -1275,35 +1234,113 @@ s->qdev.type = TYPE_DISK; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); - bdrv_set_removable(s->bs, is_cd); + bdrv_set_removable(s->bs, kind == SCSI_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, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .cancel_io = scsi_cancel_io, - .get_buf = scsi_get_buf, - .qdev.props = (Property[]) { - 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(), - }, +static int scsi_hd_initfn(SCSIDevice *dev) +{ + return scsi_initfn(dev, SCSI_HD); +} + +static int scsi_cd_initfn(SCSIDevice *dev) +{ + return scsi_initfn(dev, SCSI_CD); +} + +static int scsi_disk_initfn(SCSIDevice *dev) +{ + SCSIDriveKind kind; + DriveInfo *dinfo; + + if (!dev->conf.bs) { + kind = SCSI_HD; /* will die in scsi_initfn() */ + } else { + dinfo = drive_get_by_blockdev(dev->conf.bs); + kind = dinfo->media_cd ? SCSI_CD : SCSI_HD; + } + + return scsi_initfn(dev, kind); +} + +#define DEFINE_SCSI_DISK_PROPERTIES() \ + DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ + DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ + DEFINE_PROP_STRING("serial", SCSIDiskState, serial) + +static SCSIDeviceInfo scsi_disk_info[] = { + { + .qdev.name = "scsi-hd", + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI disk", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_hd_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), + DEFINE_PROP_END_OF_LIST(), + } + },{ + .qdev.name = "scsi-cd", + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI CD-ROM", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_cd_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + }, + },{ + .qdev.name = "scsi-disk", /* legacy -device scsi-disk */ + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI disk or CD-ROM (legacy)", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_disk_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), + DEFINE_PROP_END_OF_LIST(), + } + } }; static void scsi_disk_register_devices(void) { - scsi_qdev_register(&scsi_disk_info); + int i; + + for (i = 0; i < ARRAY_SIZE(scsi_disk_info); i++) { + scsi_qdev_register(&scsi_disk_info[i]); + } } device_init(scsi_disk_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/scsi-generic.c qemu-kvm-0.15.0+noroms/hw/scsi-generic.c --- qemu-kvm-0.14.1+noroms/hw/scsi-generic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/scsi-generic.c 2011-08-09 07:40:29.000000000 -0500 @@ -7,7 +7,7 @@ * * Written by Laurent Vivier * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ @@ -66,23 +66,50 @@ uint8_t senselen; }; -static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) +static void scsi_set_sense(SCSIGenericState *s, SCSISense sense) { - SCSIRequest *req; + s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); + s->driver_status = SG_ERR_DRIVER_SENSE; +} - req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); - return DO_UPCAST(SCSIGenericReq, req, req); +static void scsi_clear_sense(SCSIGenericState *s) +{ + memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE); + s->senselen = 0; + s->driver_status = 0; } -static void scsi_remove_request(SCSIGenericReq *r) +static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) { - qemu_free(r->buf); - scsi_req_free(&r->req); + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + int size = SCSI_SENSE_BUF_SIZE; + + if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) { + size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf, + SCSI_SENSE_BUF_SIZE, 0); + } + if (size > len) { + size = len; + } + memcpy(outbuf, s->sensebuf, size); + + return size; } -static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private) { - return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag)); + SCSIRequest *req; + + req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun, hba_private); + return req; +} + +static void scsi_free_request(SCSIRequest *req) +{ + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + qemu_free(r->buf); } /* Helper function for command completion. */ @@ -91,13 +118,30 @@ SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); + r->req.aiocb = NULL; s->driver_status = r->io_header.driver_status; if (s->driver_status & SG_ERR_DRIVER_SENSE) s->senselen = r->io_header.sb_len_wr; - if (ret != 0) - r->req.status = BUSY; - else { + if (ret != 0) { + switch (ret) { + case -EDOM: + r->req.status = TASK_SET_FULL; + break; + case -EINVAL: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(INVALID_FIELD)); + break; + case -ENOMEM: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE)); + break; + default: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(IO_ERROR)); + break; + } + } else { if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { r->req.status = BUSY; BADF("Driver Timeout\n"); @@ -112,23 +156,18 @@ r, r->req.tag, r->req.status); scsi_req_complete(&r->req); - scsi_remove_request(r); } /* Cancel a pending data transfer. */ -static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) +static void scsi_cancel_io(SCSIRequest *req) { - DPRINTF("scsi_cancel_io 0x%x\n", tag); - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - DPRINTF("Cancel tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (r) { - if (r->req.aiocb) - bdrv_aio_cancel(r->req.aiocb); - r->req.aiocb = NULL; - scsi_remove_request(r); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + DPRINTF("Cancel tag=0x%x\n", req->tag); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); } + r->req.aiocb = NULL; } static int execute_command(BlockDriverState *bdrv, @@ -152,7 +191,7 @@ r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r); if (r->req.aiocb == NULL) { BADF("execute_command: read failed !\n"); - return -1; + return -ENOMEM; } return 0; @@ -163,6 +202,7 @@ SCSIGenericReq *r = (SCSIGenericReq *)opaque; int len; + r->req.aiocb = NULL; if (ret) { DPRINTF("IO error ret %d\n", ret); scsi_command_complete(r, ret); @@ -172,27 +212,21 @@ DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); r->len = -1; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); - if (len == 0) + if (len == 0) { scsi_command_complete(r, 0); + } else { + scsi_req_data(&r->req, len); + } } /* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) +static void scsi_read_data(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); int ret; - DPRINTF("scsi_read_data 0x%x\n", tag); - 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, -EINVAL); - return; - } - + DPRINTF("scsi_read_data 0x%x\n", req->tag); if (r->len == -1) { scsi_command_complete(r, 0); return; @@ -210,13 +244,15 @@ DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", r->buf[0], r->buf[1], r->buf[2], r->buf[3], r->buf[4], r->buf[5], r->buf[6], r->buf[7]); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen); + scsi_req_data(&r->req, s->senselen); + /* Clear sensebuf after REQUEST_SENSE */ + scsi_clear_sense(s); return; } ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); + if (ret < 0) { + scsi_command_complete(r, ret); return; } } @@ -227,6 +263,7 @@ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); DPRINTF("scsi_write_complete() ret = %d\n", ret); + r->req.aiocb = NULL; if (ret) { DPRINTF("IO error\n"); scsi_command_complete(r, ret); @@ -244,46 +281,30 @@ /* Write data to a scsi device. Returns nonzero on failure. The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIDevice *d, uint32_t tag) +static void scsi_write_data(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; - DPRINTF("scsi_write_data 0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad write tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, -EINVAL); - return 0; - } - + DPRINTF("scsi_write_data 0x%x\n", req->tag); if (r->len == 0) { r->len = r->buflen; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len); - return 0; + scsi_req_data(&r->req, r->len); + return; } ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); - return 1; + if (ret < 0) { + scsi_command_complete(r, ret); } - - return 0; } /* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) +static uint8_t *scsi_get_buf(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad buffer tag 0x%x\n", tag); - return NULL; - } + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + return r->buf; } @@ -311,42 +332,23 @@ (eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, - uint8_t *cmd, int lun) +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - SCSIBus *bus; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; - if (cmd[0] != REQUEST_SENSE && - (lun != s->lun || (cmd[1] >> 5) != s->lun)) { - DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5); - - s->sensebuf[0] = 0x70; - s->sensebuf[1] = 0x00; - s->sensebuf[2] = ILLEGAL_REQUEST; - s->sensebuf[3] = 0x00; - s->sensebuf[4] = 0x00; - s->sensebuf[5] = 0x00; - s->sensebuf[6] = 0x00; - 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); + if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) { + DPRINTF("Unimplemented LUN %d\n", req->lun); + scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED)); + r->req.status = CHECK_CONDITION; + scsi_req_complete(&r->req); return 0; } - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use %p\n", tag, r); - scsi_cancel_io(d, tag); - } - r = scsi_new_request(d, tag, lun); - if (-1 == scsi_req_parse(&r->req, cmd)) { BADF("Unsupported command length, command %x\n", cmd[0]); - scsi_remove_request(r); + scsi_command_complete(r, -EINVAL); return 0; } scsi_req_fixup(&r->req); @@ -370,8 +372,8 @@ r->buflen = 0; r->buf = NULL; ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); + if (ret < 0) { + scsi_command_complete(r, ret); return 0; } return 0; @@ -389,9 +391,9 @@ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { r->len = 0; return -r->req.cmd.xfer; + } else { + return r->req.cmd.xfer; } - - return r->req.cmd.xfer; } static int get_blocksize(BlockDriverState *bdrv) @@ -455,31 +457,18 @@ return (buf[9] << 16) | (buf[10] << 8) | buf[11]; } -static void scsi_generic_purge_requests(SCSIGenericState *s) -{ - 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); - } -} - static void scsi_generic_reset(DeviceState *dev) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev); - scsi_generic_purge_requests(s); + scsi_device_purge_requests(&s->qdev); } static void scsi_destroy(SCSIDevice *d) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - scsi_generic_purge_requests(s); + scsi_device_purge_requests(&s->qdev); blockdev_mark_auto_del(s->qdev.conf.bs); } @@ -556,11 +545,14 @@ .qdev.reset = scsi_generic_reset, .init = scsi_generic_initfn, .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/scsi.h qemu-kvm-0.15.0+noroms/hw/scsi.h --- qemu-kvm-0.14.1+noroms/hw/scsi.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/scsi.h 2011-08-09 07:40:29.000000000 -0500 @@ -9,17 +9,11 @@ #define SCSI_CMD_BUF_SIZE 16 -/* scsi-disk.c */ -enum scsi_reason { - SCSI_REASON_DONE, /* Command complete. */ - SCSI_REASON_DATA /* Transfer complete, more data required. */ -}; - typedef struct SCSIBus SCSIBus; +typedef struct SCSIBusOps SCSIBusOps; typedef struct SCSIDevice SCSIDevice; typedef struct SCSIDeviceInfo SCSIDeviceInfo; -typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg); +typedef struct SCSIRequest SCSIRequest; enum SCSIXferMode { SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ @@ -27,9 +21,16 @@ SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ }; -typedef struct SCSIRequest { +typedef struct SCSISense { + uint8_t key; + uint8_t asc; + uint8_t ascq; +} SCSISense; + +struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; + uint32_t refcount; uint32_t tag; uint32_t lun; uint32_t status; @@ -42,8 +43,9 @@ } cmd; BlockDriverAIOCB *aiocb; bool enqueued; + void *hba_private; QTAILQ_ENTRY(SCSIRequest) next; -} SCSIRequest; +}; struct SCSIDevice { @@ -66,28 +68,35 @@ DeviceInfo qdev; scsi_qdev_initfn init; void (*destroy)(SCSIDevice *s); - int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf, - int lun); - void (*read_data)(SCSIDevice *s, uint32_t tag); - int (*write_data)(SCSIDevice *s, uint32_t tag); - void (*cancel_io)(SCSIDevice *s, uint32_t tag); - uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag); + SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun, + void *hba_private); + void (*free_req)(SCSIRequest *req); + int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); + void (*read_data)(SCSIRequest *req); + void (*write_data)(SCSIRequest *req); + void (*cancel_io)(SCSIRequest *req); + uint8_t *(*get_buf)(SCSIRequest *req); + int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len); +}; + +struct SCSIBusOps { + void (*transfer_data)(SCSIRequest *req, uint32_t arg); + void (*complete)(SCSIRequest *req, uint32_t arg); + void (*cancel)(SCSIRequest *req); }; -typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv, - int unit); struct SCSIBus { BusState qbus; int busnr; int tcq, ndev; - scsi_completionfn complete; + const SCSIBusOps *ops; SCSIDevice *devs[MAX_SCSI_DEVS]; }; void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - scsi_completionfn complete); + const SCSIBusOps *ops); void scsi_qdev_register(SCSIDeviceInfo *info); static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) @@ -99,12 +108,56 @@ 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); +/* + * Predefined sense codes + */ + +/* No sense data available */ +extern const struct SCSISense sense_code_NO_SENSE; +/* LUN not ready, Manual intervention required */ +extern const struct SCSISense sense_code_LUN_NOT_READY; +/* LUN not ready, Medium not present */ +extern const struct SCSISense sense_code_NO_MEDIUM; +/* Hardware error, internal target failure */ +extern const struct SCSISense sense_code_TARGET_FAILURE; +/* Illegal request, invalid command operation code */ +extern const struct SCSISense sense_code_INVALID_OPCODE; +/* Illegal request, LBA out of range */ +extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE; +/* Illegal request, Invalid field in CDB */ +extern const struct SCSISense sense_code_INVALID_FIELD; +/* Illegal request, LUN not supported */ +extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED; +/* Command aborted, I/O process terminated */ +extern const struct SCSISense sense_code_IO_ERROR; +/* Command aborted, I_T Nexus loss occurred */ +extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; +/* Command aborted, Logical Unit failure */ +extern const struct SCSISense sense_code_LUN_FAILURE; + +#define SENSE_CODE(x) sense_code_ ## x + +int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); +int scsi_sense_valid(SCSISense sense); + +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, + uint32_t lun, void *hba_private); +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private); +int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf); void scsi_req_free(SCSIRequest *req); +SCSIRequest *scsi_req_ref(SCSIRequest *req); +void scsi_req_unref(SCSIRequest *req); int scsi_req_parse(SCSIRequest *req, uint8_t *buf); void scsi_req_print(SCSIRequest *req); +void scsi_req_continue(SCSIRequest *req); +void scsi_req_data(SCSIRequest *req, int len); void scsi_req_complete(SCSIRequest *req); +uint8_t *scsi_req_get_buf(SCSIRequest *req); +int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); +void scsi_req_abort(SCSIRequest *req, int status); +void scsi_req_cancel(SCSIRequest *req); +void scsi_device_purge_requests(SCSIDevice *sdev); #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/sd.c qemu-kvm-0.15.0+noroms/hw/sd.c --- qemu-kvm-0.14.1+noroms/hw/sd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sd.c 2011-08-09 07:40:29.000000000 -0500 @@ -460,8 +460,8 @@ { sd->readonly_cb = readonly; sd->inserted_cb = insert; - qemu_set_irq(readonly, bdrv_is_read_only(sd->bdrv)); - qemu_set_irq(insert, bdrv_is_inserted(sd->bdrv)); + qemu_set_irq(readonly, sd->bdrv ? bdrv_is_read_only(sd->bdrv) : 0); + qemu_set_irq(insert, sd->bdrv ? bdrv_is_inserted(sd->bdrv) : 0); } static void sd_erase(SDState *sd) @@ -1104,6 +1104,17 @@ } break; + case 52: + case 53: + /* CMD52, CMD53: reserved for SDIO cards + * (see the SDIO Simplified Specification V2.0) + * Handle as illegal command but do not complain + * on stderr, as some OSes may use these in their + * probing for presence of an SDIO card. + */ + sd->card_status |= ILLEGAL_COMMAND; + return sd_r0; + /* Application specific commands (Class 8) */ case 55: /* CMD55: APP_CMD */ if (sd->rca != rca) diff -Nru qemu-kvm-0.14.1+noroms/hw/serial.c qemu-kvm-0.15.0+noroms/hw/serial.c --- qemu-kvm-0.14.1+noroms/hw/serial.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/serial.c 2011-08-09 07:40:29.000000000 -0500 @@ -312,13 +312,13 @@ We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */ if (s->poll_msl) - qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + get_ticks_per_sec() / 100); + qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 100); } static void serial_xmit(void *opaque) { SerialState *s = opaque; - uint64_t new_xmit_ts = qemu_get_clock(vm_clock); + uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock); if (s->tsr_retry <= 0) { if (s->fcr & UART_FCR_FE) { @@ -350,7 +350,7 @@ s->tsr_retry = 0; } - s->last_xmit_ts = qemu_get_clock(vm_clock); + s->last_xmit_ts = qemu_get_clock_ns(vm_clock); if (!(s->lsr & UART_LSR_THRE)) qemu_mod_timer(s->transmit_timer, s->last_xmit_ts + s->char_transmit_time); @@ -494,7 +494,7 @@ qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); /* Update the modem status after a one-character-send wait-time, since there may be a response from the device/computer at the other end of the serial line */ - qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + s->char_transmit_time); + qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + s->char_transmit_time); } } break; @@ -525,7 +525,7 @@ if (s->recv_fifo.count == 0) s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); else - qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock (vm_clock) + s->char_transmit_time * 4); + qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4); s->timeout_ipending = 0; } else { ret = s->rbr; @@ -641,7 +641,7 @@ } s->lsr |= UART_LSR_DR; /* 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); + qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4); } else { if (s->lsr & UART_LSR_DR) s->lsr |= UART_LSR_OE; @@ -720,7 +720,7 @@ fifo_clear(s,RECV_FIFO); fifo_clear(s,XMIT_FIFO); - s->last_xmit_ts = qemu_get_clock(vm_clock); + s->last_xmit_ts = qemu_get_clock_ns(vm_clock); s->thr_ipending = 0; s->last_break_enable = 0; @@ -734,10 +734,10 @@ exit(1); } - s->modem_status_poll = qemu_new_timer(vm_clock, (QEMUTimerCB *) serial_update_msl, s); + s->modem_status_poll = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_update_msl, s); - s->fifo_timeout_timer = qemu_new_timer(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s); - s->transmit_timer = qemu_new_timer(vm_clock, (QEMUTimerCB *) serial_xmit, s); + s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s); + s->transmit_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_xmit, s); qemu_register_reset(serial_reset, s); @@ -782,18 +782,6 @@ return 0; } -SerialState *serial_isa_init(int index, CharDriverState *chr) -{ - ISADevice *dev; - - dev = isa_create("isa-serial"); - qdev_prop_set_uint32(&dev->qdev, "index", index); - qdev_prop_set_chr(&dev->qdev, "chardev", chr); - if (qdev_init(&dev->qdev) < 0) - return NULL; - return &DO_UPCAST(ISASerialState, dev, dev)->state; -} - static const VMStateDescription vmstate_isa_serial = { .name = "serial", .version_id = 3, diff -Nru qemu-kvm-0.14.1+noroms/hw/sga.c qemu-kvm-0.15.0+noroms/hw/sga.c --- qemu-kvm-0.14.1+noroms/hw/sga.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/sga.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,56 @@ +/* + * QEMU dummy ISA device for loading sgabios option rom. + * + * Copyright (c) 2011 Glauber Costa, 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. + * + * sgabios code originally available at code.google.com/p/sgabios + * + */ +#include "pci.h" +#include "pc.h" +#include "loader.h" +#include "sysemu.h" + +#define SGABIOS_FILENAME "sgabios.bin" + +typedef struct ISAGAState { + ISADevice dev; +} ISASGAState; + +static int isa_cirrus_vga_initfn(ISADevice *dev) +{ + rom_add_vga(SGABIOS_FILENAME); + return 0; +} + +static ISADeviceInfo sga_info = { + .qdev.name = "sga", + .qdev.desc = "Serial Graphics Adapter", + .qdev.size = sizeof(ISASGAState), + .init = isa_cirrus_vga_initfn, +}; + +static void sga_register(void) +{ + isa_qdev_register(&sga_info); +} + +device_init(sga_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/sh7750.c qemu-kvm-0.15.0+noroms/hw/sh7750.c --- qemu-kvm-0.14.1+noroms/hw/sh7750.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sh7750.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,7 +29,6 @@ #include "sh7750_regs.h" #include "sh7750_regnames.h" #include "sh_intc.h" -#include "exec-all.h" #include "cpu.h" #define NB_DEVICES 4 diff -Nru qemu-kvm-0.14.1+noroms/hw/sh7750_regs.h qemu-kvm-0.15.0+noroms/hw/sh7750_regs.h --- qemu-kvm-0.14.1+noroms/hw/sh7750_regs.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sh7750_regs.h 2011-08-09 07:40:29.000000000 -0500 @@ -23,9 +23,9 @@ * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and * in 0x1f000000 - 0x1fffffff (area 7 address) */ -#define SH7750_P4_BASE 0xff000000 /* Accessable only in - priveleged mode */ -#define SH7750_A7_BASE 0x1f000000 /* Accessable only using TLB */ +#define SH7750_P4_BASE 0xff000000 /* Accessible only in + privileged mode */ +#define SH7750_A7_BASE 0x1f000000 /* Accessible only using TLB */ #define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs)) #define SH7750_A7_REG32(ofs) (SH7750_A7_BASE + (ofs)) diff -Nru qemu-kvm-0.14.1+noroms/hw/sh_intc.c qemu-kvm-0.15.0+noroms/hw/sh_intc.c --- qemu-kvm-0.14.1+noroms/hw/sh_intc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sh_intc.c 2011-08-09 07:40:29.000000000 -0500 @@ -5,7 +5,7 @@ * Based on sh_timer.c and arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sh_intc.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/sh_pci.c qemu-kvm-0.15.0+noroms/hw/sh_pci.c --- qemu-kvm-0.14.1+noroms/hw/sh_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sh_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -137,8 +137,6 @@ static int sh_pci_host_init(PCIDevice *d) { - 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); @@ -149,6 +147,8 @@ .qdev.name = "sh_pci_host", .qdev.size = sizeof(PCIDevice), .init = sh_pci_host_init, + .vendor_id = PCI_VENDOR_ID_HITACHI, + .device_id = PCI_DEVICE_ID_HITACHI_SH7751R, }; static void sh_pci_register_devices(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/sh_timer.c qemu-kvm-0.15.0+noroms/hw/sh_timer.c --- qemu-kvm-0.14.1+noroms/hw/sh_timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sh_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -5,7 +5,7 @@ * Based on arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/sm501_template.h qemu-kvm-0.15.0+noroms/hw/sm501_template.h --- qemu-kvm-0.14.1+noroms/hw/sm501_template.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sm501_template.h 2011-08-09 07:40:29.000000000 -0500 @@ -120,7 +120,7 @@ /* get pixel value */ if (i % 4 == 0) { - cpu_physical_memory_rw(cursor_addr, &bitset, 1, 0); + bitset = ldub_phys(cursor_addr); cursor_addr++; } v = bitset & 3; diff -Nru qemu-kvm-0.14.1+noroms/hw/smbus.c qemu-kvm-0.15.0+noroms/hw/smbus.c --- qemu-kvm-0.14.1+noroms/hw/smbus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/smbus.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* TODO: Implement PEC. */ diff -Nru qemu-kvm-0.14.1+noroms/hw/smbus_eeprom.c qemu-kvm-0.15.0+noroms/hw/smbus_eeprom.c --- qemu-kvm-0.14.1+noroms/hw/smbus_eeprom.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/smbus_eeprom.c 2011-08-09 07:40:29.000000000 -0500 @@ -96,7 +96,7 @@ return eeprom_receive_byte(dev); } -static int smbus_eeprom_init(SMBusDevice *dev) +static int smbus_eeprom_initfn(SMBusDevice *dev) { SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev; @@ -111,7 +111,7 @@ DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data), DEFINE_PROP_END_OF_LIST(), }, - .init = smbus_eeprom_init, + .init = smbus_eeprom_initfn, .quick_cmd = eeprom_quick_cmd, .send_byte = eeprom_send_byte, .receive_byte = eeprom_receive_byte, @@ -125,3 +125,21 @@ } device_init(smbus_eeprom_register_devices) + +void smbus_eeprom_init(i2c_bus *smbus, int nb_eeprom, + const uint8_t *eeprom_spd, int eeprom_spd_size) +{ + int i; + uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ + if (eeprom_spd_size > 0) { + memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size); + } + + for (i = 0; i < nb_eeprom; 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); + } +} diff -Nru qemu-kvm-0.14.1+noroms/hw/smbus.h qemu-kvm-0.15.0+noroms/hw/smbus.h --- qemu-kvm-0.14.1+noroms/hw/smbus.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/smbus.h 2011-08-09 07:40:29.000000000 -0500 @@ -66,3 +66,6 @@ int smbus_read_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data); void smbus_write_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data, int len); + +void smbus_eeprom_init(i2c_bus *smbus, int nb_eeprom, + const uint8_t *eeprom_spd, int size); diff -Nru qemu-kvm-0.14.1+noroms/hw/smc91c111.c qemu-kvm-0.15.0+noroms/hw/smc91c111.c --- qemu-kvm-0.14.1+noroms/hw/smc91c111.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/smc91c111.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" @@ -252,8 +252,9 @@ smc91c111_do_tx(s); } -static void smc91c111_reset(smc91c111_state *s) +static void smc91c111_reset(DeviceState *dev) { + smc91c111_state *s = FROM_SYSBUS(smc91c111_state, sysbus_from_qdev(dev)); s->bank = 0; s->tx_fifo_len = 0; s->tx_fifo_done_len = 0; @@ -302,7 +303,7 @@ case 5: SET_HIGH(rcr, value); if (s->rcr & RCR_SOFT_RST) - smc91c111_reset(s); + smc91c111_reset(&s->busdev.qdev); return; case 10: case 11: /* RPCR */ /* Ignored */ @@ -753,9 +754,6 @@ sysbus_init_mmio(dev, 16, s->mmio_index); sysbus_init_irq(dev, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); - - smc91c111_reset(s); - s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf, dev->qdev.info->name, dev->qdev.id, s); qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); @@ -768,6 +766,7 @@ .qdev.name = "smc91c111", .qdev.size = sizeof(smc91c111_state), .qdev.vmsd = &vmstate_smc91c111, + .qdev.reset = smc91c111_reset, .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(smc91c111_state, conf), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/soc_dma.c qemu-kvm-0.15.0+noroms/hw/soc_dma.c --- qemu-kvm-0.14.1+noroms/hw/soc_dma.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/soc_dma.c 2011-08-09 07:40:29.000000000 -0500 @@ -84,7 +84,7 @@ static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) { - int64_t now = qemu_get_clock(vm_clock); + int64_t now = qemu_get_clock_ns(vm_clock); struct dma_s *dma = (struct dma_s *) ch->dma; qemu_mod_timer(ch->timer, now + delay_bytes / dma->channel_freq); @@ -246,7 +246,7 @@ for (i = 0; i < n; i ++) { s->ch[i].dma = &s->soc; s->ch[i].num = i; - s->ch[i].timer = qemu_new_timer(vm_clock, soc_dma_ch_run, &s->ch[i]); + s->ch[i].timer = qemu_new_timer_ns(vm_clock, soc_dma_ch_run, &s->ch[i]); } soc_dma_reset(&s->soc); diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr.c qemu-kvm-0.15.0+noroms/hw/spapr.c --- qemu-kvm-0.14.1+noroms/hw/spapr.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,476 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * Copyright (c) 2010 David Gibson, IBM Corporation. + * + * 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 "hw.h" +#include "elf.h" +#include "net.h" +#include "blockdev.h" + +#include "hw/boards.h" +#include "hw/ppc.h" +#include "hw/loader.h" + +#include "hw/spapr.h" +#include "hw/spapr_vio.h" +#include "hw/xics.h" + +#include + +#define KERNEL_LOAD_ADDR 0x00000000 +#define INITRD_LOAD_ADDR 0x02800000 +#define FDT_MAX_SIZE 0x10000 +#define RTAS_MAX_SIZE 0x10000 +#define FW_MAX_SIZE 0x400000 +#define FW_FILE_NAME "slof.bin" + +#define MIN_RAM_SLOF 512UL + +#define TIMEBASE_FREQ 512000000ULL + +#define MAX_CPUS 256 +#define XICS_IRQS 1024 + +sPAPREnvironment *spapr; + +static void *spapr_create_fdt_skel(const char *cpu_model, + target_phys_addr_t initrd_base, + target_phys_addr_t initrd_size, + const char *boot_device, + const char *kernel_cmdline, + long hash_shift) +{ + void *fdt; + CPUState *env; + uint64_t mem_reg_property[] = { 0, cpu_to_be64(ram_size) }; + uint32_t start_prop = cpu_to_be32(initrd_base); + uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); + uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)}; + char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt" + "\0hcall-tce\0hcall-vio\0hcall-splpar"; + uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; + int i; + char *modelname; + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ + #exp, fdt_strerror(ret)); \ + exit(1); \ + } \ + } while (0) + + fdt = qemu_mallocz(FDT_MAX_SIZE); + _FDT((fdt_create(fdt, FDT_MAX_SIZE))); + + _FDT((fdt_finish_reservemap(fdt))); + + /* Root node */ + _FDT((fdt_begin_node(fdt, ""))); + _FDT((fdt_property_string(fdt, "device_type", "chrp"))); + _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)"))); + + _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); + _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); + + /* /chosen */ + _FDT((fdt_begin_node(fdt, "chosen"))); + + _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline))); + _FDT((fdt_property(fdt, "linux,initrd-start", + &start_prop, sizeof(start_prop)))); + _FDT((fdt_property(fdt, "linux,initrd-end", + &end_prop, sizeof(end_prop)))); + _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); + + _FDT((fdt_end_node(fdt))); + + /* memory node */ + _FDT((fdt_begin_node(fdt, "memory@0"))); + + _FDT((fdt_property_string(fdt, "device_type", "memory"))); + _FDT((fdt_property(fdt, "reg", + mem_reg_property, sizeof(mem_reg_property)))); + + _FDT((fdt_end_node(fdt))); + + /* cpus */ + _FDT((fdt_begin_node(fdt, "cpus"))); + + _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); + _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); + + modelname = qemu_strdup(cpu_model); + + for (i = 0; i < strlen(modelname); i++) { + modelname[i] = toupper(modelname[i]); + } + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + int index = env->cpu_index; + uint32_t gserver_prop[] = {cpu_to_be32(index), 0}; /* HACK! */ + char *nodename; + uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), + 0xffffffff, 0xffffffff}; + + if (asprintf(&nodename, "%s@%x", modelname, index) < 0) { + fprintf(stderr, "Allocation failure\n"); + exit(1); + } + + _FDT((fdt_begin_node(fdt, nodename))); + + free(nodename); + + _FDT((fdt_property_cell(fdt, "reg", index))); + _FDT((fdt_property_string(fdt, "device_type", "cpu"))); + + _FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR]))); + _FDT((fdt_property_cell(fdt, "dcache-block-size", + env->dcache_line_size))); + _FDT((fdt_property_cell(fdt, "icache-block-size", + env->icache_line_size))); + _FDT((fdt_property_cell(fdt, "timebase-frequency", TIMEBASE_FREQ))); + /* Hardcode CPU frequency for now. It's kind of arbitrary on + * full emu, for kvm we should copy it from the host */ + _FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000))); + _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr))); + _FDT((fdt_property(fdt, "ibm,pft-size", + pft_size_prop, sizeof(pft_size_prop)))); + _FDT((fdt_property_string(fdt, "status", "okay"))); + _FDT((fdt_property(fdt, "64-bit", NULL, 0))); + _FDT((fdt_property_cell(fdt, "ibm,ppc-interrupt-server#s", index))); + _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", + gserver_prop, sizeof(gserver_prop)))); + + if (env->mmu_model & POWERPC_MMU_1TSEG) { + _FDT((fdt_property(fdt, "ibm,processor-segment-sizes", + segs, sizeof(segs)))); + } + + _FDT((fdt_end_node(fdt))); + } + + qemu_free(modelname); + + _FDT((fdt_end_node(fdt))); + + /* RTAS */ + _FDT((fdt_begin_node(fdt, "rtas"))); + + _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop, + sizeof(hypertas_prop)))); + + _FDT((fdt_end_node(fdt))); + + /* interrupt controller */ + _FDT((fdt_begin_node(fdt, "interrupt-controller@0"))); + + _FDT((fdt_property_string(fdt, "device_type", + "PowerPC-External-Interrupt-Presentation"))); + _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp"))); + _FDT((fdt_property_cell(fdt, "reg", 0))); + _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); + _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", + interrupt_server_ranges_prop, + sizeof(interrupt_server_ranges_prop)))); + + _FDT((fdt_end_node(fdt))); + + /* vdevice */ + _FDT((fdt_begin_node(fdt, "vdevice"))); + + _FDT((fdt_property_string(fdt, "device_type", "vdevice"))); + _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice"))); + _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); + _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); + _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2))); + _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); + + _FDT((fdt_end_node(fdt))); + + _FDT((fdt_end_node(fdt))); /* close root node */ + _FDT((fdt_finish(fdt))); + + return fdt; +} + +static void spapr_finalize_fdt(sPAPREnvironment *spapr, + target_phys_addr_t fdt_addr, + target_phys_addr_t rtas_addr, + target_phys_addr_t rtas_size) +{ + int ret; + void *fdt; + + fdt = qemu_malloc(FDT_MAX_SIZE); + + /* open out the base tree into a temp buffer for the final tweaks */ + _FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE))); + + ret = spapr_populate_vdevice(spapr->vio_bus, fdt); + if (ret < 0) { + fprintf(stderr, "couldn't setup vio devices in fdt\n"); + exit(1); + } + + /* RTAS */ + ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size); + if (ret < 0) { + fprintf(stderr, "Couldn't set up RTAS device tree properties\n"); + } + + _FDT((fdt_pack(fdt))); + + cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); + + qemu_free(fdt); +} + +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; +} + +static void emulate_spapr_hypercall(CPUState *env) +{ + env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]); +} + +static void spapr_reset(void *opaque) +{ + sPAPREnvironment *spapr = (sPAPREnvironment *)opaque; + + fprintf(stderr, "sPAPR reset\n"); + + /* flush out the hash table */ + memset(spapr->htab, 0, spapr->htab_size); + + /* Load the fdt */ + spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr, + spapr->rtas_size); + + /* Set up the entry state */ + first_cpu->gpr[3] = spapr->fdt_addr; + first_cpu->gpr[5] = 0; + first_cpu->halted = 0; + first_cpu->nip = spapr->entry_point; + +} + +/* pSeries LPAR / sPAPR hardware init */ +static void ppc_spapr_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; + int i; + ram_addr_t ram_offset; + uint32_t initrd_base; + long kernel_size, initrd_size, fw_size; + long pteg_shift = 17; + char *filename; + int irq = 16; + + spapr = qemu_malloc(sizeof(*spapr)); + cpu_ppc_hypercall = emulate_spapr_hypercall; + + /* We place the device tree just below either the top of RAM, or + * 2GB, so that it can be processed with 32-bit code if + * necessary */ + spapr->fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE; + spapr->rtas_addr = spapr->fdt_addr - RTAS_MAX_SIZE; + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "POWER7"; + } + for (i = 0; i < smp_cpus; i++) { + env = cpu_init(cpu_model); + + if (!env) { + fprintf(stderr, "Unable to find PowerPC CPU definition\n"); + exit(1); + } + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, TIMEBASE_FREQ); + qemu_register_reset((QEMUResetHandler *)&cpu_reset, env); + + env->hreset_vector = 0x60; + env->hreset_excp_prefix = 0; + env->gpr[3] = env->cpu_index; + } + + /* allocate RAM */ + ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size); + cpu_register_physical_memory(0, ram_size, ram_offset); + + /* allocate hash page table. For now we always make this 16mb, + * later we should probably make it scale to the size of guest + * RAM */ + spapr->htab_size = 1ULL << (pteg_shift + 7); + spapr->htab = qemu_malloc(spapr->htab_size); + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + env->external_htab = spapr->htab; + env->htab_base = -1; + env->htab_mask = spapr->htab_size - 1; + } + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); + spapr->rtas_size = load_image_targphys(filename, spapr->rtas_addr, + ram_size - spapr->rtas_addr); + if (spapr->rtas_size < 0) { + hw_error("qemu: could not load LPAR rtas '%s'\n", filename); + exit(1); + } + qemu_free(filename); + + /* Set up Interrupt Controller */ + spapr->icp = xics_system_init(XICS_IRQS); + + /* Set up VIO bus */ + spapr->vio_bus = spapr_vio_bus_init(); + + for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) { + if (serial_hds[i]) { + spapr_vty_create(spapr->vio_bus, SPAPR_VTY_BASE_ADDRESS + i, + serial_hds[i], xics_find_qirq(spapr->icp, irq), + irq); + } + } + + for (i = 0; i < nb_nics; i++, irq++) { + NICInfo *nd = &nd_table[i]; + + if (!nd->model) { + nd->model = qemu_strdup("ibmveth"); + } + + if (strcmp(nd->model, "ibmveth") == 0) { + spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd, + xics_find_qirq(spapr->icp, irq), irq); + } else { + fprintf(stderr, "pSeries (sPAPR) platform does not support " + "NIC model '%s' (only ibmveth is supported)\n", + nd->model); + exit(1); + } + } + + for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) { + spapr_vscsi_create(spapr->vio_bus, 0x2000 + i, + xics_find_qirq(spapr->icp, irq), irq); + irq++; + } + + if (kernel_filename) { + uint64_t lowaddr = 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_image_targphys(kernel_filename, + KERNEL_LOAD_ADDR, + ram_size - KERNEL_LOAD_ADDR); + } + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + + /* load initrd */ + if (initrd_filename) { + initrd_base = INITRD_LOAD_ADDR; + initrd_size = load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } else { + initrd_base = 0; + initrd_size = 0; + } + + spapr->entry_point = KERNEL_LOAD_ADDR; + } else { + if (ram_size < (MIN_RAM_SLOF << 20)) { + fprintf(stderr, "qemu: pSeries SLOF firmware requires >= " + "%ldM guest RAM\n", MIN_RAM_SLOF); + exit(1); + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin"); + fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); + if (fw_size < 0) { + hw_error("qemu: could not load LPAR rtas '%s'\n", filename); + exit(1); + } + qemu_free(filename); + spapr->entry_point = 0x100; + initrd_base = 0; + initrd_size = 0; + + /* SLOF will startup the secondary CPUs using RTAS, + rather than expecting a kexec() style entry */ + for (env = first_cpu; env != NULL; env = env->next_cpu) { + env->halted = 1; + } + } + + /* Prepare the device tree */ + spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, + initrd_base, initrd_size, + boot_device, kernel_cmdline, + pteg_shift + 7); + assert(spapr->fdt_skel != NULL); + + qemu_register_reset(spapr_reset, spapr); +} + +static QEMUMachine spapr_machine = { + .name = "pseries", + .desc = "pSeries Logical Partition (PAPR compliant)", + .init = ppc_spapr_init, + .max_cpus = MAX_CPUS, + .no_vga = 1, + .no_parallel = 1, + .use_scsi = 1, +}; + +static void spapr_machine_init(void) +{ + qemu_register_machine(&spapr_machine); +} + +machine_init(spapr_machine_init); diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr.h qemu-kvm-0.15.0+noroms/hw/spapr.h --- qemu-kvm-0.14.1+noroms/hw/spapr.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,301 @@ +#if !defined(__HW_SPAPR_H__) +#define __HW_SPAPR_H__ + +struct VIOsPAPRBus; +struct icp_state; + +typedef struct sPAPREnvironment { + struct VIOsPAPRBus *vio_bus; + struct icp_state *icp; + + void *htab; + long htab_size; + target_phys_addr_t fdt_addr, rtas_addr; + long rtas_size; + void *fdt_skel; + target_ulong entry_point; +} sPAPREnvironment; + +#define H_SUCCESS 0 +#define H_BUSY 1 /* Hardware busy -- retry later */ +#define H_CLOSED 2 /* Resource closed */ +#define H_NOT_AVAILABLE 3 +#define H_CONSTRAINED 4 /* Resource request constrained to max allowed */ +#define H_PARTIAL 5 +#define H_IN_PROGRESS 14 /* Kind of like busy */ +#define H_PAGE_REGISTERED 15 +#define H_PARTIAL_STORE 16 +#define H_PENDING 17 /* returned from H_POLL_PENDING */ +#define H_CONTINUE 18 /* Returned from H_Join on success */ +#define H_LONG_BUSY_START_RANGE 9900 /* Start of long busy range */ +#define H_LONG_BUSY_ORDER_1_MSEC 9900 /* Long busy, hint that 1msec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_10_MSEC 9901 /* Long busy, hint that 10msec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_100_MSEC 9902 /* Long busy, hint that 100msec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_1_SEC 9903 /* Long busy, hint that 1sec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_10_SEC 9904 /* Long busy, hint that 10sec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_100_SEC 9905 /* Long busy, hint that 100sec \ + is a good time to retry */ +#define H_LONG_BUSY_END_RANGE 9905 /* End of long busy range */ +#define H_HARDWARE -1 /* Hardware error */ +#define H_FUNCTION -2 /* Function not supported */ +#define H_PRIVILEGE -3 /* Caller not privileged */ +#define H_PARAMETER -4 /* Parameter invalid, out-of-range or conflicting */ +#define H_BAD_MODE -5 /* Illegal msr value */ +#define H_PTEG_FULL -6 /* PTEG is full */ +#define H_NOT_FOUND -7 /* PTE was not found" */ +#define H_RESERVED_DABR -8 /* DABR address is reserved by the hypervisor on this processor" */ +#define H_NO_MEM -9 +#define H_AUTHORITY -10 +#define H_PERMISSION -11 +#define H_DROPPED -12 +#define H_SOURCE_PARM -13 +#define H_DEST_PARM -14 +#define H_REMOTE_PARM -15 +#define H_RESOURCE -16 +#define H_ADAPTER_PARM -17 +#define H_RH_PARM -18 +#define H_RCQ_PARM -19 +#define H_SCQ_PARM -20 +#define H_EQ_PARM -21 +#define H_RT_PARM -22 +#define H_ST_PARM -23 +#define H_SIGT_PARM -24 +#define H_TOKEN_PARM -25 +#define H_MLENGTH_PARM -27 +#define H_MEM_PARM -28 +#define H_MEM_ACCESS_PARM -29 +#define H_ATTR_PARM -30 +#define H_PORT_PARM -31 +#define H_MCG_PARM -32 +#define H_VL_PARM -33 +#define H_TSIZE_PARM -34 +#define H_TRACE_PARM -35 + +#define H_MASK_PARM -37 +#define H_MCG_FULL -38 +#define H_ALIAS_EXIST -39 +#define H_P_COUNTER -40 +#define H_TABLE_FULL -41 +#define H_ALT_TABLE -42 +#define H_MR_CONDITION -43 +#define H_NOT_ENOUGH_RESOURCES -44 +#define H_R_STATE -45 +#define H_RESCINDEND -46 +#define H_MULTI_THREADS_ACTIVE -9005 + + +/* Long Busy is a condition that can be returned by the firmware + * when a call cannot be completed now, but the identical call + * should be retried later. This prevents calls blocking in the + * firmware for long periods of time. Annoyingly the firmware can return + * a range of return codes, hinting at how long we should wait before + * retrying. If you don't care for the hint, the macro below is a good + * way to check for the long_busy return codes + */ +#define H_IS_LONG_BUSY(x) ((x >= H_LONG_BUSY_START_RANGE) \ + && (x <= H_LONG_BUSY_END_RANGE)) + +/* Flags */ +#define H_LARGE_PAGE (1ULL<<(63-16)) +#define H_EXACT (1ULL<<(63-24)) /* Use exact PTE or return H_PTEG_FULL */ +#define H_R_XLATE (1ULL<<(63-25)) /* include a valid logical page num in the pte if the valid bit is set */ +#define H_READ_4 (1ULL<<(63-26)) /* Return 4 PTEs */ +#define H_PAGE_STATE_CHANGE (1ULL<<(63-28)) +#define H_PAGE_UNUSED ((1ULL<<(63-29)) | (1ULL<<(63-30))) +#define H_PAGE_SET_UNUSED (H_PAGE_STATE_CHANGE | H_PAGE_UNUSED) +#define H_PAGE_SET_LOANED (H_PAGE_SET_UNUSED | (1ULL<<(63-31))) +#define H_PAGE_SET_ACTIVE H_PAGE_STATE_CHANGE +#define H_AVPN (1ULL<<(63-32)) /* An avpn is provided as a sanity test */ +#define H_ANDCOND (1ULL<<(63-33)) +#define H_ICACHE_INVALIDATE (1ULL<<(63-40)) /* icbi, etc. (ignored for IO pages) */ +#define H_ICACHE_SYNCHRONIZE (1ULL<<(63-41)) /* dcbst, icbi, etc (ignored for IO pages */ +#define H_ZERO_PAGE (1ULL<<(63-48)) /* zero the page before mapping (ignored for IO pages) */ +#define H_COPY_PAGE (1ULL<<(63-49)) +#define H_N (1ULL<<(63-61)) +#define H_PP1 (1ULL<<(63-62)) +#define H_PP2 (1ULL<<(63-63)) + +/* VASI States */ +#define H_VASI_INVALID 0 +#define H_VASI_ENABLED 1 +#define H_VASI_ABORTED 2 +#define H_VASI_SUSPENDING 3 +#define H_VASI_SUSPENDED 4 +#define H_VASI_RESUMED 5 +#define H_VASI_COMPLETED 6 + +/* DABRX flags */ +#define H_DABRX_HYPERVISOR (1ULL<<(63-61)) +#define H_DABRX_KERNEL (1ULL<<(63-62)) +#define H_DABRX_USER (1ULL<<(63-63)) + +/* Each control block has to be on a 4K bondary */ +#define H_CB_ALIGNMENT 4096 + +/* pSeries hypervisor opcodes */ +#define H_REMOVE 0x04 +#define H_ENTER 0x08 +#define H_READ 0x0c +#define H_CLEAR_MOD 0x10 +#define H_CLEAR_REF 0x14 +#define H_PROTECT 0x18 +#define H_GET_TCE 0x1c +#define H_PUT_TCE 0x20 +#define H_SET_SPRG0 0x24 +#define H_SET_DABR 0x28 +#define H_PAGE_INIT 0x2c +#define H_SET_ASR 0x30 +#define H_ASR_ON 0x34 +#define H_ASR_OFF 0x38 +#define H_LOGICAL_CI_LOAD 0x3c +#define H_LOGICAL_CI_STORE 0x40 +#define H_LOGICAL_CACHE_LOAD 0x44 +#define H_LOGICAL_CACHE_STORE 0x48 +#define H_LOGICAL_ICBI 0x4c +#define H_LOGICAL_DCBF 0x50 +#define H_GET_TERM_CHAR 0x54 +#define H_PUT_TERM_CHAR 0x58 +#define H_REAL_TO_LOGICAL 0x5c +#define H_HYPERVISOR_DATA 0x60 +#define H_EOI 0x64 +#define H_CPPR 0x68 +#define H_IPI 0x6c +#define H_IPOLL 0x70 +#define H_XIRR 0x74 +#define H_PERFMON 0x7c +#define H_MIGRATE_DMA 0x78 +#define H_REGISTER_VPA 0xDC +#define H_CEDE 0xE0 +#define H_CONFER 0xE4 +#define H_PROD 0xE8 +#define H_GET_PPP 0xEC +#define H_SET_PPP 0xF0 +#define H_PURR 0xF4 +#define H_PIC 0xF8 +#define H_REG_CRQ 0xFC +#define H_FREE_CRQ 0x100 +#define H_VIO_SIGNAL 0x104 +#define H_SEND_CRQ 0x108 +#define H_COPY_RDMA 0x110 +#define H_REGISTER_LOGICAL_LAN 0x114 +#define H_FREE_LOGICAL_LAN 0x118 +#define H_ADD_LOGICAL_LAN_BUFFER 0x11C +#define H_SEND_LOGICAL_LAN 0x120 +#define H_BULK_REMOVE 0x124 +#define H_MULTICAST_CTRL 0x130 +#define H_SET_XDABR 0x134 +#define H_STUFF_TCE 0x138 +#define H_PUT_TCE_INDIRECT 0x13C +#define H_CHANGE_LOGICAL_LAN_MAC 0x14C +#define H_VTERM_PARTNER_INFO 0x150 +#define H_REGISTER_VTERM 0x154 +#define H_FREE_VTERM 0x158 +#define H_RESET_EVENTS 0x15C +#define H_ALLOC_RESOURCE 0x160 +#define H_FREE_RESOURCE 0x164 +#define H_MODIFY_QP 0x168 +#define H_QUERY_QP 0x16C +#define H_REREGISTER_PMR 0x170 +#define H_REGISTER_SMR 0x174 +#define H_QUERY_MR 0x178 +#define H_QUERY_MW 0x17C +#define H_QUERY_HCA 0x180 +#define H_QUERY_PORT 0x184 +#define H_MODIFY_PORT 0x188 +#define H_DEFINE_AQP1 0x18C +#define H_GET_TRACE_BUFFER 0x190 +#define H_DEFINE_AQP0 0x194 +#define H_RESIZE_MR 0x198 +#define H_ATTACH_MCQP 0x19C +#define H_DETACH_MCQP 0x1A0 +#define H_CREATE_RPT 0x1A4 +#define H_REMOVE_RPT 0x1A8 +#define H_REGISTER_RPAGES 0x1AC +#define H_DISABLE_AND_GETC 0x1B0 +#define H_ERROR_DATA 0x1B4 +#define H_GET_HCA_INFO 0x1B8 +#define H_GET_PERF_COUNT 0x1BC +#define H_MANAGE_TRACE 0x1C0 +#define H_FREE_LOGICAL_LAN_BUFFER 0x1D4 +#define H_QUERY_INT_STATE 0x1E4 +#define H_POLL_PENDING 0x1D8 +#define H_ILLAN_ATTRIBUTES 0x244 +#define H_MODIFY_HEA_QP 0x250 +#define H_QUERY_HEA_QP 0x254 +#define H_QUERY_HEA 0x258 +#define H_QUERY_HEA_PORT 0x25C +#define H_MODIFY_HEA_PORT 0x260 +#define H_REG_BCMC 0x264 +#define H_DEREG_BCMC 0x268 +#define H_REGISTER_HEA_RPAGES 0x26C +#define H_DISABLE_AND_GET_HEA 0x270 +#define H_GET_HEA_INFO 0x274 +#define H_ALLOC_HEA_RESOURCE 0x278 +#define H_ADD_CONN 0x284 +#define H_DEL_CONN 0x288 +#define H_JOIN 0x298 +#define H_VASI_STATE 0x2A4 +#define H_ENABLE_CRQ 0x2B0 +#define H_GET_EM_PARMS 0x2B8 +#define H_SET_MPP 0x2D0 +#define H_GET_MPP 0x2D4 +#define MAX_HCALL_OPCODE H_GET_MPP + +/* The hcalls above are standardized in PAPR and implemented by pHyp + * as well. + * + * We also need some hcalls which are specific to qemu / KVM-on-POWER. + * So far we just need one for H_RTAS, but in future we'll need more + * for extensions like virtio. We put those into the 0xf000-0xfffc + * range which is reserved by PAPR for "platform-specific" hcalls. + */ +#define KVMPPC_HCALL_BASE 0xf000 +#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) +#define KVMPPC_HCALL_MAX KVMPPC_H_RTAS + +extern sPAPREnvironment *spapr; + +/*#define DEBUG_SPAPR_HCALLS*/ + +#ifdef DEBUG_SPAPR_HCALLS +#define hcall_dprintf(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define hcall_dprintf(fmt, ...) \ + do { } while (0) +#endif + +typedef target_ulong (*spapr_hcall_fn)(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, + target_ulong *args); + +void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); +target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, + target_ulong *args); + +static inline uint32_t rtas_ld(target_ulong phys, int n) +{ + return ldl_be_phys(phys + 4*n); +} + +static inline void rtas_st(target_ulong phys, int n, uint32_t val) +{ + stl_be_phys(phys + 4*n, val); +} + +typedef void (*spapr_rtas_fn)(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets); +void spapr_rtas_register(const char *name, spapr_rtas_fn fn); +target_ulong spapr_rtas_call(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets); +int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr, + target_phys_addr_t rtas_size); + +#endif /* !defined (__HW_SPAPR_H__) */ diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr_hcall.c qemu-kvm-0.15.0+noroms/hw/spapr_hcall.c --- qemu-kvm-0.14.1+noroms/hw/spapr_hcall.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr_hcall.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,519 @@ +#include "sysemu.h" +#include "cpu.h" +#include "qemu-char.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "exec.h" +#include "helper_regs.h" +#include "hw/spapr.h" + +#define HPTES_PER_GROUP 8 + +#define HPTE_V_SSIZE_SHIFT 62 +#define HPTE_V_AVPN_SHIFT 7 +#define HPTE_V_AVPN 0x3fffffffffffff80ULL +#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT) +#define HPTE_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff80UL)) +#define HPTE_V_BOLTED 0x0000000000000010ULL +#define HPTE_V_LOCK 0x0000000000000008ULL +#define HPTE_V_LARGE 0x0000000000000004ULL +#define HPTE_V_SECONDARY 0x0000000000000002ULL +#define HPTE_V_VALID 0x0000000000000001ULL + +#define HPTE_R_PP0 0x8000000000000000ULL +#define HPTE_R_TS 0x4000000000000000ULL +#define HPTE_R_KEY_HI 0x3000000000000000ULL +#define HPTE_R_RPN_SHIFT 12 +#define HPTE_R_RPN 0x3ffffffffffff000ULL +#define HPTE_R_FLAGS 0x00000000000003ffULL +#define HPTE_R_PP 0x0000000000000003ULL +#define HPTE_R_N 0x0000000000000004ULL +#define HPTE_R_G 0x0000000000000008ULL +#define HPTE_R_M 0x0000000000000010ULL +#define HPTE_R_I 0x0000000000000020ULL +#define HPTE_R_W 0x0000000000000040ULL +#define HPTE_R_WIMG 0x0000000000000078ULL +#define HPTE_R_C 0x0000000000000080ULL +#define HPTE_R_R 0x0000000000000100ULL +#define HPTE_R_KEY_LO 0x0000000000000e00ULL + +#define HPTE_V_1TB_SEG 0x4000000000000000ULL +#define HPTE_V_VRMA_MASK 0x4001ffffff000000ULL + +#define HPTE_V_HVLOCK 0x40ULL + +static inline int lock_hpte(void *hpte, target_ulong bits) +{ + uint64_t pteh; + + pteh = ldq_p(hpte); + + /* We're protected by qemu's global lock here */ + if (pteh & bits) { + return 0; + } + stq_p(hpte, pteh | HPTE_V_HVLOCK); + return 1; +} + +static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, + target_ulong pte_index) +{ + target_ulong rb, va_low; + + rb = (v & ~0x7fULL) << 16; /* AVA field */ + va_low = pte_index >> 3; + if (v & HPTE_V_SECONDARY) { + va_low = ~va_low; + } + /* xor vsid from AVA */ + if (!(v & HPTE_V_1TB_SEG)) { + va_low ^= v >> 12; + } else { + va_low ^= v >> 24; + } + va_low &= 0x7ff; + if (v & HPTE_V_LARGE) { + rb |= 1; /* L field */ +#if 0 /* Disable that P7 specific bit for now */ + if (r & 0xff000) { + /* non-16MB large page, must be 64k */ + /* (masks depend on page size) */ + rb |= 0x1000; /* page encoding in LP field */ + rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */ + rb |= (va_low & 0xfe); /* AVAL field */ + } +#endif + } else { + /* 4kB page */ + rb |= (va_low & 0x7ff) << 12; /* remaining 11b of AVA */ + } + rb |= (v >> 54) & 0x300; /* B field */ + return rb; +} + +static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong pte_index = args[1]; + target_ulong pteh = args[2]; + target_ulong ptel = args[3]; + target_ulong i; + uint8_t *hpte; + + /* only handle 4k and 16M pages for now */ + if (pteh & HPTE_V_LARGE) { +#if 0 /* We don't support 64k pages yet */ + if ((ptel & 0xf000) == 0x1000) { + /* 64k page */ + } else +#endif + if ((ptel & 0xff000) == 0) { + /* 16M page */ + /* lowest AVA bit must be 0 for 16M pages */ + if (pteh & 0x80) { + return H_PARAMETER; + } + } else { + return H_PARAMETER; + } + } + + /* FIXME: bounds check the pa? */ + + /* Check WIMG */ + if ((ptel & HPTE_R_WIMG) != HPTE_R_M) { + return H_PARAMETER; + } + pteh &= ~0x60ULL; + + if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + return H_PARAMETER; + } + if (likely((flags & H_EXACT) == 0)) { + pte_index &= ~7ULL; + hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + for (i = 0; ; ++i) { + if (i == 8) { + return H_PTEG_FULL; + } + if (((ldq_p(hpte) & HPTE_V_VALID) == 0) && + lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) { + break; + } + hpte += HASH_PTE_SIZE_64; + } + } else { + i = 0; + hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + if (!lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) { + return H_PTEG_FULL; + } + } + stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel); + /* eieio(); FIXME: need some sort of barrier for smp? */ + stq_p(hpte, pteh); + + assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); + args[0] = pte_index + i; + return H_SUCCESS; +} + +static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong pte_index = args[1]; + target_ulong avpn = args[2]; + uint8_t *hpte; + target_ulong v, r, rb; + + if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + return H_PARAMETER; + } + + hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + while (!lock_hpte(hpte, HPTE_V_HVLOCK)) { + /* We have no real concurrency in qemu soft-emulation, so we + * will never actually have a contested lock */ + assert(0); + } + + v = ldq_p(hpte); + r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); + + if ((v & HPTE_V_VALID) == 0 || + ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || + ((flags & H_ANDCOND) && (v & avpn) != 0)) { + stq_p(hpte, v & ~HPTE_V_HVLOCK); + assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); + return H_NOT_FOUND; + } + args[0] = v & ~HPTE_V_HVLOCK; + args[1] = r; + stq_p(hpte, 0); + rb = compute_tlbie_rb(v, r, pte_index); + ppc_tlb_invalidate_one(env, rb); + assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); + return H_SUCCESS; +} + +static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong pte_index = args[1]; + target_ulong avpn = args[2]; + uint8_t *hpte; + target_ulong v, r, rb; + + if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + return H_PARAMETER; + } + + hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + while (!lock_hpte(hpte, HPTE_V_HVLOCK)) { + /* We have no real concurrency in qemu soft-emulation, so we + * will never actually have a contested lock */ + assert(0); + } + + v = ldq_p(hpte); + r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); + + if ((v & HPTE_V_VALID) == 0 || + ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { + stq_p(hpte, v & ~HPTE_V_HVLOCK); + assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); + return H_NOT_FOUND; + } + + r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N | + HPTE_R_KEY_HI | HPTE_R_KEY_LO); + r |= (flags << 55) & HPTE_R_PP0; + r |= (flags << 48) & HPTE_R_KEY_HI; + r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO); + rb = compute_tlbie_rb(v, r, pte_index); + stq_p(hpte, v & ~HPTE_V_VALID); + ppc_tlb_invalidate_one(env, rb); + stq_p(hpte + (HASH_PTE_SIZE_64/2), r); + /* Don't need a memory barrier, due to qemu's global lock */ + stq_p(hpte, v & ~HPTE_V_HVLOCK); + assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); + return H_SUCCESS; +} + +static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + /* FIXME: actually implement this */ + return H_HARDWARE; +} + +#define FLAGS_REGISTER_VPA 0x0000200000000000ULL +#define FLAGS_REGISTER_DTL 0x0000400000000000ULL +#define FLAGS_REGISTER_SLBSHADOW 0x0000600000000000ULL +#define FLAGS_DEREGISTER_VPA 0x0000a00000000000ULL +#define FLAGS_DEREGISTER_DTL 0x0000c00000000000ULL +#define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL + +#define VPA_MIN_SIZE 640 +#define VPA_SIZE_OFFSET 0x4 +#define VPA_SHARED_PROC_OFFSET 0x9 +#define VPA_SHARED_PROC_VAL 0x2 + +static target_ulong register_vpa(CPUState *env, target_ulong vpa) +{ + uint16_t size; + uint8_t tmp; + + if (vpa == 0) { + hcall_dprintf("Can't cope with registering a VPA at logical 0\n"); + return H_HARDWARE; + } + + if (vpa % env->dcache_line_size) { + return H_PARAMETER; + } + /* FIXME: bounds check the address */ + + size = lduw_be_phys(vpa + 0x4); + + if (size < VPA_MIN_SIZE) { + return H_PARAMETER; + } + + /* VPA is not allowed to cross a page boundary */ + if ((vpa / 4096) != ((vpa + size - 1) / 4096)) { + return H_PARAMETER; + } + + env->vpa = vpa; + + tmp = ldub_phys(env->vpa + VPA_SHARED_PROC_OFFSET); + tmp |= VPA_SHARED_PROC_VAL; + stb_phys(env->vpa + VPA_SHARED_PROC_OFFSET, tmp); + + return H_SUCCESS; +} + +static target_ulong deregister_vpa(CPUState *env, target_ulong vpa) +{ + if (env->slb_shadow) { + return H_RESOURCE; + } + + if (env->dispatch_trace_log) { + return H_RESOURCE; + } + + env->vpa = 0; + return H_SUCCESS; +} + +static target_ulong register_slb_shadow(CPUState *env, target_ulong addr) +{ + uint32_t size; + + if (addr == 0) { + hcall_dprintf("Can't cope with SLB shadow at logical 0\n"); + return H_HARDWARE; + } + + size = ldl_be_phys(addr + 0x4); + if (size < 0x8) { + return H_PARAMETER; + } + + if ((addr / 4096) != ((addr + size - 1) / 4096)) { + return H_PARAMETER; + } + + if (!env->vpa) { + return H_RESOURCE; + } + + env->slb_shadow = addr; + + return H_SUCCESS; +} + +static target_ulong deregister_slb_shadow(CPUState *env, target_ulong addr) +{ + env->slb_shadow = 0; + return H_SUCCESS; +} + +static target_ulong register_dtl(CPUState *env, target_ulong addr) +{ + uint32_t size; + + if (addr == 0) { + hcall_dprintf("Can't cope with DTL at logical 0\n"); + return H_HARDWARE; + } + + size = ldl_be_phys(addr + 0x4); + + if (size < 48) { + return H_PARAMETER; + } + + if (!env->vpa) { + return H_RESOURCE; + } + + env->dispatch_trace_log = addr; + env->dtl_size = size; + + return H_SUCCESS; +} + +static target_ulong deregister_dtl(CPUState *emv, target_ulong addr) +{ + env->dispatch_trace_log = 0; + env->dtl_size = 0; + + return H_SUCCESS; +} + +static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong procno = args[1]; + target_ulong vpa = args[2]; + target_ulong ret = H_PARAMETER; + CPUState *tenv; + + for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) { + if (tenv->cpu_index == procno) { + break; + } + } + + if (!tenv) { + return H_PARAMETER; + } + + switch (flags) { + case FLAGS_REGISTER_VPA: + ret = register_vpa(tenv, vpa); + break; + + case FLAGS_DEREGISTER_VPA: + ret = deregister_vpa(tenv, vpa); + break; + + case FLAGS_REGISTER_SLBSHADOW: + ret = register_slb_shadow(tenv, vpa); + break; + + case FLAGS_DEREGISTER_SLBSHADOW: + ret = deregister_slb_shadow(tenv, vpa); + break; + + case FLAGS_REGISTER_DTL: + ret = register_dtl(tenv, vpa); + break; + + case FLAGS_DEREGISTER_DTL: + ret = deregister_dtl(tenv, vpa); + break; + } + + return ret; +} + +static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + env->msr |= (1ULL << MSR_EE); + hreg_compute_hflags(env); + if (!cpu_has_work(env)) { + env->halted = 1; + } + return H_SUCCESS; +} + +static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong rtas_r3 = args[0]; + uint32_t token = ldl_be_phys(rtas_r3); + uint32_t nargs = ldl_be_phys(rtas_r3 + 4); + uint32_t nret = ldl_be_phys(rtas_r3 + 8); + + return spapr_rtas_call(spapr, token, nargs, rtas_r3 + 12, + nret, rtas_r3 + 12 + 4*nargs); +} + +static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; +static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; + +void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) +{ + spapr_hcall_fn *slot; + + if (opcode <= MAX_HCALL_OPCODE) { + assert((opcode & 0x3) == 0); + + slot = &papr_hypercall_table[opcode / 4]; + } else { + assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX)); + + + slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; + } + + assert(!(*slot) || (fn == *slot)); + *slot = fn; +} + +target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, + target_ulong *args) +{ + if (msr_pr) { + hcall_dprintf("Hypercall made with MSR[PR]=1\n"); + return H_PRIVILEGE; + } + + if ((opcode <= MAX_HCALL_OPCODE) + && ((opcode & 0x3) == 0)) { + spapr_hcall_fn fn = papr_hypercall_table[opcode / 4]; + + if (fn) { + return fn(env, spapr, opcode, args); + } + } else if ((opcode >= KVMPPC_HCALL_BASE) && + (opcode <= KVMPPC_HCALL_MAX)) { + spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; + + if (fn) { + return fn(env, spapr, opcode, args); + } + } + + hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode); + return H_FUNCTION; +} + +static void hypercall_init(void) +{ + /* hcall-pft */ + spapr_register_hypercall(H_ENTER, h_enter); + spapr_register_hypercall(H_REMOVE, h_remove); + spapr_register_hypercall(H_PROTECT, h_protect); + + /* hcall-dabr */ + spapr_register_hypercall(H_SET_DABR, h_set_dabr); + + /* hcall-splpar */ + spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); + spapr_register_hypercall(H_CEDE, h_cede); + + /* qemu/KVM-PPC specific hcalls */ + spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); +} +device_init(hypercall_init); diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr_llan.c qemu-kvm-0.15.0+noroms/hw/spapr_llan.c --- qemu-kvm-0.14.1+noroms/hw/spapr_llan.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr_llan.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,515 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Inter-VM Logical Lan, aka ibmveth + * + * Copyright (c) 2010,2011 David Gibson, IBM Corporation. + * + * 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 "net.h" +#include "hw/qdev.h" +#include "hw/spapr.h" +#include "hw/spapr_vio.h" + +#include + +#define ETH_ALEN 6 +#define MAX_PACKET_SIZE 65536 + +/*#define DEBUG*/ + +#ifdef DEBUG +#define dprintf(fmt...) do { fprintf(stderr, fmt); } while (0) +#else +#define dprintf(fmt...) +#endif + +/* + * Virtual LAN device + */ + +typedef uint64_t vlan_bd_t; + +#define VLAN_BD_VALID 0x8000000000000000ULL +#define VLAN_BD_TOGGLE 0x4000000000000000ULL +#define VLAN_BD_NO_CSUM 0x0200000000000000ULL +#define VLAN_BD_CSUM_GOOD 0x0100000000000000ULL +#define VLAN_BD_LEN_MASK 0x00ffffff00000000ULL +#define VLAN_BD_LEN(bd) (((bd) & VLAN_BD_LEN_MASK) >> 32) +#define VLAN_BD_ADDR_MASK 0x00000000ffffffffULL +#define VLAN_BD_ADDR(bd) ((bd) & VLAN_BD_ADDR_MASK) + +#define VLAN_VALID_BD(addr, len) (VLAN_BD_VALID | \ + (((len) << 32) & VLAN_BD_LEN_MASK) | \ + (addr & VLAN_BD_ADDR_MASK)) + +#define VLAN_RXQC_TOGGLE 0x80 +#define VLAN_RXQC_VALID 0x40 +#define VLAN_RXQC_NO_CSUM 0x02 +#define VLAN_RXQC_CSUM_GOOD 0x01 + +#define VLAN_RQ_ALIGNMENT 16 +#define VLAN_RXQ_BD_OFF 0 +#define VLAN_FILTER_BD_OFF 8 +#define VLAN_RX_BDS_OFF 16 +#define VLAN_MAX_BUFS ((SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF) / 8) + +typedef struct VIOsPAPRVLANDevice { + VIOsPAPRDevice sdev; + NICConf nicconf; + NICState *nic; + int isopen; + target_ulong buf_list; + int add_buf_ptr, use_buf_ptr, rx_bufs; + target_ulong rxq_ptr; +} VIOsPAPRVLANDevice; + +static int spapr_vlan_can_receive(VLANClientState *nc) +{ + VIOsPAPRVLANDevice *dev = DO_UPCAST(NICState, nc, nc)->opaque; + + return (dev->isopen && dev->rx_bufs > 0); +} + +static ssize_t spapr_vlan_receive(VLANClientState *nc, const uint8_t *buf, + size_t size) +{ + VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque; + VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; + vlan_bd_t rxq_bd = ldq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); + vlan_bd_t bd; + int buf_ptr = dev->use_buf_ptr; + uint64_t handle; + uint8_t control; + + dprintf("spapr_vlan_receive() [%s] rx_bufs=%d\n", sdev->qdev.id, + dev->rx_bufs); + + if (!dev->isopen) { + return -1; + } + + if (!dev->rx_bufs) { + return -1; + } + + do { + buf_ptr += 8; + if (buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) { + buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = ldq_tce(sdev, dev->buf_list + buf_ptr); + dprintf("use_buf_ptr=%d bd=0x%016llx\n", + buf_ptr, (unsigned long long)bd); + } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) + && (buf_ptr != dev->use_buf_ptr)); + + if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) { + /* Failed to find a suitable buffer */ + return -1; + } + + /* Remove the buffer from the pool */ + dev->rx_bufs--; + dev->use_buf_ptr = buf_ptr; + stq_tce(sdev, dev->buf_list + dev->use_buf_ptr, 0); + + dprintf("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs); + + /* Transfer the packet data */ + if (spapr_tce_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { + return -1; + } + + dprintf("spapr_vlan_receive: DMA write completed\n"); + + /* Update the receive queue */ + control = VLAN_RXQC_TOGGLE | VLAN_RXQC_VALID; + if (rxq_bd & VLAN_BD_TOGGLE) { + control ^= VLAN_RXQC_TOGGLE; + } + + handle = ldq_tce(sdev, VLAN_BD_ADDR(bd)); + stq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle); + stw_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size); + sth_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8); + stb_tce(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control); + + dprintf("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n", + (unsigned long long)dev->rxq_ptr, + (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + + dev->rxq_ptr), + (unsigned long long)ldq_tce(sdev, VLAN_BD_ADDR(rxq_bd) + + dev->rxq_ptr + 8)); + + dev->rxq_ptr += 16; + if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) { + dev->rxq_ptr = 0; + stq_tce(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE); + } + + if (sdev->signal_state & 1) { + qemu_irq_pulse(sdev->qirq); + } + + return size; +} + +static NetClientInfo net_spapr_vlan_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = spapr_vlan_can_receive, + .receive = spapr_vlan_receive, +}; + +static int spapr_vlan_init(VIOsPAPRDevice *sdev) +{ + VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; + + qemu_macaddr_default_if_unset(&dev->nicconf.macaddr); + + dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, + sdev->qdev.info->name, sdev->qdev.id, dev); + qemu_format_nic_info_str(&dev->nic->nc, dev->nicconf.macaddr.a); + + return 0; +} + +void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, + qemu_irq qirq, uint32_t vio_irq_num) +{ + DeviceState *dev; + VIOsPAPRDevice *sdev; + + dev = qdev_create(&bus->bus, "spapr-vlan"); + qdev_prop_set_uint32(dev, "reg", reg); + + qdev_set_nic_properties(dev, nd); + + qdev_init_nofail(dev); + sdev = (VIOsPAPRDevice *)dev; + sdev->qirq = qirq; + sdev->vio_irq_num = vio_irq_num; +} + +static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) +{ + VIOsPAPRVLANDevice *vdev = (VIOsPAPRVLANDevice *)dev; + uint8_t padded_mac[8] = {0, 0}; + int ret; + + /* Some old phyp versions give the mac address in an 8-byte + * property. The kernel driver has an insane workaround for this; + * rather than doing the obvious thing and checking the property + * length, it checks whether the first byte has 0b10 in the low + * bits. If a correct 6-byte property has a different first byte + * the kernel will get the wrong mac address, overrunning its + * buffer in the process (read only, thank goodness). + * + * Here we workaround the kernel workaround by always supplying an + * 8-byte property, with the mac address in the last six bytes */ + memcpy(&padded_mac[2], &vdev->nicconf.macaddr, ETH_ALEN); + ret = fdt_setprop(fdt, node_off, "local-mac-address", + padded_mac, sizeof(padded_mac)); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, node_off, "ibm,mac-address-filters", 0); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, + target_ulong alignment) +{ + if ((VLAN_BD_ADDR(bd) % alignment) + || (VLAN_BD_LEN(bd) % alignment)) { + return -1; + } + + if (spapr_vio_check_tces(&dev->sdev, VLAN_BD_ADDR(bd), + VLAN_BD_LEN(bd), SPAPR_TCE_RW) != 0) { + return -1; + } + + return 0; +} + +static target_ulong h_register_logical_lan(CPUState *env, + sPAPREnvironment *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong buf_list = args[1]; + target_ulong rec_queue = args[2]; + target_ulong filter_list = args[3]; + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; + vlan_bd_t filter_list_bd; + + if (!dev) { + return H_PARAMETER; + } + + if (dev->isopen) { + hcall_dprintf("H_REGISTER_LOGICAL_LAN called twice without " + "H_FREE_LOGICAL_LAN\n"); + return H_RESOURCE; + } + + if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_VIO_TCE_PAGE_SIZE), + SPAPR_VIO_TCE_PAGE_SIZE) < 0) { + hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx " for " + "H_REGISTER_LOGICAL_LAN\n", buf_list); + return H_PARAMETER; + } + + filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_VIO_TCE_PAGE_SIZE); + if (check_bd(dev, filter_list_bd, SPAPR_VIO_TCE_PAGE_SIZE) < 0) { + hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx " for " + "H_REGISTER_LOGICAL_LAN\n", filter_list); + return H_PARAMETER; + } + + if (!(rec_queue & VLAN_BD_VALID) + || (check_bd(dev, rec_queue, VLAN_RQ_ALIGNMENT) < 0)) { + hcall_dprintf("Bad receive queue for H_REGISTER_LOGICAL_LAN\n"); + return H_PARAMETER; + } + + dev->buf_list = buf_list; + sdev->signal_state = 0; + + rec_queue &= ~VLAN_BD_TOGGLE; + + /* Initialize the buffer list */ + stq_tce(sdev, buf_list, rec_queue); + stq_tce(sdev, buf_list + 8, filter_list_bd); + spapr_tce_dma_zero(sdev, buf_list + VLAN_RX_BDS_OFF, + SPAPR_VIO_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF); + dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8; + dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8; + dev->rx_bufs = 0; + dev->rxq_ptr = 0; + + /* Initialize the receive queue */ + spapr_tce_dma_zero(sdev, VLAN_BD_ADDR(rec_queue), VLAN_BD_LEN(rec_queue)); + + dev->isopen = 1; + return H_SUCCESS; +} + + +static target_ulong h_free_logical_lan(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; + + if (!dev) { + return H_PARAMETER; + } + + if (!dev->isopen) { + hcall_dprintf("H_FREE_LOGICAL_LAN called without " + "H_REGISTER_LOGICAL_LAN\n"); + return H_RESOURCE; + } + + dev->buf_list = 0; + dev->rx_bufs = 0; + dev->isopen = 0; + return H_SUCCESS; +} + +static target_ulong h_add_logical_lan_buffer(CPUState *env, + sPAPREnvironment *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong buf = args[1]; + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; + vlan_bd_t bd; + + dprintf("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx + ", 0x" TARGET_FMT_lx ")\n", reg, buf); + + if (!sdev) { + hcall_dprintf("Wrong device in h_add_logical_lan_buffer\n"); + return H_PARAMETER; + } + + if ((check_bd(dev, buf, 4) < 0) + || (VLAN_BD_LEN(buf) < 16)) { + hcall_dprintf("Bad buffer enqueued in h_add_logical_lan_buffer\n"); + return H_PARAMETER; + } + + if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) { + return H_RESOURCE; + } + + do { + dev->add_buf_ptr += 8; + if (dev->add_buf_ptr >= SPAPR_VIO_TCE_PAGE_SIZE) { + dev->add_buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = ldq_tce(sdev, dev->buf_list + dev->add_buf_ptr); + } while (bd & VLAN_BD_VALID); + + stq_tce(sdev, dev->buf_list + dev->add_buf_ptr, buf); + + dev->rx_bufs++; + + dprintf("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d" + " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs, + (unsigned long long)buf); + + return H_SUCCESS; +} + +static target_ulong h_send_logical_lan(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong *bufs = args + 1; + target_ulong continue_token = args[7]; + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; + unsigned total_len; + uint8_t *lbuf, *p; + int i, nbufs; + int ret; + + dprintf("H_SEND_LOGICAL_LAN(0x" TARGET_FMT_lx ", , 0x" + TARGET_FMT_lx ")\n", reg, continue_token); + + if (!sdev) { + return H_PARAMETER; + } + + dprintf("rxbufs = %d\n", dev->rx_bufs); + + if (!dev->isopen) { + return H_DROPPED; + } + + if (continue_token) { + return H_HARDWARE; /* FIXME actually handle this */ + } + + total_len = 0; + for (i = 0; i < 6; i++) { + dprintf(" buf desc: 0x" TARGET_FMT_lx "\n", bufs[i]); + if (!(bufs[i] & VLAN_BD_VALID)) { + break; + } + total_len += VLAN_BD_LEN(bufs[i]); + } + + nbufs = i; + dprintf("h_send_logical_lan() %d buffers, total length 0x%x\n", + nbufs, total_len); + + if (total_len == 0) { + return H_SUCCESS; + } + + if (total_len > MAX_PACKET_SIZE) { + /* Don't let the guest force too large an allocation */ + return H_RESOURCE; + } + + lbuf = alloca(total_len); + p = lbuf; + for (i = 0; i < nbufs; i++) { + ret = spapr_tce_dma_read(sdev, VLAN_BD_ADDR(bufs[i]), + p, VLAN_BD_LEN(bufs[i])); + if (ret < 0) { + return ret; + } + + p += VLAN_BD_LEN(bufs[i]); + } + + qemu_send_packet(&dev->nic->nc, lbuf, total_len); + + return H_SUCCESS; +} + +static target_ulong h_multicast_ctrl(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + + if (!dev) { + return H_PARAMETER; + } + + return H_SUCCESS; +} + +static void vlan_hcalls(VIOsPAPRBus *bus) +{ + spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan); + spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan); + spapr_register_hypercall(H_SEND_LOGICAL_LAN, h_send_logical_lan); + spapr_register_hypercall(H_ADD_LOGICAL_LAN_BUFFER, + h_add_logical_lan_buffer); + spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl); +} + +static VIOsPAPRDeviceInfo spapr_vlan = { + .init = spapr_vlan_init, + .devnode = spapr_vlan_devnode, + .dt_name = "l-lan", + .dt_type = "network", + .dt_compatible = "IBM,l-lan", + .signal_mask = 0x1, + .hcalls = vlan_hcalls, + .qdev.name = "spapr-vlan", + .qdev.size = sizeof(VIOsPAPRVLANDevice), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000), + DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size, + 0x10000000), + DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void spapr_vlan_register(void) +{ + spapr_vio_bus_register_withprop(&spapr_vlan); +} +device_init(spapr_vlan_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr_rtas.c qemu-kvm-0.15.0+noroms/hw/spapr_rtas.c --- qemu-kvm-0.14.1+noroms/hw/spapr_rtas.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr_rtas.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,279 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * Hypercall based emulated RTAS + * + * Copyright (c) 2010-2011 David Gibson, IBM Corporation. + * + * 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 "cpu.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "hw/qdev.h" +#include "device_tree.h" + +#include "hw/spapr.h" +#include "hw/spapr_vio.h" + +#include + +#define TOKEN_BASE 0x2000 +#define TOKEN_MAX 0x100 + +static void rtas_display_character(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint8_t c = rtas_ld(args, 0); + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, + SPAPR_VTY_BASE_ADDRESS); + + if (!sdev) { + rtas_st(rets, 0, -1); + } else { + vty_putchars(sdev, &c, sizeof(c)); + rtas_st(rets, 0, 0); + } +} + +static void rtas_get_time_of_day(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct tm tm; + + if (nret != 8) { + rtas_st(rets, 0, -3); + return; + } + + qemu_get_timedate(&tm, 0); + + rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 1, tm.tm_year + 1900); + rtas_st(rets, 2, tm.tm_mon + 1); + rtas_st(rets, 3, tm.tm_mday); + rtas_st(rets, 4, tm.tm_hour); + rtas_st(rets, 5, tm.tm_min); + rtas_st(rets, 6, tm.tm_sec); + rtas_st(rets, 7, 0); /* we don't do nanoseconds */ +} + +static void rtas_power_off(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + if (nargs != 2 || nret != 1) { + rtas_st(rets, 0, -3); + return; + } + qemu_system_shutdown_request(); + rtas_st(rets, 0, 0); +} + +static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong id; + CPUState *env; + + if (nargs != 1 || nret != 2) { + rtas_st(rets, 0, -3); + return; + } + + id = rtas_ld(args, 0); + for (env = first_cpu; env; env = env->next_cpu) { + if (env->cpu_index != id) { + continue; + } + + if (env->halted) { + rtas_st(rets, 1, 0); + } else { + rtas_st(rets, 1, 2); + } + + rtas_st(rets, 0, 0); + return; + } + + /* Didn't find a matching cpu */ + rtas_st(rets, 0, -3); +} + +static void rtas_start_cpu(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong id, start, r3; + CPUState *env; + + if (nargs != 3 || nret != 1) { + rtas_st(rets, 0, -3); + return; + } + + id = rtas_ld(args, 0); + start = rtas_ld(args, 1); + r3 = rtas_ld(args, 2); + + for (env = first_cpu; env; env = env->next_cpu) { + if (env->cpu_index != id) { + continue; + } + + if (!env->halted) { + rtas_st(rets, 0, -1); + return; + } + + env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); + env->nip = start; + env->gpr[3] = r3; + env->halted = 0; + + qemu_cpu_kick(env); + + rtas_st(rets, 0, 0); + return; + } + + /* Didn't find a matching cpu */ + rtas_st(rets, 0, -3); +} + +static struct rtas_call { + const char *name; + spapr_rtas_fn fn; +} rtas_table[TOKEN_MAX]; + +struct rtas_call *rtas_next = rtas_table; + +target_ulong spapr_rtas_call(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + if ((token >= TOKEN_BASE) + && ((token - TOKEN_BASE) < TOKEN_MAX)) { + struct rtas_call *call = rtas_table + (token - TOKEN_BASE); + + if (call->fn) { + call->fn(spapr, token, nargs, args, nret, rets); + return H_SUCCESS; + } + } + + /* HACK: Some Linux early debug code uses RTAS display-character, + * but assumes the token value is 0xa (which it is on some real + * machines) without looking it up in the device tree. This + * special case makes this work */ + if (token == 0xa) { + rtas_display_character(spapr, 0xa, nargs, args, nret, rets); + return H_SUCCESS; + } + + hcall_dprintf("Unknown RTAS token 0x%x\n", token); + rtas_st(rets, 0, -3); + return H_PARAMETER; +} + +void spapr_rtas_register(const char *name, spapr_rtas_fn fn) +{ + assert(rtas_next < (rtas_table + TOKEN_MAX)); + + rtas_next->name = name; + rtas_next->fn = fn; + + rtas_next++; +} + +int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr, + target_phys_addr_t rtas_size) +{ + int ret; + int i; + + ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); + if (ret < 0) { + fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n", + fdt_strerror(ret)); + return ret; + } + + ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base", + rtas_addr); + if (ret < 0) { + fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n", + fdt_strerror(ret)); + return ret; + } + + ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry", + rtas_addr); + if (ret < 0) { + fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n", + fdt_strerror(ret)); + return ret; + } + + ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size", + rtas_size); + if (ret < 0) { + fprintf(stderr, "Couldn't add rtas-size property: %s\n", + fdt_strerror(ret)); + return ret; + } + + for (i = 0; i < TOKEN_MAX; i++) { + struct rtas_call *call = &rtas_table[i]; + + if (!call->fn) { + continue; + } + + ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name, + i + TOKEN_BASE); + if (ret < 0) { + fprintf(stderr, "Couldn't add rtas token for %s: %s\n", + call->name, fdt_strerror(ret)); + return ret; + } + + } + return 0; +} + +static void register_core_rtas(void) +{ + spapr_rtas_register("display-character", rtas_display_character); + spapr_rtas_register("get-time-of-day", rtas_get_time_of_day); + spapr_rtas_register("power-off", rtas_power_off); + spapr_rtas_register("query-cpu-stopped-state", + rtas_query_cpu_stopped_state); + spapr_rtas_register("start-cpu", rtas_start_cpu); +} +device_init(register_core_rtas); diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr_vio.c qemu-kvm-0.15.0+noroms/hw/spapr_vio.c --- qemu-kvm-0.14.1+noroms/hw/spapr_vio.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr_vio.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,731 @@ +/* + * QEMU sPAPR VIO code + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus code: + * Copyright (c) 2009 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 "hw.h" +#include "sysemu.h" +#include "boards.h" +#include "monitor.h" +#include "loader.h" +#include "elf.h" +#include "hw/sysbus.h" +#include "kvm.h" +#include "device_tree.h" +#include "kvm_ppc.h" + +#include "hw/spapr.h" +#include "hw/spapr_vio.h" + +#ifdef CONFIG_FDT +#include +#endif /* CONFIG_FDT */ + +/* #define DEBUG_SPAPR */ +/* #define DEBUG_TCE */ + +#ifdef DEBUG_SPAPR +#define dprintf(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define dprintf(fmt, ...) \ + do { } while (0) +#endif + +static struct BusInfo spapr_vio_bus_info = { + .name = "spapr-vio", + .size = sizeof(VIOsPAPRBus), +}; + +VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg) +{ + DeviceState *qdev; + VIOsPAPRDevice *dev = NULL; + + QLIST_FOREACH(qdev, &bus->bus.children, sibling) { + dev = (VIOsPAPRDevice *)qdev; + if (dev->reg == reg) { + break; + } + } + + return dev; +} + +#ifdef CONFIG_FDT +static int vio_make_devnode(VIOsPAPRDevice *dev, + void *fdt) +{ + VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)dev->qdev.info; + int vdevice_off, node_off; + int ret; + + vdevice_off = fdt_path_offset(fdt, "/vdevice"); + if (vdevice_off < 0) { + return vdevice_off; + } + + node_off = fdt_add_subnode(fdt, vdevice_off, dev->qdev.id); + if (node_off < 0) { + return node_off; + } + + ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg); + if (ret < 0) { + return ret; + } + + if (info->dt_type) { + ret = fdt_setprop_string(fdt, node_off, "device_type", + info->dt_type); + if (ret < 0) { + return ret; + } + } + + if (info->dt_compatible) { + ret = fdt_setprop_string(fdt, node_off, "compatible", + info->dt_compatible); + if (ret < 0) { + return ret; + } + } + + if (dev->qirq) { + uint32_t ints_prop[] = {cpu_to_be32(dev->vio_irq_num), 0}; + + ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop, + sizeof(ints_prop)); + if (ret < 0) { + return ret; + } + } + + if (dev->rtce_window_size) { + uint32_t dma_prop[] = {cpu_to_be32(dev->reg), + 0, 0, + 0, cpu_to_be32(dev->rtce_window_size)}; + + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop(fdt, node_off, "ibm,my-dma-window", dma_prop, + sizeof(dma_prop)); + if (ret < 0) { + return ret; + } + } + + if (info->devnode) { + ret = (info->devnode)(dev, fdt, node_off); + if (ret < 0) { + return ret; + } + } + + return node_off; +} +#endif /* CONFIG_FDT */ + +/* + * RTCE handling + */ + +static void rtce_init(VIOsPAPRDevice *dev) +{ + size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT) + * sizeof(VIOsPAPR_RTCE); + + if (size) { + dev->rtce_table = qemu_mallocz(size); + } +} + +static target_ulong h_put_tce(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong liobn = args[0]; + target_ulong ioba = args[1]; + target_ulong tce = args[2]; + VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, liobn); + VIOsPAPR_RTCE *rtce; + + if (!dev) { + hcall_dprintf("spapr_vio_put_tce on non-existent LIOBN " + TARGET_FMT_lx "\n", liobn); + return H_PARAMETER; + } + + ioba &= ~(SPAPR_VIO_TCE_PAGE_SIZE - 1); + +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_vio_put_tce on %s ioba 0x" TARGET_FMT_lx + " TCE 0x" TARGET_FMT_lx "\n", dev->qdev.id, ioba, tce); +#endif + + if (ioba >= dev->rtce_window_size) { + hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x" + TARGET_FMT_lx "\n", ioba); + return H_PARAMETER; + } + + rtce = dev->rtce_table + (ioba >> SPAPR_VIO_TCE_PAGE_SHIFT); + rtce->tce = tce; + + return H_SUCCESS; +} + +int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba, + target_ulong len, enum VIOsPAPR_TCEAccess access) +{ + int start, end, i; + + start = ioba >> SPAPR_VIO_TCE_PAGE_SHIFT; + end = (ioba + len - 1) >> SPAPR_VIO_TCE_PAGE_SHIFT; + + for (i = start; i <= end; i++) { + if ((dev->rtce_table[i].tce & access) != access) { +#ifdef DEBUG_TCE + fprintf(stderr, "FAIL on %d\n", i); +#endif + return -1; + } + } + + return 0; +} + +int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, const void *buf, + uint32_t size) +{ +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n", + (unsigned long long)taddr, size); +#endif + + /* Check for bypass */ + if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) { + cpu_physical_memory_write(taddr, buf, size); + return 0; + } + + while (size) { + uint64_t tce; + uint32_t lsize; + uint64_t txaddr; + + /* Check if we are in bound */ + if (taddr >= dev->rtce_window_size) { +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_dma_write out of bounds\n"); +#endif + return H_DEST_PARM; + } + tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce; + + /* How much til end of page ? */ + lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1); + + /* Check TCE */ + if (!(tce & 2)) { + return H_DEST_PARM; + } + + /* Translate */ + txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) | + (taddr & SPAPR_VIO_TCE_PAGE_MASK); + +#ifdef DEBUG_TCE + fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n", + (unsigned long long)txaddr, lsize); +#endif + + /* Do it */ + cpu_physical_memory_write(txaddr, buf, lsize); + buf += lsize; + taddr += lsize; + size -= lsize; + } + return 0; +} + +int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size) +{ + /* FIXME: allocating a temp buffer is nasty, but just stepping + * through writing zeroes is awkward. This will do for now. */ + uint8_t zeroes[size]; + +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_dma_zero taddr=0x%llx size=0x%x\n", + (unsigned long long)taddr, size); +#endif + + memset(zeroes, 0, size); + return spapr_tce_dma_write(dev, taddr, zeroes, size); +} + +void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val) +{ + spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); +} + +void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val) +{ + val = tswap16(val); + spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); +} + + +void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val) +{ + val = tswap32(val); + spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); +} + +void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val) +{ + val = tswap64(val); + spapr_tce_dma_write(dev, taddr, &val, sizeof(val)); +} + +int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, void *buf, + uint32_t size) +{ +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_dma_write taddr=0x%llx size=0x%x\n", + (unsigned long long)taddr, size); +#endif + + /* Check for bypass */ + if (dev->flags & VIO_PAPR_FLAG_DMA_BYPASS) { + cpu_physical_memory_read(taddr, buf, size); + return 0; + } + + while (size) { + uint64_t tce; + uint32_t lsize; + uint64_t txaddr; + + /* Check if we are in bound */ + if (taddr >= dev->rtce_window_size) { +#ifdef DEBUG_TCE + fprintf(stderr, "spapr_tce_dma_read out of bounds\n"); +#endif + return H_DEST_PARM; + } + tce = dev->rtce_table[taddr >> SPAPR_VIO_TCE_PAGE_SHIFT].tce; + + /* How much til end of page ? */ + lsize = MIN(size, ((~taddr) & SPAPR_VIO_TCE_PAGE_MASK) + 1); + + /* Check TCE */ + if (!(tce & 1)) { + return H_DEST_PARM; + } + + /* Translate */ + txaddr = (tce & ~SPAPR_VIO_TCE_PAGE_MASK) | + (taddr & SPAPR_VIO_TCE_PAGE_MASK); + +#ifdef DEBUG_TCE + fprintf(stderr, " -> write to txaddr=0x%llx, size=0x%x\n", + (unsigned long long)txaddr, lsize); +#endif + /* Do it */ + cpu_physical_memory_read(txaddr, buf, lsize); + buf += lsize; + taddr += lsize; + size -= lsize; + } + return H_SUCCESS; +} + +uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr) +{ + uint64_t val; + + spapr_tce_dma_read(dev, taddr, &val, sizeof(val)); + return tswap64(val); +} + +/* + * CRQ handling + */ +static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong queue_addr = args[1]; + target_ulong queue_len = args[2]; + VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + + if (!dev) { + hcall_dprintf("h_reg_crq on non-existent unit 0x" + TARGET_FMT_lx "\n", reg); + return H_PARAMETER; + } + + /* We can't grok a queue size bigger than 256M for now */ + if (queue_len < 0x1000 || queue_len > 0x10000000) { + hcall_dprintf("h_reg_crq, queue size too small or too big (0x%llx)\n", + (unsigned long long)queue_len); + return H_PARAMETER; + } + + /* Check queue alignment */ + if (queue_addr & 0xfff) { + hcall_dprintf("h_reg_crq, queue not aligned (0x%llx)\n", + (unsigned long long)queue_addr); + return H_PARAMETER; + } + + /* Check if device supports CRQs */ + if (!dev->crq.SendFunc) { + return H_NOT_FOUND; + } + + + /* Already a queue ? */ + if (dev->crq.qsize) { + return H_RESOURCE; + } + dev->crq.qladdr = queue_addr; + dev->crq.qsize = queue_len; + dev->crq.qnext = 0; + + dprintf("CRQ for dev 0x" TARGET_FMT_lx " registered at 0x" + TARGET_FMT_lx "/0x" TARGET_FMT_lx "\n", + reg, queue_addr, queue_len); + return H_SUCCESS; +} + +static target_ulong h_free_crq(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + + if (!dev) { + hcall_dprintf("h_free_crq on non-existent unit 0x" + TARGET_FMT_lx "\n", reg); + return H_PARAMETER; + } + + dev->crq.qladdr = 0; + dev->crq.qsize = 0; + dev->crq.qnext = 0; + + dprintf("CRQ for dev 0x" TARGET_FMT_lx " freed\n", reg); + + return H_SUCCESS; +} + +static target_ulong h_send_crq(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong msg_hi = args[1]; + target_ulong msg_lo = args[2]; + VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + uint64_t crq_mangle[2]; + + if (!dev) { + hcall_dprintf("h_send_crq on non-existent unit 0x" + TARGET_FMT_lx "\n", reg); + return H_PARAMETER; + } + crq_mangle[0] = cpu_to_be64(msg_hi); + crq_mangle[1] = cpu_to_be64(msg_lo); + + if (dev->crq.SendFunc) { + return dev->crq.SendFunc(dev, (uint8_t *)crq_mangle); + } + + return H_HARDWARE; +} + +static target_ulong h_enable_crq(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + + if (!dev) { + hcall_dprintf("h_enable_crq on non-existent unit 0x" + TARGET_FMT_lx "\n", reg); + return H_PARAMETER; + } + + return 0; +} + +/* Returns negative error, 0 success, or positive: queue full */ +int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) +{ + int rc; + uint8_t byte; + + if (!dev->crq.qsize) { + fprintf(stderr, "spapr_vio_send_creq on uninitialized queue\n"); + return -1; + } + + /* Maybe do a fast path for KVM just writing to the pages */ + rc = spapr_tce_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1); + if (rc) { + return rc; + } + if (byte != 0) { + return 1; + } + + rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8, + &crq[8], 8); + if (rc) { + return rc; + } + + kvmppc_eieio(); + + rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8); + if (rc) { + return rc; + } + + dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize; + + if (dev->signal_state & 1) { + qemu_irq_pulse(dev->qirq); + } + + return 0; +} + +/* "quiesce" handling */ + +static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) +{ + dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS; + + if (dev->rtce_table) { + size_t size = (dev->rtce_window_size >> SPAPR_VIO_TCE_PAGE_SHIFT) + * sizeof(VIOsPAPR_RTCE); + memset(dev->rtce_table, 0, size); + } + + dev->crq.qladdr = 0; + dev->crq.qsize = 0; + dev->crq.qnext = 0; +} + +static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + VIOsPAPRBus *bus = spapr->vio_bus; + VIOsPAPRDevice *dev; + uint32_t unit, enable; + + if (nargs != 2) { + rtas_st(rets, 0, -3); + return; + } + unit = rtas_ld(args, 0); + enable = rtas_ld(args, 1); + dev = spapr_vio_find_by_reg(bus, unit); + if (!dev) { + rtas_st(rets, 0, -3); + return; + } + if (enable) { + dev->flags |= VIO_PAPR_FLAG_DMA_BYPASS; + } else { + dev->flags &= ~VIO_PAPR_FLAG_DMA_BYPASS; + } + + rtas_st(rets, 0, 0); +} + +static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + VIOsPAPRBus *bus = spapr->vio_bus; + DeviceState *qdev; + VIOsPAPRDevice *dev = NULL; + + if (nargs != 0) { + rtas_st(rets, 0, -3); + return; + } + + QLIST_FOREACH(qdev, &bus->bus.children, sibling) { + dev = (VIOsPAPRDevice *)qdev; + spapr_vio_quiesce_one(dev); + } + + rtas_st(rets, 0, 0); +} + +static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) +{ + VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo; + VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; + char *id; + + if (asprintf(&id, "%s@%x", info->dt_name, dev->reg) < 0) { + return -1; + } + + dev->qdev.id = id; + + rtce_init(dev); + + return info->init(dev); +} + +void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info) +{ + info->qdev.init = spapr_vio_busdev_init; + info->qdev.bus_info = &spapr_vio_bus_info; + + assert(info->qdev.size >= sizeof(VIOsPAPRDevice)); + qdev_register(&info->qdev); +} + +static target_ulong h_vio_signal(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong mode = args[1]; + VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + VIOsPAPRDeviceInfo *info; + + if (!dev) { + return H_PARAMETER; + } + + info = (VIOsPAPRDeviceInfo *)dev->qdev.info; + + if (mode & ~info->signal_mask) { + return H_PARAMETER; + } + + dev->signal_state = mode; + + return H_SUCCESS; +} + +VIOsPAPRBus *spapr_vio_bus_init(void) +{ + VIOsPAPRBus *bus; + BusState *qbus; + DeviceState *dev; + DeviceInfo *qinfo; + + /* Create bridge device */ + dev = qdev_create(NULL, "spapr-vio-bridge"); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + + qbus = qbus_create(&spapr_vio_bus_info, dev, "spapr-vio"); + bus = DO_UPCAST(VIOsPAPRBus, bus, qbus); + + /* hcall-vio */ + spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); + + /* hcall-tce */ + spapr_register_hypercall(H_PUT_TCE, h_put_tce); + + /* hcall-crq */ + spapr_register_hypercall(H_REG_CRQ, h_reg_crq); + spapr_register_hypercall(H_FREE_CRQ, h_free_crq); + spapr_register_hypercall(H_SEND_CRQ, h_send_crq); + spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq); + + /* RTAS calls */ + spapr_rtas_register("ibm,set-tce-bypass", rtas_set_tce_bypass); + spapr_rtas_register("quiesce", rtas_quiesce); + + for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) { + VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo; + + if (qinfo->bus_info != &spapr_vio_bus_info) { + continue; + } + + if (info->hcalls) { + info->hcalls(bus); + } + } + + return bus; +} + +/* Represents sPAPR hcall VIO devices */ + +static int spapr_vio_bridge_init(SysBusDevice *dev) +{ + /* nothing */ + return 0; +} + +static SysBusDeviceInfo spapr_vio_bridge_info = { + .init = spapr_vio_bridge_init, + .qdev.name = "spapr-vio-bridge", + .qdev.size = sizeof(SysBusDevice), + .qdev.no_user = 1, +}; + +static void spapr_vio_register_devices(void) +{ + sysbus_register_withprop(&spapr_vio_bridge_info); +} + +device_init(spapr_vio_register_devices) + +#ifdef CONFIG_FDT +int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) +{ + DeviceState *qdev; + int ret = 0; + + QLIST_FOREACH(qdev, &bus->bus.children, sibling) { + VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; + + ret = vio_make_devnode(dev, fdt); + + if (ret < 0) { + return ret; + } + } + + return 0; +} +#endif /* CONFIG_FDT */ diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr_vio.h qemu-kvm-0.15.0+noroms/hw/spapr_vio.h --- qemu-kvm-0.14.1+noroms/hw/spapr_vio.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr_vio.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,114 @@ +#ifndef _HW_SPAPR_VIO_H +#define _HW_SPAPR_VIO_H +/* + * QEMU sPAPR VIO bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus definitions: + * Copyright (c) 2009 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 . + */ + +#define SPAPR_VIO_TCE_PAGE_SHIFT 12 +#define SPAPR_VIO_TCE_PAGE_SIZE (1ULL << SPAPR_VIO_TCE_PAGE_SHIFT) +#define SPAPR_VIO_TCE_PAGE_MASK (SPAPR_VIO_TCE_PAGE_SIZE - 1) + +enum VIOsPAPR_TCEAccess { + SPAPR_TCE_FAULT = 0, + SPAPR_TCE_RO = 1, + SPAPR_TCE_WO = 2, + SPAPR_TCE_RW = 3, +}; + +#define SPAPR_VTY_BASE_ADDRESS 0x30000000 + +struct VIOsPAPRDevice; + +typedef struct VIOsPAPR_RTCE { + uint64_t tce; +} VIOsPAPR_RTCE; + +typedef struct VIOsPAPR_CRQ { + uint64_t qladdr; + uint32_t qsize; + uint32_t qnext; + int(*SendFunc)(struct VIOsPAPRDevice *vdev, uint8_t *crq); +} VIOsPAPR_CRQ; + +typedef struct VIOsPAPRDevice { + DeviceState qdev; + uint32_t reg; + uint32_t flags; +#define VIO_PAPR_FLAG_DMA_BYPASS 0x1 + qemu_irq qirq; + uint32_t vio_irq_num; + target_ulong signal_state; + uint32_t rtce_window_size; + VIOsPAPR_RTCE *rtce_table; + VIOsPAPR_CRQ crq; +} VIOsPAPRDevice; + +typedef struct VIOsPAPRBus { + BusState bus; +} VIOsPAPRBus; + +typedef struct { + DeviceInfo qdev; + const char *dt_name, *dt_type, *dt_compatible; + target_ulong signal_mask; + int (*init)(VIOsPAPRDevice *dev); + void (*hcalls)(VIOsPAPRBus *bus); + int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); +} VIOsPAPRDeviceInfo; + +extern VIOsPAPRBus *spapr_vio_bus_init(void); +extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg); +extern void spapr_vio_bus_register_withprop(VIOsPAPRDeviceInfo *info); +extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt); + +extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode); + +int spapr_vio_check_tces(VIOsPAPRDevice *dev, target_ulong ioba, + target_ulong len, + enum VIOsPAPR_TCEAccess access); + +int spapr_tce_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, + void *buf, uint32_t size); +int spapr_tce_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, + const void *buf, uint32_t size); +int spapr_tce_dma_zero(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size); +void stb_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint8_t val); +void sth_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint16_t val); +void stw_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t val); +void stq_tce(VIOsPAPRDevice *dev, uint64_t taddr, uint64_t val); +uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr); + +int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); + +void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); +void spapr_vty_create(VIOsPAPRBus *bus, + uint32_t reg, CharDriverState *chardev, + qemu_irq qirq, uint32_t vio_irq_num); + +void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, + qemu_irq qirq, uint32_t vio_irq_num); + +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg, + qemu_irq qirq, uint32_t vio_irq_num); + +int spapr_tce_set_bypass(uint32_t unit, uint32_t enable); +void spapr_vio_quiesce(void); + +#endif /* _HW_SPAPR_VIO_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr_vscsi.c qemu-kvm-0.15.0+noroms/hw/spapr_vscsi.c --- qemu-kvm-0.14.1+noroms/hw/spapr_vscsi.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr_vscsi.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,999 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtual SCSI, aka ibmvscsi + * + * Copyright (c) 2010,2011 Benjamin Herrenschmidt, IBM Corporation. + * + * 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. + * + * TODO: + * + * - Cleanups :-) + * - Sort out better how to assign devices to VSCSI instances + * - Fix residual counts + * - Add indirect descriptors support + * - Maybe do autosense (PAPR seems to mandate it, linux doesn't care) + */ +#include "hw.h" +#include "scsi.h" +#include "scsi-defs.h" +#include "net.h" /* Remove that when we can */ +#include "srp.h" +#include "hw/qdev.h" +#include "hw/spapr.h" +#include "hw/spapr_vio.h" +#include "hw/ppc-viosrp.h" + +#include + +/*#define DEBUG_VSCSI*/ + +#ifdef DEBUG_VSCSI +#define dprintf(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define dprintf(fmt, ...) \ + do { } while (0) +#endif + +/* + * Virtual SCSI device + */ + +/* Random numbers */ +#define VSCSI_MAX_SECTORS 4096 +#define VSCSI_REQ_LIMIT 24 + +#define SCSI_SENSE_BUF_SIZE 96 +#define SRP_RSP_SENSE_DATA_LEN 18 + +typedef union vscsi_crq { + struct viosrp_crq s; + uint8_t raw[16]; +} vscsi_crq; + +typedef struct vscsi_req { + vscsi_crq crq; + union viosrp_iu iu; + + /* SCSI request tracking */ + SCSIRequest *sreq; + uint32_t qtag; /* qemu tag != srp tag */ + int lun; + int active; + long data_len; + int writing; + int sensing; + int senselen; + uint8_t sense[SCSI_SENSE_BUF_SIZE]; + + /* RDMA related bits */ + uint8_t dma_fmt; + struct srp_direct_buf ext_desc; + struct srp_direct_buf *cur_desc; + struct srp_indirect_buf *ind_desc; + int local_desc; + int total_desc; +} vscsi_req; + + +typedef struct { + VIOsPAPRDevice vdev; + SCSIBus bus; + vscsi_req reqs[VSCSI_REQ_LIMIT]; +} VSCSIState; + +/* XXX Debug only */ +static VSCSIState *dbg_vscsi_state; + + +static struct vscsi_req *vscsi_get_req(VSCSIState *s) +{ + vscsi_req *req; + int i; + + for (i = 0; i < VSCSI_REQ_LIMIT; i++) { + req = &s->reqs[i]; + if (!req->active) { + memset(req, 0, sizeof(*req)); + req->qtag = i; + req->active = 1; + return req; + } + } + return NULL; +} + +static void vscsi_put_req(vscsi_req *req) +{ + if (req->sreq != NULL) { + scsi_req_unref(req->sreq); + } + req->sreq = NULL; + req->active = 0; +} + +static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun) +{ + /* XXX Figure that one out properly ! This is crackpot */ + *id = (srp_lun >> 56) & 0x7f; + *lun = (srp_lun >> 48) & 0xff; +} + +static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, + uint64_t length, uint8_t format) +{ + long rc, rc1; + + /* First copy the SRP */ + rc = spapr_tce_dma_write(&s->vdev, req->crq.s.IU_data_ptr, + &req->iu, length); + if (rc) { + fprintf(stderr, "vscsi_send_iu: DMA write failure !\n"); + } + + req->crq.s.valid = 0x80; + req->crq.s.format = format; + req->crq.s.reserved = 0x00; + req->crq.s.timeout = cpu_to_be16(0x0000); + req->crq.s.IU_length = cpu_to_be16(length); + req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */ + + if (rc == 0) { + req->crq.s.status = 0x99; /* Just needs to be non-zero */ + } else { + req->crq.s.status = 0x00; + } + + rc1 = spapr_vio_send_crq(&s->vdev, req->crq.raw); + if (rc1) { + fprintf(stderr, "vscsi_send_iu: Error sending response\n"); + return rc1; + } + + return rc; +} + +static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req, + uint8_t key, uint8_t asc, uint8_t ascq) +{ + req->senselen = SRP_RSP_SENSE_DATA_LEN; + + /* Valid bit and 'current errors' */ + req->sense[0] = (0x1 << 7 | 0x70); + /* Sense key */ + req->sense[2] = key; + /* Additional sense length */ + req->sense[7] = 0xa; /* 10 bytes */ + /* Additional sense code */ + req->sense[12] = asc; + req->sense[13] = ascq; +} + +static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, + uint8_t status, int32_t res_in, int32_t res_out) +{ + union viosrp_iu *iu = &req->iu; + uint64_t tag = iu->srp.rsp.tag; + int total_len = sizeof(iu->srp.rsp); + + dprintf("VSCSI: Sending resp status: 0x%x, " + "res_in: %d, res_out: %d\n", status, res_in, res_out); + + memset(iu, 0, sizeof(struct srp_rsp)); + iu->srp.rsp.opcode = SRP_RSP; + iu->srp.rsp.req_lim_delta = cpu_to_be32(1); + iu->srp.rsp.tag = tag; + + /* Handle residuals */ + if (res_in < 0) { + iu->srp.rsp.flags |= SRP_RSP_FLAG_DIUNDER; + res_in = -res_in; + } else if (res_in) { + iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER; + } + if (res_out < 0) { + iu->srp.rsp.flags |= SRP_RSP_FLAG_DOUNDER; + res_out = -res_out; + } else if (res_out) { + iu->srp.rsp.flags |= SRP_RSP_FLAG_DOOVER; + } + iu->srp.rsp.data_in_res_cnt = cpu_to_be32(res_in); + iu->srp.rsp.data_out_res_cnt = cpu_to_be32(res_out); + + /* We don't do response data */ + /* iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; */ + iu->srp.rsp.resp_data_len = cpu_to_be32(0); + + /* Handle success vs. failure */ + iu->srp.rsp.status = status; + if (status) { + iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not & 0x04) >> 2; + if (req->senselen) { + req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen); + memcpy(req->iu.srp.rsp.data, req->sense, req->senselen); + total_len += req->senselen; + } + } else { + iu->srp.rsp.sol_not = (iu->srp.cmd.sol_not & 0x02) >> 1; + } + + vscsi_send_iu(s, req, total_len, VIOSRP_SRP_FORMAT); + return 0; +} + +static inline void vscsi_swap_desc(struct srp_direct_buf *desc) +{ + desc->va = be64_to_cpu(desc->va); + desc->len = be32_to_cpu(desc->len); +} + +static int vscsi_srp_direct_data(VSCSIState *s, vscsi_req *req, + uint8_t *buf, uint32_t len) +{ + struct srp_direct_buf *md = req->cur_desc; + uint32_t llen; + int rc = 0; + + dprintf("VSCSI: direct segment 0x%x bytes, va=0x%llx desc len=0x%x\n", + len, (unsigned long long)md->va, md->len); + + llen = MIN(len, md->len); + if (llen) { + if (req->writing) { /* writing = to device = reading from memory */ + rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen); + } else { + rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen); + } + } + md->len -= llen; + md->va += llen; + + if (rc) { + return -1; + } + return llen; +} + +static int vscsi_srp_indirect_data(VSCSIState *s, vscsi_req *req, + uint8_t *buf, uint32_t len) +{ + struct srp_direct_buf *td = &req->ind_desc->table_desc; + struct srp_direct_buf *md = req->cur_desc; + int rc = 0; + uint32_t llen, total = 0; + + dprintf("VSCSI: indirect segment 0x%x bytes, td va=0x%llx len=0x%x\n", + len, (unsigned long long)td->va, td->len); + + /* While we have data ... */ + while (len) { + /* If we have a descriptor but it's empty, go fetch a new one */ + if (md && md->len == 0) { + /* More local available, use one */ + if (req->local_desc) { + md = ++req->cur_desc; + --req->local_desc; + --req->total_desc; + td->va += sizeof(struct srp_direct_buf); + } else { + md = req->cur_desc = NULL; + } + } + /* No descriptor at hand, fetch one */ + if (!md) { + if (!req->total_desc) { + dprintf("VSCSI: Out of descriptors !\n"); + break; + } + md = req->cur_desc = &req->ext_desc; + dprintf("VSCSI: Reading desc from 0x%llx\n", + (unsigned long long)td->va); + rc = spapr_tce_dma_read(&s->vdev, td->va, md, + sizeof(struct srp_direct_buf)); + if (rc) { + dprintf("VSCSI: tce_dma_read -> %d reading ext_desc\n", rc); + break; + } + vscsi_swap_desc(md); + td->va += sizeof(struct srp_direct_buf); + --req->total_desc; + } + dprintf("VSCSI: [desc va=0x%llx,len=0x%x] remaining=0x%x\n", + (unsigned long long)md->va, md->len, len); + + /* Perform transfer */ + llen = MIN(len, md->len); + if (req->writing) { /* writing = to device = reading from memory */ + rc = spapr_tce_dma_read(&s->vdev, md->va, buf, llen); + } else { + rc = spapr_tce_dma_write(&s->vdev, md->va, buf, llen); + } + if (rc) { + dprintf("VSCSI: tce_dma_r/w(%d) -> %d\n", req->writing, rc); + break; + } + dprintf("VSCSI: data: %02x %02x %02x %02x...\n", + buf[0], buf[1], buf[2], buf[3]); + + len -= llen; + buf += llen; + total += llen; + md->va += llen; + md->len -= llen; + } + return rc ? -1 : total; +} + +static int vscsi_srp_transfer_data(VSCSIState *s, vscsi_req *req, + int writing, uint8_t *buf, uint32_t len) +{ + int err = 0; + + switch (req->dma_fmt) { + case SRP_NO_DATA_DESC: + dprintf("VSCSI: no data desc transfer, skipping 0x%x bytes\n", len); + break; + case SRP_DATA_DESC_DIRECT: + err = vscsi_srp_direct_data(s, req, buf, len); + break; + case SRP_DATA_DESC_INDIRECT: + err = vscsi_srp_indirect_data(s, req, buf, len); + break; + } + return err; +} + +/* Bits from linux srp */ +static int data_out_desc_size(struct srp_cmd *cmd) +{ + int size = 0; + uint8_t fmt = cmd->buf_fmt >> 4; + + switch (fmt) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + size = sizeof(struct srp_direct_buf); + break; + case SRP_DATA_DESC_INDIRECT: + size = sizeof(struct srp_indirect_buf) + + sizeof(struct srp_direct_buf)*cmd->data_out_desc_cnt; + break; + default: + break; + } + return size; +} + +static int vscsi_preprocess_desc(vscsi_req *req) +{ + struct srp_cmd *cmd = &req->iu.srp.cmd; + int offset, i; + + offset = cmd->add_cdb_len & ~3; + + if (req->writing) { + req->dma_fmt = cmd->buf_fmt >> 4; + } else { + offset += data_out_desc_size(cmd); + req->dma_fmt = cmd->buf_fmt & ((1U << 4) - 1); + } + + switch (req->dma_fmt) { + case SRP_NO_DATA_DESC: + break; + case SRP_DATA_DESC_DIRECT: + req->cur_desc = (struct srp_direct_buf *)(cmd->add_data + offset); + req->total_desc = req->local_desc = 1; + vscsi_swap_desc(req->cur_desc); + dprintf("VSCSI: using direct RDMA %s, 0x%x bytes MD: 0x%llx\n", + req->writing ? "write" : "read", + req->cur_desc->len, (unsigned long long)req->cur_desc->va); + break; + case SRP_DATA_DESC_INDIRECT: + req->ind_desc = (struct srp_indirect_buf *)(cmd->add_data + offset); + vscsi_swap_desc(&req->ind_desc->table_desc); + req->total_desc = req->ind_desc->table_desc.len / + sizeof(struct srp_direct_buf); + req->local_desc = req->writing ? cmd->data_out_desc_cnt : + cmd->data_in_desc_cnt; + for (i = 0; i < req->local_desc; i++) { + vscsi_swap_desc(&req->ind_desc->desc_list[i]); + } + req->cur_desc = req->local_desc ? &req->ind_desc->desc_list[0] : NULL; + dprintf("VSCSI: using indirect RDMA %s, 0x%x bytes %d descs " + "(%d local) VA: 0x%llx\n", + req->writing ? "read" : "write", + be32_to_cpu(req->ind_desc->len), + req->total_desc, req->local_desc, + (unsigned long long)req->ind_desc->table_desc.va); + break; + default: + fprintf(stderr, + "vscsi_preprocess_desc: Unknown format %x\n", req->dma_fmt); + return -1; + } + + return 0; +} + +static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) +{ + uint8_t *cdb = req->iu.srp.cmd.cdb; + int n; + + n = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense)); + if (n) { + req->senselen = n; + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + vscsi_put_req(req); + return; + } + + dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n"); + cdb[0] = 3; + cdb[1] = 0; + cdb[2] = 0; + cdb[3] = 0; + cdb[4] = 96; + cdb[5] = 0; + req->sensing = 1; + n = scsi_req_enqueue(req->sreq, cdb); + dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag); + if (n < 0) { + fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n"); + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); + scsi_req_abort(req->sreq, CHECK_CONDITION); + return; + } else if (n == 0) { + return; + } + scsi_req_continue(req->sreq); +} + +/* Callback to indicate that the SCSI layer has completed a transfer. */ +static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) +{ + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); + vscsi_req *req = sreq->hba_private; + uint8_t *buf; + int rc = 0; + + dprintf("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n", + sreq->tag, len, req); + if (req == NULL) { + fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); + return; + } + + if (req->sensing) { + uint8_t *buf = scsi_req_get_buf(sreq); + + len = MIN(len, SCSI_SENSE_BUF_SIZE); + dprintf("VSCSI: Sense data, %d bytes:\n", len); + dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15]); + memcpy(req->sense, buf, len); + req->senselen = len; + scsi_req_continue(req->sreq); + return; + } + + if (len) { + buf = scsi_req_get_buf(sreq); + rc = vscsi_srp_transfer_data(s, req, req->writing, buf, len); + } + if (rc < 0) { + fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc); + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); + scsi_req_abort(req->sreq, CHECK_CONDITION); + return; + } + + /* Start next chunk */ + req->data_len -= rc; + scsi_req_continue(sreq); +} + +/* Callback to indicate that the SCSI layer has completed a transfer. */ +static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) +{ + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); + vscsi_req *req = sreq->hba_private; + int32_t res_in = 0, res_out = 0; + + dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n", + reason, sreq->tag, status, req); + if (req == NULL) { + fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); + return; + } + + if (!req->sensing && status == CHECK_CONDITION) { + vscsi_send_request_sense(s, req); + return; + } + + if (req->sensing) { + dprintf("VSCSI: Sense done !\n"); + status = CHECK_CONDITION; + } else { + dprintf("VSCSI: Command complete err=%d\n", status); + if (status == 0) { + /* We handle overflows, not underflows for normal commands, + * but hopefully nobody cares + */ + if (req->writing) { + res_out = req->data_len; + } else { + res_in = req->data_len; + } + } + } + vscsi_send_rsp(s, req, 0, res_in, res_out); + vscsi_put_req(req); +} + +static void vscsi_request_cancelled(SCSIRequest *sreq) +{ + vscsi_req *req = sreq->hba_private; + + vscsi_put_req(req); +} + +static void vscsi_process_login(VSCSIState *s, vscsi_req *req) +{ + union viosrp_iu *iu = &req->iu; + struct srp_login_rsp *rsp = &iu->srp.login_rsp; + uint64_t tag = iu->srp.rsp.tag; + + dprintf("VSCSI: Got login, sendin response !\n"); + + /* TODO handle case that requested size is wrong and + * buffer format is wrong + */ + memset(iu, 0, sizeof(struct srp_login_rsp)); + rsp->opcode = SRP_LOGIN_RSP; + /* Don't advertise quite as many request as we support to + * keep room for management stuff etc... + */ + rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2); + rsp->tag = tag; + rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); + rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); + /* direct and indirect */ + rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); + + vscsi_send_iu(s, req, sizeof(*rsp), VIOSRP_SRP_FORMAT); +} + +static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) +{ + uint8_t *cdb = req->iu.srp.cmd.cdb; + uint8_t resp_data[36]; + int rc, len, alen; + + /* We dont do EVPD. Also check that page_code is 0 */ + if ((cdb[1] & 0x01) || (cdb[1] & 0x01) || cdb[2] != 0) { + /* Send INVALID FIELD IN CDB */ + vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0); + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + return; + } + alen = cdb[3]; + alen = (alen << 8) | cdb[4]; + len = MIN(alen, 36); + + /* Fake up inquiry using PQ=3 */ + memset(resp_data, 0, 36); + resp_data[0] = 0x7f; /* Not capable of supporting a device here */ + resp_data[2] = 0x06; /* SPS-4 */ + resp_data[3] = 0x02; /* Resp data format */ + resp_data[4] = 36 - 5; /* Additional length */ + resp_data[7] = 0x10; /* Sync transfers */ + memcpy(&resp_data[16], "QEMU EMPTY ", 16); + memcpy(&resp_data[8], "QEMU ", 8); + + req->writing = 0; + vscsi_preprocess_desc(req); + rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); + if (rc < 0) { + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + } else { + vscsi_send_rsp(s, req, 0, 36 - rc, 0); + } +} + +static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) +{ + union srp_iu *srp = &req->iu.srp; + SCSIDevice *sdev; + int n, id, lun; + + vscsi_decode_id_lun(be64_to_cpu(srp->cmd.lun), &id, &lun); + + /* Qemu vs. linux issue with LUNs to be sorted out ... */ + sdev = (id < 8 && lun < 16) ? s->bus.devs[id] : NULL; + if (!sdev) { + dprintf("VSCSI: Command for id %d with no drive\n", id); + if (srp->cmd.cdb[0] == INQUIRY) { + vscsi_inquiry_no_target(s, req); + } else { + vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0x00); + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + } return 1; + } + + req->lun = lun; + req->sreq = scsi_req_new(sdev, req->qtag, lun, req); + n = scsi_req_enqueue(req->sreq, srp->cmd.cdb); + + dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n", + req->qtag, srp->cmd.cdb[0], id, lun, n); + + if (n) { + /* Transfer direction must be set before preprocessing the + * descriptors + */ + req->writing = (n < 1); + + /* Preprocess RDMA descriptors */ + vscsi_preprocess_desc(req); + + /* Get transfer direction and initiate transfer */ + if (n > 0) { + req->data_len = n; + } else if (n < 0) { + req->data_len = -n; + } + scsi_req_continue(req->sreq); + } + /* Don't touch req here, it may have been recycled already */ + + return 0; +} + +static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) +{ + union viosrp_iu *iu = &req->iu; + int fn; + + fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n", + iu->srp.tsk_mgmt.tsk_mgmt_func); + + switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { +#if 0 /* We really don't deal with these for now */ + case SRP_TSK_ABORT_TASK: + fn = ABORT_TASK; + break; + case SRP_TSK_ABORT_TASK_SET: + fn = ABORT_TASK_SET; + break; + case SRP_TSK_CLEAR_TASK_SET: + fn = CLEAR_TASK_SET; + break; + case SRP_TSK_LUN_RESET: + fn = LOGICAL_UNIT_RESET; + break; + case SRP_TSK_CLEAR_ACA: + fn = CLEAR_ACA; + break; +#endif + default: + fn = 0; + } + if (fn) { + /* XXX Send/Handle target task management */ + ; + } else { + vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0); + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + } + return !fn; +} + +static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req) +{ + union srp_iu *srp = &req->iu.srp; + int done = 1; + uint8_t opcode = srp->rsp.opcode; + + switch (opcode) { + case SRP_LOGIN_REQ: + vscsi_process_login(s, req); + break; + case SRP_TSK_MGMT: + done = vscsi_process_tsk_mgmt(s, req); + break; + case SRP_CMD: + done = vscsi_queue_cmd(s, req); + break; + case SRP_LOGIN_RSP: + case SRP_I_LOGOUT: + case SRP_T_LOGOUT: + case SRP_RSP: + case SRP_CRED_REQ: + case SRP_CRED_RSP: + case SRP_AER_REQ: + case SRP_AER_RSP: + fprintf(stderr, "VSCSI: Unsupported opcode %02x\n", opcode); + break; + default: + fprintf(stderr, "VSCSI: Unknown type %02x\n", opcode); + } + + return done; +} + +static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) +{ + struct viosrp_adapter_info *sinfo; + struct mad_adapter_info_data info; + int rc; + + sinfo = &req->iu.mad.adapter_info; + +#if 0 /* What for ? */ + rc = spapr_tce_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), + &info, be16_to_cpu(sinfo->common.length)); + if (rc) { + fprintf(stderr, "vscsi_send_adapter_info: DMA read failure !\n"); + } +#endif + memset(&info, 0, sizeof(info)); + strcpy(info.srp_version, SRP_VERSION); + strncpy(info.partition_name, "qemu", sizeof("qemu")); + info.partition_number = cpu_to_be32(0); + info.mad_version = cpu_to_be32(1); + info.os_type = cpu_to_be32(2); + info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS << 9); + + rc = spapr_tce_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer), + &info, be16_to_cpu(sinfo->common.length)); + if (rc) { + fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n"); + } + + sinfo->common.status = rc ? cpu_to_be32(1) : 0; + + return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT); +} + +static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req) +{ + union mad_iu *mad = &req->iu.mad; + + switch (be32_to_cpu(mad->empty_iu.common.type)) { + case VIOSRP_EMPTY_IU_TYPE: + fprintf(stderr, "Unsupported EMPTY MAD IU\n"); + break; + case VIOSRP_ERROR_LOG_TYPE: + fprintf(stderr, "Unsupported ERROR LOG MAD IU\n"); + mad->error_log.common.status = cpu_to_be16(1); + vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT); + break; + case VIOSRP_ADAPTER_INFO_TYPE: + vscsi_send_adapter_info(s, req); + break; + case VIOSRP_HOST_CONFIG_TYPE: + mad->host_config.common.status = cpu_to_be16(1); + vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT); + break; + default: + fprintf(stderr, "VSCSI: Unknown MAD type %02x\n", + be32_to_cpu(mad->empty_iu.common.type)); + } + + return 1; +} + +static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) +{ + vscsi_req *req; + int done; + + req = vscsi_get_req(s); + if (req == NULL) { + fprintf(stderr, "VSCSI: Failed to get a request !\n"); + return; + } + + /* We only support a limited number of descriptors, we know + * the ibmvscsi driver uses up to 10 max, so it should fit + * in our 256 bytes IUs. If not we'll have to increase the size + * of the structure. + */ + if (crq->s.IU_length > sizeof(union viosrp_iu)) { + fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", + crq->s.IU_length); + return; + } + + /* XXX Handle failure differently ? */ + if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, + crq->s.IU_length)) { + fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); + qemu_free(req); + } + memcpy(&req->crq, crq, sizeof(vscsi_crq)); + + if (crq->s.format == VIOSRP_MAD_FORMAT) { + done = vscsi_handle_mad_req(s, req); + } else { + done = vscsi_handle_srp_req(s, req); + } + + if (done) { + vscsi_put_req(req); + } +} + + +static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) +{ + VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev); + vscsi_crq crq; + + memcpy(crq.raw, crq_data, 16); + crq.s.timeout = be16_to_cpu(crq.s.timeout); + crq.s.IU_length = be16_to_cpu(crq.s.IU_length); + crq.s.IU_data_ptr = be64_to_cpu(crq.s.IU_data_ptr); + + dprintf("VSCSI: do_crq %02x %02x ...\n", crq.raw[0], crq.raw[1]); + + switch (crq.s.valid) { + case 0xc0: /* Init command/response */ + + /* Respond to initialization request */ + if (crq.s.format == 0x01) { + memset(crq.raw, 0, 16); + crq.s.valid = 0xc0; + crq.s.format = 0x02; + spapr_vio_send_crq(dev, crq.raw); + } + + /* Note that in hotplug cases, we might get a 0x02 + * as a result of us emitting the init request + */ + + break; + case 0xff: /* Link event */ + + /* Not handled for now */ + + break; + case 0x80: /* Payloads */ + switch (crq.s.format) { + case VIOSRP_SRP_FORMAT: /* AKA VSCSI request */ + case VIOSRP_MAD_FORMAT: /* AKA VSCSI response */ + vscsi_got_payload(s, &crq); + break; + case VIOSRP_OS400_FORMAT: + case VIOSRP_AIX_FORMAT: + case VIOSRP_LINUX_FORMAT: + case VIOSRP_INLINE_FORMAT: + fprintf(stderr, "vscsi_do_srq: Unsupported payload format %02x\n", + crq.s.format); + break; + default: + fprintf(stderr, "vscsi_do_srq: Unknown payload format %02x\n", + crq.s.format); + } + break; + default: + fprintf(stderr, "vscsi_do_crq: unknown CRQ %02x %02x ...\n", + crq.raw[0], crq.raw[1]); + }; + + return 0; +} + +static const struct SCSIBusOps vscsi_scsi_ops = { + .transfer_data = vscsi_transfer_data, + .complete = vscsi_command_complete, + .cancel = vscsi_request_cancelled +}; + +static int spapr_vscsi_init(VIOsPAPRDevice *dev) +{ + VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev); + int i; + + dbg_vscsi_state = s; + + /* Initialize qemu request tags */ + memset(s->reqs, 0, sizeof(s->reqs)); + for (i = 0; i < VSCSI_REQ_LIMIT; i++) { + s->reqs[i].qtag = i; + } + + dev->crq.SendFunc = vscsi_do_crq; + + scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT, + &vscsi_scsi_ops); + if (!dev->qdev.hotplugged) { + scsi_bus_legacy_handle_cmdline(&s->bus); + } + + return 0; +} + +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg, + qemu_irq qirq, uint32_t vio_irq_num) +{ + DeviceState *dev; + VIOsPAPRDevice *sdev; + + dev = qdev_create(&bus->bus, "spapr-vscsi"); + qdev_prop_set_uint32(dev, "reg", reg); + + qdev_init_nofail(dev); + + sdev = (VIOsPAPRDevice *)dev; + sdev->qirq = qirq; + sdev->vio_irq_num = vio_irq_num; +} + +static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) +{ + int ret; + + ret = fdt_setprop_cell(fdt, node_off, "#address-cells", 2); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, node_off, "#size-cells", 0); + if (ret < 0) { + return ret; + } + + return 0; +} + +static VIOsPAPRDeviceInfo spapr_vscsi = { + .init = spapr_vscsi_init, + .devnode = spapr_vscsi_devnode, + .dt_name = "v-scsi", + .dt_type = "vscsi", + .dt_compatible = "IBM,v-scsi", + .signal_mask = 0x00000001, + .qdev.name = "spapr-vscsi", + .qdev.size = sizeof(VSCSIState), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000), + DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, + rtce_window_size, 0x10000000), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void spapr_vscsi_register(void) +{ + spapr_vio_bus_register_withprop(&spapr_vscsi); +} +device_init(spapr_vscsi_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/spapr_vty.c qemu-kvm-0.15.0+noroms/hw/spapr_vty.c --- qemu-kvm-0.14.1+noroms/hw/spapr_vty.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/spapr_vty.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,159 @@ +#include "qdev.h" +#include "qemu-char.h" +#include "hw/spapr.h" +#include "hw/spapr_vio.h" + +#define VTERM_BUFSIZE 16 + +typedef struct VIOsPAPRVTYDevice { + VIOsPAPRDevice sdev; + CharDriverState *chardev; + uint32_t in, out; + uint8_t buf[VTERM_BUFSIZE]; +} VIOsPAPRVTYDevice; + +static int vty_can_receive(void *opaque) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; + + return (dev->in - dev->out) < VTERM_BUFSIZE; +} + +static void vty_receive(void *opaque, const uint8_t *buf, int size) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; + int i; + + if ((dev->in == dev->out) && size) { + /* toggle line to simulate edge interrupt */ + qemu_irq_pulse(dev->sdev.qirq); + } + for (i = 0; i < size; i++) { + assert((dev->in - dev->out) < VTERM_BUFSIZE); + dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i]; + } +} + +static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + int n = 0; + + while ((n < max) && (dev->out != dev->in)) { + buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE]; + } + + return n; +} + +void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + + /* FIXME: should check the qemu_chr_write() return value */ + qemu_chr_write(dev->chardev, buf, len); +} + +static int spapr_vty_init(VIOsPAPRDevice *sdev) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + + qemu_chr_add_handlers(dev->chardev, vty_can_receive, + vty_receive, NULL, dev); + + return 0; +} + +static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong len = args[1]; + target_ulong char0_7 = args[2]; + target_ulong char8_15 = args[3]; + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + uint8_t buf[16]; + + if (!sdev) { + return H_PARAMETER; + } + + if (len > 16) { + return H_PARAMETER; + } + + *((uint64_t *)buf) = cpu_to_be64(char0_7); + *((uint64_t *)buf + 1) = cpu_to_be64(char8_15); + + vty_putchars(sdev, buf, len); + + return H_SUCCESS; +} + +static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong *len = args + 0; + target_ulong *char0_7 = args + 1; + target_ulong *char8_15 = args + 2; + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + uint8_t buf[16]; + + if (!sdev) { + return H_PARAMETER; + } + + *len = vty_getchars(sdev, buf, sizeof(buf)); + if (*len < 16) { + memset(buf + *len, 0, 16 - *len); + } + + *char0_7 = be64_to_cpu(*((uint64_t *)buf)); + *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1)); + + return H_SUCCESS; +} + +void spapr_vty_create(VIOsPAPRBus *bus, + uint32_t reg, CharDriverState *chardev, + qemu_irq qirq, uint32_t vio_irq_num) +{ + DeviceState *dev; + VIOsPAPRDevice *sdev; + + dev = qdev_create(&bus->bus, "spapr-vty"); + qdev_prop_set_uint32(dev, "reg", reg); + qdev_prop_set_chr(dev, "chardev", chardev); + qdev_init_nofail(dev); + sdev = (VIOsPAPRDevice *)dev; + sdev->qirq = qirq; + sdev->vio_irq_num = vio_irq_num; +} + +static void vty_hcalls(VIOsPAPRBus *bus) +{ + spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char); + spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char); +} + +static VIOsPAPRDeviceInfo spapr_vty = { + .init = spapr_vty_init, + .dt_name = "vty", + .dt_type = "serial", + .dt_compatible = "hvterm1", + .hcalls = vty_hcalls, + .qdev.name = "spapr-vty", + .qdev.size = sizeof(VIOsPAPRVTYDevice), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0), + DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void spapr_vty_register(void) +{ + spapr_vio_bus_register_withprop(&spapr_vty); +} +device_init(spapr_vty_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/spitz.c qemu-kvm-0.15.0+noroms/hw/spitz.c --- qemu-kvm-0.14.1+noroms/hw/spitz.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/spitz.c 2011-08-09 07:40:29.000000000 -0500 @@ -393,7 +393,7 @@ s->fifopos = 0; } - qemu_mod_timer(s->kbdtimer, qemu_get_clock(vm_clock) + + qemu_mod_timer(s->kbdtimer, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 32); } @@ -485,7 +485,7 @@ 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_mod_timer(s->kbdtimer, qemu_get_clock_ns(vm_clock)); qemu_add_kbd_event_handler(spitz_keyboard_handler, s); } @@ -506,7 +506,7 @@ spitz_keyboard_pre_map(s); - s->kbdtimer = qemu_new_timer(vm_clock, spitz_keyboard_tick, s); + s->kbdtimer = qemu_new_timer_ns(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); diff -Nru qemu-kvm-0.14.1+noroms/hw/srp.h qemu-kvm-0.15.0+noroms/hw/srp.h --- qemu-kvm-0.14.1+noroms/hw/srp.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/srp.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2005 Cisco Systems. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - 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. + * + * 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 SCSI_SRP_H +#define SCSI_SRP_H + +/* + * Structures and constants for the SCSI RDMA Protocol (SRP) as + * defined by the INCITS T10 committee. This file was written using + * draft Revision 16a of the SRP standard. + */ + +enum { + + SRP_LOGIN_REQ = 0x00, + SRP_TSK_MGMT = 0x01, + SRP_CMD = 0x02, + SRP_I_LOGOUT = 0x03, + SRP_LOGIN_RSP = 0xc0, + SRP_RSP = 0xc1, + SRP_LOGIN_REJ = 0xc2, + SRP_T_LOGOUT = 0x80, + SRP_CRED_REQ = 0x81, + SRP_AER_REQ = 0x82, + SRP_CRED_RSP = 0x41, + SRP_AER_RSP = 0x42 +}; + +enum { + SRP_BUF_FORMAT_DIRECT = 1 << 1, + SRP_BUF_FORMAT_INDIRECT = 1 << 2 +}; + +enum { + SRP_NO_DATA_DESC = 0, + SRP_DATA_DESC_DIRECT = 1, + SRP_DATA_DESC_INDIRECT = 2 +}; + +enum { + SRP_TSK_ABORT_TASK = 0x01, + SRP_TSK_ABORT_TASK_SET = 0x02, + SRP_TSK_CLEAR_TASK_SET = 0x04, + SRP_TSK_LUN_RESET = 0x08, + SRP_TSK_CLEAR_ACA = 0x40 +}; + +enum srp_login_rej_reason { + SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL = 0x00010000, + SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES = 0x00010001, + SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE = 0x00010002, + SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL = 0x00010003, + SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT = 0x00010004, + SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED = 0x00010005, + SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED = 0x00010006 +}; + +enum { + SRP_REV10_IB_IO_CLASS = 0xff00, + SRP_REV16A_IB_IO_CLASS = 0x0100 +}; + +struct srp_direct_buf { + uint64_t va; + uint32_t key; + uint32_t len; +}; + +/* + * We need the packed attribute because the SRP spec puts the list of + * descriptors at an offset of 20, which is not aligned to the size of + * struct srp_direct_buf. The whole structure must be packed to avoid + * having the 20-byte structure padded to 24 bytes on 64-bit architectures. + */ +struct srp_indirect_buf { + struct srp_direct_buf table_desc; + uint32_t len; + struct srp_direct_buf desc_list[0]; +} __attribute__((packed)); + +enum { + SRP_MULTICHAN_SINGLE = 0, + SRP_MULTICHAN_MULTI = 1 +}; + +struct srp_login_req { + uint8_t opcode; + uint8_t reserved1[7]; + uint64_t tag; + uint32_t req_it_iu_len; + uint8_t reserved2[4]; + uint16_t req_buf_fmt; + uint8_t req_flags; + uint8_t reserved3[5]; + uint8_t initiator_port_id[16]; + uint8_t target_port_id[16]; +}; + +/* + * The SRP spec defines the size of the LOGIN_RSP structure to be 52 + * bytes, so it needs to be packed to avoid having it padded to 56 + * bytes on 64-bit architectures. + */ +struct srp_login_rsp { + uint8_t opcode; + uint8_t reserved1[3]; + uint32_t req_lim_delta; + uint64_t tag; + uint32_t max_it_iu_len; + uint32_t max_ti_iu_len; + uint16_t buf_fmt; + uint8_t rsp_flags; + uint8_t reserved2[25]; +} __attribute__((packed)); + +struct srp_login_rej { + uint8_t opcode; + uint8_t reserved1[3]; + uint32_t reason; + uint64_t tag; + uint8_t reserved2[8]; + uint16_t buf_fmt; + uint8_t reserved3[6]; +}; + +struct srp_i_logout { + uint8_t opcode; + uint8_t reserved[7]; + uint64_t tag; +}; + +struct srp_t_logout { + uint8_t opcode; + uint8_t sol_not; + uint8_t reserved[2]; + uint32_t reason; + uint64_t tag; +}; + +/* + * We need the packed attribute because the SRP spec only aligns the + * 8-byte LUN field to 4 bytes. + */ +struct srp_tsk_mgmt { + uint8_t opcode; + uint8_t sol_not; + uint8_t reserved1[6]; + uint64_t tag; + uint8_t reserved2[4]; + uint64_t lun __attribute__((packed)); + uint8_t reserved3[2]; + uint8_t tsk_mgmt_func; + uint8_t reserved4; + uint64_t task_tag; + uint8_t reserved5[8]; +}; + +/* + * We need the packed attribute because the SRP spec only aligns the + * 8-byte LUN field to 4 bytes. + */ +struct srp_cmd { + uint8_t opcode; + uint8_t sol_not; + uint8_t reserved1[3]; + uint8_t buf_fmt; + uint8_t data_out_desc_cnt; + uint8_t data_in_desc_cnt; + uint64_t tag; + uint8_t reserved2[4]; + uint64_t lun __attribute__((packed)); + uint8_t reserved3; + uint8_t task_attr; + uint8_t reserved4; + uint8_t add_cdb_len; + uint8_t cdb[16]; + uint8_t add_data[0]; +}; + +enum { + SRP_RSP_FLAG_RSPVALID = 1 << 0, + SRP_RSP_FLAG_SNSVALID = 1 << 1, + SRP_RSP_FLAG_DOOVER = 1 << 2, + SRP_RSP_FLAG_DOUNDER = 1 << 3, + SRP_RSP_FLAG_DIOVER = 1 << 4, + SRP_RSP_FLAG_DIUNDER = 1 << 5 +}; + +/* + * The SRP spec defines the size of the RSP structure to be 36 bytes, + * so it needs to be packed to avoid having it padded to 40 bytes on + * 64-bit architectures. + */ +struct srp_rsp { + uint8_t opcode; + uint8_t sol_not; + uint8_t reserved1[2]; + uint32_t req_lim_delta; + uint64_t tag; + uint8_t reserved2[2]; + uint8_t flags; + uint8_t status; + uint32_t data_out_res_cnt; + uint32_t data_in_res_cnt; + uint32_t sense_data_len; + uint32_t resp_data_len; + uint8_t data[0]; +} __attribute__((packed)); + +#endif /* SCSI_SRP_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/ssd0303.c qemu-kvm-0.15.0+noroms/hw/ssd0303.c --- qemu-kvm-0.14.1+noroms/hw/ssd0303.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ssd0303.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only @@ -93,7 +93,7 @@ DPRINTF("cmd 0x%02x\n", data); s->mode = SSD0303_IDLE; switch (data) { - case 0x00 ... 0x0f: /* Set lower colum address. */ + case 0x00 ... 0x0f: /* Set lower column address. */ s->col = (s->col & 0xf0) | (data & 0xf); break; case 0x10 ... 0x20: /* Set higher column address. */ diff -Nru qemu-kvm-0.14.1+noroms/hw/ssd0323.c qemu-kvm-0.15.0+noroms/hw/ssd0323.c --- qemu-kvm-0.14.1+noroms/hw/ssd0323.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ssd0323.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only diff -Nru qemu-kvm-0.14.1+noroms/hw/ssi.c qemu-kvm-0.15.0+noroms/hw/ssi.c --- qemu-kvm-0.14.1+noroms/hw/ssi.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ssi.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "ssi.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/ssi-sd.c qemu-kvm-0.15.0+noroms/hw/ssi-sd.c --- qemu-kvm-0.14.1+noroms/hw/ssi-sd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/ssi-sd.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "blockdev.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/stellaris.c qemu-kvm-0.15.0+noroms/hw/stellaris.c --- qemu-kvm-0.14.1+noroms/hw/stellaris.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/stellaris.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -14,7 +14,6 @@ #include "qemu-timer.h" #include "i2c.h" #include "net.h" -#include "sysemu.h" #include "boards.h" #define GPIO_A 0 @@ -79,7 +78,7 @@ { int64_t tick; if (reset) - tick = qemu_get_clock(vm_clock); + tick = qemu_get_clock_ns(vm_clock); else tick = s->tick[n]; @@ -280,64 +279,28 @@ gptm_write }; -static void gptm_save(QEMUFile *f, void *opaque) -{ - gptm_state *s = (gptm_state *)opaque; - - qemu_put_be32(f, s->config); - qemu_put_be32(f, s->mode[0]); - qemu_put_be32(f, s->mode[1]); - qemu_put_be32(f, s->control); - qemu_put_be32(f, s->state); - qemu_put_be32(f, s->mask); - qemu_put_be32(f, s->mode[0]); - qemu_put_be32(f, s->mode[0]); - qemu_put_be32(f, s->load[0]); - qemu_put_be32(f, s->load[1]); - qemu_put_be32(f, s->match[0]); - qemu_put_be32(f, s->match[1]); - qemu_put_be32(f, s->prescale[0]); - qemu_put_be32(f, s->prescale[1]); - qemu_put_be32(f, s->match_prescale[0]); - qemu_put_be32(f, s->match_prescale[1]); - qemu_put_be32(f, s->rtc); - qemu_put_be64(f, s->tick[0]); - qemu_put_be64(f, s->tick[1]); - qemu_put_timer(f, s->timer[0]); - qemu_put_timer(f, s->timer[1]); -} - -static int gptm_load(QEMUFile *f, void *opaque, int version_id) -{ - gptm_state *s = (gptm_state *)opaque; - - if (version_id != 1) - return -EINVAL; - - s->config = qemu_get_be32(f); - s->mode[0] = qemu_get_be32(f); - s->mode[1] = qemu_get_be32(f); - s->control = qemu_get_be32(f); - s->state = qemu_get_be32(f); - s->mask = qemu_get_be32(f); - s->mode[0] = qemu_get_be32(f); - s->mode[0] = qemu_get_be32(f); - s->load[0] = qemu_get_be32(f); - s->load[1] = qemu_get_be32(f); - s->match[0] = qemu_get_be32(f); - s->match[1] = qemu_get_be32(f); - s->prescale[0] = qemu_get_be32(f); - s->prescale[1] = qemu_get_be32(f); - s->match_prescale[0] = qemu_get_be32(f); - s->match_prescale[1] = qemu_get_be32(f); - s->rtc = qemu_get_be32(f); - s->tick[0] = qemu_get_be64(f); - s->tick[1] = qemu_get_be64(f); - qemu_get_timer(f, s->timer[0]); - qemu_get_timer(f, s->timer[1]); - - return 0; -} +static const VMStateDescription vmstate_stellaris_gptm = { + .name = "stellaris_gptm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(config, gptm_state), + VMSTATE_UINT32_ARRAY(mode, gptm_state, 2), + VMSTATE_UINT32(control, gptm_state), + VMSTATE_UINT32(state, gptm_state), + VMSTATE_UINT32(mask, gptm_state), + VMSTATE_UNUSED(8), + VMSTATE_UINT32_ARRAY(load, gptm_state, 2), + VMSTATE_UINT32_ARRAY(match, gptm_state, 2), + VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2), + VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2), + VMSTATE_UINT32(rtc, gptm_state), + VMSTATE_INT64_ARRAY(tick, gptm_state, 2), + VMSTATE_TIMER_ARRAY(timer, gptm_state, 2), + VMSTATE_END_OF_LIST() + } +}; static int stellaris_gptm_init(SysBusDevice *dev) { @@ -353,10 +316,9 @@ 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(&dev->qdev, "stellaris_gptm", -1, 1, - gptm_save, gptm_load, s); + s->timer[0] = qemu_new_timer_ns(vm_clock, gptm_tick, &s->opaque[0]); + s->timer[1] = qemu_new_timer_ns(vm_clock, gptm_tick, &s->opaque[1]); + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_gptm, s); return 0; } @@ -605,58 +567,37 @@ s->dcgc[0] = 1; } -static void ssys_save(QEMUFile *f, void *opaque) +static int stellaris_sys_post_load(void *opaque, int version_id) { - ssys_state *s = (ssys_state *)opaque; + ssys_state *s = opaque; - qemu_put_be32(f, s->pborctl); - qemu_put_be32(f, s->ldopctl); - qemu_put_be32(f, s->int_mask); - qemu_put_be32(f, s->int_status); - qemu_put_be32(f, s->resc); - qemu_put_be32(f, s->rcc); - qemu_put_be32(f, s->rcgc[0]); - qemu_put_be32(f, s->rcgc[1]); - qemu_put_be32(f, s->rcgc[2]); - qemu_put_be32(f, s->scgc[0]); - qemu_put_be32(f, s->scgc[1]); - qemu_put_be32(f, s->scgc[2]); - qemu_put_be32(f, s->dcgc[0]); - qemu_put_be32(f, s->dcgc[1]); - qemu_put_be32(f, s->dcgc[2]); - qemu_put_be32(f, s->clkvclr); - qemu_put_be32(f, s->ldoarst); -} - -static int ssys_load(QEMUFile *f, void *opaque, int version_id) -{ - ssys_state *s = (ssys_state *)opaque; - - if (version_id != 1) - return -EINVAL; - - s->pborctl = qemu_get_be32(f); - s->ldopctl = qemu_get_be32(f); - s->int_mask = qemu_get_be32(f); - s->int_status = qemu_get_be32(f); - s->resc = qemu_get_be32(f); - s->rcc = qemu_get_be32(f); - s->rcgc[0] = qemu_get_be32(f); - s->rcgc[1] = qemu_get_be32(f); - s->rcgc[2] = qemu_get_be32(f); - s->scgc[0] = qemu_get_be32(f); - s->scgc[1] = qemu_get_be32(f); - s->scgc[2] = qemu_get_be32(f); - s->dcgc[0] = qemu_get_be32(f); - s->dcgc[1] = qemu_get_be32(f); - s->dcgc[2] = qemu_get_be32(f); - s->clkvclr = qemu_get_be32(f); - s->ldoarst = qemu_get_be32(f); ssys_calculate_system_clock(s); return 0; } +static const VMStateDescription vmstate_stellaris_sys = { + .name = "stellaris_sys", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = stellaris_sys_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(pborctl, ssys_state), + VMSTATE_UINT32(ldopctl, ssys_state), + VMSTATE_UINT32(int_mask, ssys_state), + VMSTATE_UINT32(int_status, ssys_state), + VMSTATE_UINT32(resc, ssys_state), + VMSTATE_UINT32(rcc, ssys_state), + VMSTATE_UINT32_ARRAY(rcgc, ssys_state, 3), + VMSTATE_UINT32_ARRAY(scgc, ssys_state, 3), + VMSTATE_UINT32_ARRAY(dcgc, ssys_state, 3), + VMSTATE_UINT32(clkvclr, ssys_state), + VMSTATE_UINT32(ldoarst, ssys_state), + VMSTATE_END_OF_LIST() + } +}; + static int stellaris_sys_init(uint32_t base, qemu_irq irq, stellaris_board_info * board, uint8_t *macaddr) @@ -676,7 +617,7 @@ DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00001000, iomemtype); ssys_reset(s); - register_savevm(NULL, "stellaris_sys", -1, 1, ssys_save, ssys_load, s); + vmstate_register(NULL, -1, &vmstate_stellaris_sys, s); return 0; } @@ -844,36 +785,22 @@ stellaris_i2c_write }; -static void stellaris_i2c_save(QEMUFile *f, void *opaque) -{ - stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; - - qemu_put_be32(f, s->msa); - qemu_put_be32(f, s->mcs); - qemu_put_be32(f, s->mdr); - qemu_put_be32(f, s->mtpr); - qemu_put_be32(f, s->mimr); - qemu_put_be32(f, s->mris); - qemu_put_be32(f, s->mcr); -} - -static int stellaris_i2c_load(QEMUFile *f, void *opaque, int version_id) -{ - stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; - - if (version_id != 1) - return -EINVAL; - - s->msa = qemu_get_be32(f); - s->mcs = qemu_get_be32(f); - s->mdr = qemu_get_be32(f); - s->mtpr = qemu_get_be32(f); - s->mimr = qemu_get_be32(f); - s->mris = qemu_get_be32(f); - s->mcr = qemu_get_be32(f); - - return 0; -} +static const VMStateDescription vmstate_stellaris_i2c = { + .name = "stellaris_i2c", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(msa, stellaris_i2c_state), + VMSTATE_UINT32(mcs, stellaris_i2c_state), + VMSTATE_UINT32(mdr, stellaris_i2c_state), + VMSTATE_UINT32(mtpr, stellaris_i2c_state), + VMSTATE_UINT32(mimr, stellaris_i2c_state), + VMSTATE_UINT32(mris, stellaris_i2c_state), + VMSTATE_UINT32(mcr, stellaris_i2c_state), + VMSTATE_END_OF_LIST() + } +}; static int stellaris_i2c_init(SysBusDevice * dev) { @@ -891,8 +818,7 @@ sysbus_init_mmio(dev, 0x1000, iomemtype); /* ??? For now we only implement the master interface. */ stellaris_i2c_reset(s); - register_savevm(&dev->qdev, "stellaris_i2c", -1, 1, - stellaris_i2c_save, stellaris_i2c_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_i2c, s); return 0; } @@ -1130,60 +1056,40 @@ stellaris_adc_write }; -static void stellaris_adc_save(QEMUFile *f, void *opaque) -{ - stellaris_adc_state *s = (stellaris_adc_state *)opaque; - int i; - int j; - - qemu_put_be32(f, s->actss); - qemu_put_be32(f, s->ris); - qemu_put_be32(f, s->im); - qemu_put_be32(f, s->emux); - qemu_put_be32(f, s->ostat); - qemu_put_be32(f, s->ustat); - qemu_put_be32(f, s->sspri); - qemu_put_be32(f, s->sac); - for (i = 0; i < 4; i++) { - qemu_put_be32(f, s->fifo[i].state); - for (j = 0; j < 16; j++) { - qemu_put_be32(f, s->fifo[i].data[j]); - } - qemu_put_be32(f, s->ssmux[i]); - qemu_put_be32(f, s->ssctl[i]); - } - qemu_put_be32(f, s->noise); -} - -static int stellaris_adc_load(QEMUFile *f, void *opaque, int version_id) -{ - stellaris_adc_state *s = (stellaris_adc_state *)opaque; - int i; - int j; - - if (version_id != 1) - return -EINVAL; - - s->actss = qemu_get_be32(f); - s->ris = qemu_get_be32(f); - s->im = qemu_get_be32(f); - s->emux = qemu_get_be32(f); - s->ostat = qemu_get_be32(f); - s->ustat = qemu_get_be32(f); - s->sspri = qemu_get_be32(f); - s->sac = qemu_get_be32(f); - for (i = 0; i < 4; i++) { - s->fifo[i].state = qemu_get_be32(f); - for (j = 0; j < 16; j++) { - s->fifo[i].data[j] = qemu_get_be32(f); - } - s->ssmux[i] = qemu_get_be32(f); - s->ssctl[i] = qemu_get_be32(f); +static const VMStateDescription vmstate_stellaris_adc = { + .name = "stellaris_adc", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(actss, stellaris_adc_state), + VMSTATE_UINT32(ris, stellaris_adc_state), + VMSTATE_UINT32(im, stellaris_adc_state), + VMSTATE_UINT32(emux, stellaris_adc_state), + VMSTATE_UINT32(ostat, stellaris_adc_state), + VMSTATE_UINT32(ustat, stellaris_adc_state), + VMSTATE_UINT32(sspri, stellaris_adc_state), + VMSTATE_UINT32(sac, stellaris_adc_state), + VMSTATE_UINT32(fifo[0].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[0].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[0], stellaris_adc_state), + VMSTATE_UINT32(ssctl[0], stellaris_adc_state), + VMSTATE_UINT32(fifo[1].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[1].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[1], stellaris_adc_state), + VMSTATE_UINT32(ssctl[1], stellaris_adc_state), + VMSTATE_UINT32(fifo[2].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[2].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[2], stellaris_adc_state), + VMSTATE_UINT32(ssctl[2], stellaris_adc_state), + VMSTATE_UINT32(fifo[3].state, stellaris_adc_state), + VMSTATE_UINT32_ARRAY(fifo[3].data, stellaris_adc_state, 16), + VMSTATE_UINT32(ssmux[3], stellaris_adc_state), + VMSTATE_UINT32(ssctl[3], stellaris_adc_state), + VMSTATE_UINT32(noise, stellaris_adc_state), + VMSTATE_END_OF_LIST() } - s->noise = qemu_get_be32(f); - - return 0; -} +}; static int stellaris_adc_init(SysBusDevice *dev) { @@ -1201,8 +1107,7 @@ sysbus_init_mmio(dev, 0x1000, iomemtype); stellaris_adc_reset(s); qdev_init_gpio_in(&dev->qdev, stellaris_adc_trigger, 1); - register_savevm(&dev->qdev, "stellaris_adc", -1, 1, - stellaris_adc_save, stellaris_adc_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_adc, s); return 0; } @@ -1234,24 +1139,16 @@ return ssi_transfer(s->bus[s->current_dev], val); } -static void stellaris_ssi_bus_save(QEMUFile *f, void *opaque) -{ - stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque; - - qemu_put_be32(f, s->current_dev); -} - -static int stellaris_ssi_bus_load(QEMUFile *f, void *opaque, int version_id) -{ - stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque; - - if (version_id != 1) - return -EINVAL; - - s->current_dev = qemu_get_be32(f); - - return 0; -} +static const VMStateDescription vmstate_stellaris_ssi_bus = { + .name = "stellaris_ssi_bus", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(current_dev, stellaris_ssi_bus_state), + VMSTATE_END_OF_LIST() + } +}; static int stellaris_ssi_bus_init(SSISlave *dev) { @@ -1261,8 +1158,7 @@ s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1"); qdev_init_gpio_in(&dev->qdev, stellaris_ssi_bus_select, 1); - register_savevm(&dev->qdev, "stellaris_ssi_bus", -1, 1, - stellaris_ssi_bus_save, stellaris_ssi_bus_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_stellaris_ssi_bus, s); return 0; } @@ -1334,11 +1230,11 @@ } } - stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr); + stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a); for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { - gpio_dev[i] = sysbus_create_simple("pl061", gpio_addr[i], + gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i], pic[gpio_irq[i]]); for (j = 0; j < 8; j++) { gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j); diff -Nru qemu-kvm-0.14.1+noroms/hw/stellaris_enet.c qemu-kvm-0.15.0+noroms/hw/stellaris_enet.c --- qemu-kvm-0.14.1+noroms/hw/stellaris_enet.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/stellaris_enet.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" #include "net.h" diff -Nru qemu-kvm-0.14.1+noroms/hw/stellaris_input.c qemu-kvm-0.15.0+noroms/hw/stellaris_input.c --- qemu-kvm-0.14.1+noroms/hw/stellaris_input.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/stellaris_input.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" #include "devices.h" @@ -13,7 +13,7 @@ typedef struct { qemu_irq irq; int keycode; - int pressed; + uint8_t pressed; } gamepad_button; typedef struct { @@ -47,30 +47,29 @@ s->extension = 0; } -static void stellaris_gamepad_save(QEMUFile *f, void *opaque) -{ - gamepad_state *s = (gamepad_state *)opaque; - int i; - - qemu_put_be32(f, s->extension); - for (i = 0; i < s->num_buttons; i++) - qemu_put_byte(f, s->buttons[i].pressed); -} - -static int stellaris_gamepad_load(QEMUFile *f, void *opaque, int version_id) -{ - gamepad_state *s = (gamepad_state *)opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - s->extension = qemu_get_be32(f); - for (i = 0; i < s->num_buttons; i++) - s->buttons[i].pressed = qemu_get_byte(f); +static const VMStateDescription vmstate_stellaris_button = { + .name = "stellaris_button", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(pressed, gamepad_button), + VMSTATE_END_OF_LIST() + } +}; - return 0; -} +static const VMStateDescription vmstate_stellaris_gamepad = { + .name = "stellaris_gamepad", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(extension, gamepad_state), + VMSTATE_STRUCT_VARRAY_INT32(buttons, gamepad_state, num_buttons, 0, + vmstate_stellaris_button, gamepad_button), + VMSTATE_END_OF_LIST() + } +}; /* Returns an array 5 ouput slots. */ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) @@ -86,6 +85,5 @@ } s->num_buttons = n; qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); - register_savevm(NULL, "stellaris_gamepad", -1, 1, - stellaris_gamepad_save, stellaris_gamepad_load, s); + vmstate_register(NULL, -1, &vmstate_stellaris_gamepad, s); } diff -Nru qemu-kvm-0.14.1+noroms/hw/strongarm.c qemu-kvm-0.15.0+noroms/hw/strongarm.c --- qemu-kvm-0.14.1+noroms/hw/strongarm.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/strongarm.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,1598 @@ +/* + * StrongARM SA-1100/SA-1110 emulation + * + * Copyright (C) 2011 Dmitry Eremin-Solenikov + * + * Largely based on StrongARM emulation: + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * UART code based on QEMU 16550A UART emulation + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix Systems, Inc. + * + * 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, see . + */ +#include "sysbus.h" +#include "strongarm.h" +#include "qemu-error.h" +#include "arm-misc.h" +#include "sysemu.h" +#include "ssi.h" + +//#define DEBUG + +/* + TODO + - Implement cp15, c14 ? + - Implement cp15, c15 !!! (idle used in L) + - Implement idle mode handling/DIM + - Implement sleep mode/Wake sources + - Implement reset control + - Implement memory control regs + - PCMCIA handling + - Maybe support MBGNT/MBREQ + - DMA channels + - GPCLK + - IrDA + - MCP + - Enhance UART with modem signals + */ + +#ifdef DEBUG +# define DPRINTF(format, ...) printf(format , ## __VA_ARGS__) +#else +# define DPRINTF(format, ...) do { } while (0) +#endif + +static struct { + target_phys_addr_t io_base; + int irq; +} sa_serial[] = { + { 0x80010000, SA_PIC_UART1 }, + { 0x80030000, SA_PIC_UART2 }, + { 0x80050000, SA_PIC_UART3 }, + { 0, 0 } +}; + +/* Interrupt Controller */ +typedef struct { + SysBusDevice busdev; + qemu_irq irq; + qemu_irq fiq; + + uint32_t pending; + uint32_t enabled; + uint32_t is_fiq; + uint32_t int_idle; +} StrongARMPICState; + +#define ICIP 0x00 +#define ICMR 0x04 +#define ICLR 0x08 +#define ICFP 0x10 +#define ICPR 0x20 +#define ICCR 0x0c + +#define SA_PIC_SRCS 32 + + +static void strongarm_pic_update(void *opaque) +{ + StrongARMPICState *s = opaque; + + /* FIXME: reflect DIM */ + qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq); + qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq); +} + +static void strongarm_pic_set_irq(void *opaque, int irq, int level) +{ + StrongARMPICState *s = opaque; + + if (level) { + s->pending |= 1 << irq; + } else { + s->pending &= ~(1 << irq); + } + + strongarm_pic_update(s); +} + +static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset) +{ + StrongARMPICState *s = opaque; + + switch (offset) { + case ICIP: + return s->pending & ~s->is_fiq & s->enabled; + case ICMR: + return s->enabled; + case ICLR: + return s->is_fiq; + case ICCR: + return s->int_idle == 0; + case ICFP: + return s->pending & s->is_fiq & s->enabled; + case ICPR: + return s->pending; + default: + printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n", + __func__, offset); + return 0; + } +} + +static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + StrongARMPICState *s = opaque; + + switch (offset) { + case ICMR: + s->enabled = value; + break; + case ICLR: + s->is_fiq = value; + break; + case ICCR: + s->int_idle = (value & 1) ? 0 : ~0; + break; + default: + printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n", + __func__, offset); + break; + } + strongarm_pic_update(s); +} + +static CPUReadMemoryFunc * const strongarm_pic_readfn[] = { + strongarm_pic_mem_read, + strongarm_pic_mem_read, + strongarm_pic_mem_read, +}; + +static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = { + strongarm_pic_mem_write, + strongarm_pic_mem_write, + strongarm_pic_mem_write, +}; + +static int strongarm_pic_initfn(SysBusDevice *dev) +{ + StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev); + int iomemtype; + + qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS); + iomemtype = cpu_register_io_memory(strongarm_pic_readfn, + strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x1000, iomemtype); + sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(dev, &s->fiq); + + return 0; +} + +static int strongarm_pic_post_load(void *opaque, int version_id) +{ + strongarm_pic_update(opaque); + return 0; +} + +static VMStateDescription vmstate_strongarm_pic_regs = { + .name = "strongarm_pic", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = strongarm_pic_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(pending, StrongARMPICState), + VMSTATE_UINT32(enabled, StrongARMPICState), + VMSTATE_UINT32(is_fiq, StrongARMPICState), + VMSTATE_UINT32(int_idle, StrongARMPICState), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo strongarm_pic_info = { + .init = strongarm_pic_initfn, + .qdev.name = "strongarm_pic", + .qdev.desc = "StrongARM PIC", + .qdev.size = sizeof(StrongARMPICState), + .qdev.vmsd = &vmstate_strongarm_pic_regs, +}; + +/* Real-Time Clock */ +#define RTAR 0x00 /* RTC Alarm register */ +#define RCNR 0x04 /* RTC Counter register */ +#define RTTR 0x08 /* RTC Timer Trim register */ +#define RTSR 0x10 /* RTC Status register */ + +#define RTSR_AL (1 << 0) /* RTC Alarm detected */ +#define RTSR_HZ (1 << 1) /* RTC 1Hz detected */ +#define RTSR_ALE (1 << 2) /* RTC Alarm enable */ +#define RTSR_HZE (1 << 3) /* RTC 1Hz enable */ + +/* 16 LSB of RTTR are clockdiv for internal trim logic, + * trim delete isn't emulated, so + * f = 32 768 / (RTTR_trim + 1) */ + +typedef struct { + SysBusDevice busdev; + uint32_t rttr; + uint32_t rtsr; + uint32_t rtar; + uint32_t last_rcnr; + int64_t last_hz; + QEMUTimer *rtc_alarm; + QEMUTimer *rtc_hz; + qemu_irq rtc_irq; + qemu_irq rtc_hz_irq; +} StrongARMRTCState; + +static inline void strongarm_rtc_int_update(StrongARMRTCState *s) +{ + qemu_set_irq(s->rtc_irq, s->rtsr & RTSR_AL); + qemu_set_irq(s->rtc_hz_irq, s->rtsr & RTSR_HZ); +} + +static void strongarm_rtc_hzupdate(StrongARMRTCState *s) +{ + int64_t rt = qemu_get_clock_ms(rt_clock); + s->last_rcnr += ((rt - s->last_hz) << 15) / + (1000 * ((s->rttr & 0xffff) + 1)); + s->last_hz = rt; +} + +static inline void strongarm_rtc_timer_update(StrongARMRTCState *s) +{ + if ((s->rtsr & RTSR_HZE) && !(s->rtsr & RTSR_HZ)) { + qemu_mod_timer(s->rtc_hz, s->last_hz + 1000); + } else { + qemu_del_timer(s->rtc_hz); + } + + if ((s->rtsr & RTSR_ALE) && !(s->rtsr & RTSR_AL)) { + qemu_mod_timer(s->rtc_alarm, s->last_hz + + (((s->rtar - s->last_rcnr) * 1000 * + ((s->rttr & 0xffff) + 1)) >> 15)); + } else { + qemu_del_timer(s->rtc_alarm); + } +} + +static inline void strongarm_rtc_alarm_tick(void *opaque) +{ + StrongARMRTCState *s = opaque; + s->rtsr |= RTSR_AL; + strongarm_rtc_timer_update(s); + strongarm_rtc_int_update(s); +} + +static inline void strongarm_rtc_hz_tick(void *opaque) +{ + StrongARMRTCState *s = opaque; + s->rtsr |= RTSR_HZ; + strongarm_rtc_timer_update(s); + strongarm_rtc_int_update(s); +} + +static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr) +{ + StrongARMRTCState *s = opaque; + + switch (addr) { + case RTTR: + return s->rttr; + case RTSR: + return s->rtsr; + case RTAR: + return s->rtar; + case RCNR: + return s->last_rcnr + + ((qemu_get_clock_ms(rt_clock) - s->last_hz) << 15) / + (1000 * ((s->rttr & 0xffff) + 1)); + default: + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + return 0; + } +} + +static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + StrongARMRTCState *s = opaque; + uint32_t old_rtsr; + + switch (addr) { + case RTTR: + strongarm_rtc_hzupdate(s); + s->rttr = value; + strongarm_rtc_timer_update(s); + break; + + case RTSR: + old_rtsr = s->rtsr; + s->rtsr = (value & (RTSR_ALE | RTSR_HZE)) | + (s->rtsr & ~(value & (RTSR_AL | RTSR_HZ))); + + if (s->rtsr != old_rtsr) { + strongarm_rtc_timer_update(s); + } + + strongarm_rtc_int_update(s); + break; + + case RTAR: + s->rtar = value; + strongarm_rtc_timer_update(s); + break; + + case RCNR: + strongarm_rtc_hzupdate(s); + s->last_rcnr = value; + strongarm_rtc_timer_update(s); + break; + + default: + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + } +} + +static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = { + strongarm_rtc_read, + strongarm_rtc_read, + strongarm_rtc_read, +}; + +static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = { + strongarm_rtc_write, + strongarm_rtc_write, + strongarm_rtc_write, +}; + +static int strongarm_rtc_init(SysBusDevice *dev) +{ + StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev); + struct tm tm; + int iomemtype; + + s->rttr = 0x0; + s->rtsr = 0; + + qemu_get_timedate(&tm, 0); + + s->last_rcnr = (uint32_t) mktimegm(&tm); + s->last_hz = qemu_get_clock_ms(rt_clock); + + s->rtc_alarm = qemu_new_timer_ms(rt_clock, strongarm_rtc_alarm_tick, s); + s->rtc_hz = qemu_new_timer_ms(rt_clock, strongarm_rtc_hz_tick, s); + + sysbus_init_irq(dev, &s->rtc_irq); + sysbus_init_irq(dev, &s->rtc_hz_irq); + + iomemtype = cpu_register_io_memory(strongarm_rtc_readfn, + strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x10000, iomemtype); + + return 0; +} + +static void strongarm_rtc_pre_save(void *opaque) +{ + StrongARMRTCState *s = opaque; + + strongarm_rtc_hzupdate(s); +} + +static int strongarm_rtc_post_load(void *opaque, int version_id) +{ + StrongARMRTCState *s = opaque; + + strongarm_rtc_timer_update(s); + strongarm_rtc_int_update(s); + + return 0; +} + +static const VMStateDescription vmstate_strongarm_rtc_regs = { + .name = "strongarm-rtc", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .pre_save = strongarm_rtc_pre_save, + .post_load = strongarm_rtc_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(rttr, StrongARMRTCState), + VMSTATE_UINT32(rtsr, StrongARMRTCState), + VMSTATE_UINT32(rtar, StrongARMRTCState), + VMSTATE_UINT32(last_rcnr, StrongARMRTCState), + VMSTATE_INT64(last_hz, StrongARMRTCState), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo strongarm_rtc_sysbus_info = { + .init = strongarm_rtc_init, + .qdev.name = "strongarm-rtc", + .qdev.desc = "StrongARM RTC Controller", + .qdev.size = sizeof(StrongARMRTCState), + .qdev.vmsd = &vmstate_strongarm_rtc_regs, +}; + +/* GPIO */ +#define GPLR 0x00 +#define GPDR 0x04 +#define GPSR 0x08 +#define GPCR 0x0c +#define GRER 0x10 +#define GFER 0x14 +#define GEDR 0x18 +#define GAFR 0x1c + +typedef struct StrongARMGPIOInfo StrongARMGPIOInfo; +struct StrongARMGPIOInfo { + SysBusDevice busdev; + qemu_irq handler[28]; + qemu_irq irqs[11]; + qemu_irq irqX; + + uint32_t ilevel; + uint32_t olevel; + uint32_t dir; + uint32_t rising; + uint32_t falling; + uint32_t status; + uint32_t gpsr; + uint32_t gafr; + + uint32_t prev_level; +}; + + +static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s) +{ + int i; + for (i = 0; i < 11; i++) { + qemu_set_irq(s->irqs[i], s->status & (1 << i)); + } + + qemu_set_irq(s->irqX, (s->status & ~0x7ff)); +} + +static void strongarm_gpio_set(void *opaque, int line, int level) +{ + StrongARMGPIOInfo *s = opaque; + uint32_t mask; + + mask = 1 << line; + + if (level) { + s->status |= s->rising & mask & + ~s->ilevel & ~s->dir; + s->ilevel |= mask; + } else { + s->status |= s->falling & mask & + s->ilevel & ~s->dir; + s->ilevel &= ~mask; + } + + if (s->status & mask) { + strongarm_gpio_irq_update(s); + } +} + +static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s) +{ + uint32_t level, diff; + int bit; + + level = s->olevel & s->dir; + + for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { + bit = ffs(diff) - 1; + qemu_set_irq(s->handler[bit], (level >> bit) & 1); + } + + s->prev_level = level; +} + +static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset) +{ + StrongARMGPIOInfo *s = opaque; + + switch (offset) { + case GPDR: /* GPIO Pin-Direction registers */ + return s->dir; + + case GPSR: /* GPIO Pin-Output Set registers */ + DPRINTF("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n", + __func__, offset); + return s->gpsr; /* Return last written value. */ + + case GPCR: /* GPIO Pin-Output Clear registers */ + DPRINTF("%s: Read from a write-only register 0x" TARGET_FMT_plx "\n", + __func__, offset); + return 31337; /* Specified as unpredictable in the docs. */ + + case GRER: /* GPIO Rising-Edge Detect Enable registers */ + return s->rising; + + case GFER: /* GPIO Falling-Edge Detect Enable registers */ + return s->falling; + + case GAFR: /* GPIO Alternate Function registers */ + return s->gafr; + + case GPLR: /* GPIO Pin-Level registers */ + return (s->olevel & s->dir) | + (s->ilevel & ~s->dir); + + case GEDR: /* GPIO Edge Detect Status registers */ + return s->status; + + default: + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + } + + return 0; +} + +static void strongarm_gpio_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + StrongARMGPIOInfo *s = opaque; + + switch (offset) { + case GPDR: /* GPIO Pin-Direction registers */ + s->dir = value; + strongarm_gpio_handler_update(s); + break; + + case GPSR: /* GPIO Pin-Output Set registers */ + s->olevel |= value; + strongarm_gpio_handler_update(s); + s->gpsr = value; + break; + + case GPCR: /* GPIO Pin-Output Clear registers */ + s->olevel &= ~value; + strongarm_gpio_handler_update(s); + break; + + case GRER: /* GPIO Rising-Edge Detect Enable registers */ + s->rising = value; + break; + + case GFER: /* GPIO Falling-Edge Detect Enable registers */ + s->falling = value; + break; + + case GAFR: /* GPIO Alternate Function registers */ + s->gafr = value; + break; + + case GEDR: /* GPIO Edge Detect Status registers */ + s->status &= ~value; + strongarm_gpio_irq_update(s); + break; + + default: + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + } +} + +static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = { + strongarm_gpio_read, + strongarm_gpio_read, + strongarm_gpio_read +}; + +static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = { + strongarm_gpio_write, + strongarm_gpio_write, + strongarm_gpio_write +}; + +static DeviceState *strongarm_gpio_init(target_phys_addr_t base, + DeviceState *pic) +{ + DeviceState *dev; + int i; + + dev = qdev_create(NULL, "strongarm-gpio"); + qdev_init_nofail(dev); + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + for (i = 0; i < 12; i++) + sysbus_connect_irq(sysbus_from_qdev(dev), i, + qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i)); + + return dev; +} + +static int strongarm_gpio_initfn(SysBusDevice *dev) +{ + int iomemtype; + StrongARMGPIOInfo *s; + int i; + + s = FROM_SYSBUS(StrongARMGPIOInfo, dev); + + qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28); + qdev_init_gpio_out(&dev->qdev, s->handler, 28); + + iomemtype = cpu_register_io_memory(strongarm_gpio_readfn, + strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); + + sysbus_init_mmio(dev, 0x1000, iomemtype); + for (i = 0; i < 11; i++) { + sysbus_init_irq(dev, &s->irqs[i]); + } + sysbus_init_irq(dev, &s->irqX); + + return 0; +} + +static const VMStateDescription vmstate_strongarm_gpio_regs = { + .name = "strongarm-gpio", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ilevel, StrongARMGPIOInfo), + VMSTATE_UINT32(olevel, StrongARMGPIOInfo), + VMSTATE_UINT32(dir, StrongARMGPIOInfo), + VMSTATE_UINT32(rising, StrongARMGPIOInfo), + VMSTATE_UINT32(falling, StrongARMGPIOInfo), + VMSTATE_UINT32(status, StrongARMGPIOInfo), + VMSTATE_UINT32(gafr, StrongARMGPIOInfo), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo strongarm_gpio_info = { + .init = strongarm_gpio_initfn, + .qdev.name = "strongarm-gpio", + .qdev.desc = "StrongARM GPIO controller", + .qdev.size = sizeof(StrongARMGPIOInfo), +}; + +/* Peripheral Pin Controller */ +#define PPDR 0x00 +#define PPSR 0x04 +#define PPAR 0x08 +#define PSDR 0x0c +#define PPFR 0x10 + +typedef struct StrongARMPPCInfo StrongARMPPCInfo; +struct StrongARMPPCInfo { + SysBusDevice busdev; + qemu_irq handler[28]; + + uint32_t ilevel; + uint32_t olevel; + uint32_t dir; + uint32_t ppar; + uint32_t psdr; + uint32_t ppfr; + + uint32_t prev_level; +}; + +static void strongarm_ppc_set(void *opaque, int line, int level) +{ + StrongARMPPCInfo *s = opaque; + + if (level) { + s->ilevel |= 1 << line; + } else { + s->ilevel &= ~(1 << line); + } +} + +static void strongarm_ppc_handler_update(StrongARMPPCInfo *s) +{ + uint32_t level, diff; + int bit; + + level = s->olevel & s->dir; + + for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { + bit = ffs(diff) - 1; + qemu_set_irq(s->handler[bit], (level >> bit) & 1); + } + + s->prev_level = level; +} + +static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset) +{ + StrongARMPPCInfo *s = opaque; + + switch (offset) { + case PPDR: /* PPC Pin Direction registers */ + return s->dir | ~0x3fffff; + + case PPSR: /* PPC Pin State registers */ + return (s->olevel & s->dir) | + (s->ilevel & ~s->dir) | + ~0x3fffff; + + case PPAR: + return s->ppar | ~0x41000; + + case PSDR: + return s->psdr; + + case PPFR: + return s->ppfr | ~0x7f001; + + default: + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + } + + return 0; +} + +static void strongarm_ppc_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + StrongARMPPCInfo *s = opaque; + + switch (offset) { + case PPDR: /* PPC Pin Direction registers */ + s->dir = value & 0x3fffff; + strongarm_ppc_handler_update(s); + break; + + case PPSR: /* PPC Pin State registers */ + s->olevel = value & s->dir & 0x3fffff; + strongarm_ppc_handler_update(s); + break; + + case PPAR: + s->ppar = value & 0x41000; + break; + + case PSDR: + s->psdr = value & 0x3fffff; + break; + + case PPFR: + s->ppfr = value & 0x7f001; + break; + + default: + printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); + } +} + +static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = { + strongarm_ppc_read, + strongarm_ppc_read, + strongarm_ppc_read +}; + +static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = { + strongarm_ppc_write, + strongarm_ppc_write, + strongarm_ppc_write +}; + +static int strongarm_ppc_init(SysBusDevice *dev) +{ + int iomemtype; + StrongARMPPCInfo *s; + + s = FROM_SYSBUS(StrongARMPPCInfo, dev); + + qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22); + qdev_init_gpio_out(&dev->qdev, s->handler, 22); + + iomemtype = cpu_register_io_memory(strongarm_ppc_readfn, + strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN); + + sysbus_init_mmio(dev, 0x1000, iomemtype); + + return 0; +} + +static const VMStateDescription vmstate_strongarm_ppc_regs = { + .name = "strongarm-ppc", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ilevel, StrongARMPPCInfo), + VMSTATE_UINT32(olevel, StrongARMPPCInfo), + VMSTATE_UINT32(dir, StrongARMPPCInfo), + VMSTATE_UINT32(ppar, StrongARMPPCInfo), + VMSTATE_UINT32(psdr, StrongARMPPCInfo), + VMSTATE_UINT32(ppfr, StrongARMPPCInfo), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo strongarm_ppc_info = { + .init = strongarm_ppc_init, + .qdev.name = "strongarm-ppc", + .qdev.desc = "StrongARM PPC controller", + .qdev.size = sizeof(StrongARMPPCInfo), +}; + +/* UART Ports */ +#define UTCR0 0x00 +#define UTCR1 0x04 +#define UTCR2 0x08 +#define UTCR3 0x0c +#define UTDR 0x14 +#define UTSR0 0x1c +#define UTSR1 0x20 + +#define UTCR0_PE (1 << 0) /* Parity enable */ +#define UTCR0_OES (1 << 1) /* Even parity */ +#define UTCR0_SBS (1 << 2) /* 2 stop bits */ +#define UTCR0_DSS (1 << 3) /* 8-bit data */ + +#define UTCR3_RXE (1 << 0) /* Rx enable */ +#define UTCR3_TXE (1 << 1) /* Tx enable */ +#define UTCR3_BRK (1 << 2) /* Force Break */ +#define UTCR3_RIE (1 << 3) /* Rx int enable */ +#define UTCR3_TIE (1 << 4) /* Tx int enable */ +#define UTCR3_LBM (1 << 5) /* Loopback */ + +#define UTSR0_TFS (1 << 0) /* Tx FIFO nearly empty */ +#define UTSR0_RFS (1 << 1) /* Rx FIFO nearly full */ +#define UTSR0_RID (1 << 2) /* Receiver Idle */ +#define UTSR0_RBB (1 << 3) /* Receiver begin break */ +#define UTSR0_REB (1 << 4) /* Receiver end break */ +#define UTSR0_EIF (1 << 5) /* Error in FIFO */ + +#define UTSR1_RNE (1 << 1) /* Receive FIFO not empty */ +#define UTSR1_TNF (1 << 2) /* Transmit FIFO not full */ +#define UTSR1_PRE (1 << 3) /* Parity error */ +#define UTSR1_FRE (1 << 4) /* Frame error */ +#define UTSR1_ROR (1 << 5) /* Receive Over Run */ + +#define RX_FIFO_PRE (1 << 8) +#define RX_FIFO_FRE (1 << 9) +#define RX_FIFO_ROR (1 << 10) + +typedef struct { + SysBusDevice busdev; + CharDriverState *chr; + qemu_irq irq; + + uint8_t utcr0; + uint16_t brd; + uint8_t utcr3; + uint8_t utsr0; + uint8_t utsr1; + + uint8_t tx_fifo[8]; + uint8_t tx_start; + uint8_t tx_len; + uint16_t rx_fifo[12]; /* value + error flags in high bits */ + uint8_t rx_start; + uint8_t rx_len; + + uint64_t char_transmit_time; /* time to transmit a char in ticks*/ + bool wait_break_end; + QEMUTimer *rx_timeout_timer; + QEMUTimer *tx_timer; +} StrongARMUARTState; + +static void strongarm_uart_update_status(StrongARMUARTState *s) +{ + uint16_t utsr1 = 0; + + if (s->tx_len != 8) { + utsr1 |= UTSR1_TNF; + } + + if (s->rx_len != 0) { + uint16_t ent = s->rx_fifo[s->rx_start]; + + utsr1 |= UTSR1_RNE; + if (ent & RX_FIFO_PRE) { + s->utsr1 |= UTSR1_PRE; + } + if (ent & RX_FIFO_FRE) { + s->utsr1 |= UTSR1_FRE; + } + if (ent & RX_FIFO_ROR) { + s->utsr1 |= UTSR1_ROR; + } + } + + s->utsr1 = utsr1; +} + +static void strongarm_uart_update_int_status(StrongARMUARTState *s) +{ + uint16_t utsr0 = s->utsr0 & + (UTSR0_REB | UTSR0_RBB | UTSR0_RID); + int i; + + if ((s->utcr3 & UTCR3_TXE) && + (s->utcr3 & UTCR3_TIE) && + s->tx_len <= 4) { + utsr0 |= UTSR0_TFS; + } + + if ((s->utcr3 & UTCR3_RXE) && + (s->utcr3 & UTCR3_RIE) && + s->rx_len > 4) { + utsr0 |= UTSR0_RFS; + } + + for (i = 0; i < s->rx_len && i < 4; i++) + if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) { + utsr0 |= UTSR0_EIF; + break; + } + + s->utsr0 = utsr0; + qemu_set_irq(s->irq, utsr0); +} + +static void strongarm_uart_update_parameters(StrongARMUARTState *s) +{ + int speed, parity, data_bits, stop_bits, frame_size; + QEMUSerialSetParams ssp; + + /* Start bit. */ + frame_size = 1; + if (s->utcr0 & UTCR0_PE) { + /* Parity bit. */ + frame_size++; + if (s->utcr0 & UTCR0_OES) { + parity = 'E'; + } else { + parity = 'O'; + } + } else { + parity = 'N'; + } + if (s->utcr0 & UTCR0_SBS) { + stop_bits = 2; + } else { + stop_bits = 1; + } + + data_bits = (s->utcr0 & UTCR0_DSS) ? 8 : 7; + frame_size += data_bits + stop_bits; + speed = 3686400 / 16 / (s->brd + 1); + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size; + if (s->chr) { + qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + } + + DPRINTF(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label, + speed, parity, data_bits, stop_bits); +} + +static void strongarm_uart_rx_to(void *opaque) +{ + StrongARMUARTState *s = opaque; + + if (s->rx_len) { + s->utsr0 |= UTSR0_RID; + strongarm_uart_update_int_status(s); + } +} + +static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c) +{ + if ((s->utcr3 & UTCR3_RXE) == 0) { + /* rx disabled */ + return; + } + + if (s->wait_break_end) { + s->utsr0 |= UTSR0_REB; + s->wait_break_end = false; + } + + if (s->rx_len < 12) { + s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c; + s->rx_len++; + } else + s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR; +} + +static int strongarm_uart_can_receive(void *opaque) +{ + StrongARMUARTState *s = opaque; + + if (s->rx_len == 12) { + return 0; + } + /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */ + if (s->rx_len < 8) { + return 8 - s->rx_len; + } + return 1; +} + +static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + StrongARMUARTState *s = opaque; + int i; + + for (i = 0; i < size; i++) { + strongarm_uart_rx_push(s, buf[i]); + } + + /* call the timeout receive callback in 3 char transmit time */ + qemu_mod_timer(s->rx_timeout_timer, + qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3); + + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); +} + +static void strongarm_uart_event(void *opaque, int event) +{ + StrongARMUARTState *s = opaque; + if (event == CHR_EVENT_BREAK) { + s->utsr0 |= UTSR0_RBB; + strongarm_uart_rx_push(s, RX_FIFO_FRE); + s->wait_break_end = true; + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); + } +} + +static void strongarm_uart_tx(void *opaque) +{ + StrongARMUARTState *s = opaque; + uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock); + + if (s->utcr3 & UTCR3_LBM) /* loopback */ { + strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1); + } else if (s->chr) { + qemu_chr_write(s->chr, &s->tx_fifo[s->tx_start], 1); + } + + s->tx_start = (s->tx_start + 1) % 8; + s->tx_len--; + if (s->tx_len) { + qemu_mod_timer(s->tx_timer, new_xmit_ts + s->char_transmit_time); + } + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); +} + +static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr) +{ + StrongARMUARTState *s = opaque; + uint16_t ret; + + switch (addr) { + case UTCR0: + return s->utcr0; + + case UTCR1: + return s->brd >> 8; + + case UTCR2: + return s->brd & 0xff; + + case UTCR3: + return s->utcr3; + + case UTDR: + if (s->rx_len != 0) { + ret = s->rx_fifo[s->rx_start]; + s->rx_start = (s->rx_start + 1) % 12; + s->rx_len--; + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); + return ret; + } + return 0; + + case UTSR0: + return s->utsr0; + + case UTSR1: + return s->utsr1; + + default: + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + return 0; + } +} + +static void strongarm_uart_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + StrongARMUARTState *s = opaque; + + switch (addr) { + case UTCR0: + s->utcr0 = value & 0x7f; + strongarm_uart_update_parameters(s); + break; + + case UTCR1: + s->brd = (s->brd & 0xff) | ((value & 0xf) << 8); + strongarm_uart_update_parameters(s); + break; + + case UTCR2: + s->brd = (s->brd & 0xf00) | (value & 0xff); + strongarm_uart_update_parameters(s); + break; + + case UTCR3: + s->utcr3 = value & 0x3f; + if ((s->utcr3 & UTCR3_RXE) == 0) { + s->rx_len = 0; + } + if ((s->utcr3 & UTCR3_TXE) == 0) { + s->tx_len = 0; + } + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); + break; + + case UTDR: + if ((s->utcr3 & UTCR3_TXE) && s->tx_len != 8) { + s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value; + s->tx_len++; + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); + if (s->tx_len == 1) { + strongarm_uart_tx(s); + } + } + break; + + case UTSR0: + s->utsr0 = s->utsr0 & ~(value & + (UTSR0_REB | UTSR0_RBB | UTSR0_RID)); + strongarm_uart_update_int_status(s); + break; + + default: + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + } +} + +static CPUReadMemoryFunc * const strongarm_uart_readfn[] = { + strongarm_uart_read, + strongarm_uart_read, + strongarm_uart_read, +}; + +static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = { + strongarm_uart_write, + strongarm_uart_write, + strongarm_uart_write, +}; + +static int strongarm_uart_init(SysBusDevice *dev) +{ + StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev); + int iomemtype; + + iomemtype = cpu_register_io_memory(strongarm_uart_readfn, + strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x10000, iomemtype); + sysbus_init_irq(dev, &s->irq); + + s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s); + s->tx_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_tx, s); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, + strongarm_uart_can_receive, + strongarm_uart_receive, + strongarm_uart_event, + s); + } + + return 0; +} + +static void strongarm_uart_reset(DeviceState *dev) +{ + StrongARMUARTState *s = DO_UPCAST(StrongARMUARTState, busdev.qdev, dev); + + s->utcr0 = UTCR0_DSS; /* 8 data, no parity */ + s->brd = 23; /* 9600 */ + /* enable send & recv - this actually violates spec */ + s->utcr3 = UTCR3_TXE | UTCR3_RXE; + + s->rx_len = s->tx_len = 0; + + strongarm_uart_update_parameters(s); + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); +} + +static int strongarm_uart_post_load(void *opaque, int version_id) +{ + StrongARMUARTState *s = opaque; + + strongarm_uart_update_parameters(s); + strongarm_uart_update_status(s); + strongarm_uart_update_int_status(s); + + /* tx and restart timer */ + if (s->tx_len) { + strongarm_uart_tx(s); + } + + /* restart rx timeout timer */ + if (s->rx_len) { + qemu_mod_timer(s->rx_timeout_timer, + qemu_get_clock_ns(vm_clock) + s->char_transmit_time * 3); + } + + return 0; +} + +static const VMStateDescription vmstate_strongarm_uart_regs = { + .name = "strongarm-uart", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = strongarm_uart_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(utcr0, StrongARMUARTState), + VMSTATE_UINT16(brd, StrongARMUARTState), + VMSTATE_UINT8(utcr3, StrongARMUARTState), + VMSTATE_UINT8(utsr0, StrongARMUARTState), + VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8), + VMSTATE_UINT8(tx_start, StrongARMUARTState), + VMSTATE_UINT8(tx_len, StrongARMUARTState), + VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12), + VMSTATE_UINT8(rx_start, StrongARMUARTState), + VMSTATE_UINT8(rx_len, StrongARMUARTState), + VMSTATE_BOOL(wait_break_end, StrongARMUARTState), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo strongarm_uart_info = { + .init = strongarm_uart_init, + .qdev.name = "strongarm-uart", + .qdev.desc = "StrongARM UART controller", + .qdev.size = sizeof(StrongARMUARTState), + .qdev.reset = strongarm_uart_reset, + .qdev.vmsd = &vmstate_strongarm_uart_regs, + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr), + DEFINE_PROP_END_OF_LIST(), + } +}; + +/* Synchronous Serial Ports */ +typedef struct { + SysBusDevice busdev; + qemu_irq irq; + SSIBus *bus; + + uint16_t sscr[2]; + uint16_t sssr; + + uint16_t rx_fifo[8]; + uint8_t rx_level; + uint8_t rx_start; +} StrongARMSSPState; + +#define SSCR0 0x60 /* SSP Control register 0 */ +#define SSCR1 0x64 /* SSP Control register 1 */ +#define SSDR 0x6c /* SSP Data register */ +#define SSSR 0x74 /* SSP Status register */ + +/* Bitfields for above registers */ +#define SSCR0_SPI(x) (((x) & 0x30) == 0x00) +#define SSCR0_SSP(x) (((x) & 0x30) == 0x10) +#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20) +#define SSCR0_PSP(x) (((x) & 0x30) == 0x30) +#define SSCR0_SSE (1 << 7) +#define SSCR0_DSS(x) (((x) & 0xf) + 1) +#define SSCR1_RIE (1 << 0) +#define SSCR1_TIE (1 << 1) +#define SSCR1_LBM (1 << 2) +#define SSSR_TNF (1 << 2) +#define SSSR_RNE (1 << 3) +#define SSSR_TFS (1 << 5) +#define SSSR_RFS (1 << 6) +#define SSSR_ROR (1 << 7) +#define SSSR_RW 0x0080 + +static void strongarm_ssp_int_update(StrongARMSSPState *s) +{ + int level = 0; + + level |= (s->sssr & SSSR_ROR); + level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE); + level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE); + qemu_set_irq(s->irq, level); +} + +static void strongarm_ssp_fifo_update(StrongARMSSPState *s) +{ + s->sssr &= ~SSSR_TFS; + s->sssr &= ~SSSR_TNF; + if (s->sscr[0] & SSCR0_SSE) { + if (s->rx_level >= 4) { + s->sssr |= SSSR_RFS; + } else { + s->sssr &= ~SSSR_RFS; + } + 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; + } + + strongarm_ssp_int_update(s); +} + +static uint32_t strongarm_ssp_read(void *opaque, target_phys_addr_t addr) +{ + StrongARMSSPState *s = opaque; + uint32_t retval; + + switch (addr) { + case SSCR0: + return s->sscr[0]; + case SSCR1: + return s->sscr[1]; + case SSSR: + return s->sssr; + case SSDR: + if (~s->sscr[0] & SSCR0_SSE) { + return 0xffffffff; + } + if (s->rx_level < 1) { + printf("%s: SSP Rx Underrun\n", __func__); + return 0xffffffff; + } + s->rx_level--; + retval = s->rx_fifo[s->rx_start++]; + s->rx_start &= 0x7; + strongarm_ssp_fifo_update(s); + return retval; + default: + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + break; + } + return 0; +} + +static void strongarm_ssp_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + StrongARMSSPState *s = opaque; + + switch (addr) { + case SSCR0: + s->sscr[0] = value & 0xffbf; + if ((s->sscr[0] & SSCR0_SSE) && SSCR0_DSS(value) < 4) { + printf("%s: Wrong data size: %i bits\n", __func__, + SSCR0_DSS(value)); + } + if (!(value & SSCR0_SSE)) { + s->sssr = 0; + s->rx_level = 0; + } + strongarm_ssp_fifo_update(s); + break; + + case SSCR1: + s->sscr[1] = value & 0x2f; + if (value & SSCR1_LBM) { + printf("%s: Attempt to use SSP LBM mode\n", __func__); + } + strongarm_ssp_fifo_update(s); + break; + + case SSSR: + s->sssr &= ~(value & SSSR_RW); + strongarm_ssp_int_update(s); + break; + + case SSDR: + if (SSCR0_UWIRE(s->sscr[0])) { + value &= 0xff; + } else + /* Note how 32bits overflow does no harm here */ + value &= (1 << SSCR0_DSS(s->sscr[0])) - 1; + + /* Data goes from here to the Tx FIFO and is shifted out from + * there directly to the slave, no need to buffer it. + */ + if (s->sscr[0] & SSCR0_SSE) { + uint32_t readval; + if (s->sscr[1] & SSCR1_LBM) { + readval = value; + } else { + readval = ssi_transfer(s->bus, value); + } + + if (s->rx_level < 0x08) { + s->rx_fifo[(s->rx_start + s->rx_level++) & 0x7] = readval; + } else { + s->sssr |= SSSR_ROR; + } + } + strongarm_ssp_fifo_update(s); + break; + + default: + printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); + break; + } +} + +static CPUReadMemoryFunc * const strongarm_ssp_readfn[] = { + strongarm_ssp_read, + strongarm_ssp_read, + strongarm_ssp_read, +}; + +static CPUWriteMemoryFunc * const strongarm_ssp_writefn[] = { + strongarm_ssp_write, + strongarm_ssp_write, + strongarm_ssp_write, +}; + +static int strongarm_ssp_post_load(void *opaque, int version_id) +{ + StrongARMSSPState *s = opaque; + + strongarm_ssp_fifo_update(s); + + return 0; +} + +static int strongarm_ssp_init(SysBusDevice *dev) +{ + int iomemtype; + StrongARMSSPState *s = FROM_SYSBUS(StrongARMSSPState, dev); + + sysbus_init_irq(dev, &s->irq); + + iomemtype = cpu_register_io_memory(strongarm_ssp_readfn, + strongarm_ssp_writefn, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x1000, iomemtype); + + s->bus = ssi_create_bus(&dev->qdev, "ssi"); + return 0; +} + +static void strongarm_ssp_reset(DeviceState *dev) +{ + StrongARMSSPState *s = DO_UPCAST(StrongARMSSPState, busdev.qdev, dev); + s->sssr = 0x03; /* 3 bit data, SPI, disabled */ + s->rx_start = 0; + s->rx_level = 0; +} + +static const VMStateDescription vmstate_strongarm_ssp_regs = { + .name = "strongarm-ssp", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = strongarm_ssp_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16_ARRAY(sscr, StrongARMSSPState, 2), + VMSTATE_UINT16(sssr, StrongARMSSPState), + VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMSSPState, 8), + VMSTATE_UINT8(rx_start, StrongARMSSPState), + VMSTATE_UINT8(rx_level, StrongARMSSPState), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo strongarm_ssp_info = { + .init = strongarm_ssp_init, + .qdev.name = "strongarm-ssp", + .qdev.desc = "StrongARM SSP controller", + .qdev.size = sizeof(StrongARMSSPState), + .qdev.reset = strongarm_ssp_reset, + .qdev.vmsd = &vmstate_strongarm_ssp_regs, +}; + +/* Main CPU functions */ +StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev) +{ + StrongARMState *s; + qemu_irq *pic; + int i; + + s = qemu_mallocz(sizeof(StrongARMState)); + + if (!rev) { + rev = "sa1110-b5"; + } + + if (strncmp(rev, "sa1110", 6)) { + error_report("Machine requires a SA1110 processor."); + exit(1); + } + + s->env = cpu_init(rev); + + if (!s->env) { + error_report("Unable to find CPU definition"); + exit(1); + } + + cpu_register_physical_memory(SA_SDCS0, + sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram", + sdram_size) | IO_MEM_RAM); + + pic = arm_pic_init_cpu(s->env); + s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000, + pic[ARM_PIC_CPU_IRQ], pic[ARM_PIC_CPU_FIQ], NULL); + + sysbus_create_varargs("pxa25x-timer", 0x90000000, + qdev_get_gpio_in(s->pic, SA_PIC_OSTC0), + qdev_get_gpio_in(s->pic, SA_PIC_OSTC1), + qdev_get_gpio_in(s->pic, SA_PIC_OSTC2), + qdev_get_gpio_in(s->pic, SA_PIC_OSTC3), + NULL); + + sysbus_create_simple("strongarm-rtc", 0x90010000, + qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM)); + + s->gpio = strongarm_gpio_init(0x90040000, s->pic); + + s->ppc = sysbus_create_varargs("strongarm-ppc", 0x90060000, NULL); + + for (i = 0; sa_serial[i].io_base; i++) { + DeviceState *dev = qdev_create(NULL, "strongarm-uart"); + qdev_prop_set_chr(dev, "chardev", serial_hds[i]); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, + sa_serial[i].io_base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, + qdev_get_gpio_in(s->pic, sa_serial[i].irq)); + } + + s->ssp = sysbus_create_varargs("strongarm-ssp", 0x80070000, + qdev_get_gpio_in(s->pic, SA_PIC_SSP), NULL); + s->ssp_bus = (SSIBus *)qdev_get_child_bus(s->ssp, "ssi"); + + return s; +} + +static void strongarm_register_devices(void) +{ + sysbus_register_withprop(&strongarm_pic_info); + sysbus_register_withprop(&strongarm_rtc_sysbus_info); + sysbus_register_withprop(&strongarm_gpio_info); + sysbus_register_withprop(&strongarm_ppc_info); + sysbus_register_withprop(&strongarm_uart_info); + sysbus_register_withprop(&strongarm_ssp_info); +} +device_init(strongarm_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/strongarm.h qemu-kvm-0.15.0+noroms/hw/strongarm.h --- qemu-kvm-0.14.1+noroms/hw/strongarm.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/strongarm.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,64 @@ +#ifndef _STRONGARM_H +#define _STRONGARM_H + +#define SA_CS0 0x00000000 +#define SA_CS1 0x08000000 +#define SA_CS2 0x10000000 +#define SA_CS3 0x18000000 +#define SA_PCMCIA_CS0 0x20000000 +#define SA_PCMCIA_CS1 0x30000000 +#define SA_CS4 0x40000000 +#define SA_CS5 0x48000000 +/* system registers here */ +#define SA_SDCS0 0xc0000000 +#define SA_SDCS1 0xc8000000 +#define SA_SDCS2 0xd0000000 +#define SA_SDCS3 0xd8000000 + +enum { + SA_PIC_GPIO0_EDGE = 0, + SA_PIC_GPIO1_EDGE, + SA_PIC_GPIO2_EDGE, + SA_PIC_GPIO3_EDGE, + SA_PIC_GPIO4_EDGE, + SA_PIC_GPIO5_EDGE, + SA_PIC_GPIO6_EDGE, + SA_PIC_GPIO7_EDGE, + SA_PIC_GPIO8_EDGE, + SA_PIC_GPIO9_EDGE, + SA_PIC_GPIO10_EDGE, + SA_PIC_GPIOX_EDGE, + SA_PIC_LCD, + SA_PIC_UDC, + SA_PIC_RSVD1, + SA_PIC_UART1, + SA_PIC_UART2, + SA_PIC_UART3, + SA_PIC_MCP, + SA_PIC_SSP, + SA_PIC_DMA_CH0, + SA_PIC_DMA_CH1, + SA_PIC_DMA_CH2, + SA_PIC_DMA_CH3, + SA_PIC_DMA_CH4, + SA_PIC_DMA_CH5, + SA_PIC_OSTC0, + SA_PIC_OSTC1, + SA_PIC_OSTC2, + SA_PIC_OSTC3, + SA_PIC_RTC_HZ, + SA_PIC_RTC_ALARM, +}; + +typedef struct { + CPUState *env; + DeviceState *pic; + DeviceState *gpio; + DeviceState *ppc; + DeviceState *ssp; + SSIBus *ssp_bus; +} StrongARMState; + +StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/hw/sun4m.c qemu-kvm-0.15.0+noroms/hw/sun4m.c --- qemu-kvm-0.14.1+noroms/hw/sun4m.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sun4m.c 2011-08-09 07:40:29.000000000 -0500 @@ -253,15 +253,21 @@ } } +static void cpu_kick_irq(CPUState *env) +{ + env->halted = 0; + cpu_check_irqs(env); + qemu_cpu_kick(env); +} + static void cpu_set_irq(void *opaque, int irq, int level) { CPUState *env = opaque; if (level) { trace_sun4m_cpu_set_irq_raise(irq); - env->halted = 0; env->pil_in |= 1 << irq; - cpu_check_irqs(env); + cpu_kick_irq(env); } else { trace_sun4m_cpu_set_irq_lower(irq); env->pil_in &= ~(1 << irq); diff -Nru qemu-kvm-0.14.1+noroms/hw/sun4m_iommu.c qemu-kvm-0.15.0+noroms/hw/sun4m_iommu.c --- qemu-kvm-0.14.1+noroms/hw/sun4m_iommu.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sun4m_iommu.c 2011-08-09 07:40:29.000000000 -0500 @@ -118,7 +118,7 @@ #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_WRITE 0x00000004 /* Writable */ #define IOPTE_VALID 0x00000002 /* IOPTE is valid */ #define IOPTE_WAZ 0x00000001 /* Write as zeros */ diff -Nru qemu-kvm-0.14.1+noroms/hw/sun4u.c qemu-kvm-0.15.0+noroms/hw/sun4u.c --- qemu-kvm-0.14.1+noroms/hw/sun4u.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sun4u.c 2011-08-09 07:40:29.000000000 -0500 @@ -298,6 +298,7 @@ { env->halted = 0; cpu_check_irqs(env); + qemu_cpu_kick(env); } static void cpu_set_irq(void *opaque, int irq, int level) @@ -306,9 +307,8 @@ if (level) { CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq); - env->halted = 0; env->pil_in |= 1 << irq; - cpu_check_irqs(env); + cpu_kick_irq(env); } else { CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq); env->pil_in &= ~(1 << irq); @@ -352,9 +352,9 @@ timer->disabled_mask = disabled_mask; timer->disabled = 1; - timer->clock_offset = qemu_get_clock(vm_clock); + timer->clock_offset = qemu_get_clock_ns(vm_clock); - timer->qtimer = qemu_new_timer(vm_clock, cb, env); + timer->qtimer = qemu_new_timer_ns(vm_clock, cb, env); return timer; } @@ -362,7 +362,7 @@ static void cpu_timer_reset(CPUTimer *timer) { timer->disabled = 1; - timer->clock_offset = qemu_get_clock(vm_clock); + timer->clock_offset = qemu_get_clock_ns(vm_clock); qemu_del_timer(timer->qtimer); } @@ -457,7 +457,7 @@ 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) - + int64_t vm_clock_offset = qemu_get_clock_ns(vm_clock) - cpu_to_timer_ticks(real_count, timer->frequency); TIMER_DPRINTF("%s set_count count=0x%016lx (%s) p=%p\n", @@ -471,7 +471,7 @@ uint64_t cpu_tick_get_count(CPUTimer *timer) { uint64_t real_count = timer_to_cpu_ticks( - qemu_get_clock(vm_clock) - timer->clock_offset, + qemu_get_clock_ns(vm_clock) - timer->clock_offset, timer->frequency); TIMER_DPRINTF("%s get_count count=0x%016lx (%s) p=%p\n", @@ -486,7 +486,7 @@ void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit) { - int64_t now = qemu_get_clock(vm_clock); + int64_t now = qemu_get_clock_ns(vm_clock); uint64_t real_limit = limit & ~timer->disabled_mask; timer->disabled = (limit & timer->disabled_mask) ? 1 : 0; @@ -553,15 +553,11 @@ { isa_bus_new(&s->qdev); - pci_config_set_vendor_id(s->config, PCI_VENDOR_ID_SUN); - pci_config_set_device_id(s->config, PCI_DEVICE_ID_SUN_EBUS); s->config[0x04] = 0x06; // command = bus master, pci mem s->config[0x05] = 0x00; s->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error s->config[0x07] = 0x03; // status = medium devsel - s->config[0x08] = 0x01; // revision s->config[0x09] = 0x00; // programming i/f - pci_config_set_class(s->config, PCI_CLASS_BRIDGE_OTHER); s->config[0x0D] = 0x0a; // latency_timer pci_register_bar(s, 0, 0x1000000, PCI_BASE_ADDRESS_SPACE_MEMORY, @@ -575,6 +571,10 @@ .qdev.name = "ebus", .qdev.size = sizeof(PCIDevice), .init = pci_ebus_init1, + .vendor_id = PCI_VENDOR_ID_SUN, + .device_id = PCI_DEVICE_ID_SUN_EBUS, + .revision = 0x01, + .class_id = PCI_CLASS_BRIDGE_OTHER, }; static void pci_ebus_register(void) @@ -793,14 +793,7 @@ for(i = 0; i < nb_nics; i++) pci_nic_init_nofail(&nd_table[i], "ne2k_pci", 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); - } + ide_drive_get(hd, MAX_IDE_BUS); pci_cmd646_ide_init(pci_bus, hd, 1); diff -Nru qemu-kvm-0.14.1+noroms/hw/syborg.c qemu-kvm-0.15.0+noroms/hw/syborg.c --- qemu-kvm-0.14.1+noroms/hw/syborg.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/syborg.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,7 +25,6 @@ #include "sysbus.h" #include "boards.h" #include "arm-misc.h" -#include "sysemu.h" #include "net.h" static struct arm_boot_info syborg_binfo; diff -Nru qemu-kvm-0.14.1+noroms/hw/syborg_keyboard.c qemu-kvm-0.15.0+noroms/hw/syborg_keyboard.c --- qemu-kvm-0.14.1+noroms/hw/syborg_keyboard.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/syborg_keyboard.c 2011-08-09 07:40:29.000000000 -0500 @@ -51,11 +51,11 @@ typedef struct { SysBusDevice busdev; - int int_enabled; + uint32_t int_enabled; int extension_bit; uint32_t fifo_size; uint32_t *key_fifo; - int read_pos, read_count; + uint32_t read_pos, read_count; qemu_irq irq; } SyborgKeyboardState; @@ -165,43 +165,21 @@ syborg_keyboard_update(s); } -static void syborg_keyboard_save(QEMUFile *f, void *opaque) -{ - SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; - int i; - - qemu_put_be32(f, s->fifo_size); - qemu_put_be32(f, s->int_enabled); - qemu_put_be32(f, s->extension_bit); - qemu_put_be32(f, s->read_pos); - qemu_put_be32(f, s->read_count); - for (i = 0; i < s->fifo_size; i++) { - qemu_put_be32(f, s->key_fifo[i]); - } -} - -static int syborg_keyboard_load(QEMUFile *f, void *opaque, int version_id) -{ - SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; - uint32_t val; - int i; - - if (version_id != 1) - return -EINVAL; - - val = qemu_get_be32(f); - if (val != s->fifo_size) - return -EINVAL; - - s->int_enabled = qemu_get_be32(f); - s->extension_bit = qemu_get_be32(f); - s->read_pos = qemu_get_be32(f); - s->read_count = qemu_get_be32(f); - for (i = 0; i < s->fifo_size; i++) { - s->key_fifo[i] = qemu_get_be32(f); +static const VMStateDescription vmstate_syborg_keyboard = { + .name = "syborg_keyboard", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_EQUAL(fifo_size, SyborgKeyboardState), + VMSTATE_UINT32(int_enabled, SyborgKeyboardState), + VMSTATE_UINT32(read_pos, SyborgKeyboardState), + VMSTATE_UINT32(read_count, SyborgKeyboardState), + VMSTATE_VARRAY_UINT32(key_fifo, SyborgKeyboardState, fifo_size, 1, + vmstate_info_uint32, uint32), + VMSTATE_END_OF_LIST() } - return 0; -} +}; static int syborg_keyboard_init(SysBusDevice *dev) { @@ -221,8 +199,7 @@ qemu_add_kbd_event_handler(syborg_keyboard_event, s); - register_savevm(&dev->qdev, "syborg_keyboard", -1, 1, - syborg_keyboard_save, syborg_keyboard_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_syborg_keyboard, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/syborg_pointer.c qemu-kvm-0.15.0+noroms/hw/syborg_pointer.c --- qemu-kvm-0.14.1+noroms/hw/syborg_pointer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/syborg_pointer.c 2011-08-09 07:40:29.000000000 -0500 @@ -152,52 +152,36 @@ syborg_pointer_update(s); } -static void syborg_pointer_save(QEMUFile *f, void *opaque) -{ - SyborgPointerState *s = (SyborgPointerState *)opaque; - int i; - - qemu_put_be32(f, s->fifo_size); - qemu_put_be32(f, s->absolute); - qemu_put_be32(f, s->int_enabled); - qemu_put_be32(f, s->read_pos); - qemu_put_be32(f, s->read_count); - for (i = 0; i < s->fifo_size; i++) { - qemu_put_be32(f, s->event_fifo[i].x); - qemu_put_be32(f, s->event_fifo[i].y); - qemu_put_be32(f, s->event_fifo[i].z); - qemu_put_be32(f, s->event_fifo[i].pointer_buttons); +static const VMStateDescription vmstate_event_data = { + .name = "dbma_channel", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_INT32(x, event_data), + VMSTATE_INT32(y, event_data), + VMSTATE_INT32(z, event_data), + VMSTATE_INT32(pointer_buttons, event_data), + VMSTATE_END_OF_LIST() } -} +}; -static int syborg_pointer_load(QEMUFile *f, void *opaque, int version_id) -{ - SyborgPointerState *s = (SyborgPointerState *)opaque; - uint32_t val; - int i; - - if (version_id != 1) - return -EINVAL; - - val = qemu_get_be32(f); - if (val != s->fifo_size) - return -EINVAL; - - val = qemu_get_be32(f); - if (val != s->absolute) - return -EINVAL; - - s->int_enabled = qemu_get_be32(f); - s->read_pos = qemu_get_be32(f); - s->read_count = qemu_get_be32(f); - for (i = 0; i < s->fifo_size; i++) { - s->event_fifo[i].x = qemu_get_be32(f); - s->event_fifo[i].y = qemu_get_be32(f); - s->event_fifo[i].z = qemu_get_be32(f); - s->event_fifo[i].pointer_buttons = qemu_get_be32(f); +static const VMStateDescription vmstate_syborg_pointer = { + .name = "syborg_pointer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_EQUAL(fifo_size, SyborgPointerState), + VMSTATE_UINT32_EQUAL(absolute, SyborgPointerState), + VMSTATE_INT32(int_enabled, SyborgPointerState), + VMSTATE_INT32(read_pos, SyborgPointerState), + VMSTATE_INT32(read_count, SyborgPointerState), + VMSTATE_STRUCT_VARRAY_UINT32(event_fifo, SyborgPointerState, fifo_size, + 1, vmstate_event_data, event_data), + VMSTATE_END_OF_LIST() } - return 0; -} +}; static int syborg_pointer_init(SysBusDevice *dev) { @@ -219,8 +203,7 @@ qemu_add_mouse_event_handler(syborg_pointer_event, s, s->absolute, "Syborg Pointer"); - register_savevm(&dev->qdev, "syborg_pointer", -1, 1, - syborg_pointer_save, syborg_pointer_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_syborg_pointer, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/syborg_rtc.c qemu-kvm-0.15.0+noroms/hw/syborg_rtc.c --- qemu-kvm-0.14.1+noroms/hw/syborg_rtc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/syborg_rtc.c 2011-08-09 07:40:29.000000000 -0500 @@ -66,7 +66,7 @@ offset &= 0xfff; switch (offset >> 2) { case RTC_LATCH: - now = qemu_get_clock(vm_clock); + now = qemu_get_clock_ns(vm_clock); if (value >= 4) { s->offset = s->data - now; } else { @@ -102,26 +102,17 @@ syborg_rtc_write }; -static void syborg_rtc_save(QEMUFile *f, void *opaque) -{ - SyborgRTCState *s = opaque; - - qemu_put_be64(f, s->offset); - qemu_put_be64(f, s->data); -} - -static int syborg_rtc_load(QEMUFile *f, void *opaque, int version_id) -{ - SyborgRTCState *s = opaque; - - if (version_id != 1) - return -EINVAL; - - s->offset = qemu_get_be64(f); - s->data = qemu_get_be64(f); - - return 0; -} +static const VMStateDescription vmstate_syborg_rtc = { + .name = "syborg_keyboard", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(offset, SyborgRTCState), + VMSTATE_INT64(data, SyborgRTCState), + VMSTATE_END_OF_LIST() + } +}; static int syborg_rtc_init(SysBusDevice *dev) { @@ -137,8 +128,7 @@ qemu_get_timedate(&tm, 0); s->offset = (uint64_t)mktime(&tm) * 1000000000; - register_savevm(&dev->qdev, "syborg_rtc", -1, 1, - syborg_rtc_save, syborg_rtc_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_syborg_rtc, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/syborg_serial.c qemu-kvm-0.15.0+noroms/hw/syborg_serial.c --- qemu-kvm-0.14.1+noroms/hw/syborg_serial.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/syborg_serial.c 2011-08-09 07:40:29.000000000 -0500 @@ -126,7 +126,7 @@ s->dma_tx_ptr += count; } /* QEMU char backends do not have a nonblocking mode, so we transmit all - the data imediately and the interrupt status will be unchanged. */ + the data immediately and the interrupt status will be unchanged. */ } /* Initiate RX DMA, and transfer data from the FIFO. */ @@ -273,47 +273,24 @@ syborg_serial_write }; -static void syborg_serial_save(QEMUFile *f, void *opaque) -{ - SyborgSerialState *s = opaque; - int i; - - qemu_put_be32(f, s->fifo_size); - qemu_put_be32(f, s->int_enable); - qemu_put_be32(f, s->read_pos); - qemu_put_be32(f, s->read_count); - qemu_put_be32(f, s->dma_tx_ptr); - qemu_put_be32(f, s->dma_rx_ptr); - qemu_put_be32(f, s->dma_rx_size); - for (i = 0; i < s->fifo_size; i++) { - qemu_put_be32(f, s->read_fifo[i]); - } -} - -static int syborg_serial_load(QEMUFile *f, void *opaque, int version_id) -{ - SyborgSerialState *s = opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - i = qemu_get_be32(f); - if (s->fifo_size != i) - return -EINVAL; - - s->int_enable = qemu_get_be32(f); - s->read_pos = qemu_get_be32(f); - s->read_count = qemu_get_be32(f); - s->dma_tx_ptr = qemu_get_be32(f); - s->dma_rx_ptr = qemu_get_be32(f); - s->dma_rx_size = qemu_get_be32(f); - for (i = 0; i < s->fifo_size; i++) { - s->read_fifo[i] = qemu_get_be32(f); +static const VMStateDescription vmstate_syborg_serial = { + .name = "syborg_serial", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_EQUAL(fifo_size, SyborgSerialState), + VMSTATE_UINT32(int_enable, SyborgSerialState), + VMSTATE_INT32(read_pos, SyborgSerialState), + VMSTATE_INT32(read_count, SyborgSerialState), + VMSTATE_UINT32(dma_tx_ptr, SyborgSerialState), + VMSTATE_UINT32(dma_rx_ptr, SyborgSerialState), + VMSTATE_UINT32(dma_rx_size, SyborgSerialState), + VMSTATE_VARRAY_UINT32(read_fifo, SyborgSerialState, fifo_size, 1, + vmstate_info_uint32, uint32), + VMSTATE_END_OF_LIST() } - - return 0; -} +}; static int syborg_serial_init(SysBusDevice *dev) { @@ -336,8 +313,6 @@ } s->read_fifo = qemu_mallocz(s->fifo_size * sizeof(s->read_fifo[0])); - register_savevm(&dev->qdev, "syborg_serial", -1, 1, - syborg_serial_save, syborg_serial_load, s); return 0; } @@ -345,6 +320,7 @@ .init = syborg_serial_init, .qdev.name = "syborg,serial", .qdev.size = sizeof(SyborgSerialState), + .qdev.vmsd = &vmstate_syborg_serial, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("fifo-size", SyborgSerialState, fifo_size, 16), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.14.1+noroms/hw/syborg_timer.c qemu-kvm-0.15.0+noroms/hw/syborg_timer.c --- qemu-kvm-0.14.1+noroms/hw/syborg_timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/syborg_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -174,34 +174,21 @@ syborg_timer_write }; -static void syborg_timer_save(QEMUFile *f, void *opaque) -{ - SyborgTimerState *s = opaque; - - qemu_put_be32(f, s->running); - qemu_put_be32(f, s->oneshot); - qemu_put_be32(f, s->limit); - qemu_put_be32(f, s->int_level); - qemu_put_be32(f, s->int_enabled); - qemu_put_ptimer(f, s->timer); -} - -static int syborg_timer_load(QEMUFile *f, void *opaque, int version_id) -{ - SyborgTimerState *s = opaque; - - if (version_id != 1) - return -EINVAL; - - s->running = qemu_get_be32(f); - s->oneshot = qemu_get_be32(f); - s->limit = qemu_get_be32(f); - s->int_level = qemu_get_be32(f); - s->int_enabled = qemu_get_be32(f); - qemu_get_ptimer(f, s->timer); - - return 0; -} +static const VMStateDescription vmstate_syborg_timer = { + .name = "syborg_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(running, SyborgTimerState), + VMSTATE_INT32(oneshot, SyborgTimerState), + VMSTATE_UINT32(limit, SyborgTimerState), + VMSTATE_UINT32(int_level, SyborgTimerState), + VMSTATE_UINT32(int_enabled, SyborgTimerState), + VMSTATE_PTIMER(timer, SyborgTimerState), + VMSTATE_END_OF_LIST() + } +}; static int syborg_timer_init(SysBusDevice *dev) { @@ -222,8 +209,7 @@ bh = qemu_bh_new(syborg_timer_tick, s); s->timer = ptimer_init(bh); ptimer_set_freq(s->timer, s->freq); - register_savevm(&dev->qdev, "syborg_timer", -1, 1, - syborg_timer_save, syborg_timer_load, s); + vmstate_register(&dev->qdev, -1, &vmstate_syborg_timer, s); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/syborg_virtio.c qemu-kvm-0.15.0+noroms/hw/syborg_virtio.c --- qemu-kvm-0.14.1+noroms/hw/syborg_virtio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/syborg_virtio.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,7 +26,6 @@ #include "sysbus.h" #include "virtio.h" #include "virtio-net.h" -#include "sysemu.h" //#define DEBUG_SYBORG_VIRTIO @@ -147,7 +146,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); diff -Nru qemu-kvm-0.14.1+noroms/hw/sysbus.c qemu-kvm-0.15.0+noroms/hw/sysbus.c --- qemu-kvm-0.14.1+noroms/hw/sysbus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sysbus.c 2011-08-09 07:40:29.000000000 -0500 @@ -18,7 +18,6 @@ */ #include "sysbus.h" -#include "sysemu.h" #include "monitor.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); @@ -173,11 +172,43 @@ return dev; } +DeviceState *sysbus_try_create_varargs(const char *name, + target_phys_addr_t addr, ...) +{ + DeviceState *dev; + SysBusDevice *s; + va_list va; + qemu_irq irq; + int n; + + dev = qdev_try_create(NULL, name); + if (!dev) { + return NULL; + } + s = sysbus_from_qdev(dev); + qdev_init_nofail(dev); + if (addr != (target_phys_addr_t)-1) { + sysbus_mmio_map(s, 0, addr); + } + va_start(va, addr); + n = 0; + while (1) { + irq = va_arg(va, qemu_irq); + if (!irq) { + break; + } + sysbus_connect_irq(s, n, irq); + n++; + } + return dev; +} + static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) { SysBusDevice *s = sysbus_from_qdev(dev); int i; + monitor_printf(mon, "%*sirq %d\n", indent, "", s->num_irq); for (i = 0; i < s->num_mmio; i++) { monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n", indent, "", s->mmio[i].addr, s->mmio[i].size); diff -Nru qemu-kvm-0.14.1+noroms/hw/sysbus.h qemu-kvm-0.15.0+noroms/hw/sysbus.h --- qemu-kvm-0.14.1+noroms/hw/sysbus.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/sysbus.h 2011-08-09 07:40:29.000000000 -0500 @@ -57,6 +57,8 @@ /* Legacy helper function for creating devices. */ DeviceState *sysbus_create_varargs(const char *name, target_phys_addr_t addr, ...); +DeviceState *sysbus_try_create_varargs(const char *name, + target_phys_addr_t addr, ...); static inline DeviceState *sysbus_create_simple(const char *name, target_phys_addr_t addr, qemu_irq irq) @@ -64,4 +66,11 @@ return sysbus_create_varargs(name, addr, irq, NULL); } +static inline DeviceState *sysbus_try_create_simple(const char *name, + target_phys_addr_t addr, + qemu_irq irq) +{ + return sysbus_try_create_varargs(name, addr, irq, NULL); +} + #endif /* !HW_SYSBUS_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/tc58128.c qemu-kvm-0.15.0+noroms/hw/tc58128.c --- qemu-kvm-0.14.1+noroms/hw/tc58128.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/tc58128.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,6 +1,5 @@ #include "hw.h" #include "sh.h" -#include "sysemu.h" #include "loader.h" #define CE1 0x0100 diff -Nru qemu-kvm-0.14.1+noroms/hw/tc6393xb.c qemu-kvm-0.15.0+noroms/hw/tc6393xb.c --- qemu-kvm-0.14.1+noroms/hw/tc6393xb.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/tc6393xb.c 2011-08-09 07:40:29.000000000 -0500 @@ -8,7 +8,6 @@ * This code is licensed under the GNU GPL v2. */ #include "hw.h" -#include "pxa.h" #include "devices.h" #include "flash.h" #include "console.h" @@ -381,7 +380,7 @@ case NAND_DATA + 2: case NAND_DATA + 3: nand_setio(s->flash, value); - s->nand.isr &= 1; + s->nand.isr |= 1; tc6393xb_nand_irq(s); return; case NAND_MODE: diff -Nru qemu-kvm-0.14.1+noroms/hw/testdev.c qemu-kvm-0.15.0+noroms/hw/testdev.c --- qemu-kvm-0.14.1+noroms/hw/testdev.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/testdev.c 2011-08-09 07:40:29.000000000 -0500 @@ -28,11 +28,9 @@ return ram_size; } -extern qemu_irq *ioapic_irq_hack; - static void test_device_irq_line(void *opaque, uint32_t addr, uint32_t data) { - qemu_set_irq(ioapic_irq_hack[addr - 0x2000], !!data); + qemu_set_irq(isa_get_irq(addr - 0x2000), !!data); } static uint32 test_device_ioport_data; diff -Nru qemu-kvm-0.14.1+noroms/hw/tosa.c qemu-kvm-0.15.0+noroms/hw/tosa.c --- qemu-kvm-0.14.1+noroms/hw/tosa.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/tosa.c 2011-08-09 07:40:29.000000000 -0500 @@ -11,7 +11,6 @@ #include "hw.h" #include "pxa.h" #include "arm-misc.h" -#include "sysemu.h" #include "devices.h" #include "sharpsl.h" #include "pcmcia.h" @@ -25,6 +24,7 @@ #define TOSA_RAM 0x04000000 #define TOSA_ROM 0x00800000 +#define TOSA_GPIO_USB_IN (5) #define TOSA_GPIO_nSD_DETECT (9) #define TOSA_GPIO_ON_RESET (19) #define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */ @@ -115,6 +115,9 @@ qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]); qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); + + /* UDC Vbus */ + qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_USB_IN)); } static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value) diff -Nru qemu-kvm-0.14.1+noroms/hw/tsc2005.c qemu-kvm-0.15.0+noroms/hw/tsc2005.c --- qemu-kvm-0.14.1+noroms/hw/tsc2005.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/tsc2005.c 2011-08-09 07:40:29.000000000 -0500 @@ -290,7 +290,7 @@ s->precision = s->nextprecision; s->function = s->nextfunction; s->pdst = !s->pnd0; /* Synchronised on internal clock */ - expires = qemu_get_clock(vm_clock) + (get_ticks_per_sec() >> 7); + expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 7); qemu_mod_timer(s->timer, expires); } @@ -529,7 +529,7 @@ s->y = 240; s->pressure = 0; s->precision = s->nextprecision = 0; - s->timer = qemu_new_timer(vm_clock, tsc2005_timer_tick, s); + s->timer = qemu_new_timer_ns(vm_clock, tsc2005_timer_tick, s); s->pint = pintdav; s->model = 0x2005; diff -Nru qemu-kvm-0.14.1+noroms/hw/tsc210x.c qemu-kvm-0.15.0+noroms/hw/tsc210x.c --- qemu-kvm-0.14.1+noroms/hw/tsc210x.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/tsc210x.c 2011-08-09 07:40:29.000000000 -0500 @@ -503,9 +503,9 @@ l_ch = 1; r_ch = 1; if (s->softstep && !(s->dac_power & (1 << 10))) { - l_ch = (qemu_get_clock(vm_clock) > + l_ch = (qemu_get_clock_ns(vm_clock) > s->volume_change + TSC_SOFTSTEP_DELAY); - r_ch = (qemu_get_clock(vm_clock) > + r_ch = (qemu_get_clock_ns(vm_clock) > s->volume_change + TSC_SOFTSTEP_DELAY); } @@ -514,7 +514,7 @@ case 0x05: /* Stereo DAC Power Control */ return 0x2aa0 | s->dac_power | (((s->dac_power & (1 << 10)) && - (qemu_get_clock(vm_clock) > + (qemu_get_clock_ns(vm_clock) > s->powerdown + TSC_POWEROFF_DELAY)) << 6); case 0x06: /* Audio Control 3 */ @@ -695,7 +695,7 @@ case 0x02: /* DAC Volume Control */ s->volume = value; - s->volume_change = qemu_get_clock(vm_clock); + s->volume_change = qemu_get_clock_ns(vm_clock); return; case 0x03: @@ -717,7 +717,7 @@ case 0x05: /* Stereo DAC Power Control */ if ((value & ~s->dac_power) & (1 << 10)) - s->powerdown = qemu_get_clock(vm_clock); + s->powerdown = qemu_get_clock_ns(vm_clock); s->dac_power = value & 0x9543; #ifdef TSC_VERBOSE @@ -864,7 +864,7 @@ s->busy = 1; s->precision = s->nextprecision; s->function = s->nextfunction; - expires = qemu_get_clock(vm_clock) + (get_ticks_per_sec() >> 10); + expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 10); qemu_mod_timer(s->timer, expires); } @@ -1005,7 +1005,7 @@ static void tsc210x_save(QEMUFile *f, void *opaque) { TSC210xState *s = (TSC210xState *) opaque; - int64_t now = qemu_get_clock(vm_clock); + int64_t now = qemu_get_clock_ns(vm_clock); int i; qemu_put_be16(f, s->x); @@ -1051,7 +1051,7 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) { TSC210xState *s = (TSC210xState *) opaque; - int64_t now = qemu_get_clock(vm_clock); + int64_t now = qemu_get_clock_ns(vm_clock); int i; s->x = qemu_get_be16(f); @@ -1111,7 +1111,7 @@ s->y = 160; s->pressure = 0; s->precision = s->nextprecision = 0; - s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s); + s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s); s->pint = pint; s->model = 0x2102; s->name = "tsc2102"; @@ -1160,7 +1160,7 @@ s->y = 240; s->pressure = 0; s->precision = s->nextprecision = 0; - s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s); + s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s); s->pint = penirq; s->kbint = kbirq; s->davint = dav; diff -Nru qemu-kvm-0.14.1+noroms/hw/tusb6010.c qemu-kvm-0.15.0+noroms/hw/tusb6010.c --- qemu-kvm-0.14.1+noroms/hw/tusb6010.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/tusb6010.c 2011-08-09 07:40:29.000000000 -0500 @@ -520,7 +520,7 @@ case TUSB_DEV_OTG_TIMER: s->otg_timer_val = value; if (value & TUSB_DEV_OTG_TIMER_ENABLE) - qemu_mod_timer(s->otg_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(s->otg_timer, qemu_get_clock_ns(vm_clock) + muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), get_ticks_per_sec(), TUSB_DEVCLOCK)); else @@ -742,8 +742,8 @@ s->iomemtype[1] = cpu_register_io_memory(tusb_async_readfn, 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); + s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s); + s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s); s->musb = musb_init(qemu_allocate_irqs(tusb_musb_core_intr, s, __musb_irq_max)); @@ -761,6 +761,6 @@ s->intr_ok = 0; tusb_intr_update(s); qemu_mod_timer(s->pwr_timer, - qemu_get_clock(vm_clock) + get_ticks_per_sec() / 2); + qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 2); } } diff -Nru qemu-kvm-0.14.1+noroms/hw/twl92230.c qemu-kvm-0.15.0+noroms/hw/twl92230.c --- qemu-kvm-0.14.1+noroms/hw/twl92230.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/twl92230.c 2011-08-09 07:40:29.000000000 -0500 @@ -22,7 +22,6 @@ #include "hw.h" #include "qemu-timer.h" #include "i2c.h" -#include "sysemu.h" #include "console.h" #define VERBOSE 1 @@ -74,14 +73,14 @@ static inline void menelaus_rtc_start(MenelausState *s) { - s->rtc.next += qemu_get_clock(rt_clock); + s->rtc.next += qemu_get_clock_ms(rt_clock); qemu_mod_timer(s->rtc.hz_tm, s->rtc.next); } static inline void menelaus_rtc_stop(MenelausState *s) { qemu_del_timer(s->rtc.hz_tm); - s->rtc.next -= qemu_get_clock(rt_clock); + s->rtc.next -= qemu_get_clock_ms(rt_clock); if (s->rtc.next < 1) s->rtc.next = 1; } @@ -786,7 +785,7 @@ { MenelausState *s = opaque; /* Should be <= 1000 */ - s->rtc_next_vmstate = s->rtc.next - qemu_get_clock(rt_clock); + s->rtc_next_vmstate = s->rtc.next - qemu_get_clock_ms(rt_clock); } static int menelaus_post_load(void *opaque, int version_id) @@ -847,7 +846,7 @@ { MenelausState *s = FROM_I2C_SLAVE(MenelausState, i2c); - s->rtc.hz_tm = qemu_new_timer(rt_clock, menelaus_rtc_hz, s); + s->rtc.hz_tm = qemu_new_timer_ms(rt_clock, menelaus_rtc_hz, s); /* Three output pins plus one interrupt pin. */ qdev_init_gpio_out(&i2c->qdev, s->out, 4); qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 3); diff -Nru qemu-kvm-0.14.1+noroms/hw/unin_pci.c qemu-kvm-0.15.0+noroms/hw/unin_pci.c --- qemu-kvm-0.14.1+noroms/hw/unin_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/unin_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -63,23 +63,6 @@ qemu_set_irq(pic[unin_irq_line[irq_num]], level); } -static void pci_unin_save(QEMUFile* f, void *opaque) -{ - PCIDevice *d = opaque; - - pci_device_save(d, f); -} - -static int pci_unin_load(QEMUFile* f, void *opaque, int version_id) -{ - PCIDevice *d = opaque; - - if (version_id != 1) - return -EINVAL; - - return pci_device_load(d, f); -} - static void pci_unin_reset(void *opaque) { } @@ -158,8 +141,6 @@ 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; } @@ -181,8 +162,6 @@ 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; @@ -300,10 +279,6 @@ static int unin_main_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_UNI_N_PCI); - d->config[0x08] = 0x00; // revision - 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[0x34] = 0x00; // capabilities_pointer @@ -312,10 +287,6 @@ static int unin_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_UNI_N_AGP); - d->config[0x08] = 0x00; // revision - 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[0x34] = 0x80; // capabilities_pointer @@ -324,11 +295,6 @@ 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 */ @@ -338,10 +304,6 @@ static int unin_internal_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_UNI_N_I_PCI); - d->config[0x08] = 0x00; // revision - 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[0x34] = 0x00; // capabilities_pointer @@ -352,24 +314,40 @@ .qdev.name = "uni-north", .qdev.size = sizeof(PCIDevice), .init = unin_main_pci_host_init, + .vendor_id = PCI_VENDOR_ID_APPLE, + .device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI, + .revision = 0x00, + .class_id = PCI_CLASS_BRIDGE_HOST, }; static PCIDeviceInfo u3_agp_pci_host_info = { .qdev.name = "u3-agp", .qdev.size = sizeof(PCIDevice), .init = u3_agp_pci_host_init, + .vendor_id = PCI_VENDOR_ID_APPLE, + .device_id = PCI_DEVICE_ID_APPLE_U3_AGP, + .revision = 0x00, + .class_id = PCI_CLASS_BRIDGE_HOST, }; static PCIDeviceInfo unin_agp_pci_host_info = { .qdev.name = "uni-north-agp", .qdev.size = sizeof(PCIDevice), .init = unin_agp_pci_host_init, + .vendor_id = PCI_VENDOR_ID_APPLE, + .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP, + .revision = 0x00, + .class_id = PCI_CLASS_BRIDGE_HOST, }; static PCIDeviceInfo unin_internal_pci_host_info = { .qdev.name = "uni-north-pci", .qdev.size = sizeof(PCIDevice), .init = unin_internal_pci_host_init, + .vendor_id = PCI_VENDOR_ID_APPLE, + .device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI, + .revision = 0x00, + .class_id = PCI_CLASS_BRIDGE_HOST, }; static void unin_register_devices(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-bt.c qemu-kvm-0.15.0+noroms/hw/usb-bt.c --- qemu-kvm-0.14.1+noroms/hw/usb-bt.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-bt.c 2011-08-09 07:40:29.000000000 -0500 @@ -99,13 +99,13 @@ .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0, .bInterval = 0x01, }, @@ -120,13 +120,13 @@ .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x09, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x09, .bInterval = 0x01, }, @@ -141,13 +141,13 @@ .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x11, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x11, .bInterval = 0x01, }, @@ -162,13 +162,13 @@ .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x19, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x19, .bInterval = 0x01, }, @@ -183,13 +183,13 @@ .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x21, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x21, .bInterval = 0x01, }, @@ -204,13 +204,13 @@ .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x31, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x31, .bInterval = 0x01, }, @@ -372,13 +372,13 @@ s->altsetting = 0; } -static int usb_bt_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_bt_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { struct USBBtState *s = (struct USBBtState *) dev->opaque; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { switch (request) { case DeviceRequest | USB_REQ_GET_CONFIGURATION: diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-bus.c qemu-kvm-0.15.0+noroms/hw/usb-bus.c --- qemu-kvm-0.14.1+noroms/hw/usb-bus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-bus.c 2011-08-09 07:40:29.000000000 -0500 @@ -39,9 +39,10 @@ } }; -void usb_bus_new(USBBus *bus, DeviceState *host) +void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host) { qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL); + bus->ops = ops; bus->busnr = next_usb_bus++; bus->qbus.allow_hotplug = 1; /* Yes, we can */ QTAILQ_INIT(&bus->free); @@ -74,7 +75,7 @@ QLIST_INIT(&dev->strings); rc = dev->info->init(dev); if (rc == 0 && dev->auto_attach) - usb_device_attach(dev); + rc = usb_device_attach(dev); return rc; } @@ -82,7 +83,9 @@ { USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); - usb_device_detach(dev); + if (dev->attached) { + usb_device_detach(dev); + } if (dev->info->handle_destroy) { dev->info->handle_destroy(dev); } @@ -116,7 +119,7 @@ bus = usb_bus_find(-1); if (!bus) return NULL; - fprintf(stderr, "%s: no bus specified, using \"%s\" for \"%s\"\n", + error_report("%s: no bus specified, using \"%s\" for \"%s\"\n", __FUNCTION__, bus->qbus.name, name); } #endif @@ -135,19 +138,55 @@ return dev; } -void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - USBPortOps *ops, int speedmask) +static void usb_fill_port(USBPort *port, void *opaque, int index, + USBPortOps *ops, int speedmask) { port->opaque = opaque; port->index = index; - port->opaque = opaque; - port->index = index; port->ops = ops; port->speedmask = speedmask; + usb_port_location(port, NULL, index + 1); +} + +void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + USBPortOps *ops, int speedmask) +{ + usb_fill_port(port, opaque, index, ops, speedmask); QTAILQ_INSERT_TAIL(&bus->free, port, next); bus->nfree++; } +int usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, USBPortOps *ops, int speedmask) +{ + USBBus *bus; + int i; + + QTAILQ_FOREACH(bus, &busses, next) { + if (strcmp(bus->qbus.name, masterbus) == 0) { + break; + } + } + + if (!bus || !bus->ops->register_companion) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", + "an USB masterbus"); + if (bus) { + error_printf_unless_qmp( + "USB bus '%s' does not allow companion controllers\n", + masterbus); + } + return -1; + } + + for (i = 0; i < portcount; i++) { + usb_fill_port(ports[i], opaque, i, ops, speedmask); + } + + return bus->ops->register_companion(bus, ports, portcount, firstport); +} + void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) { if (upstream) { @@ -166,15 +205,20 @@ bus->nfree--; } -static void do_attach(USBDevice *dev) +static int do_attach(USBDevice *dev) { USBBus *bus = usb_bus_from_device(dev); USBPort *port; if (dev->attached) { - fprintf(stderr, "Warning: tried to attach usb device %s twice\n", + error_report("Error: tried to attach usb device %s twice\n", dev->product_desc); - return; + return -1; + } + if (bus->nfree == 0) { + error_report("Error: tried to attach usb device %s to a bus with no free ports\n", + dev->product_desc); + return -1; } if (dev->port_path) { QTAILQ_FOREACH(port, &bus->free, next) { @@ -183,13 +227,18 @@ } } if (port == NULL) { - fprintf(stderr, "Warning: usb port %s (bus %s) not found\n", + error_report("Error: usb port %s (bus %s) not found\n", dev->port_path, bus->qbus.name); - return; + return -1; } } else { port = QTAILQ_FIRST(&bus->free); } + if (!(port->speedmask & dev->speedmask)) { + error_report("Warning: speed mismatch trying to attach usb device %s to bus %s\n", + dev->product_desc, bus->qbus.name); + return -1; + } dev->attached++; QTAILQ_REMOVE(&bus->free, port, next); @@ -199,6 +248,8 @@ QTAILQ_INSERT_TAIL(&bus->used, port, next); bus->nused++; + + return 0; } int usb_device_attach(USBDevice *dev) @@ -210,8 +261,7 @@ (unless a physical port location is specified). */ usb_create_simple(bus, "usb-hub"); } - do_attach(dev); - return 0; + return do_attach(dev); } int usb_device_detach(USBDevice *dev) @@ -220,7 +270,7 @@ USBPort *port; if (!dev->attached) { - fprintf(stderr, "Warning: tried to detach unattached usb device %s\n", + error_report("Error: tried to detach unattached usb device %s\n", dev->product_desc); return -1; } @@ -270,6 +320,7 @@ [ USB_SPEED_LOW ] = "1.5", [ USB_SPEED_FULL ] = "12", [ USB_SPEED_HIGH ] = "480", + [ USB_SPEED_SUPER ] = "5000", }; if (speed >= ARRAY_SIZE(txt)) return "?"; diff -Nru qemu-kvm-0.14.1+noroms/hw/usb.c qemu-kvm-0.15.0+noroms/hw/usb.c --- qemu-kvm-0.14.1+noroms/hw/usb.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb.c 2011-08-09 07:40:29.000000000 -0500 @@ -40,19 +40,18 @@ } else { /* detach */ dev = port->dev; + assert(dev); port->ops->detach(port); - if (dev) { - usb_send_msg(dev, USB_MSG_DETACH); - dev->port = NULL; - port->dev = NULL; - } + 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); + dev->port->ops->wakeup(dev->port); } } @@ -63,9 +62,10 @@ protocol) */ -#define SETUP_STATE_IDLE 0 -#define SETUP_STATE_DATA 1 -#define SETUP_STATE_ACK 2 +#define SETUP_STATE_IDLE 0 +#define SETUP_STATE_SETUP 1 +#define SETUP_STATE_DATA 2 +#define SETUP_STATE_ACK 3 static int do_token_setup(USBDevice *s, USBPacket *p) { @@ -82,10 +82,14 @@ request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - + if (s->setup_buf[0] & USB_DIR_IN) { - ret = s->info->handle_control(s, request, value, index, + ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); + if (ret == USB_RET_ASYNC) { + s->setup_state = SETUP_STATE_SETUP; + return USB_RET_ASYNC; + } if (ret < 0) return ret; @@ -93,6 +97,12 @@ s->setup_len = ret; s->setup_state = SETUP_STATE_DATA; } else { + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + return USB_RET_STALL; + } if (s->setup_len == 0) s->setup_state = SETUP_STATE_ACK; else @@ -117,9 +127,12 @@ switch(s->setup_state) { case SETUP_STATE_ACK: if (!(s->setup_buf[0] & USB_DIR_IN)) { - s->setup_state = SETUP_STATE_IDLE; - ret = s->info->handle_control(s, request, value, index, + ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); + if (ret == USB_RET_ASYNC) { + return USB_RET_ASYNC; + } + s->setup_state = SETUP_STATE_IDLE; if (ret > 0) return 0; return ret; @@ -232,6 +245,36 @@ } } +/* ctrl complete function for devices which use usb_generic_handle_packet and + may return USB_RET_ASYNC from their handle_control callback. Device code + which does this *must* call this function instead of the normal + usb_packet_complete to complete their async control packets. */ +void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) +{ + if (p->len < 0) { + s->setup_state = SETUP_STATE_IDLE; + } + + switch (s->setup_state) { + case SETUP_STATE_SETUP: + if (p->len < s->setup_len) { + s->setup_len = p->len; + } + s->setup_state = SETUP_STATE_DATA; + p->len = 8; + break; + + case SETUP_STATE_ACK: + s->setup_state = SETUP_STATE_IDLE; + p->len = 0; + break; + + default: + break; + } + usb_packet_complete(s, p); +} + /* XXX: fix overflow */ int set_usb_string(uint8_t *buf, const char *str) { @@ -253,9 +296,54 @@ void usb_send_msg(USBDevice *dev, int msg) { USBPacket p; + int ret; + memset(&p, 0, sizeof(p)); p.pid = msg; - dev->info->handle_packet(dev, &p); - + ret = usb_handle_packet(dev, &p); /* This _must_ be synchronous */ + assert(ret != USB_RET_ASYNC); +} + +/* Hand over a packet to a device for processing. Return value + USB_RET_ASYNC indicates the processing isn't finished yet, the + driver will call usb_packet_complete() when done processing it. */ +int usb_handle_packet(USBDevice *dev, USBPacket *p) +{ + int ret; + + assert(p->owner == NULL); + ret = dev->info->handle_packet(dev, p); + if (ret == USB_RET_ASYNC) { + if (p->owner == NULL) { + p->owner = dev; + } else { + /* We'll end up here when usb_handle_packet is called + * recursively due to a hub being in the chain. Nothing + * to do. Leave p->owner pointing to the device, not the + * hub. */; + } + } + return ret; +} + +/* Notify the controller that an async packet is complete. This should only + be called for packets previously deferred by returning USB_RET_ASYNC from + handle_packet. */ +void usb_packet_complete(USBDevice *dev, USBPacket *p) +{ + /* Note: p->owner != dev is possible in case dev is a hub */ + assert(p->owner != NULL); + dev->port->ops->complete(dev->port, p); + p->owner = NULL; +} + +/* Cancel an active packet. The packed must have been deferred by + returning USB_RET_ASYNC from handle_packet, and not yet + completed. */ +void usb_cancel_packet(USBPacket * p) +{ + assert(p->owner != NULL); + p->owner->info->cancel_packet(p->owner, p); + p->owner = NULL; } diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-ccid.c qemu-kvm-0.15.0+noroms/hw/usb-ccid.c --- qemu-kvm-0.14.1+noroms/hw/usb-ccid.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/usb-ccid.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,1397 @@ +/* + * Copyright (C) 2011 Red Hat, Inc. + * + * CCID Device emulation + * + * Written by Alon Levy, with contributions from Robert Relyea. + * + * Based on usb-serial.c, see it's copyright and attributions below. + * + * This work is licensed under the terms of the GNU GPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + * ------- (original copyright & attribution for usb-serial.c below) -------- + * Copyright (c) 2006 CodeSourcery. + * Copyright (c) 2008 Samuel Thibault + * Written by Paul Brook, reused for FTDI by Samuel Thibault, + */ + +/* + * References: + * + * CCID Specification Revision 1.1 April 22nd 2005 + * "Universal Serial Bus, Device Class: Smart Card" + * Specification for Integrated Circuit(s) Cards Interface Devices + * + * Endianness note: from the spec (1.3) + * "Fields that are larger than a byte are stored in little endian" + * + * KNOWN BUGS + * 1. remove/insert can sometimes result in removed state instead of inserted. + * This is a result of the following: + * symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This can happen + * when a short packet is sent, as seen in uhci-usb.c, resulting from a urb + * from the guest requesting SPD and us returning a smaller packet. + * Not sure which messages trigger this. + */ + +#include "qemu-common.h" +#include "qemu-error.h" +#include "usb.h" +#include "monitor.h" + +#include "hw/ccid.h" + +#define DPRINTF(s, lvl, fmt, ...) \ +do { \ + if (lvl <= s->debug) { \ + printf("usb-ccid: " fmt , ## __VA_ARGS__); \ + } \ +} while (0) + +#define D_WARN 1 +#define D_INFO 2 +#define D_MORE_INFO 3 +#define D_VERBOSE 4 + +#define CCID_DEV_NAME "usb-ccid" + +/* + * The two options for variable sized buffers: + * make them constant size, for large enough constant, + * or handle the migration complexity - VMState doesn't handle this case. + * sizes are expected never to be exceeded, unless guest misbehaves. + */ +#define BULK_OUT_DATA_SIZE 65536 +#define PENDING_ANSWERS_NUM 128 + +#define BULK_IN_BUF_SIZE 384 +#define BULK_IN_PENDING_NUM 8 + +#define InterfaceOutClass \ + ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8) + +#define InterfaceInClass \ + ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8) + +#define CCID_MAX_PACKET_SIZE 64 + +#define CCID_CONTROL_ABORT 0x1 +#define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x2 +#define CCID_CONTROL_GET_DATA_RATES 0x3 + +#define CCID_PRODUCT_DESCRIPTION "QEMU USB CCID" +#define CCID_VENDOR_DESCRIPTION "QEMU " QEMU_VERSION +#define CCID_INTERFACE_NAME "CCID Interface" +#define CCID_SERIAL_NUMBER_STRING "1" +/* + * Using Gemplus Vendor and Product id + * Effect on various drivers: + * usbccid.sys (winxp, others untested) is a class driver so it doesn't care. + * linux has a number of class drivers, but openct filters based on + * vendor/product (/etc/openct.conf under fedora), hence Gemplus. + */ +#define CCID_VENDOR_ID 0x08e6 +#define CCID_PRODUCT_ID 0x4433 +#define CCID_DEVICE_VERSION 0x0000 + +/* + * BULK_OUT messages from PC to Reader + * Defined in CCID Rev 1.1 6.1 (page 26) + */ +#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn 0x62 +#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff 0x63 +#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus 0x65 +#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock 0x6f +#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters 0x6c +#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters 0x6d +#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters 0x61 +#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape 0x6b +#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock 0x6e +#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU 0x6a +#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure 0x69 +#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical 0x71 +#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort 0x72 +#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73 + +/* + * BULK_IN messages from Reader to PC + * Defined in CCID Rev 1.1 6.2 (page 48) + */ +#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock 0x80 +#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus 0x81 +#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters 0x82 +#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape 0x83 +#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84 + +/* + * INTERRUPT_IN messages from Reader to PC + * Defined in CCID Rev 1.1 6.3 (page 56) + */ +#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange 0x50 +#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError 0x51 + +/* + * Endpoints for CCID - addresses are up to us to decide. + * To support slot insertion and removal we must have an interrupt in ep + * in addition we need a bulk in and bulk out ep + * 5.2, page 20 + */ +#define CCID_INT_IN_EP 1 +#define CCID_BULK_IN_EP 2 +#define CCID_BULK_OUT_EP 3 + +/* bmSlotICCState masks */ +#define SLOT_0_STATE_MASK 1 +#define SLOT_0_CHANGED_MASK 2 + +/* Status codes that go in bStatus (see 6.2.6) */ +enum { + ICC_STATUS_PRESENT_ACTIVE = 0, + ICC_STATUS_PRESENT_INACTIVE, + ICC_STATUS_NOT_PRESENT +}; + +enum { + COMMAND_STATUS_NO_ERROR = 0, + COMMAND_STATUS_FAILED, + COMMAND_STATUS_TIME_EXTENSION_REQUIRED +}; + +/* Error codes that go in bError (see 6.2.6) */ +enum { + ERROR_CMD_NOT_SUPPORTED = 0, + ERROR_CMD_ABORTED = -1, + ERROR_ICC_MUTE = -2, + ERROR_XFR_PARITY_ERROR = -3, + ERROR_XFR_OVERRUN = -4, + ERROR_HW_ERROR = -5, +}; + +/* 6.2.6 RDR_to_PC_SlotStatus definitions */ +enum { + CLOCK_STATUS_RUNNING = 0, + /* + * 0 - Clock Running, 1 - Clock stopped in State L, 2 - H, + * 3 - unknown state. rest are RFU + */ +}; + +typedef struct __attribute__ ((__packed__)) CCID_Header { + uint8_t bMessageType; + uint32_t dwLength; + uint8_t bSlot; + uint8_t bSeq; +} CCID_Header; + +typedef struct __attribute__ ((__packed__)) CCID_BULK_IN { + CCID_Header hdr; + uint8_t bStatus; /* Only used in BULK_IN */ + uint8_t bError; /* Only used in BULK_IN */ +} CCID_BULK_IN; + +typedef struct __attribute__ ((__packed__)) CCID_SlotStatus { + CCID_BULK_IN b; + uint8_t bClockStatus; +} CCID_SlotStatus; + +typedef struct __attribute__ ((__packed__)) CCID_Parameter { + CCID_BULK_IN b; + uint8_t bProtocolNum; + uint8_t abProtocolDataStructure[0]; +} CCID_Parameter; + +typedef struct __attribute__ ((__packed__)) CCID_DataBlock { + CCID_BULK_IN b; + uint8_t bChainParameter; + uint8_t abData[0]; +} CCID_DataBlock; + +/* 6.1.4 PC_to_RDR_XfrBlock */ +typedef struct __attribute__ ((__packed__)) CCID_XferBlock { + CCID_Header hdr; + uint8_t bBWI; /* Block Waiting Timeout */ + uint16_t wLevelParameter; /* XXX currently unused */ + uint8_t abData[0]; +} CCID_XferBlock; + +typedef struct __attribute__ ((__packed__)) CCID_IccPowerOn { + CCID_Header hdr; + uint8_t bPowerSelect; + uint16_t abRFU; +} CCID_IccPowerOn; + +typedef struct __attribute__ ((__packed__)) CCID_IccPowerOff { + CCID_Header hdr; + uint16_t abRFU; +} CCID_IccPowerOff; + +typedef struct __attribute__ ((__packed__)) CCID_SetParameters { + CCID_Header hdr; + uint8_t bProtocolNum; + uint16_t abRFU; + uint8_t abProtocolDataStructure[0]; +} CCID_SetParameters; + +typedef struct CCID_Notify_Slot_Change { + uint8_t bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */ + uint8_t bmSlotICCState; +} CCID_Notify_Slot_Change; + +/* used for DataBlock response to XferBlock */ +typedef struct Answer { + uint8_t slot; + uint8_t seq; +} Answer; + +/* pending BULK_IN messages */ +typedef struct BulkIn { + uint8_t data[BULK_IN_BUF_SIZE]; + uint32_t len; + uint32_t pos; +} BulkIn; + +enum { + MIGRATION_NONE, + MIGRATION_MIGRATED, +}; + +typedef struct CCIDBus { + BusState qbus; +} CCIDBus; + +#define MAX_PROTOCOL_SIZE 7 + +/* + * powered - defaults to true, changed by PowerOn/PowerOff messages + */ +typedef struct USBCCIDState { + USBDevice dev; + CCIDBus bus; + CCIDCardState *card; + CCIDCardInfo *cardinfo; /* caching the info pointer */ + BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */ + uint32_t bulk_in_pending_start; + uint32_t bulk_in_pending_end; /* first free */ + uint32_t bulk_in_pending_num; + BulkIn *current_bulk_in; + uint8_t bulk_out_data[BULK_OUT_DATA_SIZE]; + uint32_t bulk_out_pos; + uint64_t last_answer_error; + Answer pending_answers[PENDING_ANSWERS_NUM]; + uint32_t pending_answers_start; + uint32_t pending_answers_end; + uint32_t pending_answers_num; + uint8_t bError; + uint8_t bmCommandStatus; + uint8_t bProtocolNum; + uint8_t abProtocolDataStructure[MAX_PROTOCOL_SIZE]; + uint32_t ulProtocolDataStructureSize; + uint32_t state_vmstate; + uint32_t migration_target_ip; + uint16_t migration_target_port; + uint8_t migration_state; + uint8_t bmSlotICCState; + uint8_t powered; + uint8_t notify_slot_change; + uint8_t debug; +} USBCCIDState; + +/* + * CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9, + * "USB Device Framework", section 9.6.1, in the Universal Serial Bus + * Specification. + * + * This device implemented based on the spec and with an Athena Smart Card + * Reader as reference: + * 0dc3:1004 Athena Smartcard Solutions, Inc. + */ + +static const uint8_t qemu_ccid_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + USB_DT_DEVICE, /* u8 bDescriptorType; Device */ + 0x10, 0x01, /* u16 bcdUSB; v1.1 */ + + 0x00, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x40, /* u8 bMaxPacketSize0; 8 Bytes (valid: 8,16,32,64) */ + + /* Vendor and product id are arbitrary. */ + /* u16 idVendor */ + CCID_VENDOR_ID & 0xff, CCID_VENDOR_ID >> 8, + /* u16 idProduct */ + CCID_PRODUCT_ID & 0xff, CCID_PRODUCT_ID >> 8, + /* u16 bcdDevice */ + CCID_DEVICE_VERSION & 0xff, CCID_DEVICE_VERSION >> 8, + 0x01, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x03, /* u8 iSerialNumber; */ + 0x01, /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_ccid_config_descriptor[] = { + + /* one configuration */ + 0x09, /* u8 bLength; */ + USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */ + 0x5d, 0x00, /* u16 wTotalLength; 9+9+54+7+7+7 */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0xe0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 100/2, /* u8 MaxPower; 50 == 100mA */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x03, /* u8 if_bNumEndpoints; */ + 0x0b, /* u8 if_bInterfaceClass; Smart Card Device Class */ + 0x00, /* u8 if_bInterfaceSubClass; Subclass code */ + 0x00, /* u8 if_bInterfaceProtocol; Protocol code */ + 0x04, /* u8 if_iInterface; Index of string descriptor */ + + /* Smart Card Device Class Descriptor */ + 0x36, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; Functional */ + 0x10, 0x01, /* u16 bcdCCID; CCID Specification Release Number. */ + 0x00, /* + * u8 bMaxSlotIndex; The index of the highest available + * slot on this device. All slots are consecutive starting + * at 00h. + */ + 0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ + + 0x03, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ + 0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ + /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ + 0xa0, 0x0f, 0x00, 0x00, + /* u32 dwMaximumClock; */ + 0x00, 0x00, 0x01, 0x00, + 0x00, /* u8 bNumClockSupported; * + * 0 means just the default and max. */ + /* u32 dwDataRate ;bps. 9600 == 00002580h */ + 0x80, 0x25, 0x00, 0x00, + /* u32 dwMaxDataRate ; 11520 bps == 0001C200h */ + 0x00, 0xC2, 0x01, 0x00, + 0x00, /* u8 bNumDataRatesSupported; 00 means all rates between + * default and max */ + /* u32 dwMaxIFSD; * + * maximum IFSD supported by CCID for protocol * + * T=1 (Maximum seen from various cards) */ + 0xfe, 0x00, 0x00, 0x00, + /* u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */ + 0x00, 0x00, 0x00, 0x00, + /* u32 dwMechanical; 0 - no special characteristics. */ + 0x00, 0x00, 0x00, 0x00, + /* + * u32 dwFeatures; + * 0 - No special characteristics + * + 2 Automatic parameter configuration based on ATR data + * + 4 Automatic activation of ICC on inserting + * + 8 Automatic ICC voltage selection + * + 10 Automatic ICC clock frequency change + * + 20 Automatic baud rate change + * + 40 Automatic parameters negotiation made by the CCID + * + 80 automatic PPS made by the CCID + * 100 CCID can set ICC in clock stop mode + * 200 NAD value other then 00 accepted (T=1 protocol) + * + 400 Automatic IFSD exchange as first exchange (T=1) + * One of the following only: + * + 10000 TPDU level exchanges with CCID + * 20000 Short APDU level exchange with CCID + * 40000 Short and Extended APDU level exchange with CCID + * + * + 100000 USB Wake up signaling supported on card + * insertion and removal. Must set bit 5 in bmAttributes + * in Configuration descriptor if 100000 is set. + */ + 0xfe, 0x04, 0x11, 0x00, + /* + * u32 dwMaxCCIDMessageLength; For extended APDU in + * [261 + 10 , 65544 + 10]. Otherwise the minimum is + * wMaxPacketSize of the Bulk-OUT endpoint + */ + 0x12, 0x00, 0x01, 0x00, + 0xFF, /* + * u8 bClassGetResponse; Significant only for CCID that + * offers an APDU level for exchanges. Indicates the + * default class value used by the CCID when it sends a + * Get Response command to perform the transportation of + * an APDU by T=0 protocol + * FFh indicates that the CCID echos the class of the APDU. + */ + 0xFF, /* + * u8 bClassEnvelope; EAPDU only. Envelope command for + * T=0 + */ + 0x00, 0x00, /* + * u16 wLcdLayout; XXYY Number of lines (XX) and chars per + * line for LCD display used for PIN entry. 0000 - no LCD + */ + 0x01, /* + * u8 bPINSupport; 01h PIN Verification, + * 02h PIN Modification + */ + 0x01, /* u8 bMaxCCIDBusySlots; */ + + /* Interrupt-IN endpoint */ + 0x07, /* u8 ep_bLength; */ + /* u8 ep_bDescriptorType; Endpoint */ + USB_DT_ENDPOINT, + /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x80 | CCID_INT_IN_EP, + 0x03, /* u8 ep_bmAttributes; Interrupt */ + /* u16 ep_wMaxPacketSize; */ + CCID_MAX_PACKET_SIZE & 0xff, (CCID_MAX_PACKET_SIZE >> 8), + 0xff, /* u8 ep_bInterval; */ + + /* Bulk-In endpoint */ + 0x07, /* u8 ep_bLength; */ + /* u8 ep_bDescriptorType; Endpoint */ + USB_DT_ENDPOINT, + /* u8 ep_bEndpointAddress; IN Endpoint 2 */ + 0x80 | CCID_BULK_IN_EP, + 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x00, /* u8 ep_bInterval; */ + + /* Bulk-Out endpoint */ + 0x07, /* u8 ep_bLength; */ + /* u8 ep_bDescriptorType; Endpoint */ + USB_DT_ENDPOINT, + /* u8 ep_bEndpointAddress; OUT Endpoint 3 */ + CCID_BULK_OUT_EP, + 0x02, /* u8 ep_bmAttributes; Bulk */ + 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x00, /* u8 ep_bInterval; */ + +}; + +static bool ccid_has_pending_answers(USBCCIDState *s) +{ + return s->pending_answers_num > 0; +} + +static void ccid_clear_pending_answers(USBCCIDState *s) +{ + s->pending_answers_num = 0; + s->pending_answers_start = 0; + s->pending_answers_end = 0; +} + +static void ccid_print_pending_answers(USBCCIDState *s) +{ + Answer *answer; + int i, count; + + DPRINTF(s, D_VERBOSE, "usb-ccid: pending answers:"); + if (!ccid_has_pending_answers(s)) { + DPRINTF(s, D_VERBOSE, " empty\n"); + return; + } + for (i = s->pending_answers_start, count = s->pending_answers_num ; + count > 0; count--, i++) { + answer = &s->pending_answers[i % PENDING_ANSWERS_NUM]; + if (count == 1) { + DPRINTF(s, D_VERBOSE, "%d:%d\n", answer->slot, answer->seq); + } else { + DPRINTF(s, D_VERBOSE, "%d:%d,", answer->slot, answer->seq); + } + } +} + +static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr) +{ + Answer *answer; + + assert(s->pending_answers_num < PENDING_ANSWERS_NUM); + s->pending_answers_num++; + answer = + &s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM]; + answer->slot = hdr->bSlot; + answer->seq = hdr->bSeq; + ccid_print_pending_answers(s); +} + +static void ccid_remove_pending_answer(USBCCIDState *s, + uint8_t *slot, uint8_t *seq) +{ + Answer *answer; + + assert(s->pending_answers_num > 0); + s->pending_answers_num--; + answer = + &s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM]; + *slot = answer->slot; + *seq = answer->seq; + ccid_print_pending_answers(s); +} + +static void ccid_bulk_in_clear(USBCCIDState *s) +{ + s->bulk_in_pending_start = 0; + s->bulk_in_pending_end = 0; + s->bulk_in_pending_num = 0; +} + +static void ccid_bulk_in_release(USBCCIDState *s) +{ + assert(s->current_bulk_in != NULL); + s->current_bulk_in->pos = 0; + s->current_bulk_in = NULL; +} + +static void ccid_bulk_in_get(USBCCIDState *s) +{ + if (s->current_bulk_in != NULL || s->bulk_in_pending_num == 0) { + return; + } + assert(s->bulk_in_pending_num > 0); + s->bulk_in_pending_num--; + s->current_bulk_in = + &s->bulk_in_pending[(s->bulk_in_pending_start++) % BULK_IN_PENDING_NUM]; +} + +static void *ccid_reserve_recv_buf(USBCCIDState *s, uint16_t len) +{ + BulkIn *bulk_in; + + DPRINTF(s, D_VERBOSE, "%s: QUEUE: reserve %d bytes\n", __func__, len); + + /* look for an existing element */ + if (len > BULK_IN_BUF_SIZE) { + DPRINTF(s, D_WARN, "usb-ccid.c: %s: len larger then max (%d>%d). " + "discarding message.\n", + __func__, len, BULK_IN_BUF_SIZE); + return NULL; + } + if (s->bulk_in_pending_num >= BULK_IN_PENDING_NUM) { + DPRINTF(s, D_WARN, "usb-ccid.c: %s: No free bulk_in buffers. " + "discarding message.\n", __func__); + return NULL; + } + bulk_in = + &s->bulk_in_pending[(s->bulk_in_pending_end++) % BULK_IN_PENDING_NUM]; + s->bulk_in_pending_num++; + bulk_in->len = len; + return bulk_in->data; +} + +static void ccid_reset(USBCCIDState *s) +{ + ccid_bulk_in_clear(s); + ccid_clear_pending_answers(s); +} + +static void ccid_detach(USBCCIDState *s) +{ + ccid_reset(s); +} + +static void ccid_handle_reset(USBDevice *dev) +{ + USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); + + DPRINTF(s, 1, "Reset\n"); + + ccid_reset(s); +} + +static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request, + int value, int index, int length, uint8_t *data) +{ + USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); + int ret = 0; + + DPRINTF(s, 1, "got control %x, value %x\n", request, value); + 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_ccid_dev_descriptor, + sizeof(qemu_ccid_dev_descriptor)); + ret = sizeof(qemu_ccid_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, qemu_ccid_config_descriptor, + sizeof(qemu_ccid_config_descriptor)); + ret = sizeof(qemu_ccid_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, CCID_VENDOR_DESCRIPTION); + break; + case 2: + /* product description */ + ret = set_usb_string(data, CCID_PRODUCT_DESCRIPTION); + break; + case 3: + /* serial number */ + ret = set_usb_string(data, CCID_SERIAL_NUMBER_STRING); + break; + case 4: + /* interface name */ + ret = set_usb_string(data, CCID_INTERFACE_NAME); + 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: + /* Only one configuration - we just ignore the request */ + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + ret = 0; + break; + + /* Class specific requests. */ + case InterfaceOutClass | CCID_CONTROL_ABORT: + DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n"); + ret = USB_RET_STALL; + break; + case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES: + DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n"); + ret = USB_RET_STALL; + break; + case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES: + DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n"); + ret = USB_RET_STALL; + break; + default: +fail: + DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n", + request, value); + ret = USB_RET_STALL; + break; + } + return ret; +} + +static bool ccid_card_inserted(USBCCIDState *s) +{ + return s->bmSlotICCState & SLOT_0_STATE_MASK; +} + +static uint8_t ccid_card_status(USBCCIDState *s) +{ + return ccid_card_inserted(s) + ? (s->powered ? + ICC_STATUS_PRESENT_ACTIVE + : ICC_STATUS_PRESENT_INACTIVE + ) + : ICC_STATUS_NOT_PRESENT; +} + +static uint8_t ccid_calc_status(USBCCIDState *s) +{ + /* + * page 55, 6.2.6, calculation of bStatus from bmICCStatus and + * bmCommandStatus + */ + uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6); + DPRINTF(s, D_VERBOSE, "status = %d\n", ret); + return ret; +} + +static void ccid_reset_error_status(USBCCIDState *s) +{ + s->bError = ERROR_CMD_NOT_SUPPORTED; + s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; +} + +static void ccid_write_slot_status(USBCCIDState *s, CCID_Header *recv) +{ + CCID_SlotStatus *h = ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus)); + if (h == NULL) { + return; + } + h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus; + h->b.hdr.dwLength = 0; + h->b.hdr.bSlot = recv->bSlot; + h->b.hdr.bSeq = recv->bSeq; + h->b.bStatus = ccid_calc_status(s); + h->b.bError = s->bError; + h->bClockStatus = CLOCK_STATUS_RUNNING; + ccid_reset_error_status(s); +} + +static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv) +{ + CCID_Parameter *h; + uint32_t len = s->ulProtocolDataStructureSize; + + h = ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len); + if (h == NULL) { + return; + } + h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters; + h->b.hdr.dwLength = 0; + h->b.hdr.bSlot = recv->bSlot; + h->b.hdr.bSeq = recv->bSeq; + h->b.bStatus = ccid_calc_status(s); + h->b.bError = s->bError; + h->bProtocolNum = s->bProtocolNum; + memcpy(h->abProtocolDataStructure, s->abProtocolDataStructure, len); + ccid_reset_error_status(s); +} + +static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq, + const uint8_t *data, uint32_t len) +{ + CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len); + + if (p == NULL) { + return; + } + p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock; + p->b.hdr.dwLength = cpu_to_le32(len); + p->b.hdr.bSlot = slot; + p->b.hdr.bSeq = seq; + p->b.bStatus = ccid_calc_status(s); + p->b.bError = s->bError; + if (p->b.bError) { + DPRINTF(s, D_VERBOSE, "error %d", p->b.bError); + } + memcpy(p->abData, data, len); + ccid_reset_error_status(s); +} + +static void ccid_write_data_block_answer(USBCCIDState *s, + const uint8_t *data, uint32_t len) +{ + uint8_t seq; + uint8_t slot; + + if (!ccid_has_pending_answers(s)) { + abort(); + } + ccid_remove_pending_answer(s, &slot, &seq); + ccid_write_data_block(s, slot, seq, data, len); +} + +static void ccid_write_data_block_atr(USBCCIDState *s, CCID_Header *recv) +{ + const uint8_t *atr = NULL; + uint32_t len = 0; + + if (s->card) { + atr = s->cardinfo->get_atr(s->card, &len); + } + ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len); +} + +static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv) +{ + CCID_SetParameters *ph = (CCID_SetParameters *) recv; + uint32_t len = 0; + if ((ph->bProtocolNum & 3) == 0) { + len = 5; + } + if ((ph->bProtocolNum & 3) == 1) { + len = 7; + } + if (len == 0) { + s->bmCommandStatus = COMMAND_STATUS_FAILED; + s->bError = 7; /* Protocol invalid or not supported */ + return; + } + s->bProtocolNum = ph->bProtocolNum; + memcpy(s->abProtocolDataStructure, ph->abProtocolDataStructure, len); + s->ulProtocolDataStructureSize = len; + DPRINTF(s, 1, "%s: using len %d\n", __func__, len); +} + +/* + * must be 5 bytes for T=0, 7 bytes for T=1 + * See page 52 + */ +static const uint8_t abDefaultProtocolDataStructure[7] = { + 0x77, 0x00, 0x00, 0x00, 0x00, 0xfe /*IFSC*/, 0x00 /*NAD*/ }; + +static void ccid_reset_parameters(USBCCIDState *s) +{ + uint32_t len = sizeof(abDefaultProtocolDataStructure); + + s->bProtocolNum = 1; /* T=1 */ + s->ulProtocolDataStructureSize = len; + memcpy(s->abProtocolDataStructure, abDefaultProtocolDataStructure, len); +} + +static void ccid_report_error_failed(USBCCIDState *s, uint8_t error) +{ + s->bmCommandStatus = COMMAND_STATUS_FAILED; + s->bError = error; +} + +/* NOTE: only a single slot is supported (SLOT_0) */ +static void ccid_on_slot_change(USBCCIDState *s, bool full) +{ + /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56 */ + uint8_t current = s->bmSlotICCState; + if (full) { + s->bmSlotICCState |= SLOT_0_STATE_MASK; + } else { + s->bmSlotICCState &= ~SLOT_0_STATE_MASK; + } + if (current != s->bmSlotICCState) { + s->bmSlotICCState |= SLOT_0_CHANGED_MASK; + } + s->notify_slot_change = true; +} + +static void ccid_write_data_block_error( + USBCCIDState *s, uint8_t slot, uint8_t seq) +{ + ccid_write_data_block(s, slot, seq, NULL, 0); +} + +static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv) +{ + uint32_t len; + + if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) { + DPRINTF(s, 1, + "usb-ccid: not sending apdu to client, no card connected\n"); + ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq); + return; + } + len = le32_to_cpu(recv->hdr.dwLength); + DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__, + recv->hdr.bSeq, len); + ccid_add_pending_answer(s, (CCID_Header *)recv); + if (s->card) { + s->cardinfo->apdu_from_guest(s->card, recv->abData, len); + } else { + DPRINTF(s, D_WARN, "warning: discarded apdu\n"); + } +} + +/* + * Handle a single USB_TOKEN_OUT, return value returned to guest. + * Return value: + * 0 - all ok + * USB_RET_STALL - failed to handle packet + */ +static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) +{ + CCID_Header *ccid_header; + + if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { + return USB_RET_STALL; + } + ccid_header = (CCID_Header *)s->bulk_out_data; + memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len); + s->bulk_out_pos += p->len; + if (p->len == CCID_MAX_PACKET_SIZE) { + DPRINTF(s, D_VERBOSE, + "usb-ccid: bulk_in: expecting more packets (%d/%d)\n", + p->len, ccid_header->dwLength); + return 0; + } + if (s->bulk_out_pos < 10) { + DPRINTF(s, 1, + "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n", + __func__); + } else { + DPRINTF(s, D_MORE_INFO, "%s %x\n", __func__, ccid_header->bMessageType); + switch (ccid_header->bMessageType) { + case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus: + ccid_write_slot_status(s, ccid_header); + break; + case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn: + DPRINTF(s, 1, "PowerOn: %d\n", + ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect); + s->powered = true; + if (!ccid_card_inserted(s)) { + ccid_report_error_failed(s, ERROR_ICC_MUTE); + } + /* atr is written regardless of error. */ + ccid_write_data_block_atr(s, ccid_header); + break; + case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff: + DPRINTF(s, 1, "PowerOff\n"); + ccid_reset_error_status(s); + s->powered = false; + ccid_write_slot_status(s, ccid_header); + break; + case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock: + ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data); + break; + case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters: + ccid_reset_error_status(s); + ccid_set_parameters(s, ccid_header); + ccid_write_parameters(s, ccid_header); + break; + case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters: + ccid_reset_error_status(s); + ccid_reset_parameters(s); + ccid_write_parameters(s, ccid_header); + break; + case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters: + ccid_reset_error_status(s); + ccid_write_parameters(s, ccid_header); + break; + default: + DPRINTF(s, 1, + "handle_data: ERROR: unhandled message type %Xh\n", + ccid_header->bMessageType); + /* + * The caller is expecting the device to respond, tell it we + * don't support the operation. + */ + ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED); + ccid_write_slot_status(s, ccid_header); + break; + } + } + s->bulk_out_pos = 0; + return 0; +} + +static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) +{ + int ret = 0; + + assert(len > 0); + ccid_bulk_in_get(s); + if (s->current_bulk_in != NULL) { + ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len); + memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret); + s->current_bulk_in->pos += ret; + if (s->current_bulk_in->pos == s->current_bulk_in->len) { + ccid_bulk_in_release(s); + } + } else { + /* return when device has no data - usb 2.0 spec Table 8-4 */ + ret = USB_RET_NAK; + } + if (ret > 0) { + DPRINTF(s, D_MORE_INFO, + "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret); + } + if (ret != USB_RET_NAK && ret < len) { + DPRINTF(s, 1, + "%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len); + } + return ret; +} + +static int ccid_handle_data(USBDevice *dev, USBPacket *p) +{ + USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); + int ret = 0; + uint8_t *data = p->data; + int len = p->len; + + switch (p->pid) { + case USB_TOKEN_OUT: + ret = ccid_handle_bulk_out(s, p); + break; + + case USB_TOKEN_IN: + switch (p->devep & 0xf) { + case CCID_BULK_IN_EP: + if (!len) { + ret = USB_RET_NAK; + } else { + ret = ccid_bulk_in_copy_to_guest(s, data, len); + } + break; + case CCID_INT_IN_EP: + if (s->notify_slot_change) { + /* page 56, RDR_to_PC_NotifySlotChange */ + data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; + data[1] = s->bmSlotICCState; + ret = 2; + s->notify_slot_change = false; + s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; + DPRINTF(s, D_INFO, + "handle_data: int_in: notify_slot_change %X, " + "requested len %d\n", + s->bmSlotICCState, len); + } + break; + default: + DPRINTF(s, 1, "Bad endpoint\n"); + break; + } + break; + default: + DPRINTF(s, 1, "Bad token\n"); + ret = USB_RET_STALL; + break; + } + + return ret; +} + +static void ccid_handle_destroy(USBDevice *dev) +{ + USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); + + ccid_bulk_in_clear(s); +} + +static void ccid_flush_pending_answers(USBCCIDState *s) +{ + while (ccid_has_pending_answers(s)) { + ccid_write_data_block_answer(s, NULL, 0); + } +} + +static Answer *ccid_peek_next_answer(USBCCIDState *s) +{ + return s->pending_answers_num == 0 + ? NULL + : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; +} + +static struct BusInfo ccid_bus_info = { + .name = "ccid-bus", + .size = sizeof(CCIDBus), + .props = (Property[]) { + DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +void ccid_card_send_apdu_to_guest(CCIDCardState *card, + uint8_t *apdu, uint32_t len) +{ + USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev, + card->qdev.parent_bus->parent); + Answer *answer; + + if (!ccid_has_pending_answers(s)) { + DPRINTF(s, 1, "CCID ERROR: got an APDU without pending answers\n"); + return; + } + s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; + answer = ccid_peek_next_answer(s); + if (answer == NULL) { + abort(); + } + DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n", + len, answer->seq, answer->slot); + ccid_write_data_block_answer(s, apdu, len); +} + +void ccid_card_card_removed(CCIDCardState *card) +{ + USBCCIDState *s = + DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent); + + ccid_on_slot_change(s, false); + ccid_flush_pending_answers(s); + ccid_reset(s); +} + +int ccid_card_ccid_attach(CCIDCardState *card) +{ + USBCCIDState *s = + DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent); + + DPRINTF(s, 1, "CCID Attach\n"); + if (s->migration_state == MIGRATION_MIGRATED) { + s->migration_state = MIGRATION_NONE; + } + return 0; +} + +void ccid_card_ccid_detach(CCIDCardState *card) +{ + USBCCIDState *s = + DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent); + + DPRINTF(s, 1, "CCID Detach\n"); + if (ccid_card_inserted(s)) { + ccid_on_slot_change(s, false); + } + ccid_detach(s); +} + +void ccid_card_card_error(CCIDCardState *card, uint64_t error) +{ + USBCCIDState *s = + DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent); + + s->bmCommandStatus = COMMAND_STATUS_FAILED; + s->last_answer_error = error; + DPRINTF(s, 1, "VSC_Error: %" PRIX64 "\n", s->last_answer_error); + /* TODO: these errors should be more verbose and propagated to the guest.*/ + /* + * We flush all pending answers on CardRemove message in ccid-card-passthru, + * so check that first to not trigger abort + */ + if (ccid_has_pending_answers(s)) { + ccid_write_data_block_answer(s, NULL, 0); + } +} + +void ccid_card_card_inserted(CCIDCardState *card) +{ + USBCCIDState *s = + DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent); + + s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; + ccid_flush_pending_answers(s); + ccid_on_slot_change(s, true); +} + +static int ccid_card_exit(DeviceState *qdev) +{ + int ret = 0; + CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev); + CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info); + USBCCIDState *s = + DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent); + + if (ccid_card_inserted(s)) { + ccid_card_card_removed(card); + } + if (info->exitfn) { + ret = info->exitfn(card); + } + s->card = NULL; + s->cardinfo = NULL; + return ret; +} + +static int ccid_card_init(DeviceState *qdev, DeviceInfo *base) +{ + CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev); + CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, base); + USBCCIDState *s = + DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent); + int ret = 0; + + if (card->slot != 0) { + error_report("Warning: usb-ccid supports one slot, can't add %d", + card->slot); + return -1; + } + if (s->card != NULL) { + error_report("Warning: usb-ccid card already full, not adding"); + return -1; + } + ret = info->initfn ? info->initfn(card) : ret; + if (ret == 0) { + s->card = card; + s->cardinfo = info; + } + return ret; +} + +void ccid_card_qdev_register(CCIDCardInfo *card) +{ + card->qdev.bus_info = &ccid_bus_info; + card->qdev.init = ccid_card_init; + card->qdev.exit = ccid_card_exit; + qdev_register(&card->qdev); +} + +static int ccid_initfn(USBDevice *dev) +{ + USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); + + qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL); + s->bus.qbus.allow_hotplug = 1; + s->card = NULL; + s->cardinfo = NULL; + s->migration_state = MIGRATION_NONE; + s->migration_target_ip = 0; + s->migration_target_port = 0; + s->dev.speed = USB_SPEED_FULL; + s->dev.speedmask = USB_SPEED_MASK_FULL; + s->notify_slot_change = false; + s->powered = true; + s->pending_answers_num = 0; + s->last_answer_error = 0; + s->bulk_in_pending_start = 0; + s->bulk_in_pending_end = 0; + s->current_bulk_in = NULL; + ccid_reset_error_status(s); + s->bulk_out_pos = 0; + ccid_reset_parameters(s); + ccid_reset(s); + return 0; +} + +static int ccid_post_load(void *opaque, int version_id) +{ + USBCCIDState *s = opaque; + + /* + * This must be done after usb_device_attach, which sets state to ATTACHED, + * while it must be DEFAULT in order to accept packets (like it is after + * reset, but reset will reset our addr and call our reset handler which + * may change state, and we don't want to do that when migrating). + */ + s->dev.state = s->state_vmstate; + return 0; +} + +static void ccid_pre_save(void *opaque) +{ + USBCCIDState *s = opaque; + + s->state_vmstate = s->dev.state; + if (s->dev.attached) { + /* + * Migrating an open device, ignore reconnection CHR_EVENT to avoid an + * erroneous detach. + */ + s->migration_state = MIGRATION_MIGRATED; + } +} + +static VMStateDescription bulk_in_vmstate = { + .name = "CCID BulkIn state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(data, BulkIn), + VMSTATE_UINT32(len, BulkIn), + VMSTATE_UINT32(pos, BulkIn), + VMSTATE_END_OF_LIST() + } +}; + +static VMStateDescription answer_vmstate = { + .name = "CCID Answer state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(slot, Answer), + VMSTATE_UINT8(seq, Answer), + VMSTATE_END_OF_LIST() + } +}; + +static VMStateDescription usb_device_vmstate = { + .name = "usb_device", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(addr, USBDevice), + VMSTATE_BUFFER(setup_buf, USBDevice), + VMSTATE_BUFFER(data_buf, USBDevice), + VMSTATE_END_OF_LIST() + } +}; + +static VMStateDescription ccid_vmstate = { + .name = CCID_DEV_NAME, + .version_id = 1, + .minimum_version_id = 1, + .post_load = ccid_post_load, + .pre_save = ccid_pre_save, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice), + VMSTATE_UINT8(debug, USBCCIDState), + VMSTATE_BUFFER(bulk_out_data, USBCCIDState), + VMSTATE_UINT32(bulk_out_pos, USBCCIDState), + VMSTATE_UINT8(bmSlotICCState, USBCCIDState), + VMSTATE_UINT8(powered, USBCCIDState), + VMSTATE_UINT8(notify_slot_change, USBCCIDState), + VMSTATE_UINT64(last_answer_error, USBCCIDState), + VMSTATE_UINT8(bError, USBCCIDState), + VMSTATE_UINT8(bmCommandStatus, USBCCIDState), + VMSTATE_UINT8(bProtocolNum, USBCCIDState), + VMSTATE_BUFFER(abProtocolDataStructure, USBCCIDState), + VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState), + VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState, + BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn), + VMSTATE_UINT32(bulk_in_pending_start, USBCCIDState), + VMSTATE_UINT32(bulk_in_pending_end, USBCCIDState), + VMSTATE_STRUCT_ARRAY(pending_answers, USBCCIDState, + PENDING_ANSWERS_NUM, 1, answer_vmstate, Answer), + VMSTATE_UINT32(pending_answers_num, USBCCIDState), + VMSTATE_UINT8(migration_state, USBCCIDState), + VMSTATE_UINT32(state_vmstate, USBCCIDState), + VMSTATE_END_OF_LIST() + } +}; + +static struct USBDeviceInfo ccid_info = { + .product_desc = "QEMU USB CCID", + .qdev.name = CCID_DEV_NAME, + .qdev.desc = "CCID Rev 1.1 smartcard reader", + .qdev.size = sizeof(USBCCIDState), + .init = ccid_initfn, + .handle_packet = usb_generic_handle_packet, + .handle_reset = ccid_handle_reset, + .handle_control = ccid_handle_control, + .handle_data = ccid_handle_data, + .handle_destroy = ccid_handle_destroy, + .usbdevice_name = "ccid", + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0), + DEFINE_PROP_END_OF_LIST(), + }, + .qdev.vmsd = &ccid_vmstate, +}; + +static void ccid_register_devices(void) +{ + usb_qdev_register(&ccid_info); +} +device_init(ccid_register_devices) diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-desc.c qemu-kvm-0.15.0+noroms/hw/usb-desc.c --- qemu-kvm-0.14.1+noroms/hw/usb-desc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-desc.c 2011-08-09 07:40:29.000000000 -0500 @@ -76,7 +76,7 @@ { uint8_t bLength = 0x09; uint16_t wTotalLength = 0; - int i, rc, count; + int i, rc; if (len < bLength) { return -1; @@ -91,8 +91,19 @@ dest[0x08] = conf->bMaxPower; wTotalLength += bLength; - count = conf->nif ? conf->nif : conf->bNumInterfaces; - for (i = 0; i < count; i++) { + /* handle grouped interfaces if any*/ + for (i = 0; i < conf->nif_groups; i++) { + rc = usb_desc_iface_group(&(conf->if_groups[i]), + dest + wTotalLength, + len - wTotalLength); + if (rc < 0) { + return rc; + } + wTotalLength += rc; + } + + /* handle normal (ungrouped / no IAD) interfaces if any */ + for (i = 0; i < conf->nif; i++) { rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength); if (rc < 0) { return rc; @@ -105,6 +116,41 @@ return wTotalLength; } +int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, + size_t len) +{ + int pos = 0; + int i = 0; + + /* handle interface association descriptor */ + uint8_t bLength = 0x08; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_INTERFACE_ASSOC; + dest[0x02] = iad->bFirstInterface; + dest[0x03] = iad->bInterfaceCount; + dest[0x04] = iad->bFunctionClass; + dest[0x05] = iad->bFunctionSubClass; + dest[0x06] = iad->bFunctionProtocol; + dest[0x07] = iad->iFunction; + pos += bLength; + + /* handle associated interfaces in this group */ + for (i = 0; i < iad->nif; i++) { + int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos); + if (rc < 0) { + return rc; + } + pos += rc; + } + + return pos; +} + int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) { uint8_t bLength = 0x09; @@ -196,7 +242,17 @@ void usb_desc_init(USBDevice *dev) { + const USBDesc *desc = dev->info->usb_desc; + + assert(desc != NULL); dev->speed = USB_SPEED_FULL; + dev->speedmask = 0; + if (desc->full) { + dev->speedmask |= USB_SPEED_MASK_FULL; + } + if (desc->high) { + dev->speedmask |= USB_SPEED_MASK_HIGH; + } usb_desc_setdefaults(dev); } @@ -329,6 +385,10 @@ trace_usb_desc_other_speed_config(dev->addr, index, len, ret); break; + case USB_DT_DEBUG: + /* ignore silently */ + break; + default: fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__, dev->addr, type, len); @@ -344,8 +404,8 @@ return ret; } -int usb_desc_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +int usb_desc_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { const USBDesc *desc = dev->info->usb_desc; int i, ret = -1; diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-desc.h qemu-kvm-0.15.0+noroms/hw/usb-desc.h --- qemu-kvm-0.14.1+noroms/hw/usb-desc.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-desc.h 2011-08-09 07:40:29.000000000 -0500 @@ -30,6 +30,24 @@ uint8_t bmAttributes; uint8_t bMaxPower; + /* grouped interfaces */ + uint8_t nif_groups; + const USBDescIfaceAssoc *if_groups; + + /* "normal" interfaces */ + uint8_t nif; + const USBDescIface *ifs; +}; + +/* conceptually an Interface Association Descriptor, and releated interfaces */ +struct USBDescIfaceAssoc { + uint8_t bFirstInterface; + uint8_t bInterfaceCount; + uint8_t bFunctionClass; + uint8_t bFunctionSubClass; + uint8_t bFunctionProtocol; + uint8_t iFunction; + uint8_t nif; const USBDescIface *ifs; }; @@ -75,6 +93,8 @@ 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_group(const USBDescIfaceAssoc *iad, 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); @@ -86,7 +106,7 @@ 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); +int usb_desc_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data); #endif /* QEMU_HW_USB_DESC_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-ehci.c qemu-kvm-0.15.0+noroms/hw/usb-ehci.c --- qemu-kvm-0.14.1+noroms/hw/usb-ehci.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/usb-ehci.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,2359 @@ +/* + * QEMU USB EHCI Emulation + * + * Copyright(c) 2008 Emutex Ltd. (address@hidden) + * + * EHCI project was started by Mark Burkley, with contributions by + * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, + * Jan Kiszka and Vincent Palatin contributed bugfixes. + * + * + * 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 General Public License + * along with this program; if not, see . + */ + +#include "hw.h" +#include "qemu-timer.h" +#include "usb.h" +#include "pci.h" +#include "monitor.h" +#include "trace.h" + +#define EHCI_DEBUG 0 + +#if EHCI_DEBUG +#define DPRINTF printf +#else +#define DPRINTF(...) +#endif + +/* internal processing - reset HC to try and recover */ +#define USB_RET_PROCERR (-99) + +#define MMIO_SIZE 0x1000 + +/* Capability Registers Base Address - section 2.2 */ +#define CAPREGBASE 0x0000 +#define CAPLENGTH CAPREGBASE + 0x0000 // 1-byte, 0x0001 reserved +#define HCIVERSION CAPREGBASE + 0x0002 // 2-bytes, i/f version # +#define HCSPARAMS CAPREGBASE + 0x0004 // 4-bytes, structural params +#define HCCPARAMS CAPREGBASE + 0x0008 // 4-bytes, capability params +#define EECP HCCPARAMS + 1 +#define HCSPPORTROUTE1 CAPREGBASE + 0x000c +#define HCSPPORTROUTE2 CAPREGBASE + 0x0010 + +#define OPREGBASE 0x0020 // Operational Registers Base Address + +#define USBCMD OPREGBASE + 0x0000 +#define USBCMD_RUNSTOP (1 << 0) // run / Stop +#define USBCMD_HCRESET (1 << 1) // HC Reset +#define USBCMD_FLS (3 << 2) // Frame List Size +#define USBCMD_FLS_SH 2 // Frame List Size Shift +#define USBCMD_PSE (1 << 4) // Periodic Schedule Enable +#define USBCMD_ASE (1 << 5) // Asynch Schedule Enable +#define USBCMD_IAAD (1 << 6) // Int Asynch Advance Doorbell +#define USBCMD_LHCR (1 << 7) // Light Host Controller Reset +#define USBCMD_ASPMC (3 << 8) // Async Sched Park Mode Count +#define USBCMD_ASPME (1 << 11) // Async Sched Park Mode Enable +#define USBCMD_ITC (0x7f << 16) // Int Threshold Control +#define USBCMD_ITC_SH 16 // Int Threshold Control Shift + +#define USBSTS OPREGBASE + 0x0004 +#define USBSTS_RO_MASK 0x0000003f +#define USBSTS_INT (1 << 0) // USB Interrupt +#define USBSTS_ERRINT (1 << 1) // Error Interrupt +#define USBSTS_PCD (1 << 2) // Port Change Detect +#define USBSTS_FLR (1 << 3) // Frame List Rollover +#define USBSTS_HSE (1 << 4) // Host System Error +#define USBSTS_IAA (1 << 5) // Interrupt on Async Advance +#define USBSTS_HALT (1 << 12) // HC Halted +#define USBSTS_REC (1 << 13) // Reclamation +#define USBSTS_PSS (1 << 14) // Periodic Schedule Status +#define USBSTS_ASS (1 << 15) // Asynchronous Schedule Status + +/* + * Interrupt enable bits correspond to the interrupt active bits in USBSTS + * so no need to redefine here. + */ +#define USBINTR OPREGBASE + 0x0008 +#define USBINTR_MASK 0x0000003f + +#define FRINDEX OPREGBASE + 0x000c +#define CTRLDSSEGMENT OPREGBASE + 0x0010 +#define PERIODICLISTBASE OPREGBASE + 0x0014 +#define ASYNCLISTADDR OPREGBASE + 0x0018 +#define ASYNCLISTADDR_MASK 0xffffffe0 + +#define CONFIGFLAG OPREGBASE + 0x0040 + +#define PORTSC (OPREGBASE + 0x0044) +#define PORTSC_BEGIN PORTSC +#define PORTSC_END (PORTSC + 4 * NB_PORTS) +/* + * Bits that are reserved or are read-only are masked out of values + * written to us by software + */ +#define PORTSC_RO_MASK 0x007001c0 +#define PORTSC_RWC_MASK 0x0000002a +#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable +#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable +#define PORTSC_WKCN_E (1 << 20) // Wake on Connect Enable +#define PORTSC_PTC (15 << 16) // Port Test Control +#define PORTSC_PTC_SH 16 // Port Test Control shift +#define PORTSC_PIC (3 << 14) // Port Indicator Control +#define PORTSC_PIC_SH 14 // Port Indicator Control Shift +#define PORTSC_POWNER (1 << 13) // Port Owner +#define PORTSC_PPOWER (1 << 12) // Port Power +#define PORTSC_LINESTAT (3 << 10) // Port Line Status +#define PORTSC_LINESTAT_SH 10 // Port Line Status Shift +#define PORTSC_PRESET (1 << 8) // Port Reset +#define PORTSC_SUSPEND (1 << 7) // Port Suspend +#define PORTSC_FPRES (1 << 6) // Force Port Resume +#define PORTSC_OCC (1 << 5) // Over Current Change +#define PORTSC_OCA (1 << 4) // Over Current Active +#define PORTSC_PEDC (1 << 3) // Port Enable/Disable Change +#define PORTSC_PED (1 << 2) // Port Enable/Disable +#define PORTSC_CSC (1 << 1) // Connect Status Change +#define PORTSC_CONNECT (1 << 0) // Current Connect Status + +#define FRAME_TIMER_FREQ 1000 +#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) + +#define NB_MAXINTRATE 8 // Max rate at which controller issues ints +#define NB_PORTS 6 // Number of downstream ports +#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction +#define MAX_ITERATIONS 20 // Max number of QH before we break the loop +#define MAX_QH 100 // Max allowable queue heads in a chain + +/* Internal periodic / asynchronous schedule state machine states + */ +typedef enum { + EST_INACTIVE = 1000, + EST_ACTIVE, + EST_EXECUTING, + EST_SLEEPING, + /* The following states are internal to the state machine function + */ + EST_WAITLISTHEAD, + EST_FETCHENTRY, + EST_FETCHQH, + EST_FETCHITD, + EST_ADVANCEQUEUE, + EST_FETCHQTD, + EST_EXECUTE, + EST_WRITEBACK, + EST_HORIZONTALQH +} EHCI_STATES; + +/* macros for accessing fields within next link pointer entry */ +#define NLPTR_GET(x) ((x) & 0xffffffe0) +#define NLPTR_TYPE_GET(x) (((x) >> 1) & 3) +#define NLPTR_TBIT(x) ((x) & 1) // 1=invalid, 0=valid + +/* link pointer types */ +#define NLPTR_TYPE_ITD 0 // isoc xfer descriptor +#define NLPTR_TYPE_QH 1 // queue head +#define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor +#define NLPTR_TYPE_FSTN 3 // frame span traversal node + + +/* EHCI spec version 1.0 Section 3.3 + */ +typedef struct EHCIitd { + uint32_t next; + + uint32_t transact[8]; +#define ITD_XACT_ACTIVE (1 << 31) +#define ITD_XACT_DBERROR (1 << 30) +#define ITD_XACT_BABBLE (1 << 29) +#define ITD_XACT_XACTERR (1 << 28) +#define ITD_XACT_LENGTH_MASK 0x0fff0000 +#define ITD_XACT_LENGTH_SH 16 +#define ITD_XACT_IOC (1 << 15) +#define ITD_XACT_PGSEL_MASK 0x00007000 +#define ITD_XACT_PGSEL_SH 12 +#define ITD_XACT_OFFSET_MASK 0x00000fff + + uint32_t bufptr[7]; +#define ITD_BUFPTR_MASK 0xfffff000 +#define ITD_BUFPTR_SH 12 +#define ITD_BUFPTR_EP_MASK 0x00000f00 +#define ITD_BUFPTR_EP_SH 8 +#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f +#define ITD_BUFPTR_DEVADDR_SH 0 +#define ITD_BUFPTR_DIRECTION (1 << 11) +#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff +#define ITD_BUFPTR_MAXPKT_SH 0 +#define ITD_BUFPTR_MULT_MASK 0x00000003 +#define ITD_BUFPTR_MULT_SH 0 +} EHCIitd; + +/* EHCI spec version 1.0 Section 3.4 + */ +typedef struct EHCIsitd { + uint32_t next; // Standard next link pointer + uint32_t epchar; +#define SITD_EPCHAR_IO (1 << 31) +#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 +#define SITD_EPCHAR_PORTNUM_SH 24 +#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 +#define SITD_EPCHAR_HUBADDR_SH 16 +#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 +#define SITD_EPCHAR_EPNUM_SH 8 +#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f + + uint32_t uframe; +#define SITD_UFRAME_CMASK_MASK 0x0000ff00 +#define SITD_UFRAME_CMASK_SH 8 +#define SITD_UFRAME_SMASK_MASK 0x000000ff + + uint32_t results; +#define SITD_RESULTS_IOC (1 << 31) +#define SITD_RESULTS_PGSEL (1 << 30) +#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 +#define SITD_RESULTS_TYBYTES_SH 16 +#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 +#define SITD_RESULTS_CPROGMASK_SH 8 +#define SITD_RESULTS_ACTIVE (1 << 7) +#define SITD_RESULTS_ERR (1 << 6) +#define SITD_RESULTS_DBERR (1 << 5) +#define SITD_RESULTS_BABBLE (1 << 4) +#define SITD_RESULTS_XACTERR (1 << 3) +#define SITD_RESULTS_MISSEDUF (1 << 2) +#define SITD_RESULTS_SPLITXSTATE (1 << 1) + + uint32_t bufptr[2]; +#define SITD_BUFPTR_MASK 0xfffff000 +#define SITD_BUFPTR_CURROFF_MASK 0x00000fff +#define SITD_BUFPTR_TPOS_MASK 0x00000018 +#define SITD_BUFPTR_TPOS_SH 3 +#define SITD_BUFPTR_TCNT_MASK 0x00000007 + + uint32_t backptr; // Standard next link pointer +} EHCIsitd; + +/* EHCI spec version 1.0 Section 3.5 + */ +typedef struct EHCIqtd { + uint32_t next; // Standard next link pointer + uint32_t altnext; // Standard next link pointer + uint32_t token; +#define QTD_TOKEN_DTOGGLE (1 << 31) +#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 +#define QTD_TOKEN_TBYTES_SH 16 +#define QTD_TOKEN_IOC (1 << 15) +#define QTD_TOKEN_CPAGE_MASK 0x00007000 +#define QTD_TOKEN_CPAGE_SH 12 +#define QTD_TOKEN_CERR_MASK 0x00000c00 +#define QTD_TOKEN_CERR_SH 10 +#define QTD_TOKEN_PID_MASK 0x00000300 +#define QTD_TOKEN_PID_SH 8 +#define QTD_TOKEN_ACTIVE (1 << 7) +#define QTD_TOKEN_HALT (1 << 6) +#define QTD_TOKEN_DBERR (1 << 5) +#define QTD_TOKEN_BABBLE (1 << 4) +#define QTD_TOKEN_XACTERR (1 << 3) +#define QTD_TOKEN_MISSEDUF (1 << 2) +#define QTD_TOKEN_SPLITXSTATE (1 << 1) +#define QTD_TOKEN_PING (1 << 0) + + uint32_t bufptr[5]; // Standard buffer pointer +#define QTD_BUFPTR_MASK 0xfffff000 +} EHCIqtd; + +/* EHCI spec version 1.0 Section 3.6 + */ +typedef struct EHCIqh { + uint32_t next; // Standard next link pointer + + /* endpoint characteristics */ + uint32_t epchar; +#define QH_EPCHAR_RL_MASK 0xf0000000 +#define QH_EPCHAR_RL_SH 28 +#define QH_EPCHAR_C (1 << 27) +#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 +#define QH_EPCHAR_MPLEN_SH 16 +#define QH_EPCHAR_H (1 << 15) +#define QH_EPCHAR_DTC (1 << 14) +#define QH_EPCHAR_EPS_MASK 0x00003000 +#define QH_EPCHAR_EPS_SH 12 +#define EHCI_QH_EPS_FULL 0 +#define EHCI_QH_EPS_LOW 1 +#define EHCI_QH_EPS_HIGH 2 +#define EHCI_QH_EPS_RESERVED 3 + +#define QH_EPCHAR_EP_MASK 0x00000f00 +#define QH_EPCHAR_EP_SH 8 +#define QH_EPCHAR_I (1 << 7) +#define QH_EPCHAR_DEVADDR_MASK 0x0000007f +#define QH_EPCHAR_DEVADDR_SH 0 + + /* endpoint capabilities */ + uint32_t epcap; +#define QH_EPCAP_MULT_MASK 0xc0000000 +#define QH_EPCAP_MULT_SH 30 +#define QH_EPCAP_PORTNUM_MASK 0x3f800000 +#define QH_EPCAP_PORTNUM_SH 23 +#define QH_EPCAP_HUBADDR_MASK 0x007f0000 +#define QH_EPCAP_HUBADDR_SH 16 +#define QH_EPCAP_CMASK_MASK 0x0000ff00 +#define QH_EPCAP_CMASK_SH 8 +#define QH_EPCAP_SMASK_MASK 0x000000ff +#define QH_EPCAP_SMASK_SH 0 + + uint32_t current_qtd; // Standard next link pointer + uint32_t next_qtd; // Standard next link pointer + uint32_t altnext_qtd; +#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e +#define QH_ALTNEXT_NAKCNT_SH 1 + + uint32_t token; // Same as QTD token + uint32_t bufptr[5]; // Standard buffer pointer +#define BUFPTR_CPROGMASK_MASK 0x000000ff +#define BUFPTR_FRAMETAG_MASK 0x0000001f +#define BUFPTR_SBYTES_MASK 0x00000fe0 +#define BUFPTR_SBYTES_SH 5 +} EHCIqh; + +/* EHCI spec version 1.0 Section 3.7 + */ +typedef struct EHCIfstn { + uint32_t next; // Standard next link pointer + uint32_t backptr; // Standard next link pointer +} EHCIfstn; + +typedef struct EHCIQueue EHCIQueue; +typedef struct EHCIState EHCIState; + +enum async_state { + EHCI_ASYNC_NONE = 0, + EHCI_ASYNC_INFLIGHT, + EHCI_ASYNC_FINISHED, +}; + +struct EHCIQueue { + EHCIState *ehci; + QTAILQ_ENTRY(EHCIQueue) next; + bool async_schedule; + uint32_t seen; + uint64_t ts; + + /* cached data from guest - needs to be flushed + * when guest removes an entry (doorbell, handshake sequence) + */ + EHCIqh qh; // copy of current QH (being worked on) + uint32_t qhaddr; // address QH read from + EHCIqtd qtd; // copy of current QTD (being worked on) + uint32_t qtdaddr; // address QTD read from + + USBPacket packet; + uint8_t buffer[BUFF_SIZE]; + int pid; + uint32_t tbytes; + enum async_state async; + int usb_status; +}; + +struct EHCIState { + PCIDevice dev; + USBBus bus; + qemu_irq irq; + target_phys_addr_t mem_base; + int mem; + int companion_count; + + /* properties */ + uint32_t freq; + uint32_t maxframes; + + /* + * EHCI spec version 1.0 Section 2.3 + * Host Controller Operational Registers + */ + union { + uint8_t mmio[MMIO_SIZE]; + struct { + uint8_t cap[OPREGBASE]; + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t notused[9]; + uint32_t configflag; + uint32_t portsc[NB_PORTS]; + }; + }; + + /* + * Internal states, shadow registers, etc + */ + uint32_t sofv; + QEMUTimer *frame_timer; + int attach_poll_counter; + int astate; // Current state in asynchronous schedule + int pstate; // Current state in periodic schedule + USBPort ports[NB_PORTS]; + USBPort *companion_ports[NB_PORTS]; + uint32_t usbsts_pending; + QTAILQ_HEAD(, EHCIQueue) queues; + + uint32_t a_fetch_addr; // which address to look at next + uint32_t p_fetch_addr; // which address to look at next + + USBPacket ipacket; + uint8_t ibuffer[BUFF_SIZE]; + int isoch_pause; + + uint64_t last_run_ns; +}; + +#define SET_LAST_RUN_CLOCK(s) \ + (s)->last_run_ns = qemu_get_clock_ns(vm_clock); + +/* nifty macros from Arnon's EHCI version */ +#define get_field(data, field) \ + (((data) & field##_MASK) >> field##_SH) + +#define set_field(data, newval, field) do { \ + uint32_t val = *data; \ + val &= ~ field##_MASK; \ + val |= ((newval) << field##_SH) & field##_MASK; \ + *data = val; \ + } while(0) + +static const char *ehci_state_names[] = { + [ EST_INACTIVE ] = "INACTIVE", + [ EST_ACTIVE ] = "ACTIVE", + [ EST_EXECUTING ] = "EXECUTING", + [ EST_SLEEPING ] = "SLEEPING", + [ EST_WAITLISTHEAD ] = "WAITLISTHEAD", + [ EST_FETCHENTRY ] = "FETCH ENTRY", + [ EST_FETCHQH ] = "FETCH QH", + [ EST_FETCHITD ] = "FETCH ITD", + [ EST_ADVANCEQUEUE ] = "ADVANCEQUEUE", + [ EST_FETCHQTD ] = "FETCH QTD", + [ EST_EXECUTE ] = "EXECUTE", + [ EST_WRITEBACK ] = "WRITEBACK", + [ EST_HORIZONTALQH ] = "HORIZONTALQH", +}; + +static const char *ehci_mmio_names[] = { + [ CAPLENGTH ] = "CAPLENGTH", + [ HCIVERSION ] = "HCIVERSION", + [ HCSPARAMS ] = "HCSPARAMS", + [ HCCPARAMS ] = "HCCPARAMS", + [ USBCMD ] = "USBCMD", + [ USBSTS ] = "USBSTS", + [ USBINTR ] = "USBINTR", + [ FRINDEX ] = "FRINDEX", + [ PERIODICLISTBASE ] = "P-LIST BASE", + [ ASYNCLISTADDR ] = "A-LIST ADDR", + [ PORTSC_BEGIN ] = "PORTSC #0", + [ PORTSC_BEGIN + 4] = "PORTSC #1", + [ PORTSC_BEGIN + 8] = "PORTSC #2", + [ PORTSC_BEGIN + 12] = "PORTSC #3", + [ CONFIGFLAG ] = "CONFIGFLAG", +}; + +static const char *nr2str(const char **n, size_t len, uint32_t nr) +{ + if (nr < len && n[nr] != NULL) { + return n[nr]; + } else { + return "unknown"; + } +} + +static const char *state2str(uint32_t state) +{ + return nr2str(ehci_state_names, ARRAY_SIZE(ehci_state_names), state); +} + +static const char *addr2str(target_phys_addr_t addr) +{ + return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); +} + +static void ehci_trace_usbsts(uint32_t mask, int state) +{ + /* interrupts */ + if (mask & USBSTS_INT) { + trace_usb_ehci_usbsts("INT", state); + } + if (mask & USBSTS_ERRINT) { + trace_usb_ehci_usbsts("ERRINT", state); + } + if (mask & USBSTS_PCD) { + trace_usb_ehci_usbsts("PCD", state); + } + if (mask & USBSTS_FLR) { + trace_usb_ehci_usbsts("FLR", state); + } + if (mask & USBSTS_HSE) { + trace_usb_ehci_usbsts("HSE", state); + } + if (mask & USBSTS_IAA) { + trace_usb_ehci_usbsts("IAA", state); + } + + /* status */ + if (mask & USBSTS_HALT) { + trace_usb_ehci_usbsts("HALT", state); + } + if (mask & USBSTS_REC) { + trace_usb_ehci_usbsts("REC", state); + } + if (mask & USBSTS_PSS) { + trace_usb_ehci_usbsts("PSS", state); + } + if (mask & USBSTS_ASS) { + trace_usb_ehci_usbsts("ASS", state); + } +} + +static inline void ehci_set_usbsts(EHCIState *s, int mask) +{ + if ((s->usbsts & mask) == mask) { + return; + } + ehci_trace_usbsts(mask, 1); + s->usbsts |= mask; +} + +static inline void ehci_clear_usbsts(EHCIState *s, int mask) +{ + if ((s->usbsts & mask) == 0) { + return; + } + ehci_trace_usbsts(mask, 0); + s->usbsts &= ~mask; +} + +static inline void ehci_set_interrupt(EHCIState *s, int intr) +{ + int level = 0; + + // TODO honour interrupt threshold requests + + ehci_set_usbsts(s, intr); + + if ((s->usbsts & USBINTR_MASK) & s->usbintr) { + level = 1; + } + + qemu_set_irq(s->irq, level); +} + +static inline void ehci_record_interrupt(EHCIState *s, int intr) +{ + s->usbsts_pending |= intr; +} + +static inline void ehci_commit_interrupt(EHCIState *s) +{ + if (!s->usbsts_pending) { + return; + } + ehci_set_interrupt(s, s->usbsts_pending); + s->usbsts_pending = 0; +} + +static void ehci_set_state(EHCIState *s, int async, int state) +{ + if (async) { + trace_usb_ehci_state("async", state2str(state)); + s->astate = state; + } else { + trace_usb_ehci_state("periodic", state2str(state)); + s->pstate = state; + } +} + +static int ehci_get_state(EHCIState *s, int async) +{ + return async ? s->astate : s->pstate; +} + +static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) +{ + if (async) { + s->a_fetch_addr = addr; + } else { + s->p_fetch_addr = addr; + } +} + +static int ehci_get_fetch_addr(EHCIState *s, int async) +{ + return async ? s->a_fetch_addr : s->p_fetch_addr; +} + +static void ehci_trace_qh(EHCIQueue *q, target_phys_addr_t addr, EHCIqh *qh) +{ + /* need three here due to argument count limits */ + trace_usb_ehci_qh_ptrs(q, addr, qh->next, + qh->current_qtd, qh->next_qtd, qh->altnext_qtd); + trace_usb_ehci_qh_fields(addr, + get_field(qh->epchar, QH_EPCHAR_RL), + get_field(qh->epchar, QH_EPCHAR_MPLEN), + get_field(qh->epchar, QH_EPCHAR_EPS), + get_field(qh->epchar, QH_EPCHAR_EP), + get_field(qh->epchar, QH_EPCHAR_DEVADDR)); + trace_usb_ehci_qh_bits(addr, + (bool)(qh->epchar & QH_EPCHAR_C), + (bool)(qh->epchar & QH_EPCHAR_H), + (bool)(qh->epchar & QH_EPCHAR_DTC), + (bool)(qh->epchar & QH_EPCHAR_I)); +} + +static void ehci_trace_qtd(EHCIQueue *q, target_phys_addr_t addr, EHCIqtd *qtd) +{ + /* need three here due to argument count limits */ + trace_usb_ehci_qtd_ptrs(q, addr, qtd->next, qtd->altnext); + trace_usb_ehci_qtd_fields(addr, + get_field(qtd->token, QTD_TOKEN_TBYTES), + get_field(qtd->token, QTD_TOKEN_CPAGE), + get_field(qtd->token, QTD_TOKEN_CERR), + get_field(qtd->token, QTD_TOKEN_PID)); + trace_usb_ehci_qtd_bits(addr, + (bool)(qtd->token & QTD_TOKEN_IOC), + (bool)(qtd->token & QTD_TOKEN_ACTIVE), + (bool)(qtd->token & QTD_TOKEN_HALT), + (bool)(qtd->token & QTD_TOKEN_BABBLE), + (bool)(qtd->token & QTD_TOKEN_XACTERR)); +} + +static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd) +{ + trace_usb_ehci_itd(addr, itd->next, + get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT), + get_field(itd->bufptr[2], ITD_BUFPTR_MULT), + get_field(itd->bufptr[0], ITD_BUFPTR_EP), + get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR)); +} + +/* queue management */ + +static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) +{ + EHCIQueue *q; + + q = qemu_mallocz(sizeof(*q)); + q->ehci = ehci; + q->async_schedule = async; + QTAILQ_INSERT_HEAD(&ehci->queues, q, next); + trace_usb_ehci_queue_action(q, "alloc"); + return q; +} + +static void ehci_free_queue(EHCIQueue *q) +{ + trace_usb_ehci_queue_action(q, "free"); + if (q->async == EHCI_ASYNC_INFLIGHT) { + usb_cancel_packet(&q->packet); + } + QTAILQ_REMOVE(&q->ehci->queues, q, next); + qemu_free(q); +} + +static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr) +{ + EHCIQueue *q; + + QTAILQ_FOREACH(q, &ehci->queues, next) { + if (addr == q->qhaddr) { + return q; + } + } + return NULL; +} + +static void ehci_queues_rip_unused(EHCIState *ehci) +{ + EHCIQueue *q, *tmp; + + QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { + if (q->seen) { + q->seen = 0; + q->ts = ehci->last_run_ns; + continue; + } + if (ehci->last_run_ns < q->ts + 250000000) { + /* allow 0.25 sec idle */ + continue; + } + ehci_free_queue(q); + } +} + +static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev) +{ + EHCIQueue *q, *tmp; + + QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { + if (q->packet.owner != dev) { + continue; + } + ehci_free_queue(q); + } +} + +static void ehci_queues_rip_all(EHCIState *ehci) +{ + EHCIQueue *q, *tmp; + + QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { + ehci_free_queue(q); + } +} + +/* Attach or detach a device on root hub */ + +static void ehci_attach(USBPort *port) +{ + EHCIState *s = port->opaque; + uint32_t *portsc = &s->portsc[port->index]; + + trace_usb_ehci_port_attach(port->index, port->dev->product_desc); + + if (*portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->dev = port->dev; + companion->ops->attach(companion); + return; + } + + *portsc |= PORTSC_CONNECT; + *portsc |= PORTSC_CSC; + + ehci_set_interrupt(s, USBSTS_PCD); +} + +static void ehci_detach(USBPort *port) +{ + EHCIState *s = port->opaque; + uint32_t *portsc = &s->portsc[port->index]; + + trace_usb_ehci_port_detach(port->index); + + if (*portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->detach(companion); + companion->dev = NULL; + return; + } + + ehci_queues_rip_device(s, port->dev); + + *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); + *portsc |= PORTSC_CSC; + + ehci_set_interrupt(s, USBSTS_PCD); +} + +static void ehci_child_detach(USBPort *port, USBDevice *child) +{ + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->child_detach(companion, child); + companion->dev = NULL; + return; + } + + ehci_queues_rip_device(s, child); +} + +static void ehci_wakeup(USBPort *port) +{ + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + if (companion->ops->wakeup) { + companion->ops->wakeup(companion); + } + } +} + +static int ehci_register_companion(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport) +{ + EHCIState *s = container_of(bus, EHCIState, bus); + uint32_t i; + + if (firstport + portcount > NB_PORTS) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport", + "firstport on masterbus"); + error_printf_unless_qmp( + "firstport value of %u makes companion take ports %u - %u, which " + "is outside of the valid range of 0 - %u\n", firstport, firstport, + firstport + portcount - 1, NB_PORTS - 1); + return -1; + } + + for (i = 0; i < portcount; i++) { + if (s->companion_ports[firstport + i]) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", + "an USB masterbus"); + error_printf_unless_qmp( + "port %u on masterbus %s already has a companion assigned\n", + firstport + i, bus->qbus.name); + return -1; + } + } + + for (i = 0; i < portcount; i++) { + s->companion_ports[firstport + i] = ports[i]; + s->ports[firstport + i].speedmask |= + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL; + /* Ensure devs attached before the initial reset go to the companion */ + s->portsc[firstport + i] = PORTSC_POWNER; + } + + s->companion_count++; + s->mmio[0x05] = (s->companion_count << 4) | portcount; + + return 0; +} + +/* 4.1 host controller initialization */ +static void ehci_reset(void *opaque) +{ + EHCIState *s = opaque; + int i; + USBDevice *devs[NB_PORTS]; + + trace_usb_ehci_reset(); + + /* + * Do the detach before touching portsc, so that it correctly gets send to + * us or to our companion based on PORTSC_POWNER before the reset. + */ + for(i = 0; i < NB_PORTS; i++) { + devs[i] = s->ports[i].dev; + if (devs[i]) { + usb_attach(&s->ports[i], NULL); + } + } + + memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); + + s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; + s->usbsts = USBSTS_HALT; + + s->astate = EST_INACTIVE; + s->pstate = EST_INACTIVE; + s->isoch_pause = -1; + s->attach_poll_counter = 0; + + for(i = 0; i < NB_PORTS; i++) { + if (s->companion_ports[i]) { + s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; + } else { + s->portsc[i] = PORTSC_PPOWER; + } + if (devs[i]) { + usb_attach(&s->ports[i], devs[i]); + } + } + ehci_queues_rip_all(s); +} + +static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) +{ + EHCIState *s = ptr; + uint32_t val; + + val = s->mmio[addr]; + + return val; +} + +static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr) +{ + EHCIState *s = ptr; + uint32_t val; + + val = s->mmio[addr] | (s->mmio[addr+1] << 8); + + return val; +} + +static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr) +{ + EHCIState *s = ptr; + uint32_t val; + + val = s->mmio[addr] | (s->mmio[addr+1] << 8) | + (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24); + + trace_usb_ehci_mmio_readl(addr, addr2str(addr), val); + return val; +} + +static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val) +{ + fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n"); + exit(1); +} + +static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) +{ + fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n"); + exit(1); +} + +static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) +{ + USBDevice *dev = s->ports[port].dev; + uint32_t *portsc = &s->portsc[port]; + uint32_t orig; + + if (s->companion_ports[port] == NULL) + return; + + owner = owner & PORTSC_POWNER; + orig = *portsc & PORTSC_POWNER; + + if (!(owner ^ orig)) { + return; + } + + if (dev) { + usb_attach(&s->ports[port], NULL); + } + + *portsc &= ~PORTSC_POWNER; + *portsc |= owner; + + if (dev) { + usb_attach(&s->ports[port], dev); + } +} + +static void handle_port_status_write(EHCIState *s, int port, uint32_t val) +{ + uint32_t *portsc = &s->portsc[port]; + USBDevice *dev = s->ports[port].dev; + + /* Clear rwc bits */ + *portsc &= ~(val & PORTSC_RWC_MASK); + /* The guest may clear, but not set the PED bit */ + *portsc &= val | ~PORTSC_PED; + /* POWNER is masked out by RO_MASK as it is RO when we've no companion */ + handle_port_owner_write(s, port, val); + /* And finally apply RO_MASK */ + val &= PORTSC_RO_MASK; + + if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { + trace_usb_ehci_port_reset(port, 1); + } + + if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { + trace_usb_ehci_port_reset(port, 0); + if (dev) { + usb_attach(&s->ports[port], dev); + usb_send_msg(dev, USB_MSG_RESET); + *portsc &= ~PORTSC_CSC; + } + + /* + * Table 2.16 Set the enable bit(and enable bit change) to indicate + * to SW that this port has a high speed device attached + */ + if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) { + val |= PORTSC_PED; + } + } + + *portsc &= ~PORTSC_RO_MASK; + *portsc |= val; +} + +static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) +{ + EHCIState *s = ptr; + uint32_t *mmio = (uint32_t *)(&s->mmio[addr]); + uint32_t old = *mmio; + int i; + + trace_usb_ehci_mmio_writel(addr, addr2str(addr), val); + + /* Only aligned reads are allowed on OHCI */ + if (addr & 3) { + fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x" + TARGET_FMT_plx "\n", addr); + return; + } + + if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) { + handle_port_status_write(s, (addr-PORTSC)/4, val); + trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old); + return; + } + + if (addr < OPREGBASE) { + fprintf(stderr, "usb-ehci: write attempt to read-only register" + TARGET_FMT_plx "\n", addr); + return; + } + + + /* Do any register specific pre-write processing here. */ + switch(addr) { + case USBCMD: + if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) { + qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); + SET_LAST_RUN_CLOCK(s); + ehci_clear_usbsts(s, USBSTS_HALT); + } + + if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) { + qemu_del_timer(s->frame_timer); + // TODO - should finish out some stuff before setting halt + ehci_set_usbsts(s, USBSTS_HALT); + } + + if (val & USBCMD_HCRESET) { + ehci_reset(s); + val &= ~USBCMD_HCRESET; + } + + /* not supporting dynamic frame list size at the moment */ + if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { + fprintf(stderr, "attempt to set frame list size -- value %d\n", + val & USBCMD_FLS); + val &= ~USBCMD_FLS; + } + break; + + case USBSTS: + val &= USBSTS_RO_MASK; // bits 6 thru 31 are RO + ehci_clear_usbsts(s, val); // bits 0 thru 5 are R/WC + val = s->usbsts; + ehci_set_interrupt(s, 0); + break; + + case USBINTR: + val &= USBINTR_MASK; + break; + + case FRINDEX: + s->sofv = val >> 3; + break; + + case CONFIGFLAG: + val &= 0x1; + if (val) { + for(i = 0; i < NB_PORTS; i++) + handle_port_owner_write(s, i, 0); + } + break; + + case PERIODICLISTBASE: + if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) { + fprintf(stderr, + "ehci: PERIODIC list base register set while periodic schedule\n" + " is enabled and HC is enabled\n"); + } + break; + + case ASYNCLISTADDR: + if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) { + fprintf(stderr, + "ehci: ASYNC list address register set while async schedule\n" + " is enabled and HC is enabled\n"); + } + break; + } + + *mmio = val; + trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old); +} + + +// TODO : Put in common header file, duplication from usb-ohci.c + +/* Get an array of dwords from main memory */ +static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + cpu_physical_memory_rw(addr,(uint8_t *)buf, sizeof(*buf), 0); + *buf = le32_to_cpu(*buf); + } + + return 1; +} + +/* Put an array of dwords in to main memory */ +static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + uint32_t tmp = cpu_to_le32(*buf); + cpu_physical_memory_rw(addr,(uint8_t *)&tmp, sizeof(tmp), 1); + } + + return 1; +} + +// 4.10.2 + +static int ehci_qh_do_overlay(EHCIQueue *q) +{ + int i; + int dtoggle; + int ping; + int eps; + int reload; + + // remember values in fields to preserve in qh after overlay + + dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE; + ping = q->qh.token & QTD_TOKEN_PING; + + q->qh.current_qtd = q->qtdaddr; + q->qh.next_qtd = q->qtd.next; + q->qh.altnext_qtd = q->qtd.altnext; + q->qh.token = q->qtd.token; + + + eps = get_field(q->qh.epchar, QH_EPCHAR_EPS); + if (eps == EHCI_QH_EPS_HIGH) { + q->qh.token &= ~QTD_TOKEN_PING; + q->qh.token |= ping; + } + + reload = get_field(q->qh.epchar, QH_EPCHAR_RL); + set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); + + for (i = 0; i < 5; i++) { + q->qh.bufptr[i] = q->qtd.bufptr[i]; + } + + if (!(q->qh.epchar & QH_EPCHAR_DTC)) { + // preserve QH DT bit + q->qh.token &= ~QTD_TOKEN_DTOGGLE; + q->qh.token |= dtoggle; + } + + q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK; + q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK; + + put_dwords(NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); + + return 0; +} + +static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) +{ + int bufpos = 0; + int cpage, offset; + uint32_t head; + uint32_t tail; + + + if (!bytes) { + return 0; + } + + cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); + if (cpage > 4) { + fprintf(stderr, "cpage out of range (%d)\n", cpage); + return USB_RET_PROCERR; + } + + offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; + + do { + /* start and end of this page */ + head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; + tail = head + ~QTD_BUFPTR_MASK + 1; + /* add offset into page */ + head |= offset; + + if (bytes <= (tail - head)) { + tail = head + bytes; + } + + trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos); + cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw); + + bufpos += (tail - head); + offset += (tail - head); + bytes -= (tail - head); + + if (bytes > 0) { + cpage++; + offset = 0; + } + } while (bytes > 0); + + /* save cpage */ + set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); + + /* save offset into cpage */ + q->qh.bufptr[0] &= QTD_BUFPTR_MASK; + q->qh.bufptr[0] |= offset; + + return 0; +} + +static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) +{ + EHCIQueue *q; + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->complete(companion, packet); + return; + } + + q = container_of(packet, EHCIQueue, packet); + trace_usb_ehci_queue_action(q, "wakeup"); + assert(q->async == EHCI_ASYNC_INFLIGHT); + q->async = EHCI_ASYNC_FINISHED; + q->usb_status = packet->len; +} + +static void ehci_execute_complete(EHCIQueue *q) +{ + int c_err, reload; + + assert(q->async != EHCI_ASYNC_INFLIGHT); + q->async = EHCI_ASYNC_NONE; + + DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", + q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); + + if (q->usb_status < 0) { +err: + /* TO-DO: put this is in a function that can be invoked below as well */ + c_err = get_field(q->qh.token, QTD_TOKEN_CERR); + c_err--; + set_field(&q->qh.token, c_err, QTD_TOKEN_CERR); + + switch(q->usb_status) { + case USB_RET_NODEV: + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); + ehci_record_interrupt(q->ehci, USBSTS_ERRINT); + break; + case USB_RET_STALL: + q->qh.token |= QTD_TOKEN_HALT; + ehci_record_interrupt(q->ehci, USBSTS_ERRINT); + break; + case USB_RET_NAK: + /* 4.10.3 */ + reload = get_field(q->qh.epchar, QH_EPCHAR_RL); + if ((q->pid == USB_TOKEN_IN) && reload) { + int nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); + nakcnt--; + set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); + } else if (!reload) { + return; + } + break; + case USB_RET_BABBLE: + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); + ehci_record_interrupt(q->ehci, USBSTS_ERRINT); + break; + default: + /* should not be triggerable */ + fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status); + assert(0); + break; + } + } else { + // DPRINTF("Short packet condition\n"); + // TODO check 4.12 for splits + + if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) { + q->usb_status = USB_RET_BABBLE; + goto err; + } + + if (q->tbytes && q->pid == USB_TOKEN_IN) { + if (ehci_buffer_rw(q, q->usb_status, 1) != 0) { + q->usb_status = USB_RET_PROCERR; + return; + } + q->tbytes -= q->usb_status; + } else { + q->tbytes = 0; + } + + DPRINTF("updating tbytes to %d\n", q->tbytes); + set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES); + } + + q->qh.token ^= QTD_TOKEN_DTOGGLE; + q->qh.token &= ~QTD_TOKEN_ACTIVE; + + if ((q->usb_status >= 0) && (q->qh.token & QTD_TOKEN_IOC)) { + ehci_record_interrupt(q->ehci, USBSTS_INT); + } +} + +// 4.10.3 + +static int ehci_execute(EHCIQueue *q) +{ + USBPort *port; + USBDevice *dev; + int ret; + int i; + int endp; + int devadr; + + if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) { + fprintf(stderr, "Attempting to execute inactive QH\n"); + return USB_RET_PROCERR; + } + + q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; + if (q->tbytes > BUFF_SIZE) { + fprintf(stderr, "Request for more bytes than allowed\n"); + return USB_RET_PROCERR; + } + + q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; + switch(q->pid) { + case 0: q->pid = USB_TOKEN_OUT; break; + case 1: q->pid = USB_TOKEN_IN; break; + case 2: q->pid = USB_TOKEN_SETUP; break; + default: fprintf(stderr, "bad token\n"); break; + } + + if ((q->tbytes && q->pid != USB_TOKEN_IN) && + (ehci_buffer_rw(q, q->tbytes, 0) != 0)) { + return USB_RET_PROCERR; + } + + endp = get_field(q->qh.epchar, QH_EPCHAR_EP); + devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); + + ret = USB_RET_NODEV; + + // TO-DO: associating device with ehci port + for(i = 0; i < NB_PORTS; i++) { + port = &q->ehci->ports[i]; + dev = port->dev; + + if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) { + DPRINTF("Port %d, no exec, not connected(%08X)\n", + i, q->ehci->portsc[i]); + continue; + } + + q->packet.pid = q->pid; + q->packet.devaddr = devadr; + q->packet.devep = endp; + q->packet.data = q->buffer; + q->packet.len = q->tbytes; + + ret = usb_handle_packet(dev, &q->packet); + + DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n", + q->qhaddr, q->qh.next, q->qtdaddr, q->pid, + q->packet.len, q->tbytes, endp, ret); + + if (ret != USB_RET_NODEV) { + break; + } + } + + if (ret > BUFF_SIZE) { + fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); + return USB_RET_PROCERR; + } + + return ret; +} + +/* 4.7.2 + */ + +static int ehci_process_itd(EHCIState *ehci, + EHCIitd *itd) +{ + USBPort *port; + USBDevice *dev; + int ret; + uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp; + uint32_t pg, off, ptr1, ptr2, max, mult; + + dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); + devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR); + endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP); + max = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT); + mult = get_field(itd->bufptr[2], ITD_BUFPTR_MULT); + + for(i = 0; i < 8; i++) { + if (itd->transact[i] & ITD_XACT_ACTIVE) { + pg = get_field(itd->transact[i], ITD_XACT_PGSEL); + off = itd->transact[i] & ITD_XACT_OFFSET_MASK; + ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); + ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK); + len = get_field(itd->transact[i], ITD_XACT_LENGTH); + + if (len > max * mult) { + len = max * mult; + } + + if (len > BUFF_SIZE) { + return USB_RET_PROCERR; + } + + if (off + len > 4096) { + /* transfer crosses page border */ + len2 = off + len - 4096; + len1 = len - len2; + } else { + len1 = len; + len2 = 0; + } + + if (!dir) { + pid = USB_TOKEN_OUT; + trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0); + cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0); + if (len2) { + trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1); + cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0); + } + } else { + pid = USB_TOKEN_IN; + } + + ret = USB_RET_NODEV; + + for (j = 0; j < NB_PORTS; j++) { + port = &ehci->ports[j]; + dev = port->dev; + + if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { + continue; + } + + ehci->ipacket.pid = pid; + ehci->ipacket.devaddr = devaddr; + ehci->ipacket.devep = endp; + ehci->ipacket.data = ehci->ibuffer; + ehci->ipacket.len = len; + + ret = usb_handle_packet(dev, &ehci->ipacket); + + if (ret != USB_RET_NODEV) { + break; + } + } + +#if 0 + /* In isoch, there is no facility to indicate a NAK so let's + * instead just complete a zero-byte transaction. Setting + * DBERR seems too draconian. + */ + + if (ret == USB_RET_NAK) { + if (ehci->isoch_pause > 0) { + DPRINTF("ISOCH: received a NAK but paused so returning\n"); + ehci->isoch_pause--; + return 0; + } else if (ehci->isoch_pause == -1) { + DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n"); + // Pause frindex for up to 50 msec waiting for data from + // remote + ehci->isoch_pause = 50; + return 0; + } else { + DPRINTF("ISOCH: isoch pause timeout! return 0\n"); + ret = 0; + } + } else { + DPRINTF("ISOCH: received ACK, clearing pause\n"); + ehci->isoch_pause = -1; + } +#else + if (ret == USB_RET_NAK) { + ret = 0; + } +#endif + + if (ret >= 0) { + if (!dir) { + /* OUT */ + set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); + } else { + /* IN */ + if (len1 > ret) { + len1 = ret; + } + if (len2 > ret - len1) { + len2 = ret - len1; + } + if (len1) { + trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0); + cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1); + } + if (len2) { + trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1); + cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1); + } + set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); + } + + if (itd->transact[i] & ITD_XACT_IOC) { + ehci_record_interrupt(ehci, USBSTS_INT); + } + } + itd->transact[i] &= ~ITD_XACT_ACTIVE; + } + } + return 0; +} + +/* This state is the entry point for asynchronous schedule + * processing. Entry here consitutes a EHCI start event state (4.8.5) + */ +static int ehci_state_waitlisthead(EHCIState *ehci, int async) +{ + EHCIqh qh; + int i = 0; + int again = 0; + uint32_t entry = ehci->asynclistaddr; + + /* set reclamation flag at start event (4.8.6) */ + if (async) { + ehci_set_usbsts(ehci, USBSTS_REC); + } + + ehci_queues_rip_unused(ehci); + + /* Find the head of the list (4.9.1.1) */ + for(i = 0; i < MAX_QH; i++) { + get_dwords(NLPTR_GET(entry), (uint32_t *) &qh, sizeof(EHCIqh) >> 2); + ehci_trace_qh(NULL, NLPTR_GET(entry), &qh); + + if (qh.epchar & QH_EPCHAR_H) { + if (async) { + entry |= (NLPTR_TYPE_QH << 1); + } + + ehci_set_fetch_addr(ehci, async, entry); + ehci_set_state(ehci, async, EST_FETCHENTRY); + again = 1; + goto out; + } + + entry = qh.next; + if (entry == ehci->asynclistaddr) { + break; + } + } + + /* no head found for list. */ + + ehci_set_state(ehci, async, EST_ACTIVE); + +out: + return again; +} + + +/* This state is the entry point for periodic schedule processing as + * well as being a continuation state for async processing. + */ +static int ehci_state_fetchentry(EHCIState *ehci, int async) +{ + int again = 0; + uint32_t entry = ehci_get_fetch_addr(ehci, async); + + if (entry < 0x1000) { + DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry); + ehci_set_state(ehci, async, EST_ACTIVE); + goto out; + } + + /* section 4.8, only QH in async schedule */ + if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) { + fprintf(stderr, "non queue head request in async schedule\n"); + return -1; + } + + switch (NLPTR_TYPE_GET(entry)) { + case NLPTR_TYPE_QH: + ehci_set_state(ehci, async, EST_FETCHQH); + again = 1; + break; + + case NLPTR_TYPE_ITD: + ehci_set_state(ehci, async, EST_FETCHITD); + again = 1; + break; + + default: + // TODO: handle siTD and FSTN types + fprintf(stderr, "FETCHENTRY: entry at %X is of type %d " + "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); + return -1; + } + +out: + return again; +} + +static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) +{ + uint32_t entry; + EHCIQueue *q; + int reload; + + entry = ehci_get_fetch_addr(ehci, async); + q = ehci_find_queue_by_qh(ehci, entry); + if (NULL == q) { + q = ehci_alloc_queue(ehci, async); + } + q->qhaddr = entry; + q->seen++; + + if (q->seen > 1) { + /* we are going in circles -- stop processing */ + ehci_set_state(ehci, async, EST_ACTIVE); + q = NULL; + goto out; + } + + get_dwords(NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); + ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); + + if (q->async == EHCI_ASYNC_INFLIGHT) { + /* I/O still in progress -- skip queue */ + ehci_set_state(ehci, async, EST_HORIZONTALQH); + goto out; + } + if (q->async == EHCI_ASYNC_FINISHED) { + /* I/O finished -- continue processing queue */ + trace_usb_ehci_queue_action(q, "resume"); + ehci_set_state(ehci, async, EST_EXECUTING); + goto out; + } + + if (async && (q->qh.epchar & QH_EPCHAR_H)) { + + /* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */ + if (ehci->usbsts & USBSTS_REC) { + ehci_clear_usbsts(ehci, USBSTS_REC); + } else { + DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" + " - done processing\n", q->qhaddr); + ehci_set_state(ehci, async, EST_ACTIVE); + q = NULL; + goto out; + } + } + +#if EHCI_DEBUG + if (q->qhaddr != q->qh.next) { + DPRINTF("FETCHQH: QH 0x%08x (h %x halt %x active %x) next 0x%08x\n", + q->qhaddr, + q->qh.epchar & QH_EPCHAR_H, + q->qh.token & QTD_TOKEN_HALT, + q->qh.token & QTD_TOKEN_ACTIVE, + q->qh.next); + } +#endif + + reload = get_field(q->qh.epchar, QH_EPCHAR_RL); + if (reload) { + set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); + } + + if (q->qh.token & QTD_TOKEN_HALT) { + ehci_set_state(ehci, async, EST_HORIZONTALQH); + + } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && (q->qh.current_qtd > 0x1000)) { + q->qtdaddr = q->qh.current_qtd; + ehci_set_state(ehci, async, EST_FETCHQTD); + + } else { + /* EHCI spec version 1.0 Section 4.10.2 */ + ehci_set_state(ehci, async, EST_ADVANCEQUEUE); + } + +out: + return q; +} + +static int ehci_state_fetchitd(EHCIState *ehci, int async) +{ + uint32_t entry; + EHCIitd itd; + + assert(!async); + entry = ehci_get_fetch_addr(ehci, async); + + get_dwords(NLPTR_GET(entry),(uint32_t *) &itd, + sizeof(EHCIitd) >> 2); + ehci_trace_itd(ehci, entry, &itd); + + if (ehci_process_itd(ehci, &itd) != 0) { + return -1; + } + + put_dwords(NLPTR_GET(entry), (uint32_t *) &itd, + sizeof(EHCIitd) >> 2); + ehci_set_fetch_addr(ehci, async, itd.next); + ehci_set_state(ehci, async, EST_FETCHENTRY); + + return 1; +} + +/* Section 4.10.2 - paragraph 3 */ +static int ehci_state_advqueue(EHCIQueue *q, int async) +{ +#if 0 + /* TO-DO: 4.10.2 - paragraph 2 + * if I-bit is set to 1 and QH is not active + * go to horizontal QH + */ + if (I-bit set) { + ehci_set_state(ehci, async, EST_HORIZONTALQH); + goto out; + } +#endif + + /* + * want data and alt-next qTD is valid + */ + if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && + (q->qh.altnext_qtd > 0x1000) && + (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { + q->qtdaddr = q->qh.altnext_qtd; + ehci_set_state(q->ehci, async, EST_FETCHQTD); + + /* + * next qTD is valid + */ + } else if ((q->qh.next_qtd > 0x1000) && + (NLPTR_TBIT(q->qh.next_qtd) == 0)) { + q->qtdaddr = q->qh.next_qtd; + ehci_set_state(q->ehci, async, EST_FETCHQTD); + + /* + * no valid qTD, try next QH + */ + } else { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + } + + return 1; +} + +/* Section 4.10.2 - paragraph 4 */ +static int ehci_state_fetchqtd(EHCIQueue *q, int async) +{ + int again = 0; + + get_dwords(NLPTR_GET(q->qtdaddr),(uint32_t *) &q->qtd, sizeof(EHCIqtd) >> 2); + ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd); + + if (q->qtd.token & QTD_TOKEN_ACTIVE) { + ehci_set_state(q->ehci, async, EST_EXECUTE); + again = 1; + } else { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + again = 1; + } + + return again; +} + +static int ehci_state_horizqh(EHCIQueue *q, int async) +{ + int again = 0; + + if (ehci_get_fetch_addr(q->ehci, async) != q->qh.next) { + ehci_set_fetch_addr(q->ehci, async, q->qh.next); + ehci_set_state(q->ehci, async, EST_FETCHENTRY); + again = 1; + } else { + ehci_set_state(q->ehci, async, EST_ACTIVE); + } + + return again; +} + +/* + * Write the qh back to guest physical memory. This step isn't + * in the EHCI spec but we need to do it since we don't share + * physical memory with our guest VM. + * + * The first three dwords are read-only for the EHCI, so skip them + * when writing back the qh. + */ +static void ehci_flush_qh(EHCIQueue *q) +{ + uint32_t *qh = (uint32_t *) &q->qh; + uint32_t dwords = sizeof(EHCIqh) >> 2; + uint32_t addr = NLPTR_GET(q->qhaddr); + + put_dwords(addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); +} + +static int ehci_state_execute(EHCIQueue *q, int async) +{ + int again = 0; + int reload, nakcnt; + int smask; + + if (ehci_qh_do_overlay(q) != 0) { + return -1; + } + + smask = get_field(q->qh.epcap, QH_EPCAP_SMASK); + + if (!smask) { + reload = get_field(q->qh.epchar, QH_EPCHAR_RL); + nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); + if (reload && !nakcnt) { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + again = 1; + goto out; + } + } + + // TODO verify enough time remains in the uframe as in 4.4.1.1 + // TODO write back ptr to async list when done or out of time + // TODO Windows does not seem to ever set the MULT field + + if (!async) { + int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); + if (!transactCtr) { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + again = 1; + goto out; + } + } + + if (async) { + ehci_set_usbsts(q->ehci, USBSTS_REC); + } + + q->usb_status = ehci_execute(q); + if (q->usb_status == USB_RET_PROCERR) { + again = -1; + goto out; + } + if (q->usb_status == USB_RET_ASYNC) { + ehci_flush_qh(q); + trace_usb_ehci_queue_action(q, "suspend"); + q->async = EHCI_ASYNC_INFLIGHT; + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + again = 1; + goto out; + } + + ehci_set_state(q->ehci, async, EST_EXECUTING); + again = 1; + +out: + return again; +} + +static int ehci_state_executing(EHCIQueue *q, int async) +{ + int again = 0; + int reload, nakcnt; + + ehci_execute_complete(q); + if (q->usb_status == USB_RET_ASYNC) { + goto out; + } + if (q->usb_status == USB_RET_PROCERR) { + again = -1; + goto out; + } + + // 4.10.3 + if (!async) { + int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); + transactCtr--; + set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT); + // 4.10.3, bottom of page 82, should exit this state when transaction + // counter decrements to 0 + } + + reload = get_field(q->qh.epchar, QH_EPCHAR_RL); + if (reload) { + nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); + if (q->usb_status == USB_RET_NAK) { + if (nakcnt) { + nakcnt--; + } + } else { + nakcnt = reload; + } + set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); + } + + /* 4.10.5 */ + if ((q->usb_status == USB_RET_NAK) || (q->qh.token & QTD_TOKEN_ACTIVE)) { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + } else { + ehci_set_state(q->ehci, async, EST_WRITEBACK); + } + + again = 1; + +out: + ehci_flush_qh(q); + return again; +} + + +static int ehci_state_writeback(EHCIQueue *q, int async) +{ + int again = 0; + + /* Write back the QTD from the QH area */ + ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd); + put_dwords(NLPTR_GET(q->qtdaddr),(uint32_t *) &q->qh.next_qtd, + sizeof(EHCIqtd) >> 2); + + /* + * EHCI specs say go horizontal here. + * + * We can also advance the queue here for performance reasons. We + * need to take care to only take that shortcut in case we've + * processed the qtd just written back without errors, i.e. halt + * bit is clear. + */ + if (q->qh.token & QTD_TOKEN_HALT) { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + again = 1; + } else { + ehci_set_state(q->ehci, async, EST_ADVANCEQUEUE); + again = 1; + } + return again; +} + +/* + * This is the state machine that is common to both async and periodic + */ + +static void ehci_advance_state(EHCIState *ehci, + int async) +{ + EHCIQueue *q = NULL; + int again; + int iter = 0; + + do { + if (ehci_get_state(ehci, async) == EST_FETCHQH) { + iter++; + /* if we are roaming a lot of QH without executing a qTD + * something is wrong with the linked list. TO-DO: why is + * this hack needed? + */ + assert(iter < MAX_ITERATIONS); +#if 0 + if (iter > MAX_ITERATIONS) { + DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n"); + ehci_set_state(ehci, async, EST_ACTIVE); + break; + } +#endif + } + switch(ehci_get_state(ehci, async)) { + case EST_WAITLISTHEAD: + again = ehci_state_waitlisthead(ehci, async); + break; + + case EST_FETCHENTRY: + again = ehci_state_fetchentry(ehci, async); + break; + + case EST_FETCHQH: + q = ehci_state_fetchqh(ehci, async); + again = q ? 1 : 0; + break; + + case EST_FETCHITD: + again = ehci_state_fetchitd(ehci, async); + break; + + case EST_ADVANCEQUEUE: + again = ehci_state_advqueue(q, async); + break; + + case EST_FETCHQTD: + again = ehci_state_fetchqtd(q, async); + break; + + case EST_HORIZONTALQH: + again = ehci_state_horizqh(q, async); + break; + + case EST_EXECUTE: + iter = 0; + again = ehci_state_execute(q, async); + break; + + case EST_EXECUTING: + assert(q != NULL); + again = ehci_state_executing(q, async); + break; + + case EST_WRITEBACK: + again = ehci_state_writeback(q, async); + break; + + default: + fprintf(stderr, "Bad state!\n"); + again = -1; + assert(0); + break; + } + + if (again < 0) { + fprintf(stderr, "processing error - resetting ehci HC\n"); + ehci_reset(ehci); + again = 0; + assert(0); + } + } + while (again); + + ehci_commit_interrupt(ehci); +} + +static void ehci_advance_async_state(EHCIState *ehci) +{ + int async = 1; + + switch(ehci_get_state(ehci, async)) { + case EST_INACTIVE: + if (!(ehci->usbcmd & USBCMD_ASE)) { + break; + } + ehci_set_usbsts(ehci, USBSTS_ASS); + ehci_set_state(ehci, async, EST_ACTIVE); + // No break, fall through to ACTIVE + + case EST_ACTIVE: + if ( !(ehci->usbcmd & USBCMD_ASE)) { + ehci_clear_usbsts(ehci, USBSTS_ASS); + ehci_set_state(ehci, async, EST_INACTIVE); + break; + } + + /* If the doorbell is set, the guest wants to make a change to the + * schedule. The host controller needs to release cached data. + * (section 4.8.2) + */ + if (ehci->usbcmd & USBCMD_IAAD) { + DPRINTF("ASYNC: doorbell request acknowledged\n"); + ehci->usbcmd &= ~USBCMD_IAAD; + ehci_set_interrupt(ehci, USBSTS_IAA); + break; + } + + /* make sure guest has acknowledged */ + /* TO-DO: is this really needed? */ + if (ehci->usbsts & USBSTS_IAA) { + DPRINTF("IAA status bit still set.\n"); + break; + } + + /* check that address register has been set */ + if (ehci->asynclistaddr == 0) { + break; + } + + ehci_set_state(ehci, async, EST_WAITLISTHEAD); + ehci_advance_state(ehci, async); + break; + + default: + /* this should only be due to a developer mistake */ + fprintf(stderr, "ehci: Bad asynchronous state %d. " + "Resetting to active\n", ehci->astate); + assert(0); + } +} + +static void ehci_advance_periodic_state(EHCIState *ehci) +{ + uint32_t entry; + uint32_t list; + int async = 0; + + // 4.6 + + switch(ehci_get_state(ehci, async)) { + case EST_INACTIVE: + if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) { + ehci_set_usbsts(ehci, USBSTS_PSS); + ehci_set_state(ehci, async, EST_ACTIVE); + // No break, fall through to ACTIVE + } else + break; + + case EST_ACTIVE: + if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { + ehci_clear_usbsts(ehci, USBSTS_PSS); + ehci_set_state(ehci, async, EST_INACTIVE); + break; + } + + list = ehci->periodiclistbase & 0xfffff000; + /* check that register has been set */ + if (list == 0) { + break; + } + list |= ((ehci->frindex & 0x1ff8) >> 1); + + cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0); + entry = le32_to_cpu(entry); + + DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", + ehci->frindex / 8, list, entry); + ehci_set_fetch_addr(ehci, async,entry); + ehci_set_state(ehci, async, EST_FETCHENTRY); + ehci_advance_state(ehci, async); + break; + + default: + /* this should only be due to a developer mistake */ + fprintf(stderr, "ehci: Bad periodic state %d. " + "Resetting to active\n", ehci->pstate); + assert(0); + } +} + +static void ehci_frame_timer(void *opaque) +{ + EHCIState *ehci = opaque; + int64_t expire_time, t_now; + uint64_t ns_elapsed; + int frames; + int i; + int skipped_frames = 0; + + t_now = qemu_get_clock_ns(vm_clock); + expire_time = t_now + (get_ticks_per_sec() / ehci->freq); + + ns_elapsed = t_now - ehci->last_run_ns; + frames = ns_elapsed / FRAME_TIMER_NS; + + for (i = 0; i < frames; i++) { + if ( !(ehci->usbsts & USBSTS_HALT)) { + if (ehci->isoch_pause <= 0) { + ehci->frindex += 8; + } + + if (ehci->frindex > 0x00001fff) { + ehci->frindex = 0; + ehci_set_interrupt(ehci, USBSTS_FLR); + } + + ehci->sofv = (ehci->frindex - 1) >> 3; + ehci->sofv &= 0x000003ff; + } + + if (frames - i > ehci->maxframes) { + skipped_frames++; + } else { + ehci_advance_periodic_state(ehci); + } + + ehci->last_run_ns += FRAME_TIMER_NS; + } + +#if 0 + if (skipped_frames) { + DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames); + } +#endif + + /* Async is not inside loop since it executes everything it can once + * called + */ + ehci_advance_async_state(ehci); + + qemu_mod_timer(ehci->frame_timer, expire_time); +} + +static CPUReadMemoryFunc *ehci_readfn[3]={ + ehci_mem_readb, + ehci_mem_readw, + ehci_mem_readl +}; + +static CPUWriteMemoryFunc *ehci_writefn[3]={ + ehci_mem_writeb, + ehci_mem_writew, + ehci_mem_writel +}; + +static void ehci_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + EHCIState *s =(EHCIState *)pci_dev; + + DPRINTF("ehci_map: region %d, addr %08" PRIx64 ", size %" PRId64 ", s->mem %08X\n", + region_num, addr, size, s->mem); + s->mem_base = addr; + cpu_register_physical_memory(addr, size, s->mem); +} + +static int usb_ehci_initfn(PCIDevice *dev); + +static USBPortOps ehci_port_ops = { + .attach = ehci_attach, + .detach = ehci_detach, + .child_detach = ehci_child_detach, + .wakeup = ehci_wakeup, + .complete = ehci_async_complete_packet, +}; + +static USBBusOps ehci_bus_ops = { + .register_companion = ehci_register_companion, +}; + +static Property ehci_properties[] = { + DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), + DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), + DEFINE_PROP_END_OF_LIST(), +}; + +static PCIDeviceInfo ehci_info[] = { + { + .qdev.name = "usb-ehci", + .qdev.size = sizeof(EHCIState), + .init = usb_ehci_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ + .revision = 0x10, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = ehci_properties, + },{ + .qdev.name = "ich9-usb-ehci1", + .qdev.size = sizeof(EHCIState), + .init = usb_ehci_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = ehci_properties, + },{ + /* end of list */ + } +}; + +static int usb_ehci_initfn(PCIDevice *dev) +{ + EHCIState *s = DO_UPCAST(EHCIState, dev, dev); + uint8_t *pci_conf = s->dev.config; + int i; + + pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); + + /* capabilities pointer */ + pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); + //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); + + pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrupt pin 3 + pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); + pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); + + // pci_conf[0x50] = 0x01; // power management caps + + pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); // release number (2.1.4) + pci_set_byte(&pci_conf[0x61], 0x20); // frame length adjustment (2.1.5) + pci_set_word(&pci_conf[0x62], 0x00); // port wake up capability (2.1.6) + + pci_conf[0x64] = 0x00; + pci_conf[0x65] = 0x00; + pci_conf[0x66] = 0x00; + pci_conf[0x67] = 0x00; + pci_conf[0x68] = 0x01; + pci_conf[0x69] = 0x00; + pci_conf[0x6a] = 0x00; + pci_conf[0x6b] = 0x00; // USBLEGSUP + pci_conf[0x6c] = 0x00; + pci_conf[0x6d] = 0x00; + pci_conf[0x6e] = 0x00; + pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS + + // 2.2 host controller interface version + s->mmio[0x00] = (uint8_t) OPREGBASE; + s->mmio[0x01] = 0x00; + s->mmio[0x02] = 0x00; + s->mmio[0x03] = 0x01; // HC version + s->mmio[0x04] = NB_PORTS; // Number of downstream ports + s->mmio[0x05] = 0x00; // No companion ports at present + s->mmio[0x06] = 0x00; + s->mmio[0x07] = 0x00; + s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable + s->mmio[0x09] = 0x68; // EECP + s->mmio[0x0a] = 0x00; + s->mmio[0x0b] = 0x00; + + s->irq = s->dev.irq[3]; + + usb_bus_new(&s->bus, &ehci_bus_ops, &s->dev.qdev); + for(i = 0; i < NB_PORTS; i++) { + usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, + USB_SPEED_MASK_HIGH); + s->ports[i].dev = 0; + } + + s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s); + QTAILQ_INIT(&s->queues); + + qemu_register_reset(ehci_reset, s); + + s->mem = cpu_register_io_memory(ehci_readfn, ehci_writefn, s, + DEVICE_LITTLE_ENDIAN); + + pci_register_bar(&s->dev, 0, MMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY, + ehci_map); + + fprintf(stderr, "*** EHCI support is under development ***\n"); + + return 0; +} + +static void ehci_register(void) +{ + pci_qdev_register_many(ehci_info); +} +device_init(ehci_register); + +/* + * vim: expandtab ts=4 + */ diff -Nru qemu-kvm-0.14.1+noroms/hw/usb.h qemu-kvm-0.15.0+noroms/hw/usb.h --- qemu-kvm-0.14.1+noroms/hw/usb.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb.h 2011-08-09 07:40:29.000000000 -0500 @@ -26,6 +26,12 @@ #include "qdev.h" #include "qemu-queue.h" +/* Constants related to the USB / PCI interaction */ +#define USB_SBRN 0x60 /* Serial Bus Release Number Register */ +#define USB_RELEASE_1 0x10 /* USB 1.0 */ +#define USB_RELEASE_2 0x20 /* USB 2.0 */ +#define USB_RELEASE_3 0x30 /* USB 3.0 */ + #define USB_TOKEN_SETUP 0x2d #define USB_TOKEN_IN 0x69 /* device -> host */ #define USB_TOKEN_OUT 0xe1 /* host -> device */ @@ -124,6 +130,8 @@ #define USB_DT_ENDPOINT 0x05 #define USB_DT_DEVICE_QUALIFIER 0x06 #define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_DEBUG 0x0A +#define USB_DT_INTERFACE_ASSOC 0x0B #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 @@ -131,6 +139,7 @@ #define USB_ENDPOINT_XFER_INT 3 typedef struct USBBus USBBus; +typedef struct USBBusOps USBBusOps; typedef struct USBPort USBPort; typedef struct USBDevice USBDevice; typedef struct USBDeviceInfo USBDeviceInfo; @@ -140,6 +149,7 @@ typedef struct USBDescID USBDescID; typedef struct USBDescDevice USBDescDevice; typedef struct USBDescConfig USBDescConfig; +typedef struct USBDescIfaceAssoc USBDescIfaceAssoc; typedef struct USBDescIface USBDescIface; typedef struct USBDescEndpoint USBDescEndpoint; typedef struct USBDescOther USBDescOther; @@ -159,7 +169,10 @@ char *port_path; void *opaque; + /* Actual connected speed */ int speed; + /* Supported speeds, not in info because it may be variable (hostdevs) */ + int speedmask; uint8_t addr; char product_desc[32]; int auto_attach; @@ -167,7 +180,7 @@ int32_t state; uint8_t setup_buf[8]; - uint8_t data_buf[1024]; + uint8_t data_buf[4096]; int32_t remote_wakeup; int32_t setup_state; int32_t setup_len; @@ -192,6 +205,11 @@ int (*handle_packet)(USBDevice *dev, USBPacket *p); /* + * Called when a packet is canceled. + */ + void (*cancel_packet)(USBDevice *dev, USBPacket *p); + + /* * Called when device is destroyed. */ void (*handle_destroy)(USBDevice *dev); @@ -212,7 +230,7 @@ * * Returns length or one of the USB_RET_ codes. */ - int (*handle_control)(USBDevice *dev, int request, int value, + int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data); /* @@ -234,7 +252,18 @@ typedef struct USBPortOps { void (*attach)(USBPort *port); void (*detach)(USBPort *port); - void (*wakeup)(USBDevice *dev); + /* + * This gets called when a device downstream from the device attached to + * the port (iow attached through a hub) gets detached. + */ + void (*child_detach)(USBPort *port, USBDevice *child); + void (*wakeup)(USBPort *port); + /* + * Note that port->dev will be different then the device from which + * the packet originated when a hub is involved, if you want the orginating + * device use p->owner + */ + void (*complete)(USBPort *port, USBPacket *p); } USBPortOps; /* USB port on which a device can be connected */ @@ -259,40 +288,17 @@ uint8_t *data; int len; /* Internal use by the USB layer. */ - USBCallback *complete_cb; - void *complete_opaque; - USBCallback *cancel_cb; - void *cancel_opaque; + USBDevice *owner; }; -/* Defer completion of a USB packet. The hadle_packet routine should then - return USB_RET_ASYNC. Packets that complete immediately (before - handle_packet returns) should not call this method. */ -static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel, - void * opaque) -{ - p->cancel_cb = cancel; - p->cancel_opaque = opaque; -} - -/* Notify the controller that an async packet is complete. This should only - be called for packets previously deferred with usb_defer_packet, and - should never be called from within handle_packet. */ -static inline void usb_packet_complete(USBPacket *p) -{ - p->complete_cb(p, p->complete_opaque); -} - -/* Cancel an active packet. The packed must have been deferred with - usb_defer_packet, and not yet completed. */ -static inline void usb_cancel_packet(USBPacket * p) -{ - p->cancel_cb(p, p->cancel_opaque); -} +int usb_handle_packet(USBDevice *dev, USBPacket *p); +void usb_packet_complete(USBDevice *dev, USBPacket *p); +void usb_cancel_packet(USBPacket * p); void usb_attach(USBPort *port, USBDevice *dev); void usb_wakeup(USBDevice *dev); int usb_generic_handle_packet(USBDevice *s, USBPacket *p); +void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); void usb_send_msg(USBDevice *dev, int msg); @@ -338,6 +344,7 @@ struct USBBus { BusState qbus; + USBBusOps *ops; int busnr; int nfree; int nused; @@ -346,7 +353,12 @@ QTAILQ_ENTRY(USBBus) next; }; -void usb_bus_new(USBBus *bus, DeviceState *host); +struct USBBusOps { + int (*register_companion)(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport); +}; + +void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); USBBus *usb_bus_find(int busnr); void usb_qdev_register(USBDeviceInfo *info); void usb_qdev_register_many(USBDeviceInfo *info); @@ -355,6 +367,9 @@ USBDevice *usbdevice_create(const char *cmdline); void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, USBPortOps *ops, int speedmask); +int usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, 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); diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-hid.c qemu-kvm-0.15.0+noroms/hw/usb-hid.c --- qemu-kvm-0.14.1+noroms/hw/usb-hid.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-hid.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,7 +26,7 @@ #include "console.h" #include "usb.h" #include "usb-desc.h" -#include "sysemu.h" +#include "qemu-timer.h" /* HID interface requests */ #define GET_REPORT 0xa101 @@ -142,7 +142,6 @@ .bInterfaceNumber = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ .bInterfaceProtocol = 0x02, .ndesc = 1, .descs = (USBDescOther[]) { @@ -211,6 +210,7 @@ .iConfiguration = STR_CONFIG_MOUSE, .bmAttributes = 0xa0, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface_mouse, }, }, @@ -227,6 +227,7 @@ .iConfiguration = STR_CONFIG_TABLET, .bmAttributes = 0xa0, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface_tablet, }, }, @@ -243,6 +244,7 @@ .iConfiguration = STR_CONFIG_KEYBOARD, .bmAttributes = 0xa0, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface_keyboard, }, }, @@ -529,18 +531,15 @@ 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; } @@ -724,13 +723,13 @@ 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) +static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBHIDState *s = (USBHIDState *)dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } @@ -767,10 +766,12 @@ } break; case GET_REPORT: - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) + if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { ret = usb_pointer_poll(s, data, length); - else if (s->kind == USB_KEYBOARD) + } else if (s->kind == USB_KEYBOARD) { ret = usb_keyboard_poll(s, data, length); + } + s->changed = s->n > 0; break; case SET_REPORT: if (s->kind == USB_KEYBOARD) @@ -779,13 +780,13 @@ goto fail; break; case GET_PROTOCOL: - if (s->kind != USB_KEYBOARD) + if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) goto fail; ret = 1; data[0] = s->protocol; break; case SET_PROTOCOL: - if (s->kind != USB_KEYBOARD) + if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) goto fail; ret = 0; s->protocol = value; @@ -796,7 +797,7 @@ break; case SET_IDLE: s->idle = (uint8_t) (value >> 8); - usb_hid_set_next_idle(s, qemu_get_clock(vm_clock)); + usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); ret = 0; break; default: @@ -815,7 +816,7 @@ switch(p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { - int64_t curtime = qemu_get_clock(vm_clock); + int64_t curtime = qemu_get_clock_ns(vm_clock); if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0)) return USB_RET_NAK; usb_hid_set_next_idle(s, curtime); @@ -900,7 +901,7 @@ USBHIDState *s = opaque; if (s->idle) { - usb_hid_set_next_idle(s, qemu_get_clock(vm_clock)); + usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); } return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-hub.c qemu-kvm-0.15.0+noroms/hw/usb-hub.c --- qemu-kvm-0.14.1+noroms/hw/usb-hub.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-hub.c 2011-08-09 07:40:29.000000000 -0500 @@ -119,6 +119,7 @@ .bNumInterfaces = 1, .bConfigurationValue = 1, .bmAttributes = 0xe0, + .nif = 1, .ifs = &desc_iface_hub, }, }, @@ -137,74 +138,6 @@ .str = desc_strings, }; -static const uint8_t qemu_hub_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x10, 0x01, /* u16 bcdUSB; v1.1 */ - - 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - 0x00, 0x00, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x01, 0x01, /* u16 bcdDevice */ - - 0x03, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x01, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ -}; - -/* XXX: patch interrupt size */ -static const uint8_t qemu_hub_config_descriptor[] = { - - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xe0, /* 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 - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* u8 if_bInterfaceSubClass; */ - 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x00, /* u8 if_iInterface; */ - - /* 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 */ - 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ - 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - static const uint8_t qemu_hub_hub_descriptor[] = { 0x00, /* u8 bLength; patched in later */ @@ -237,6 +170,9 @@ USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; + /* Let upstream know the device on this port is gone */ + s->dev.port->ops->child_detach(s->dev.port, port1->dev); + port->wPortStatus &= ~PORT_STAT_CONNECTION; port->wPortChange |= PORT_STAT_C_CONNECTION; if (port->wPortStatus & PORT_STAT_ENABLE) { @@ -245,10 +181,18 @@ } } -static void usb_hub_wakeup(USBDevice *dev) +static void usb_hub_child_detach(USBPort *port1, USBDevice *child) { - USBHubState *s = dev->port->opaque; - USBHubPort *port = &s->ports[dev->port->index]; + USBHubState *s = port1->opaque; + + /* Pass along upstream */ + s->dev.port->ops->child_detach(s->dev.port, child); +} + +static void usb_hub_wakeup(USBPort *port1) +{ + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; if (port->wPortStatus & PORT_STAT_SUSPEND) { port->wPortChange |= PORT_STAT_C_SUSPEND; @@ -256,6 +200,19 @@ } } +static void usb_hub_complete(USBPort *port, USBPacket *packet) +{ + USBHubState *s = port->opaque; + + /* + * Just pass it along upstream for now. + * + * If we ever inplement usb 2.0 split transactions this will + * become a little more complicated ... + */ + usb_packet_complete(&s->dev, packet); +} + static void usb_hub_handle_attach(USBDevice *dev) { USBHubState *s = DO_UPCAST(USBHubState, dev, dev); @@ -271,13 +228,13 @@ /* XXX: do it */ } -static int usb_hub_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_hub_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBHubState *s = (USBHubState *)dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } @@ -481,7 +438,7 @@ port = &s->ports[i]; dev = port->port.dev; if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { - ret = dev->info->handle_packet(dev, p); + ret = usb_handle_packet(dev, p); if (ret != USB_RET_NODEV) { return ret; } @@ -523,7 +480,9 @@ static USBPortOps usb_hub_port_ops = { .attach = usb_hub_attach, .detach = usb_hub_detach, + .child_detach = usb_hub_child_detach, .wakeup = usb_hub_wakeup, + .complete = usb_hub_complete, }; static int usb_hub_initfn(USBDevice *dev) diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-msd.c qemu-kvm-0.15.0+noroms/hw/usb-msd.c --- qemu-kvm-0.14.1+noroms/hw/usb-msd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-msd.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" @@ -33,7 +33,7 @@ enum USBMSDMode { USB_MSDM_CBW, /* Command Block. */ - USB_MSDM_DATAOUT, /* Tranfer data to device. */ + USB_MSDM_DATAOUT, /* Transfer data to device. */ USB_MSDM_DATAIN, /* Transfer data from device. */ USB_MSDM_CSW /* Command Status. */ }; @@ -48,8 +48,10 @@ uint32_t data_len; uint32_t residue; uint32_t tag; + SCSIRequest *req; SCSIBus bus; BlockConf conf; + char *serial; SCSIDevice *scsi_dev; uint32_t removable; int result; @@ -119,6 +121,7 @@ .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_FULL, .bmAttributes = 0xc0, + .nif = 1, .ifs = &desc_iface_full, }, }, @@ -153,6 +156,7 @@ .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_HIGH, .bmAttributes = 0xc0, + .nif = 1, .ifs = &desc_iface_high, }, }, @@ -189,11 +193,7 @@ s->scsi_buf += len; s->data_len -= len; 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) { - s->scsi_dev->info->write_data(s->scsi_dev, s->tag); - } + scsi_req_continue(s->req); } } @@ -211,54 +211,71 @@ memcpy(p->data, &csw, len); } -static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) { - MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent); + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - if (tag != s->tag) { - fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag); - } - if (reason == SCSI_REASON_DONE) { - DPRINTF("Command complete %d\n", arg); - s->residue = s->data_len; - s->result = arg != 0; - if (s->packet) { - 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, p); - s->mode = USB_MSDM_CBW; - } else { - if (s->data_len) { - s->data_len -= s->usb_len; - if (s->mode == USB_MSDM_DATAIN) - memset(s->usb_buf, 0, s->usb_len); - s->usb_len = 0; - } - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - } - s->packet = NULL; - usb_packet_complete(p); - } else if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - return; - } - s->scsi_len = arg; - s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag); + assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); + s->scsi_len = len; + s->scsi_buf = scsi_req_get_buf(req); if (p) { usb_msd_copy_data(s); - if (s->usb_len == 0) { + if (s->packet && s->usb_len == 0) { /* Set s->packet to NULL before calling usb_packet_complete - because annother request may be issued before + because another request may be issued before usb_packet_complete returns. */ DPRINTF("Packet complete %p\n", p); s->packet = NULL; - usb_packet_complete(p); + usb_packet_complete(&s->dev, p); + } + } +} + +static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) +{ + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); + USBPacket *p = s->packet; + + DPRINTF("Command complete %d\n", status); + s->residue = s->data_len; + s->result = status != 0; + if (s->packet) { + 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, p); + s->mode = USB_MSDM_CBW; + } else { + if (s->data_len) { + s->data_len -= s->usb_len; + if (s->mode == USB_MSDM_DATAIN) { + memset(s->usb_buf, 0, s->usb_len); + } + s->usb_len = 0; + } + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } } + s->packet = NULL; + usb_packet_complete(&s->dev, p); + } else if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + scsi_req_unref(req); + s->req = NULL; +} + +static void usb_msd_request_cancelled(SCSIRequest *req) +{ + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); + + if (req == s->req) { + scsi_req_unref(s->req); + s->req = NULL; + s->packet = NULL; + s->scsi_len = 0; } } @@ -270,13 +287,13 @@ s->mode = USB_MSDM_CBW; } -static int usb_msd_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_msd_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { MSDState *s = (MSDState *)dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } @@ -313,12 +330,10 @@ return ret; } -static void usb_msd_cancel_io(USBPacket *p, void *opaque) +static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p) { - MSDState *s = opaque; - s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag); - s->packet = NULL; - s->scsi_len = 0; + MSDState *s = DO_UPCAST(MSDState, dev, dev); + scsi_req_cancel(s->req); } static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) @@ -364,15 +379,13 @@ DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue = 0; - s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0); + s->scsi_len = 0; + s->req = scsi_req_new(s->scsi_dev, s->tag, 0, NULL); + scsi_req_enqueue(s->req, cbw.cmd); /* ??? Should check that USB and SCSI data transfer directions match. */ - if (s->residue == 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) { - s->scsi_dev->info->write_data(s->scsi_dev, s->tag); - } + if (s->mode != USB_MSDM_CSW && s->residue == 0) { + scsi_req_continue(s->req); } ret = len; break; @@ -395,7 +408,6 @@ } if (s->usb_len) { DPRINTF("Deferring packet %p\n", p); - usb_defer_packet(p, usb_msd_cancel_io, s); s->packet = p; ret = USB_RET_ASYNC; } else { @@ -418,7 +430,6 @@ if (s->data_len != 0 || len < 13) goto fail; /* Waiting for SCSI write to complete. */ - usb_defer_packet(p, usb_msd_cancel_io, s); s->packet = p; ret = USB_RET_ASYNC; break; @@ -452,7 +463,6 @@ } if (s->usb_len) { DPRINTF("Deferring packet %p\n", p); - usb_defer_packet(p, usb_msd_cancel_io, s); s->packet = p; ret = USB_RET_ASYNC; } else { @@ -481,11 +491,18 @@ MSDState *s = opaque; if (!err) - usb_device_attach(&s->dev); - else + err = usb_device_attach(&s->dev); + + if (err) qdev_unplug(&s->dev.qdev); } +static const struct SCSIBusOps usb_msd_scsi_ops = { + .transfer_data = usb_msd_transfer_data, + .complete = usb_msd_command_complete, + .cancel = usb_msd_request_cancelled +}; + static int usb_msd_initfn(USBDevice *dev) { MSDState *s = DO_UPCAST(MSDState, dev, dev); @@ -509,13 +526,19 @@ 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); + if (!s->serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(bs); + if (*dinfo->serial) { + s->serial = strdup(dinfo->serial); + } + } + if (s->serial) { + usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial); } usb_desc_init(dev); - scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete); + scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops); s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable); if (!s->scsi_dev) { return -1; @@ -601,6 +624,7 @@ .usb_desc = &desc, .init = usb_msd_initfn, .handle_packet = usb_generic_handle_packet, + .cancel_packet = usb_msd_cancel_io, .handle_attach = usb_desc_attach, .handle_reset = usb_msd_handle_reset, .handle_control = usb_msd_handle_control, @@ -609,6 +633,7 @@ .usbdevice_init = usb_msd_init, .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(MSDState, conf), + DEFINE_PROP_STRING("serial", MSDState, serial), DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }, diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-musb.c qemu-kvm-0.15.0+noroms/hw/usb-musb.c --- qemu-kvm-0.14.1+noroms/hw/usb-musb.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-musb.c 2011-08-09 07:40:29.000000000 -0500 @@ -261,13 +261,30 @@ static void musb_attach(USBPort *port); static void musb_detach(USBPort *port); +static void musb_child_detach(USBPort *port, USBDevice *child); +static void musb_schedule_cb(USBPort *port, USBPacket *p); +static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); static USBPortOps musb_port_ops = { .attach = musb_attach, .detach = musb_detach, + .child_detach = musb_child_detach, + .complete = musb_schedule_cb, }; -typedef struct { +static USBBusOps musb_bus_ops = { +}; + +typedef struct MUSBPacket MUSBPacket; +typedef struct MUSBEndPoint MUSBEndPoint; + +struct MUSBPacket { + USBPacket p; + MUSBEndPoint *ep; + int dir; +}; + +struct MUSBEndPoint { uint16_t faddr[2]; uint8_t haddr[2]; uint8_t hport[2]; @@ -284,7 +301,7 @@ int fifolen[2]; int fifostart[2]; int fifoaddr[2]; - USBPacket packey[2]; + MUSBPacket packey[2]; int status[2]; int ext_size[2]; @@ -294,7 +311,7 @@ MUSBState *musb; USBCallback *delayed_cb[2]; QEMUTimer *intv_timer[2]; -} MUSBEndPoint; +}; struct MUSBState { qemu_irq *irqs; @@ -321,7 +338,9 @@ /* Duplicating the world since 2008!... probably we should have 32 * logical, single endpoints instead. */ MUSBEndPoint ep[16]; -} *musb_init(qemu_irq *irqs) +}; + +struct MUSBState *musb_init(qemu_irq *irqs) { MUSBState *s = qemu_mallocz(sizeof(*s)); int i; @@ -348,10 +367,9 @@ s->ep[i].epnum = i; } - usb_bus_new(&s->bus, NULL /* FIXME */); + usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */); 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; } @@ -480,29 +498,40 @@ { MUSBState *s = (MUSBState *) port->opaque; + musb_async_cancel_device(s, port->dev); + musb_intr_set(s, musb_irq_disconnect, 1); musb_session_update(s, 1, s->session); } -static inline void musb_cb_tick0(void *opaque) +static void musb_child_detach(USBPort *port, USBDevice *child) +{ + MUSBState *s = (MUSBState *) port->opaque; + + musb_async_cancel_device(s, child); +} + +static void musb_cb_tick0(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - ep->delayed_cb[0](&ep->packey[0], opaque); + ep->delayed_cb[0](&ep->packey[0].p, opaque); } -static inline void musb_cb_tick1(void *opaque) +static void musb_cb_tick1(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - ep->delayed_cb[1](&ep->packey[1], opaque); + ep->delayed_cb[1](&ep->packey[1].p, opaque); } #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) -static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir) +static void musb_schedule_cb(USBPort *port, USBPacket *packey) { - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; + MUSBPacket *p = container_of(packey, MUSBPacket, p); + MUSBEndPoint *ep = p->ep; + int dir = p->dir; int timeout = 0; if (ep->status[dir] == USB_RET_NAK) @@ -510,25 +539,15 @@ else if (ep->interrupt[dir]) timeout = 8; else - return musb_cb_tick(opaque); + return musb_cb_tick(ep); if (!ep->intv_timer[dir]) - ep->intv_timer[dir] = qemu_new_timer(vm_clock, musb_cb_tick, opaque); + ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, ep); - qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock(vm_clock) + + qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock_ns(vm_clock) + muldiv64(timeout, get_ticks_per_sec(), 8000)); } -static void musb_schedule0_cb(USBPacket *packey, void *opaque) -{ - return musb_schedule_cb(packey, opaque, 0); -} - -static void musb_schedule1_cb(USBPacket *packey, void *opaque) -{ - return musb_schedule_cb(packey, opaque, 1); -} - static int musb_timeout(int ttype, int speed, int val) { #if 1 @@ -567,7 +586,7 @@ hw_error("bad interval\n"); } -static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, +static void musb_packet(MUSBState *s, MUSBEndPoint *ep, int epnum, int pid, int len, USBCallback cb, int dir) { int ret; @@ -585,19 +604,18 @@ ep->type[idx] >> 6, ep->interval[idx]); ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->delayed_cb[dir] = cb; - cb = dir ? musb_schedule1_cb : musb_schedule0_cb; - ep->packey[dir].pid = pid; + ep->packey[dir].p.pid = pid; /* A wild guess on the FADDR semantics... */ - ep->packey[dir].devaddr = ep->faddr[idx]; - ep->packey[dir].devep = ep->type[idx] & 0xf; - ep->packey[dir].data = (void *) ep->buf[idx]; - ep->packey[dir].len = len; - ep->packey[dir].complete_cb = cb; - ep->packey[dir].complete_opaque = ep; + ep->packey[dir].p.devaddr = ep->faddr[idx]; + ep->packey[dir].p.devep = ep->type[idx] & 0xf; + ep->packey[dir].p.data = (void *) ep->buf[idx]; + ep->packey[dir].p.len = len; + ep->packey[dir].ep = ep; + ep->packey[dir].dir = dir; if (s->port.dev) - ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir]); + ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p); else ret = USB_RET_NODEV; @@ -607,7 +625,7 @@ } ep->status[dir] = ret; - usb_packet_complete(&ep->packey[dir]); + musb_schedule_cb(&s->port, &ep->packey[dir].p); } static void musb_tx_packet_complete(USBPacket *packey, void *opaque) @@ -774,6 +792,21 @@ musb_rx_intr_set(s, epnum, 1); } +static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) +{ + int ep, dir; + + for (ep = 0; ep < 16; ep++) { + for (dir = 0; dir < 2; dir++) { + if (s->ep[ep].packey[dir].p.owner != dev) { + continue; + } + usb_cancel_packet(&s->ep[ep].packey[dir].p); + /* status updates needed here? */ + } + } +} + static void musb_tx_rdy(MUSBState *s, int epnum) { MUSBEndPoint *ep = s->ep + epnum; @@ -821,14 +854,14 @@ /* 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 && + if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].len) { + ep->packey[1].p.len) { 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]), + ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; @@ -866,10 +899,11 @@ #ifdef SETUPLEN_HACK /* Why should *we* do that instead of Linux? */ if (!epnum) { - if (ep->packey[0].devaddr == 2) + if (ep->packey[0].p.devaddr == 2) { total = MIN(s->setup_len, 8); - else + } else { total = MIN(s->setup_len, 64); + } s->setup_len -= total; } #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-net.c qemu-kvm-0.15.0+noroms/hw/usb-net.c --- qemu-kvm-0.14.1+noroms/hw/usb-net.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-net.c 2011-08-09 07:40:29.000000000 -0500 @@ -1042,13 +1042,13 @@ { } -static int usb_net_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_net_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBNetState *s = (USBNetState *) dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-ohci.c qemu-kvm-0.15.0+noroms/hw/usb-ohci.c --- qemu-kvm-0.14.1+noroms/hw/usb-ohci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-ohci.c 2011-08-09 07:40:29.000000000 -0500 @@ -124,6 +124,7 @@ }; static void ohci_bus_stop(OHCIState *ohci); +static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); /* Bitfields for the first word of an Endpoint Desciptor. */ #define OHCI_ED_FA_SHIFT 0 @@ -326,6 +327,7 @@ { OHCIState *s = port1->opaque; OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; /* set connect status */ port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; @@ -343,6 +345,10 @@ } DPRINTF("usb-ohci: Attached port %d\n", port1->index); + + if (old_state != port->ctrl) { + ohci_set_interrupt(s, OHCI_INTR_RHSC); + } } static void ohci_detach(USBPort *port1) @@ -351,6 +357,8 @@ OHCIPort *port = &s->rhport[port1->index]; uint32_t old_state = port->ctrl; + ohci_async_cancel_device(s, port1->dev); + /* set connect status */ if (port->ctrl & OHCI_PORT_CCS) { port->ctrl &= ~OHCI_PORT_CCS; @@ -363,8 +371,41 @@ } DPRINTF("usb-ohci: Detached port %d\n", port1->index); - if (old_state != port->ctrl) + if (old_state != port->ctrl) { ohci_set_interrupt(s, OHCI_INTR_RHSC); + } +} + +static void ohci_wakeup(USBPort *port1) +{ + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; + uint32_t intr = 0; + if (port->ctrl & OHCI_PORT_PSS) { + DPRINTF("usb-ohci: port %d: wakeup\n", port1->index); + port->ctrl |= OHCI_PORT_PSSC; + port->ctrl &= ~OHCI_PORT_PSS; + intr = OHCI_INTR_RHSC; + } + /* Note that the controller can be suspended even if this port is not */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + DPRINTF("usb-ohci: remote-wakeup: SUSPEND->RESUME\n"); + /* This is the one state transition the controller can do by itself */ + s->ctl &= ~OHCI_CTL_HCFS; + s->ctl |= OHCI_USB_RESUME; + /* In suspend mode only ResumeDetected is possible, not RHSC: + * see the OHCI spec 5.1.2.3. + */ + intr = OHCI_INTR_RD; + } + ohci_set_interrupt(s, intr); +} + +static void ohci_child_detach(USBPort *port1, USBDevice *child) +{ + OHCIState *s = port1->opaque; + + ohci_async_cancel_device(s, child); } /* Reset the controller */ @@ -427,7 +468,7 @@ addr += ohci->localmem_base; for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); + cpu_physical_memory_read(addr, buf, sizeof(*buf)); *buf = le32_to_cpu(*buf); } @@ -444,7 +485,7 @@ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); - cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); + cpu_physical_memory_write(addr, &tmp, sizeof(tmp)); } return 1; @@ -459,7 +500,7 @@ addr += ohci->localmem_base; for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); + cpu_physical_memory_read(addr, buf, sizeof(*buf)); *buf = le16_to_cpu(*buf); } @@ -476,7 +517,7 @@ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint16_t tmp = cpu_to_le16(*buf); - cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); + cpu_physical_memory_write(addr, &tmp, sizeof(tmp)); } return 1; @@ -504,8 +545,7 @@ static inline int ohci_read_hcca(OHCIState *ohci, uint32_t addr, struct ohci_hcca *hcca) { - cpu_physical_memory_rw(addr + ohci->localmem_base, - (uint8_t *)hcca, sizeof(*hcca), 0); + cpu_physical_memory_read(addr + ohci->localmem_base, hcca, sizeof(*hcca)); return 1; } @@ -531,8 +571,7 @@ static inline int ohci_put_hcca(OHCIState *ohci, uint32_t addr, struct ohci_hcca *hcca) { - cpu_physical_memory_rw(addr + ohci->localmem_base, - (uint8_t *)hcca, sizeof(*hcca), 1); + cpu_physical_memory_write(addr + ohci->localmem_base, hcca, sizeof(*hcca)); return 1; } @@ -577,9 +616,9 @@ static void ohci_process_lists(OHCIState *ohci, int completion); -static void ohci_async_complete_packet(USBPacket *packet, void *opaque) +static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) { - OHCIState *ohci = opaque; + OHCIState *ohci = container_of(packet, OHCIState, usb_packet); #ifdef DEBUG_PACKET DPRINTF("Async packet complete\n"); #endif @@ -750,9 +789,7 @@ ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.len = len; - ohci->usb_packet.complete_cb = ohci_async_complete_packet; - ohci->usb_packet.complete_opaque = ohci; - ret = dev->info->handle_packet(dev, &ohci->usb_packet); + ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; } @@ -948,9 +985,7 @@ ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.len = len; - ohci->usb_packet.complete_cb = ohci_async_complete_packet; - ohci->usb_packet.complete_opaque = ohci; - ret = dev->info->handle_packet(dev, &ohci->usb_packet); + ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; } @@ -1101,7 +1136,7 @@ /* Generate a SOF event, and set a timer for EOF */ static void ohci_sof(OHCIState *ohci) { - ohci->sof_time = qemu_get_clock(vm_clock); + ohci->sof_time = qemu_get_clock_ns(vm_clock); qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time); ohci_set_interrupt(ohci, OHCI_INTR_SF); } @@ -1186,12 +1221,12 @@ */ static int ohci_bus_start(OHCIState *ohci) { - ohci->eof_timer = qemu_new_timer(vm_clock, + ohci->eof_timer = qemu_new_timer_ns(vm_clock, ohci_frame_boundary, ohci); if (ohci->eof_timer == NULL) { - fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n", ohci->name); + fprintf(stderr, "usb-ohci: %s: qemu_new_timer_ns failed\n", ohci->name); /* TODO: Signal unrecoverable error */ return 0; } @@ -1311,7 +1346,7 @@ /* Being in USB operational state guarnatees sof_time was * set already. */ - tks = qemu_get_clock(vm_clock) - ohci->sof_time; + tks = qemu_get_clock_ns(vm_clock) - ohci->sof_time; /* avoid muldiv if possible */ if (tks >= usb_frame_time) @@ -1581,6 +1616,10 @@ ohci->hcca = val & OHCI_HCCA_MASK; break; + case 7: /* HcPeriodCurrentED */ + /* Ignore writes to this read-only register, Linux does them */ + break; + case 8: /* HcControlHeadED */ ohci->ctrl_head = val & OHCI_EDPTR_MASK; break; @@ -1650,6 +1689,14 @@ } } +static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) +{ + if (ohci->async_td && ohci->usb_packet.owner == dev) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; + } +} + /* Only dword reads are defined on OHCI register space */ static CPUReadMemoryFunc * const ohci_readfn[3]={ ohci_mem_read, @@ -1667,10 +1714,17 @@ static USBPortOps ohci_port_ops = { .attach = ohci_attach, .detach = ohci_detach, + .child_detach = ohci_child_detach, + .wakeup = ohci_wakeup, + .complete = ohci_async_complete_packet, +}; + +static USBBusOps ohci_bus_ops = { }; -static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, uint32_t localmem_base) +static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, + int num_ports, uint32_t localmem_base, + char *masterbus, uint32_t firstport) { int i; @@ -1690,55 +1744,62 @@ usb_frame_time, usb_bit_time); } + ohci->num_ports = num_ports; + if (masterbus) { + USBPort *ports[OHCI_MAX_PORTS]; + for(i = 0; i < num_ports; i++) { + ports[i] = &ohci->rhport[i].port; + } + if (usb_register_companion(masterbus, ports, num_ports, + firstport, ohci, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { + return -1; + } + } else { + usb_bus_new(&ohci->bus, &ohci_bus_ops, dev); + for (i = 0; i < num_ports; i++) { + usb_register_port(&ohci->bus, &ohci->rhport[i].port, + ohci, i, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + } + } + ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci, DEVICE_LITTLE_ENDIAN); ohci->localmem_base = localmem_base; 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_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&ohci->rhport[i].port, NULL, i+1); - } - ohci->async_td = 0; qemu_register_reset(ohci_reset, ohci); + + return 0; } typedef struct { PCIDevice pci_dev; OHCIState state; + char *masterbus; + uint32_t num_ports; + uint32_t firstport; } OHCIPCIState; -static void ohci_mapfunc(PCIDevice *pci_dev, int i, - pcibus_t addr, pcibus_t size, int type) -{ - OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, pci_dev); - cpu_register_physical_memory(addr, size, ohci->state.mem); -} - static int usb_ohci_initfn_pci(struct PCIDevice *dev) { OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev); - int num_ports = 3; - 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[PCI_CLASS_PROG] = 0x10; /* OHCI */ - pci_config_set_class(ohci->pci_dev.config, PCI_CLASS_SERIAL_USB); /* 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, 0); + if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0, + ohci->masterbus, ohci->firstport) != 0) { + return -1; + } ohci->state.irq = ohci->pci_dev.irq[0]; /* TODO: avoid cast below by using dev */ - pci_register_bar(&ohci->pci_dev, 0, 256, - PCI_BASE_ADDRESS_SPACE_MEMORY, ohci_mapfunc); + pci_register_bar_simple(&ohci->pci_dev, 0, 256, 0, ohci->state.mem); return 0; } @@ -1758,7 +1819,8 @@ { OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev); - usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset); + /* Cannot fail as we pass NULL for masterbus */ + usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0); sysbus_init_irq(dev, &s->ohci.irq); sysbus_init_mmio(dev, 0x1000, s->ohci.mem); @@ -1770,6 +1832,15 @@ .qdev.desc = "Apple USB Controller", .qdev.size = sizeof(OHCIPCIState), .init = usb_ohci_initfn_pci, + .vendor_id = PCI_VENDOR_ID_APPLE, + .device_id = PCI_DEVICE_ID_APPLE_IPID_USB, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), + DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), + DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), + }, }; static SysBusDeviceInfo ohci_sysbus_info = { diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-serial.c qemu-kvm-0.15.0+noroms/hw/usb-serial.c --- qemu-kvm-0.14.1+noroms/hw/usb-serial.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-serial.c 2011-08-09 07:40:29.000000000 -0500 @@ -5,7 +5,7 @@ * Copyright (c) 2008 Samuel Thibault * Written by Paul Brook, reused for FTDI by Samuel Thibault * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" @@ -146,6 +146,7 @@ .bConfigurationValue = 1, .bmAttributes = 0x80, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface0, }, }, @@ -218,14 +219,14 @@ return ret; } -static int usb_serial_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBSerialState *s = (USBSerialState *)dev; int ret; DPRINTF("got control %x, value %x\n",request, value); - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-uhci.c qemu-kvm-0.15.0+noroms/hw/usb-uhci.c --- qemu-kvm-0.14.1+noroms/hw/usb-uhci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-uhci.c 2011-08-09 07:40:29.000000000 -0500 @@ -106,6 +106,8 @@ static void dump_data(const uint8_t *data, int len) {} #endif +typedef struct UHCIState UHCIState; + /* * Pending async transaction. * 'packet' must be the first field because completion @@ -113,7 +115,8 @@ */ typedef struct UHCIAsync { USBPacket packet; - struct UHCIAsync *next; + UHCIState *uhci; + QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; uint32_t token; int8_t valid; @@ -127,9 +130,9 @@ uint16_t ctrl; } UHCIPort; -typedef struct UHCIState { +struct UHCIState { PCIDevice dev; - USBBus bus; + USBBus bus; /* Note unused when we're a companion controller */ uint16_t cmd; /* cmd register */ uint16_t status; uint16_t intr; /* interrupt enable register */ @@ -145,10 +148,13 @@ uint32_t pending_int_mask; /* Active packets */ - UHCIAsync *async_pending; - UHCIAsync *async_pool; + QTAILQ_HEAD(,UHCIAsync) async_pending; uint8_t num_ports_vmstate; -} UHCIState; + + /* Properties */ + char *masterbus; + uint32_t firstport; +}; typedef struct UHCI_TD { uint32_t link; @@ -167,12 +173,12 @@ UHCIAsync *async = qemu_malloc(sizeof(UHCIAsync)); memset(&async->packet, 0, sizeof(async->packet)); + async->uhci = s; async->valid = 0; async->td = 0; async->token = 0; async->done = 0; async->isoc = 0; - async->next = NULL; return async; } @@ -184,24 +190,12 @@ static void uhci_async_link(UHCIState *s, UHCIAsync *async) { - async->next = s->async_pending; - s->async_pending = async; + QTAILQ_INSERT_HEAD(&s->async_pending, async, next); } static void uhci_async_unlink(UHCIState *s, UHCIAsync *async) { - UHCIAsync *curr = s->async_pending; - UHCIAsync **prev = &s->async_pending; - - while (curr) { - if (curr == async) { - *prev = curr->next; - return; - } - - prev = &curr->next; - curr = curr->next; - } + QTAILQ_REMOVE(&s->async_pending, async, next); } static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) @@ -220,11 +214,10 @@ */ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) { - UHCIAsync *async = s->async_pending; + UHCIAsync *async; - while (async) { + QTAILQ_FOREACH(async, &s->async_pending, next) { async->valid--; - async = async->next; } return NULL; } @@ -234,47 +227,43 @@ */ static void uhci_async_validate_end(UHCIState *s) { - UHCIAsync *curr = s->async_pending; - UHCIAsync **prev = &s->async_pending; - UHCIAsync *next; + UHCIAsync *curr, *n; - while (curr) { + QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { if (curr->valid > 0) { - prev = &curr->next; - curr = curr->next; continue; } + uhci_async_unlink(s, curr); + uhci_async_cancel(s, curr); + } +} - next = curr->next; - - /* Unlink */ - *prev = next; +static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) +{ + UHCIAsync *curr, *n; + QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { + if (curr->packet.owner != dev) { + continue; + } + uhci_async_unlink(s, curr); uhci_async_cancel(s, curr); - - curr = next; } } static void uhci_async_cancel_all(UHCIState *s) { - UHCIAsync *curr = s->async_pending; - UHCIAsync *next; - - while (curr) { - next = curr->next; + UHCIAsync *curr, *n; + QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { + uhci_async_unlink(s, curr); uhci_async_cancel(s, curr); - - curr = next; } - - s->async_pending = NULL; } static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token) { - UHCIAsync *async = s->async_pending; + UHCIAsync *async; UHCIAsync *match = NULL; int count = 0; @@ -291,7 +280,7 @@ * If we ever do we'd want to optimize this algorithm. */ - while (async) { + QTAILQ_FOREACH(async, &s->async_pending, next) { if (async->token == token) { /* Good match */ match = async; @@ -301,8 +290,6 @@ break; } } - - async = async->next; count++; } @@ -441,7 +428,9 @@ case 0x00: if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { /* start frame processing */ - qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock)); + s->expire_time = qemu_get_clock_ns(vm_clock) + + (get_ticks_per_sec() / FRAME_TIMER_FREQ); + qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); s->status &= ~UHCI_STS_HCHALTED; } else if (!(val & UHCI_CMD_RS)) { s->status |= UHCI_STS_HCHALTED; @@ -621,6 +610,8 @@ UHCIState *s = port1->opaque; UHCIPort *port = &s->ports[port1->index]; + uhci_async_cancel_device(s, port1->dev); + /* set connect status */ if (port->ctrl & UHCI_PORT_CCS) { port->ctrl &= ~UHCI_PORT_CCS; @@ -635,11 +626,17 @@ uhci_resume(s); } -static void uhci_wakeup(USBDevice *dev) +static void uhci_child_detach(USBPort *port1, USBDevice *child) { - USBBus *bus = usb_bus_from_device(dev); - UHCIState *s = container_of(bus, UHCIState, bus); - UHCIPort *port = s->ports + dev->port->index; + UHCIState *s = port1->opaque; + + uhci_async_cancel_device(s, child); +} + +static void uhci_wakeup(USBPort *port1) +{ + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { port->ctrl |= UHCI_PORT_RD; @@ -662,7 +659,7 @@ USBDevice *dev = port->port.dev; if (dev && (port->ctrl & UHCI_PORT_EN)) - ret = dev->info->handle_packet(dev, p); + ret = usb_handle_packet(dev, p); } DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); @@ -672,7 +669,7 @@ return ret; } -static void uhci_async_complete(USBPacket * packet, void *opaque); +static void uhci_async_complete(USBPort *port, USBPacket *packet); static void uhci_process_frame(UHCIState *s); /* return -1 if fatal error (frame must be stopped) @@ -732,11 +729,21 @@ case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } + uhci_update_irq(s); return 1; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } + uhci_update_irq(s); /* frame interrupted */ return -1; @@ -825,8 +832,6 @@ async->packet.devep = (td->token >> 15) & 0xf; async->packet.data = async->buffer; async->packet.len = max_len; - async->packet.complete_cb = uhci_async_complete; - async->packet.complete_opaque = s; switch(pid) { case USB_TOKEN_OUT: @@ -862,10 +867,10 @@ return len; } -static void uhci_async_complete(USBPacket *packet, void *opaque) +static void uhci_async_complete(USBPort *port, USBPacket *packet) { - UHCIState *s = opaque; - UHCIAsync *async = (UHCIAsync *) packet; + UHCIAsync *async = container_of(packet, UHCIAsync, packet); + UHCIState *s = async->uhci; DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); @@ -1112,31 +1117,45 @@ static USBPortOps uhci_port_ops = { .attach = uhci_attach, .detach = uhci_detach, + .child_detach = uhci_child_detach, .wakeup = uhci_wakeup, + .complete = uhci_async_complete, +}; + +static USBBusOps uhci_bus_ops = { }; -static int usb_uhci_common_initfn(UHCIState *s) +static int usb_uhci_common_initfn(PCIDevice *dev) { + UHCIState *s = DO_UPCAST(UHCIState, dev, dev); uint8_t *pci_conf = s->dev.config; int i; - pci_conf[PCI_REVISION_ID] = 0x01; // revision number pci_conf[PCI_CLASS_PROG] = 0x00; - pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB); /* TODO: reset value should be 0. */ pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3 - pci_conf[0x60] = 0x10; // release number + pci_conf[USB_SBRN] = USB_RELEASE_1; // 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_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); + if (s->masterbus) { + USBPort *ports[NB_PORTS]; + for(i = 0; i < NB_PORTS; i++) { + ports[i] = &s->ports[i].port; + } + if (usb_register_companion(s->masterbus, ports, NB_PORTS, + s->firstport, s, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { + return -1; + } + } else { + usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); + for (i = 0; i < NB_PORTS; i++) { + usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + } + } + s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; + QTAILQ_INIT(&s->async_pending); qemu_register_reset(uhci_reset, s); @@ -1148,34 +1167,11 @@ return 0; } -static int usb_uhci_piix3_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_INTEL); - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_2); - return usb_uhci_common_initfn(s); -} - -static int usb_uhci_piix4_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_INTEL); - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_2); - 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 */ @@ -1183,25 +1179,76 @@ /* USB legacy support */ pci_set_long(pci_conf + 0xc0,0x00002000); - return usb_uhci_common_initfn(s); + return usb_uhci_common_initfn(dev); } +static Property uhci_properties[] = { + DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), + DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static PCIDeviceInfo uhci_info[] = { { .qdev.name = "piix3-usb-uhci", .qdev.size = sizeof(UHCIState), .qdev.vmsd = &vmstate_uhci, - .init = usb_uhci_piix3_initfn, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ .qdev.name = "piix4-usb-uhci", .qdev.size = sizeof(UHCIState), .qdev.vmsd = &vmstate_uhci, - .init = usb_uhci_piix4_initfn, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ .qdev.name = "vt82c686b-usb-uhci", .qdev.size = sizeof(UHCIState), .qdev.vmsd = &vmstate_uhci, .init = usb_uhci_vt82c686b_initfn, + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_UHCI, + .revision = 0x01, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci1", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci2", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci3", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ /* end of list */ } diff -Nru qemu-kvm-0.14.1+noroms/hw/usb-wacom.c qemu-kvm-0.15.0+noroms/hw/usb-wacom.c --- qemu-kvm-0.14.1+noroms/hw/usb-wacom.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/usb-wacom.c 2011-08-09 07:40:29.000000000 -0500 @@ -108,6 +108,7 @@ .bConfigurationValue = 1, .bmAttributes = 0x80, .bMaxPower = 40, + .nif = 1, .ifs = &desc_iface_wacom, }, }, @@ -249,13 +250,13 @@ s->mode = WACOM_MODE_HID; } -static int usb_wacom_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBWacomState *s = (USBWacomState *) dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } diff -Nru qemu-kvm-0.14.1+noroms/hw/versatilepb.c qemu-kvm-0.15.0+noroms/hw/versatilepb.c --- qemu-kvm-0.14.1+noroms/hw/versatilepb.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/versatilepb.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -223,7 +223,7 @@ for(n = 0; n < nb_nics; n++) { nd = &nd_table[n]; - if ((!nd->model && !done_smc) || strcmp(nd->model, "smc91c111") == 0) { + if (!done_smc && (!nd->model || strcmp(nd->model, "smc91c111") == 0)) { smc91c111_init(nd, 0x10010000, sic[25]); done_smc = 1; } else { diff -Nru qemu-kvm-0.14.1+noroms/hw/versatile_pci.c qemu-kvm-0.15.0+noroms/hw/versatile_pci.c --- qemu-kvm-0.14.1+noroms/hw/versatile_pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/versatile_pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2006-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "sysbus.h" @@ -133,12 +133,8 @@ static int versatile_pci_host_init(PCIDevice *d) { - 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); pci_set_word(d->config + PCI_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); - pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_CO); pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); return 0; } @@ -147,6 +143,10 @@ .qdev.name = "versatile_pci_host", .qdev.size = sizeof(PCIDevice), .init = versatile_pci_host_init, + .vendor_id = PCI_VENDOR_ID_XILINX, + /* Both boards have the same device ID. Oh well. */ + .device_id = PCI_DEVICE_ID_XILINX_XC2VP30, + .class_id = PCI_CLASS_PROCESSOR_CO, }; static void versatile_pci_register_devices(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/vexpress.c qemu-kvm-0.15.0+noroms/hw/vexpress.c --- qemu-kvm-0.14.1+noroms/hw/vexpress.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/vexpress.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,224 @@ +/* + * ARM Versatile Express emulation. + * + * Copyright (c) 2010 - 2011 B Labs Ltd. + * Copyright (c) 2011 Linaro Limited + * Written by Bahadir Balban, Amit Mahajan, Peter Maydell + * + * 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, see . + */ + +#include "sysbus.h" +#include "arm-misc.h" +#include "primecell.h" +#include "devices.h" +#include "net.h" +#include "sysemu.h" +#include "boards.h" + +#define SMP_BOOT_ADDR 0xe0000000 + +#define VEXPRESS_BOARD_ID 0x8e0 + +static struct arm_boot_info vexpress_binfo = { + .smp_loader_start = SMP_BOOT_ADDR, +}; + +static void vexpress_a9_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 = NULL; + ram_addr_t ram_offset, vram_offset, sram_offset; + DeviceState *dev, *sysctl; + SysBusDevice *busdev; + qemu_irq *irqp; + qemu_irq pic[64]; + int n; + qemu_irq cpu_irq[4]; + uint32_t proc_id; + uint32_t sys_id; + ram_addr_t low_ram_size, vram_size, sram_size; + + if (!cpu_model) { + cpu_model = "cortex-a9"; + } + + for (n = 0; n < smp_cpus; n++) { + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + irqp = arm_pic_init_cpu(env); + cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ]; + } + + if (ram_size > 0x40000000) { + /* 1GB is the maximum the address space permits */ + fprintf(stderr, "vexpress: cannot model more than 1GB RAM\n"); + exit(1); + } + + ram_offset = qemu_ram_alloc(NULL, "vexpress.highmem", ram_size); + low_ram_size = ram_size; + if (low_ram_size > 0x4000000) { + low_ram_size = 0x4000000; + } + /* RAM is from 0x60000000 upwards. The bottom 64MB of the + * address space should in theory be remappable to various + * things including ROM or RAM; we always map the RAM there. + */ + cpu_register_physical_memory(0x0, low_ram_size, ram_offset | IO_MEM_RAM); + cpu_register_physical_memory(0x60000000, ram_size, + ram_offset | IO_MEM_RAM); + + /* 0x1e000000 A9MPCore (SCU) private memory region */ + dev = qdev_create(NULL, "a9mpcore_priv"); + qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + vexpress_binfo.smp_priv_base = 0x1e000000; + sysbus_mmio_map(busdev, 0, vexpress_binfo.smp_priv_base); + for (n = 0; n < smp_cpus; n++) { + sysbus_connect_irq(busdev, n, cpu_irq[n]); + } + /* Interrupts [42:0] are from the motherboard; + * [47:43] are reserved; [63:48] are daughterboard + * peripherals. Note that some documentation numbers + * external interrupts starting from 32 (because the + * A9MP has internal interrupts 0..31). + */ + for (n = 0; n < 64; n++) { + pic[n] = qdev_get_gpio_in(dev, n); + } + + /* Motherboard peripherals CS7 : 0x10000000 .. 0x10020000 */ + sys_id = 0x1190f500; + proc_id = 0x0c000191; + + /* 0x10000000 System registers */ + sysctl = qdev_create(NULL, "realview_sysctl"); + qdev_prop_set_uint32(sysctl, "sys_id", sys_id); + qdev_init_nofail(sysctl); + qdev_prop_set_uint32(sysctl, "proc_id", proc_id); + sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000); + + /* 0x10001000 SP810 system control */ + /* 0x10002000 serial bus PCI */ + /* 0x10004000 PL041 audio */ + + dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL); + /* Wire up MMC card detect and read-only signals */ + qdev_connect_gpio_out(dev, 0, + qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT)); + qdev_connect_gpio_out(dev, 1, + qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN)); + + sysbus_create_simple("pl050_keyboard", 0x10006000, pic[12]); + sysbus_create_simple("pl050_mouse", 0x10007000, pic[13]); + + sysbus_create_simple("pl011", 0x10009000, pic[5]); + sysbus_create_simple("pl011", 0x1000a000, pic[6]); + sysbus_create_simple("pl011", 0x1000b000, pic[7]); + sysbus_create_simple("pl011", 0x1000c000, pic[8]); + + /* 0x1000f000 SP805 WDT */ + + sysbus_create_simple("sp804", 0x10011000, pic[2]); + sysbus_create_simple("sp804", 0x10012000, pic[3]); + + /* 0x10016000 Serial Bus DVI */ + + sysbus_create_simple("pl031", 0x10017000, pic[4]); /* RTC */ + + /* 0x1001a000 Compact Flash */ + + /* 0x1001f000 PL111 CLCD (motherboard) */ + + /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ + + /* 0x10020000 PL111 CLCD (daughterboard) */ + sysbus_create_simple("pl110", 0x10020000, pic[44]); + + /* 0x10060000 AXI RAM */ + /* 0x100e0000 PL341 Dynamic Memory Controller */ + /* 0x100e1000 PL354 Static Memory Controller */ + /* 0x100e2000 System Configuration Controller */ + + sysbus_create_simple("sp804", 0x100e4000, pic[48]); + /* 0x100e5000 SP805 Watchdog module */ + /* 0x100e6000 BP147 TrustZone Protection Controller */ + /* 0x100e9000 PL301 'Fast' AXI matrix */ + /* 0x100ea000 PL301 'Slow' AXI matrix */ + /* 0x100ec000 TrustZone Address Space Controller */ + /* 0x10200000 CoreSight debug APB */ + /* 0x1e00a000 PL310 L2 Cache Controller */ + + /* CS0: NOR0 flash : 0x40000000 .. 0x44000000 */ + /* CS4: NOR1 flash : 0x44000000 .. 0x48000000 */ + /* CS2: SRAM : 0x48000000 .. 0x4a000000 */ + sram_size = 0x2000000; + sram_offset = qemu_ram_alloc(NULL, "vexpress.sram", sram_size); + cpu_register_physical_memory(0x48000000, sram_size, + sram_offset | IO_MEM_RAM); + + /* CS3: USB, ethernet, VRAM : 0x4c000000 .. 0x50000000 */ + + /* 0x4c000000 Video RAM */ + vram_size = 0x800000; + vram_offset = qemu_ram_alloc(NULL, "vexpress.vram", vram_size); + cpu_register_physical_memory(0x4c000000, vram_size, + vram_offset | IO_MEM_RAM); + + /* 0x4e000000 LAN9118 Ethernet */ + if (nd_table[0].vlan) { + lan9118_init(&nd_table[0], 0x4e000000, pic[15]); + } + + /* 0x4f000000 ISP1761 USB */ + + /* ??? Hack to map an additional page of ram for the secondary CPU + 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(NULL, "vexpress.hack", 0x1000); + cpu_register_physical_memory(SMP_BOOT_ADDR, 0x1000, + ram_offset | IO_MEM_RAM); + + vexpress_binfo.ram_size = ram_size; + vexpress_binfo.kernel_filename = kernel_filename; + vexpress_binfo.kernel_cmdline = kernel_cmdline; + vexpress_binfo.initrd_filename = initrd_filename; + vexpress_binfo.nb_cpus = smp_cpus; + vexpress_binfo.board_id = VEXPRESS_BOARD_ID; + vexpress_binfo.loader_start = 0x60000000; + arm_load_kernel(first_cpu, &vexpress_binfo); +} + + +static QEMUMachine vexpress_a9_machine = { + .name = "vexpress-a9", + .desc = "ARM Versatile Express for Cortex-A9", + .init = vexpress_a9_init, + .use_scsi = 1, + .max_cpus = 4, +}; + +static void vexpress_machine_init(void) +{ + qemu_register_machine(&vexpress_a9_machine); +} + +machine_init(vexpress_machine_init); diff -Nru qemu-kvm-0.14.1+noroms/hw/vga.c qemu-kvm-0.15.0+noroms/hw/vga.c --- qemu-kvm-0.14.1+noroms/hw/vga.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vga.c 2011-08-09 07:40:29.000000000 -0500 @@ -28,7 +28,6 @@ #include "vga_int.h" #include "pixel_ops.h" #include "qemu-timer.h" -#include "kvm.h" //#define DEBUG_VGA //#define DEBUG_VGA_MEM @@ -261,7 +260,7 @@ int cur_line, cur_line_char, cur_char; int64_t cur_tick; - cur_tick = qemu_get_clock(vm_clock); + cur_tick = qemu_get_clock_ns(vm_clock); cur_char = (cur_tick / r->ticks_per_char) % r->total_chars; cur_line = cur_char / r->htotal; @@ -1258,8 +1257,6 @@ vga_draw_glyph8_func *vga_draw_glyph8; vga_draw_glyph9_func *vga_draw_glyph9; - vga_dirty_log_stop(s); - /* compute font data address (in plane 2) */ v = s->sr[3]; offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; @@ -1571,65 +1568,42 @@ } #endif - vga_dirty_log_start(s); } -static int s1, s2, s3; - -static void mark_dirty(target_phys_addr_t start, target_phys_addr_t len) +void vga_dirty_log_start(VGACommonState *s) { - target_phys_addr_t end = start + len; - - while (start < end) { - cpu_physical_memory_set_dirty(cpu_get_physical_page_desc(start)); - start += TARGET_PAGE_SIZE; + if (s->map_addr) { + cpu_physical_log_start(s->map_addr, s->map_end - s->map_addr); } -} -void vga_dirty_log_start(VGACommonState *s) -{ - if (kvm_enabled() && s->map_addr) - if (!s1) { - kvm_log_start(s->map_addr, s->map_end - s->map_addr); - mark_dirty(s->map_addr, s->map_end - s->map_addr); - s1 = 1; - } - if (kvm_enabled() && s->lfb_vram_mapped) { - if (!s2) { - kvm_log_start(isa_mem_base + 0xa0000, 0x8000); - kvm_log_start(isa_mem_base + 0xa8000, 0x8000); - mark_dirty(isa_mem_base + 0xa0000, 0x10000); - } - s2 = 1; + if (s->lfb_vram_mapped) { + cpu_physical_log_start(isa_mem_base + 0xa0000, 0x8000); + cpu_physical_log_start(isa_mem_base + 0xa8000, 0x8000); } #ifdef CONFIG_BOCHS_VBE - if (kvm_enabled() && s->vbe_mapped) { - if (!s3) { - kvm_log_start(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size); - } - s3 = 1; + if (s->vbe_mapped) { + cpu_physical_log_start(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size); } #endif } void vga_dirty_log_stop(VGACommonState *s) { - if (kvm_enabled() && s->map_addr && s1) - kvm_log_stop(s->map_addr, s->map_end - s->map_addr); + if (s->map_addr) { + cpu_physical_log_stop(s->map_addr, s->map_end - s->map_addr); + } - if (kvm_enabled() && s->lfb_vram_mapped && s1) { - kvm_log_stop(isa_mem_base + 0xa0000, 0x8000); - kvm_log_stop(isa_mem_base + 0xa8000, 0x8000); + if (s->lfb_vram_mapped) { + cpu_physical_log_stop(isa_mem_base + 0xa0000, 0x8000); + cpu_physical_log_stop(isa_mem_base + 0xa8000, 0x8000); } #ifdef CONFIG_BOCHS_VBE - if (kvm_enabled() && s->vbe_mapped && s3) { - kvm_log_stop(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size); + if (s->vbe_mapped) { + cpu_physical_log_stop(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size); } #endif - - s1 = s2 = s3 = 0; } void vga_dirty_log_restart(VGACommonState *s) @@ -1867,7 +1841,6 @@ return; if (s->last_scr_width <= 0 || s->last_scr_height <= 0) return; - vga_dirty_log_stop(s); s->rgb_to_pixel = rgb_to_pixel_dup_table[get_depth_index(s->ds)]; @@ -2290,12 +2263,8 @@ } /* used by both ISA and PCI */ -void vga_init(VGACommonState *s) +int vga_init_io(VGACommonState *s) { - int vga_io_memory; - - qemu_register_reset(vga_reset, s); - register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s); register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s); @@ -2309,7 +2278,6 @@ register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s); register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s); register_ioport_read(0x3da, 1, 1, vga_ioport_read, s); - s->bank_offset = 0; #ifdef CONFIG_BOCHS_VBE #if defined (TARGET_I386) @@ -2327,8 +2295,19 @@ #endif #endif /* CONFIG_BOCHS_VBE */ - vga_io_memory = cpu_register_io_memory(vga_mem_read, vga_mem_write, s, - DEVICE_LITTLE_ENDIAN); + return cpu_register_io_memory(vga_mem_read, vga_mem_write, s, + DEVICE_LITTLE_ENDIAN); +} + +void vga_init(VGACommonState *s) +{ + int vga_io_memory; + + qemu_register_reset(vga_reset, s); + + s->bank_offset = 0; + + vga_io_memory = vga_init_io(s); cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, vga_io_memory); qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000); @@ -2370,15 +2349,19 @@ uint32_t v; int y, x; uint8_t r, g, b; + int ret; + char *linebuf, *pbuf; f = fopen(filename, "wb"); if (!f) return -1; fprintf(f, "P6\n%d %d\n%d\n", ds->width, ds->height, 255); + linebuf = qemu_malloc(ds->width * 3); d1 = ds->data; for(y = 0; y < ds->height; y++) { d = d1; + pbuf = linebuf; for(x = 0; x < ds->width; x++) { if (ds->pf.bits_per_pixel == 32) v = *(uint32_t *)d; @@ -2390,13 +2373,16 @@ (ds->pf.gmax + 1); b = ((v >> ds->pf.bshift) & ds->pf.bmax) * 256 / (ds->pf.bmax + 1); - fputc(r, f); - fputc(g, f); - fputc(b, f); + *pbuf++ = r; + *pbuf++ = g; + *pbuf++ = b; d += ds->pf.bytes_per_pixel; } d1 += ds->linesize; + ret = fwrite(linebuf, 1, pbuf - linebuf, f); + (void)ret; } + qemu_free(linebuf); fclose(f); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/vga_int.h qemu-kvm-0.15.0+noroms/hw/vga_int.h --- qemu-kvm-0.14.1+noroms/hw/vga_int.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vga_int.h 2011-08-09 07:40:29.000000000 -0500 @@ -106,13 +106,13 @@ typedef struct VGACommonState { uint8_t *vram_ptr; ram_addr_t vram_offset; + target_phys_addr_t lfb_addr; + target_phys_addr_t lfb_end; + target_phys_addr_t map_addr; + target_phys_addr_t map_end; 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 latch; + uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ uint8_t sr_index; uint8_t sr[256]; uint8_t gr_index; @@ -191,6 +191,7 @@ void vga_common_init(VGACommonState *s, int vga_ram_size); void vga_init(VGACommonState *s); +int vga_init_io(VGACommonState *s); void vga_common_reset(VGACommonState *s); void vga_dirty_log_start(VGACommonState *s); diff -Nru qemu-kvm-0.14.1+noroms/hw/vga-isa.c qemu-kvm-0.15.0+noroms/hw/vga-isa.c --- qemu-kvm-0.14.1+noroms/hw/vga-isa.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vga-isa.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,16 +29,40 @@ #include "qemu-timer.h" #include "loader.h" -int isa_vga_init(void) +typedef struct ISAVGAState { + ISADevice dev; + struct VGACommonState state; +} ISAVGAState; + +static void vga_reset_isa(DeviceState *dev) { - VGACommonState *s; + ISAVGAState *d = container_of(dev, ISAVGAState, dev.qdev); + VGACommonState *s = &d->state; - s = qemu_mallocz(sizeof(*s)); + vga_common_reset(s); +} - vga_common_init(s, VGA_RAM_SIZE); - vga_init(s); - vmstate_register(NULL, 0, &vmstate_vga_common, s); +static int vga_initfn(ISADevice *dev) +{ + ISAVGAState *d = DO_UPCAST(ISAVGAState, dev, dev); + VGACommonState *s = &d->state; + int vga_io_memory; + vga_common_init(s, VGA_RAM_SIZE); + vga_io_memory = vga_init_io(s); + cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, + vga_io_memory); + qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000); + isa_init_ioport(dev, 0x3c0); + isa_init_ioport(dev, 0x3b4); + isa_init_ioport(dev, 0x3ba); + isa_init_ioport(dev, 0x3da); + isa_init_ioport(dev, 0x3c0); +#ifdef CONFIG_BOCHS_VBE + isa_init_ioport(dev, 0x1ce); + isa_init_ioport(dev, 0x1cf); + isa_init_ioport(dev, 0x1d0); +#endif /* CONFIG_BOCHS_VBE */ s->ds = graphic_console_init(s->update, s->invalidate, s->screen_dump, s->text_update, s); @@ -47,3 +71,17 @@ rom_add_vga(VGABIOS_FILENAME); return 0; } + +static ISADeviceInfo vga_info = { + .qdev.name = "isa-vga", + .qdev.size = sizeof(ISAVGAState), + .qdev.vmsd = &vmstate_vga_common, + .qdev.reset = vga_reset_isa, + .init = vga_initfn, +}; + +static void vga_register(void) +{ + isa_qdev_register(&vga_info); +} +device_init(vga_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/vga-pci.c qemu-kvm-0.15.0+noroms/hw/vga-pci.c --- qemu-kvm-0.14.1+noroms/hw/vga-pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vga-pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -65,18 +65,15 @@ PCIVGAState *pvs = container_of(d, PCIVGAState, dev); VGACommonState *s = &pvs->vga; - vga_dirty_log_stop(s); pci_default_write_config(d, address, val, len); if (s->map_addr && pvs->dev.io_regions[0].addr == -1) s->map_addr = 0; - vga_dirty_log_start(s); } static int pci_vga_initfn(PCIDevice *dev) { PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev); VGACommonState *s = &d->vga; - uint8_t *pci_conf = d->dev.config; // vga + console init vga_common_init(s, VGA_RAM_SIZE); @@ -85,11 +82,6 @@ s->ds = graphic_console_init(s->update, s->invalidate, s->screen_dump, s->text_update, s); - // dummy VGA (same as Bochs ID) - 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); - /* 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); @@ -116,6 +108,11 @@ .init = pci_vga_initfn, .config_write = pci_vga_write_config, .romfile = "vgabios-stdvga.bin", + + /* dummy VGA (same as Bochs ID) */ + .vendor_id = PCI_VENDOR_ID_QEMU, + .device_id = PCI_DEVICE_ID_QEMU_VGA, + .class_id = PCI_CLASS_DISPLAY_VGA, }; static void vga_register(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/vhost.c qemu-kvm-0.15.0+noroms/hw/vhost.c --- qemu-kvm-0.14.1+noroms/hw/vhost.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vhost.c 2011-08-09 07:40:29.000000000 -0500 @@ -297,10 +297,50 @@ return 0; } +static struct vhost_memory_region *vhost_dev_find_reg(struct vhost_dev *dev, + uint64_t start_addr, + uint64_t size) +{ + int i, n = dev->mem->nregions; + for (i = 0; i < n; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + if (ranges_overlap(reg->guest_phys_addr, reg->memory_size, + start_addr, size)) { + return reg; + } + } + return NULL; +} + +static bool vhost_dev_cmp_memory(struct vhost_dev *dev, + uint64_t start_addr, + uint64_t size, + uint64_t uaddr) +{ + struct vhost_memory_region *reg = vhost_dev_find_reg(dev, start_addr, size); + uint64_t reglast; + uint64_t memlast; + + if (!reg) { + return true; + } + + reglast = range_get_last(reg->guest_phys_addr, reg->memory_size); + memlast = range_get_last(start_addr, size); + + /* Need to extend region? */ + if (start_addr < reg->guest_phys_addr || memlast > reglast) { + return true; + } + /* userspace_addr changed? */ + return uaddr != reg->userspace_addr + start_addr - reg->guest_phys_addr; +} + static void vhost_client_set_memory(CPUPhysMemoryClient *client, target_phys_addr_t start_addr, ram_addr_t size, - ram_addr_t phys_offset) + ram_addr_t phys_offset, + bool log_dirty) { struct vhost_dev *dev = container_of(client, struct vhost_dev, client); ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; @@ -308,10 +348,29 @@ (dev->mem->nregions + 1) * sizeof dev->mem->regions[0]; uint64_t log_size; int r; + dev->mem = qemu_realloc(dev->mem, s); + if (log_dirty) { + flags = IO_MEM_UNASSIGNED; + } + assert(size); + /* Optimize no-change case. At least cirrus_vga does this a lot at this time. */ + if (flags == IO_MEM_RAM) { + if (!vhost_dev_cmp_memory(dev, start_addr, size, + (uintptr_t)qemu_get_ram_ptr(phys_offset))) { + /* Region exists with same address. Nothing to do. */ + return; + } + } else { + if (!vhost_dev_find_reg(dev, start_addr, size)) { + /* Removing region that we don't access. Nothing to do. */ + return; + } + } + vhost_dev_unassign_memory(dev, start_addr, size); if (flags == IO_MEM_RAM) { /* Add given mapping, merging adjacent regions if any */ @@ -609,6 +668,8 @@ 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->client.log_start = NULL; + hdev->client.log_stop = NULL; hdev->mem = qemu_mallocz(offsetof(struct vhost_memory, regions)); hdev->log = NULL; hdev->log_size = 0; @@ -723,5 +784,6 @@ hdev->started = false; qemu_free(hdev->log); + hdev->log = NULL; hdev->log_size = 0; } diff -Nru qemu-kvm-0.14.1+noroms/hw/vhost_net.c qemu-kvm-0.15.0+noroms/hw/vhost_net.c --- qemu-kvm-0.14.1+noroms/hw/vhost_net.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vhost_net.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,6 +15,7 @@ #include "virtio-net.h" #include "vhost_net.h" +#include "qemu-error.h" #include "config.h" @@ -50,6 +51,9 @@ if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) { features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); } + if (!(net->dev.features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + features &= ~(1 << VIRTIO_RING_F_EVENT_IDX); + } if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) { features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); } @@ -65,6 +69,9 @@ if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC); } + if (features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + net->dev.acked_features |= (1 << VIRTIO_RING_F_EVENT_IDX); + } if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF); } @@ -197,6 +204,7 @@ struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd, bool force) { + error_report("vhost-net support is not compiled in"); return NULL; } diff -Nru qemu-kvm-0.14.1+noroms/hw/virtex_ml507.c qemu-kvm-0.15.0+noroms/hw/virtex_ml507.c --- qemu-kvm-0.14.1+noroms/hw/virtex_ml507.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtex_ml507.c 2011-08-09 07:40:29.000000000 -0500 @@ -60,7 +60,7 @@ target_ulong va, target_phys_addr_t pa) { - ppcemb_tlb_t *tlb = &env->tlb[0].tlbe; + ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); @@ -69,7 +69,7 @@ tlb->RPN = pa & TARGET_PAGE_MASK; tlb->PID = 0; - tlb = &env->tlb[1].tlbe; + tlb = &env->tlb.tlbe[1]; tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); tlb->size = 1 << 31; /* up to 0xffffffff */ diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-9p.c qemu-kvm-0.15.0+noroms/hw/virtio-9p.c --- qemu-kvm-0.14.1+noroms/hw/virtio-9p.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,3744 +0,0 @@ -/* - * 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.14.1+noroms/hw/virtio-9p-debug.c qemu-kvm-0.15.0+noroms/hw/virtio-9p-debug.c --- qemu-kvm-0.14.1+noroms/hw/virtio-9p-debug.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p-debug.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,645 +0,0 @@ -/* - * 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.14.1+noroms/hw/virtio-9p-debug.h qemu-kvm-0.15.0+noroms/hw/virtio-9p-debug.h --- qemu-kvm-0.14.1+noroms/hw/virtio-9p-debug.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p-debug.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,6 +0,0 @@ -#ifndef _QEMU_VIRTIO_9P_DEBUG_H -#define _QEMU_VIRTIO_9P_DEBUG_H - -void pprint_pdu(V9fsPDU *pdu); - -#endif diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-9p.h qemu-kvm-0.15.0+noroms/hw/virtio-9p.h --- qemu-kvm-0.14.1+noroms/hw/virtio-9p.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,507 +0,0 @@ -#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.14.1+noroms/hw/virtio-9p-local.c qemu-kvm-0.15.0+noroms/hw/virtio-9p-local.c --- qemu-kvm-0.14.1+noroms/hw/virtio-9p-local.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p-local.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,563 +0,0 @@ -/* - * 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.14.1+noroms/hw/virtio-9p-posix-acl.c qemu-kvm-0.15.0+noroms/hw/virtio-9p-posix-acl.c --- qemu-kvm-0.14.1+noroms/hw/virtio-9p-posix-acl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p-posix-acl.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,140 +0,0 @@ -/* - * 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.14.1+noroms/hw/virtio-9p-xattr.c qemu-kvm-0.15.0+noroms/hw/virtio-9p-xattr.c --- qemu-kvm-0.14.1+noroms/hw/virtio-9p-xattr.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p-xattr.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,159 +0,0 @@ -/* - * 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.14.1+noroms/hw/virtio-9p-xattr.h qemu-kvm-0.15.0+noroms/hw/virtio-9p-xattr.h --- qemu-kvm-0.14.1+noroms/hw/virtio-9p-xattr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p-xattr.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,102 +0,0 @@ -/* - * 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.14.1+noroms/hw/virtio-9p-xattr-user.c qemu-kvm-0.15.0+noroms/hw/virtio-9p-xattr-user.c --- qemu-kvm-0.14.1+noroms/hw/virtio-9p-xattr-user.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-9p-xattr-user.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,109 +0,0 @@ -/* - * 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.14.1+noroms/hw/virtio-balloon.c qemu-kvm-0.15.0+noroms/hw/virtio-balloon.c --- qemu-kvm-0.14.1+noroms/hw/virtio-balloon.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-balloon.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,7 +1,9 @@ /* - * Virtio Block Device + * Virtio Balloon Device * * Copyright IBM, Corp. 2008 + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 Amit Shah * * Authors: * Anthony Liguori @@ -15,13 +17,11 @@ #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" @@ -45,6 +45,7 @@ size_t stats_vq_offset; MonitorCompletion *stats_callback; void *stats_opaque_callback_data; + DeviceState *qdev; } VirtIOBalloon; static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) @@ -192,7 +193,7 @@ VirtIOBalloon *dev = to_virtio_balloon(vdev); struct virtio_balloon_config config; memcpy(&config, config_data, 8); - dev->actual = config.actual; + dev->actual = le32_to_cpu(config.actual); } static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) @@ -201,36 +202,44 @@ return f; } -static void virtio_balloon_to_target(void *opaque, ram_addr_t target, - MonitorCompletion cb, void *cb_data) +static void virtio_balloon_stat(void *opaque, MonitorCompletion cb, + void *cb_data) { VirtIOBalloon *dev = opaque; - if (target > ram_size) - target = ram_size; + /* 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); + return; + } + + /* 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); +} +static void virtio_balloon_to_target(void *opaque, ram_addr_t target) +{ + VirtIOBalloon *dev = opaque; + + if (target > ram_size) { + target = ram_size; + } 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); - } } } @@ -261,6 +270,7 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) { VirtIOBalloon *s; + int ret; s = (VirtIOBalloon *)virtio_common_init("virtio-balloon", VIRTIO_ID_BALLOON, @@ -270,15 +280,29 @@ s->vdev.set_config = virtio_balloon_set_config; s->vdev.get_features = virtio_balloon_get_features; + ret = qemu_add_balloon_handler(virtio_balloon_to_target, + virtio_balloon_stat, s); + if (ret < 0) { + virtio_cleanup(&s->vdev); + return NULL; + } + 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); + s->qdev = dev; register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); return &s->vdev; } + +void virtio_balloon_exit(VirtIODevice *vdev) +{ + VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + unregister_savevm(s->qdev, "virtio-balloon", s); + virtio_cleanup(vdev); +} diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-blk.c qemu-kvm-0.15.0+noroms/hw/virtio-blk.c --- qemu-kvm-0.14.1+noroms/hw/virtio-blk.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-blk.c 2011-08-09 07:40:29.000000000 -0500 @@ -28,8 +28,8 @@ void *rq; QEMUBH *bh; BlockConf *conf; + char *serial; unsigned short sector_mask; - char sn[BLOCK_SERIAL_STRLEN]; DeviceState *qdev; } VirtIOBlock; @@ -78,7 +78,7 @@ req->next = s->rq; s->rq = req; bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); - vm_stop(0); + vm_stop(VMSTOP_DISKFULL); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); @@ -362,8 +362,13 @@ } 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))); + /* + * NB: per existing s/n string convention the string is + * terminated by '\0' only when shorter than buffer. + */ + strncpy(req->elem.in_sg[0].iov_base, + s->serial ? s->serial : "", + MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); 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], @@ -531,7 +536,8 @@ } } -VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) +VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf, + char **serial) { VirtIOBlock *s; int cylinders, heads, secs; @@ -547,6 +553,14 @@ return NULL; } + if (!*serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(conf->bs); + if (*dinfo->serial) { + *serial = strdup(dinfo->serial); + } + } + s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config), sizeof(VirtIOBlock)); @@ -556,16 +570,11 @@ s->vdev.reset = virtio_blk_reset; s->bs = conf->bs; s->conf = conf; + s->serial = *serial; s->rq = NULL; s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; bdrv_guess_geometry(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); diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-blk.h qemu-kvm-0.15.0+noroms/hw/virtio-blk.h --- qemu-kvm-0.14.1+noroms/hw/virtio-blk.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-blk.h 2011-08-09 07:40:29.000000000 -0500 @@ -34,6 +34,8 @@ #define VIRTIO_BLK_F_WCACHE 9 /* write cache enabled */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ +#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ + struct virtio_blk_config { uint64_t capacity; diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio.c qemu-kvm-0.15.0+noroms/hw/virtio.c --- qemu-kvm-0.14.1+noroms/hw/virtio.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio.c 2011-08-09 07:40:29.000000000 -0500 @@ -16,7 +16,6 @@ #include "trace.h" #include "qemu-error.h" #include "virtio.h" -#include "sysemu.h" /* The alignment to use between consumer and producer parts of vring. * x86 pagesize again. */ @@ -72,7 +71,17 @@ VRing vring; target_phys_addr_t pa; uint16_t last_avail_idx; + /* Last used index value we have signalled on */ + uint16_t signalled_used; + + /* Last used index value we have signalled on */ + bool signalled_used_valid; + + /* Notification enabled? */ + bool notification; + int inuse; + uint16_t vector; void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); VirtIODevice *vdev; @@ -141,6 +150,11 @@ return lduw_phys(pa); } +static inline uint16_t vring_used_event(VirtQueue *vq) +{ + return vring_avail_ring(vq, vq->vring.num); +} + static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val) { target_phys_addr_t pa; @@ -162,11 +176,11 @@ return lduw_phys(pa); } -static inline void vring_used_idx_increment(VirtQueue *vq, uint16_t val) +static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val) { target_phys_addr_t pa; pa = vq->vring.used + offsetof(VRingUsed, idx); - stw_phys(pa, vring_used_idx(vq) + val); + stw_phys(pa, val); } static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask) @@ -183,12 +197,26 @@ stw_phys(pa, lduw_phys(pa) & ~mask); } +static inline void vring_avail_event(VirtQueue *vq, uint16_t val) +{ + target_phys_addr_t pa; + if (!vq->notification) { + return; + } + pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]); + stw_phys(pa, val); +} + void virtio_queue_set_notification(VirtQueue *vq, int enable) { - if (enable) + vq->notification = enable; + if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + vring_avail_event(vq, vring_avail_idx(vq)); + } else if (enable) { vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY); - else + } else { vring_used_flags_set_bit(vq, VRING_USED_F_NO_NOTIFY); + } } int virtio_queue_ready(VirtQueue *vq) @@ -234,11 +262,16 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count) { + uint16_t old, new; /* Make sure buffer is written before we update index. */ wmb(); trace_virtqueue_flush(vq, count); - vring_used_idx_increment(vq, count); + old = vring_used_idx(vq); + new = old + count; + vring_used_idx_set(vq, new); vq->inuse -= count; + if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old))) + vq->signalled_used_valid = false; } void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, @@ -395,6 +428,9 @@ max = vq->vring.num; i = head = virtqueue_get_head(vq, vq->last_avail_idx++); + if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + vring_avail_event(vq, vring_avail_idx(vq)); + } if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) { if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) { @@ -413,9 +449,17 @@ struct iovec *sg; if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) { + if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) { + error_report("Too many write descriptors in indirect table"); + exit(1); + } elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i); sg = &elem->in_sg[elem->in_num++]; } else { + if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) { + error_report("Too many read descriptors in indirect table"); + exit(1); + } elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i); sg = &elem->out_sg[elem->out_num++]; } @@ -478,6 +522,9 @@ vdev->vq[i].last_avail_idx = 0; vdev->vq[i].pa = 0; vdev->vq[i].vector = VIRTIO_NO_VECTOR; + vdev->vq[i].signalled_used = 0; + vdev->vq[i].signalled_used_valid = false; + vdev->vq[i].notification = true; } } @@ -586,9 +633,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) @@ -629,13 +674,45 @@ virtio_notify_vector(vq->vdev, vq->vector); } -void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) +/* Assuming a given event_idx value from the other size, if + * we have just incremented index from old to new_idx, + * should we trigger an event? */ +static inline int vring_need_event(uint16_t event, uint16_t new, uint16_t old) +{ + /* Note: Xen has similar logic for notification hold-off + * in include/xen/interface/io/ring.h with req_event and req_prod + * corresponding to event_idx + 1 and new respectively. + * Note also that req_event and req_prod in Xen start at 1, + * event indexes in virtio start at 0. */ + return (uint16_t)(new - event - 1) < (uint16_t)(new - old); +} + +static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) { + uint16_t old, new; + bool v; /* Always notify when queue is empty (when feature acknowledge) */ - if ((vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT) && - (!(vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) || - (vq->inuse || vring_avail_idx(vq) != vq->last_avail_idx))) + if (((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) && + !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx)) { + return true; + } + + if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); + } + + v = vq->signalled_used_valid; + vq->signalled_used_valid = true; + old = vq->signalled_used; + new = vq->signalled_used = vring_used_idx(vq); + return !v || vring_need_event(vring_used_event(vq), new, old); +} + +void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) +{ + if (!vring_notify(vdev, vq)) { return; + } trace_virtio_notify(vdev, vq); vdev->isr |= 0x01; @@ -718,6 +795,8 @@ vdev->vq[i].vring.num = qemu_get_be32(f); vdev->vq[i].pa = qemu_get_be64(f); qemu_get_be16s(f, &vdev->vq[i].last_avail_idx); + vdev->vq[i].signalled_used_valid = false; + vdev->vq[i].notification = true; if (vdev->vq[i].pa) { uint16_t nheads; @@ -726,7 +805,7 @@ /* 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", + "inconsistent with Host index 0x%x: delta 0x%x", i, vdev->vq[i].vring.num, vring_avail_idx(&vdev->vq[i]), vdev->vq[i].last_avail_idx, nheads); @@ -734,7 +813,7 @@ } } else if (vdev->vq[i].last_avail_idx) { error_report("VQ %d address 0x0 " - "inconsistent with Host index 0x%x\n", + "inconsistent with Host index 0x%x", i, vdev->vq[i].last_avail_idx); return -1; } @@ -790,6 +869,7 @@ vdev->queue_sel = 0; vdev->config_vector = VIRTIO_NO_VECTOR; vdev->vq = qemu_mallocz(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX); + vdev->vm_running = vm_running; for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { vdev->vq[i].vector = VIRTIO_NO_VECTOR; vdev->vq[i].vdev = vdev; diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-console.c qemu-kvm-0.15.0+noroms/hw/virtio-console.c --- qemu-kvm-0.14.1+noroms/hw/virtio-console.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-console.c 2011-08-09 07:40:29.000000000 -0500 @@ -11,6 +11,8 @@ */ #include "qemu-char.h" +#include "qemu-error.h" +#include "trace.h" #include "virtio-serial.h" typedef struct VirtConsole { @@ -23,8 +25,42 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) { VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + ssize_t ret; - return qemu_chr_write(vcon->chr, buf, len); + ret = qemu_chr_write(vcon->chr, buf, len); + trace_virtio_console_flush_buf(port->id, len, ret); + + if (ret < 0) { + /* + * Ideally we'd get a better error code than just -1, but + * that's what the chardev interface gives us right now. If + * we had a finer-grained message, like -EPIPE, we could close + * this connection. Absent such error messages, the most we + * can do is to return 0 here. + * + * This will prevent stray -1 values to go to + * virtio-serial-bus.c and cause abort()s in + * do_flush_queued_data(). + */ + ret = 0; + } + return ret; +} + +/* Callback function that's called when the guest opens the port */ +static void guest_open(VirtIOSerialPort *port) +{ + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + + qemu_chr_guest_open(vcon->chr); +} + +/* Callback function that's called when the guest closes the port */ +static void guest_close(VirtIOSerialPort *port) +{ + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + + qemu_chr_guest_close(vcon->chr); } /* Readiness of the guest to accept data on a port */ @@ -40,6 +76,7 @@ { VirtConsole *vcon = opaque; + trace_virtio_console_chr_read(vcon->port.id, size); virtio_serial_write(&vcon->port, buf, size); } @@ -47,6 +84,7 @@ { VirtConsole *vcon = opaque; + trace_virtio_console_chr_event(vcon->port.id, event); switch (event) { case CHR_EVENT_OPENED: virtio_serial_open(&vcon->port); @@ -57,36 +95,38 @@ } } -static int generic_port_init(VirtConsole *vcon, VirtIOSerialDevice *dev) +static int virtconsole_initfn(VirtIOSerialPort *port) { - vcon->port.info = dev->info; + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, + vcon->port.dev.info); + + if (port->id == 0 && !info->is_console) { + error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility."); + return -1; + } if (vcon->chr) { qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, vcon); - vcon->port.info->have_data = flush_buf; + info->have_data = flush_buf; + info->guest_open = guest_open; + info->guest_close = guest_close; } - return 0; -} -/* Virtio Console Ports */ -static int virtconsole_initfn(VirtIOSerialDevice *dev) -{ - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); - VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - - port->is_console = true; - return generic_port_init(vcon, dev); + return 0; } -static int virtconsole_exitfn(VirtIOSerialDevice *dev) +static int virtconsole_exitfn(VirtIOSerialPort *port) { - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); if (vcon->chr) { - port->info->have_data = NULL; - qemu_chr_close(vcon->chr); + /* + * Instead of closing the chardev, free it so it can be used + * for other purposes. + */ + qemu_chr_add_handlers(vcon->chr, NULL, NULL, NULL, NULL); } return 0; @@ -95,13 +135,11 @@ static VirtIOSerialPortInfo virtconsole_info = { .qdev.name = "virtconsole", .qdev.size = sizeof(VirtConsole), + .is_console = true, .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(), }, }; @@ -112,24 +150,13 @@ } 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, + .init = virtconsole_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(), }, }; diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio.h qemu-kvm-0.15.0+noroms/hw/virtio.h --- qemu-kvm-0.14.1+noroms/hw/virtio.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio.h 2011-08-09 07:40:29.000000000 -0500 @@ -46,6 +46,11 @@ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 /* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* The Guest publishes the used index for which it expects an interrupt + * at the end of the avail ring. Host should ignore the avail->flags field. */ +/* The Host publishes the avail index for which it expects a kick + * at the end of the used ring. Guest should ignore the used->flags field. */ +#define VIRTIO_RING_F_EVENT_IDX 29 /* A guest should never accept this. It implies negotiation is broken. */ #define VIRTIO_F_BAD_FEATURE 30 @@ -192,11 +197,13 @@ void *opaque); /* Base devices. */ -VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf); +VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf, + char **serial); 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); +typedef struct virtio_serial_conf virtio_serial_conf; +VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial); VirtIODevice *virtio_balloon_init(DeviceState *dev); #ifdef CONFIG_LINUX VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); @@ -206,10 +213,13 @@ void virtio_net_exit(VirtIODevice *vdev); void virtio_blk_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev); +void virtio_balloon_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ DEFINE_PROP_BIT("indirect_desc", _state, _field, \ - VIRTIO_RING_F_INDIRECT_DESC, true) + VIRTIO_RING_F_INDIRECT_DESC, true), \ + DEFINE_PROP_BIT("event_idx", _state, _field, \ + VIRTIO_RING_F_EVENT_IDX, 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); diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-net.c qemu-kvm-0.15.0+noroms/hw/virtio-net.c --- qemu-kvm-0.14.1+noroms/hw/virtio-net.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-net.c 2011-08-09 07:40:29.000000000 -0500 @@ -115,7 +115,8 @@ if (!tap_get_vhost_net(n->nic->nc.peer)) { return; } - if (!!n->vhost_started == virtio_net_started(n, status)) { + if (!!n->vhost_started == virtio_net_started(n, status) && + !n->nic->nc.peer->link_down) { return; } if (!n->vhost_started) { @@ -149,7 +150,7 @@ 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); + qemu_get_clock_ns(vm_clock) + n->tx_timeout); } else { qemu_bh_schedule(n->tx_bh); } @@ -656,7 +657,7 @@ /* copy in packet. ugh */ len = iov_from_buf(sg, elem.in_num, - buf + offset, size - offset); + buf + offset, 0, size - offset); total += len; offset += len; /* If buffers can't be merged, at this point we @@ -784,7 +785,7 @@ virtio_net_flush_tx(n, vq); } else { qemu_mod_timer(n->tx_timer, - qemu_get_clock(vm_clock) + n->tx_timeout); + qemu_get_clock_ns(vm_clock) + n->tx_timeout); n->tx_waiting = 1; virtio_queue_set_notification(vq, 0); } @@ -1018,7 +1019,7 @@ 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_timer = qemu_new_timer_ns(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); diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-pci.c qemu-kvm-0.15.0+noroms/hw/virtio-pci.c --- qemu-kvm-0.14.1+noroms/hw/virtio-pci.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-pci.c 2011-08-09 07:40:29.000000000 -0500 @@ -18,6 +18,7 @@ #include "virtio.h" #include "virtio-blk.h" #include "virtio-net.h" +#include "virtio-serial.h" #include "pci.h" #include "qemu-error.h" #include "msix.h" @@ -25,6 +26,7 @@ #include "loader.h" #include "kvm.h" #include "blockdev.h" +#include "virtio-pci.h" /* from Linux's linux/virtio_pci.h */ @@ -73,9 +75,6 @@ VIRTIO_PCI_CONFIG_MSI : \ VIRTIO_PCI_CONFIG_NOMSI) -/* Virtio ABI version, if we increment this, we break the guest driver. */ -#define VIRTIO_PCI_ABI_VERSION 0 - /* How many bits to shift physical queue address written to QUEUE_PFN. * 12 is historical, and due to x86 page size. */ #define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 @@ -94,28 +93,6 @@ */ #define wmb() do { } while (0) -/* PCI bindings. */ - -typedef struct { - PCIDevice pci_dev; - VirtIODevice *vdev; - uint32_t flags; - uint32_t addr; - uint32_t class_code; - uint32_t nvectors; - 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 */ static void virtio_pci_notify(void *opaque, uint16_t vector) @@ -348,7 +325,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)) { @@ -569,7 +548,6 @@ 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), @@ -585,9 +563,6 @@ NULL, NULL, NULL); } return 0; -#else - return -ENOSYS; -#endif } static int virtio_pci_mask_notifier(PCIDevice *dev, unsigned vector, @@ -690,10 +665,6 @@ 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); } @@ -753,9 +724,7 @@ .vmstate_change = virtio_pci_vmstate_change, }; -static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, - uint16_t vendor, uint16_t device, - uint16_t class_code, uint8_t pif) +void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev) { uint8_t *config; uint32_t size; @@ -763,19 +732,12 @@ proxy->vdev = vdev; config = proxy->pci_dev.config; - pci_config_set_vendor_id(config, vendor); - pci_config_set_device_id(config, device); - - config[0x08] = VIRTIO_PCI_ABI_VERSION; - - config[0x09] = pif; - pci_config_set_class(config, class_code); - - config[0x2c] = vendor & 0xFF; - config[0x2d] = (vendor >> 8) & 0xFF; - config[0x2e] = vdev->device_id & 0xFF; - config[0x2f] = (vdev->device_id >> 8) & 0xFF; + if (proxy->class_code) { + pci_config_set_class(config, proxy->class_code); + } + pci_set_word(config + 0x2c, pci_get_word(config + PCI_VENDOR_ID)); + pci_set_word(config + 0x2e, vdev->device_id); config[0x3d] = 1; if (vdev->nvectors && !msix_init(&proxy->pci_dev, vdev->nvectors, 1, 0)) { @@ -814,15 +776,13 @@ proxy->class_code != PCI_CLASS_STORAGE_OTHER) proxy->class_code = PCI_CLASS_STORAGE_SCSI; - vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block); + vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block, + &proxy->block_serial); if (!vdev) { return -1; } vdev->nvectors = proxy->nvectors; - virtio_init_pci(proxy, vdev, - PCI_VENDOR_ID_REDHAT_QUMRANET, - PCI_DEVICE_ID_VIRTIO_BLOCK, - proxy->class_code, 0x00); + virtio_init_pci(proxy, vdev); /* make the actual value visible */ proxy->nvectors = vdev->nvectors; return 0; @@ -853,17 +813,14 @@ proxy->class_code != PCI_CLASS_OTHERS) /* qemu-kvm */ proxy->class_code = PCI_CLASS_COMMUNICATION_OTHER; - vdev = virtio_serial_init(&pci_dev->qdev, proxy->max_virtserial_ports); + vdev = virtio_serial_init(&pci_dev->qdev, &proxy->serial); if (!vdev) { return -1; } vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED - ? proxy->max_virtserial_ports + 1 + ? proxy->serial.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); + virtio_init_pci(proxy, vdev); proxy->nvectors = vdev->nvectors; return 0; } @@ -872,6 +829,7 @@ { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + virtio_pci_stop_ioeventfd(proxy); virtio_serial_exit(proxy->vdev); return virtio_exit_pci(pci_dev); } @@ -884,11 +842,7 @@ vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net); vdev->nvectors = proxy->nvectors; - virtio_init_pci(proxy, vdev, - PCI_VENDOR_ID_REDHAT_QUMRANET, - PCI_DEVICE_ID_VIRTIO_NET, - PCI_CLASS_NETWORK_ETHERNET, - 0x00); + virtio_init_pci(proxy, vdev); /* make the actual value visible */ proxy->nvectors = vdev->nvectors; @@ -910,32 +864,21 @@ VirtIODevice *vdev; vdev = virtio_balloon_init(&pci_dev->qdev); - virtio_init_pci(proxy, vdev, - PCI_VENDOR_ID_REDHAT_QUMRANET, - PCI_DEVICE_ID_VIRTIO_BALLOON, - PCI_CLASS_MEMORY_RAM, - 0x00); + if (!vdev) { + return -1; + } + virtio_init_pci(proxy, vdev); return 0; } -#ifdef CONFIG_VIRTFS -static int virtio_9p_init_pci(PCIDevice *pci_dev) +static int virtio_balloon_exit_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; + virtio_pci_stop_ioeventfd(proxy); + virtio_balloon_exit(proxy->vdev); + return virtio_exit_pci(pci_dev); } -#endif static PCIDeviceInfo virtio_info[] = { { @@ -944,9 +887,14 @@ .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_blk_init_pci, .exit = virtio_blk_exit_pci, + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = PCI_DEVICE_ID_VIRTIO_BLOCK, + .revision = VIRTIO_PCI_ABI_VERSION, + .class_id = PCI_CLASS_STORAGE_SCSI, .qdev.props = (Property[]) { DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block), + DEFINE_PROP_STRING("serial", VirtIOPCIProxy, block_serial), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), @@ -956,10 +904,15 @@ .qdev.reset = virtio_pci_reset, },{ .qdev.name = "virtio-net-pci", + .qdev.alias = "virtio-net", .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_net_init_pci, .exit = virtio_net_exit_pci, - .romfile = "pxe-virtio.bin", + .romfile = "pxe-virtio.rom", + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = PCI_DEVICE_ID_VIRTIO_NET, + .revision = VIRTIO_PCI_ABI_VERSION, + .class_id = PCI_CLASS_NETWORK_ETHERNET, .qdev.props = (Property[]) { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), @@ -980,40 +933,38 @@ .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_serial_init_pci, .exit = virtio_serial_exit_pci, + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE, + .revision = VIRTIO_PCI_ABI_VERSION, + .class_id = PCI_CLASS_COMMUNICATION_OTHER, .qdev.props = (Property[]) { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), 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_UINT32("max_ports", VirtIOPCIProxy, + serial.max_virtserial_ports, 31), DEFINE_PROP_END_OF_LIST(), }, .qdev.reset = virtio_pci_reset, },{ .qdev.name = "virtio-balloon-pci", + .qdev.alias = "virtio-balloon", .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_balloon_init_pci, - .exit = virtio_exit_pci, + .exit = virtio_balloon_exit_pci, + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = PCI_DEVICE_ID_VIRTIO_BALLOON, + .revision = VIRTIO_PCI_ABI_VERSION, + .class_id = PCI_CLASS_MEMORY_RAM, .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.14.1+noroms/hw/virtio-pci.h qemu-kvm-0.15.0+noroms/hw/virtio-pci.h --- qemu-kvm-0.14.1+noroms/hw/virtio-pci.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/virtio-pci.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,46 @@ +/* + * Virtio PCI Bindings + * + * Copyright IBM, Corp. 2007 + * Copyright (c) 2009 CodeSourcery + * + * Authors: + * Anthony Liguori + * Paul Brook + * + * 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_PCI_H +#define QEMU_VIRTIO_PCI_H + +#include "virtio-net.h" +#include "virtio-serial.h" + +typedef struct { + PCIDevice pci_dev; + VirtIODevice *vdev; + uint32_t flags; + uint32_t addr; + uint32_t class_code; + uint32_t nvectors; + BlockConf block; + char *block_serial; + NICConf nic; + uint32_t host_features; +#ifdef CONFIG_LINUX + V9fsConf fsconf; +#endif + virtio_serial_conf serial; + virtio_net_conf net; + bool ioeventfd_disabled; + bool ioeventfd_started; +} VirtIOPCIProxy; + +void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev); + +/* Virtio ABI version, if we increment this, we break the guest driver. */ +#define VIRTIO_PCI_ABI_VERSION 0 + +#endif diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-serial-bus.c qemu-kvm-0.15.0+noroms/hw/virtio-serial-bus.c --- qemu-kvm-0.14.1+noroms/hw/virtio-serial-bus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-serial-bus.c 2011-08-09 07:40:29.000000000 -0500 @@ -19,6 +19,7 @@ #include "monitor.h" #include "qemu-queue.h" #include "sysbus.h" +#include "trace.h" #include "virtio-serial.h" /* The virtio-serial bus on top of which the ports will ride as devices */ @@ -39,7 +40,7 @@ /* Arrays of ivqs and ovqs: one per port */ VirtQueue **ivqs, **ovqs; - VirtIOSerialBus *bus; + VirtIOSerialBus bus; DeviceState *qdev; @@ -103,7 +104,7 @@ } len = iov_from_buf(elem.in_sg, elem.in_num, - buf + offset, size - offset); + buf + offset, 0, size - offset); offset += len; virtqueue_push(vq, &elem, len); @@ -129,9 +130,13 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, VirtIODevice *vdev) { + VirtIOSerialPortInfo *info; + assert(port); assert(virtio_queue_ready(vq)); + info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); + while (!port->throttled) { unsigned int i; @@ -149,10 +154,10 @@ 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); + ret = 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(); @@ -217,6 +222,7 @@ stw_p(&cpkt.event, event); stw_p(&cpkt.value, value); + trace_virtio_serial_send_control_event(port->id, event, value); return send_control_msg(port, &cpkt, sizeof(cpkt)); } @@ -285,24 +291,32 @@ return 0; } +static void flush_queued_data_bh(void *opaque) +{ + VirtIOSerialPort *port = opaque; + + flush_queued_data(port); +} + void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) { if (!port) { return; } + trace_virtio_serial_throttle_port(port->id, throttle); port->throttled = throttle; if (throttle) { return; } - - flush_queued_data(port); + qemu_bh_schedule(port->bh); } /* Guest wants to notify us of some event */ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) { struct VirtIOSerialPort *port; + struct VirtIOSerialPortInfo *info; struct virtio_console_control cpkt, *gcpkt; uint8_t *buffer; size_t buffer_len; @@ -317,16 +331,13 @@ 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; + trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); - switch(cpkt.event) { - case VIRTIO_CONSOLE_DEVICE_READY: + if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) { if (!cpkt.value) { - error_report("virtio-serial-bus: Guest failure in adding device %s\n", - vser->bus->qbus.name); - break; + error_report("virtio-serial-bus: Guest failure in adding device %s", + vser->bus.qbus.name); + return; } /* * The device is up, we can now tell the device about all the @@ -335,12 +346,25 @@ QTAILQ_FOREACH(port, &vser->ports, next) { send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1); } - break; + return; + } + + port = find_port_by_id(vser, ldl_p(&gcpkt->id)); + if (!port) { + error_report("virtio-serial-bus: Unexpected port id %u for device %s", + ldl_p(&gcpkt->id), vser->bus.qbus.name); + return; + } + + trace_virtio_serial_handle_control_message_port(port->id); + info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); + + switch(cpkt.event) { 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); + error_report("virtio-serial-bus: Guest failure in adding port %u for device %s", + port->id, vser->bus.qbus.name); break; } /* @@ -350,7 +374,7 @@ * this port is a console port so that the guest can hook it * up to hvc. */ - if (port->is_console) { + if (info->is_console) { send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1); } @@ -379,21 +403,21 @@ * initialised. If some app is interested in knowing about * this event, let it know. */ - if (port->info->guest_ready) { - port->info->guest_ready(port); + if (info->guest_ready) { + info->guest_ready(port); } break; case VIRTIO_CONSOLE_PORT_OPEN: port->guest_connected = cpkt.value; - if (cpkt.value && port->info->guest_open) { + if (cpkt.value && info->guest_open) { /* Send the guest opened notification if an app is interested */ - port->info->guest_open(port); + info->guest_open(port); } - if (!cpkt.value && port->info->guest_close) { + if (!cpkt.value && info->guest_close) { /* Send the guest closed notification if an app is interested */ - port->info->guest_close(port); + info->guest_close(port); } break; } @@ -442,25 +466,21 @@ { VirtIOSerial *vser; VirtIOSerialPort *port; - bool discard; + VirtIOSerialPortInfo *info; vser = DO_UPCAST(VirtIOSerial, vdev, vdev); port = find_port_by_vq(vser, vq); + info = port ? DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info) : NULL; - discard = false; - if (!port || !port->host_connected || !port->info->have_data) { - discard = true; - } - - if (discard) { + if (!port || !port->host_connected || !info->have_data) { discard_vq_data(vq, vdev); return; } - if (port->throttled) { + + if (!port->throttled) { + do_flush_queued_data(port, vq, vdev); return; } - - do_flush_queued_data(port, vq, vdev); } static void handle_input(VirtIODevice *vdev, VirtQueue *vq) @@ -473,7 +493,7 @@ vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - if (vser->bus->max_nr_ports > 1) { + if (vser->bus.max_nr_ports > 1) { features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT); } return features; @@ -500,7 +520,7 @@ VirtIOSerial *s = opaque; VirtIOSerialPort *port; uint32_t nr_active_ports; - unsigned int i; + unsigned int i, max_nr_ports; /* The virtio device */ virtio_save(&s->vdev, f); @@ -512,8 +532,8 @@ qemu_put_be32s(f, &s->config.max_nr_ports); /* The ports map */ - - for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) { + max_nr_ports = tswap32(s->config.max_nr_ports); + for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_put_be32s(f, &s->ports_map[i]); } @@ -574,7 +594,8 @@ qemu_get_be16s(f, &s->config.rows); qemu_get_be32s(f, &max_nr_ports); - if (max_nr_ports > s->config.max_nr_ports) { + tswap32s(&max_nr_ports); + if (max_nr_ports > tswap32(s->config.max_nr_ports)) { /* Source could have had more ports than us. Fail migration. */ return -EINVAL; } @@ -600,6 +621,9 @@ id = qemu_get_be32(f); port = find_port_by_id(s, id); + if (!port) { + return -EINVAL; + } port->guest_connected = qemu_get_byte(f); host_connected = qemu_get_byte(f); @@ -644,39 +668,31 @@ .name = "virtio-serial-bus", .size = sizeof(VirtIOSerialBus), .print_dev = virtser_bus_dev_print, + .props = (Property[]) { + DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_STRING("name", VirtIOSerialPort, name), + DEFINE_PROP_END_OF_LIST() + } }; -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); + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, 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); + monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", + indent, "", port->id, + port->guest_connected ? "on" : "off", + port->host_connected ? "on" : "off", + port->throttled ? "on" : "off"); } /* 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; + unsigned int i, max_nr_ports; - for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) { + max_nr_ports = tswap32(vser->config.max_nr_ports); + for (i = 0; i < (max_nr_ports + 31) / 32; i++) { uint32_t map, bit; map = vser->ports_map[i]; @@ -721,24 +737,24 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) { - VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, 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; + int ret, max_nr_ports; bool plugging_port0; port->vser = bus->vser; + port->bh = qemu_bh_new(flush_queued_data_bh, port); /* * 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); + plugging_port0 = info->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", + error_report("virtio-serial-bus: A port already exists at id %u", port->id); return -1; } @@ -749,20 +765,20 @@ } 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"); + error_report("virtio-serial-bus: Maximum port limit for this device reached"); 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); + max_nr_ports = tswap32(port->vser->config.max_nr_ports); + if (port->id >= max_nr_ports) { + error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u", + max_nr_ports - 1); return -1; } - dev->info = info; - ret = info->init(dev); + ret = info->init(port); if (ret) { return ret; } @@ -791,17 +807,19 @@ static int virtser_port_qdev_exit(DeviceState *qdev) { - VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); + VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, + port->dev.info); VirtIOSerial *vser = port->vser; + qemu_bh_delete(port->bh); remove_port(port->vser, port->id); QTAILQ_REMOVE(&vser->ports, port, next); - if (port->info->exit) - port->info->exit(dev); - + if (info->exit) { + info->exit(port); + } return 0; } @@ -814,19 +832,19 @@ qdev_register(&info->qdev); } -VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) +VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) { VirtIOSerial *vser; VirtIODevice *vdev; uint32_t i, max_supported_ports; - if (!max_nr_ports) + if (!conf->max_virtserial_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) { + if (conf->max_virtserial_ports > max_supported_ports) { error_report("maximum ports supported: %u", max_supported_ports); return NULL; } @@ -838,13 +856,14 @@ 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; + qbus_create_inplace(&vser->bus.qbus, &virtser_bus_info, dev, NULL); + vser->bus.qbus.allow_hotplug = 1; + 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 *)); + vser->bus.max_nr_ports = conf->max_virtserial_ports; + vser->ivqs = qemu_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *)); + vser->ovqs = qemu_malloc(conf->max_virtserial_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); @@ -862,15 +881,15 @@ /* 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++) { + 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) + vser->config.max_nr_ports = tswap32(conf->max_virtserial_ports); + vser->ports_map = qemu_mallocz(((conf->max_virtserial_ports + 31) / 32) * sizeof(vser->ports_map[0])); /* * Reserve location 0 for a console port for backward compat diff -Nru qemu-kvm-0.14.1+noroms/hw/virtio-serial.h qemu-kvm-0.15.0+noroms/hw/virtio-serial.h --- qemu-kvm-0.14.1+noroms/hw/virtio-serial.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/virtio-serial.h 2011-08-09 07:40:29.000000000 -0500 @@ -45,6 +45,11 @@ uint16_t value; /* Extra information for the key */ }; +struct virtio_serial_conf { + /* Max. number of ports we can have for a virtio-serial device */ + uint32_t max_virtserial_ports; +}; + /* Some events for the internal messages (control packets) */ #define VIRTIO_CONSOLE_DEVICE_READY 0 #define VIRTIO_CONSOLE_PORT_ADD 1 @@ -62,11 +67,6 @@ 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 @@ -75,7 +75,6 @@ */ struct VirtIOSerialPort { DeviceState dev; - VirtIOSerialPortInfo *info; QTAILQ_ENTRY(VirtIOSerialPort) next; @@ -119,8 +118,10 @@ 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; + /* + * When unthrottling we use a bottom-half to call flush_queued_data. + */ + QEMUBH *bh; /* Is the corresponding guest device open? */ bool guest_connected; @@ -132,16 +133,20 @@ struct VirtIOSerialPortInfo { DeviceInfo qdev; + + /* Is this a device that binds with hvc in the guest? */ + bool is_console; + /* * The per-port (or per-app) init function that's called when a * new device is found on the bus. */ - int (*init)(VirtIOSerialDevice *dev); + int (*init)(VirtIOSerialPort *port); /* * Per-port exit function that's called when a port gets * hot-unplugged or removed. */ - int (*exit)(VirtIOSerialDevice *dev); + int (*exit)(VirtIOSerialPort *port); /* Callbacks for guest events */ /* Guest opened device. */ diff -Nru qemu-kvm-0.14.1+noroms/hw/vmmouse.c qemu-kvm-0.15.0+noroms/hw/vmmouse.c --- qemu-kvm-0.14.1+noroms/hw/vmmouse.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vmmouse.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,6 +25,7 @@ #include "console.h" #include "ps2.h" #include "pc.h" +#include "qdev.h" /* debug only vmmouse */ //#define DEBUG_VMMOUSE @@ -52,6 +53,7 @@ typedef struct _VMMouseState { + ISADevice dev; uint32_t queue[VMMOUSE_QUEUE_SIZE]; int32_t queue_size; uint16_t nb_queue; @@ -176,30 +178,6 @@ memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue); } -static void vmmouse_get_data(uint32_t *data) -{ - CPUState *env = cpu_single_env; - - data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX]; - data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX]; - data[4] = env->regs[R_ESI]; data[5] = env->regs[R_EDI]; - - DPRINTF("get_data = {%x, %x, %x, %x, %x, %x}\n", - data[0], data[1], data[2], data[3], data[4], data[5]); -} - -static void vmmouse_set_data(const uint32_t *data) -{ - CPUState *env = cpu_single_env; - - DPRINTF("set_data = {%x, %x, %x, %x, %x, %x}\n", - data[0], data[1], data[2], data[3], data[4], data[5]); - - env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1]; - env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3]; - env->regs[R_ESI] = data[4]; env->regs[R_EDI] = data[5]; -} - static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr) { VMMouseState *s = opaque; @@ -270,22 +248,42 @@ } }; -void *vmmouse_init(void *m) +static void vmmouse_reset(DeviceState *d) { - VMMouseState *s = NULL; - - DPRINTF("vmmouse_init\n"); - - s = qemu_mallocz(sizeof(VMMouseState)); + VMMouseState *s = container_of(d, VMMouseState, dev.qdev); s->status = 0xffff; - s->ps2_mouse = m; s->queue_size = VMMOUSE_QUEUE_SIZE; +} + +static int vmmouse_initfn(ISADevice *dev) +{ + VMMouseState *s = DO_UPCAST(VMMouseState, dev, dev); + + DPRINTF("vmmouse_init\n"); 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(NULL, 0, &vmstate_vmmouse, s); - return s; + return 0; +} + +static ISADeviceInfo vmmouse_info = { + .init = vmmouse_initfn, + .qdev.name = "vmmouse", + .qdev.size = sizeof(VMMouseState), + .qdev.vmsd = &vmstate_vmmouse, + .qdev.no_user = 1, + .qdev.reset = vmmouse_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_PTR("ps2_mouse", VMMouseState, ps2_mouse), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void vmmouse_dev_register(void) +{ + isa_qdev_register(&vmmouse_info); } +device_init(vmmouse_dev_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/vmport.c qemu-kvm-0.15.0+noroms/hw/vmport.c --- qemu-kvm-0.14.1+noroms/hw/vmport.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vmport.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,8 +24,8 @@ #include "hw.h" #include "isa.h" #include "pc.h" -#include "sysemu.h" #include "kvm.h" +#include "qdev.h" //#define VMPORT_DEBUG @@ -37,19 +37,20 @@ typedef struct _VMPortState { + ISADevice dev; IOPortReadFunc *func[VMPORT_ENTRIES]; void *opaque[VMPORT_ENTRIES]; } VMPortState; -static VMPortState port_state; +static VMPortState *port_state; void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque) { if (command >= VMPORT_ENTRIES) return; - port_state.func[command] = func; - port_state.opaque[command] = opaque; + port_state->func[command] = func; + port_state->opaque[command] = opaque; } static uint32_t vmport_ioport_read(void *opaque, uint32_t addr) @@ -100,12 +101,48 @@ return ram_size; } -void vmport_init(void) +/* vmmouse helpers */ +void vmmouse_get_data(uint32_t *data) { - register_ioport_read(0x5658, 1, 4, vmport_ioport_read, &port_state); - register_ioport_write(0x5658, 1, 4, vmport_ioport_write, &port_state); + CPUState *env = cpu_single_env; + + data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX]; + data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX]; + data[4] = env->regs[R_ESI]; data[5] = env->regs[R_EDI]; +} + +void vmmouse_set_data(const uint32_t *data) +{ + CPUState *env = cpu_single_env; + + env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1]; + env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3]; + env->regs[R_ESI] = data[4]; env->regs[R_EDI] = data[5]; +} + +static int vmport_initfn(ISADevice *dev) +{ + VMPortState *s = DO_UPCAST(VMPortState, dev, dev); + register_ioport_read(0x5658, 1, 4, vmport_ioport_read, s); + register_ioport_write(0x5658, 1, 4, vmport_ioport_write, s); + isa_init_ioport(dev, 0x5658); + port_state = s; /* Register some generic port commands */ vmport_register(VMPORT_CMD_GETVERSION, vmport_cmd_get_version, NULL); vmport_register(VMPORT_CMD_GETRAMSIZE, vmport_cmd_ram_size, NULL); + return 0; +} + +static ISADeviceInfo vmport_info = { + .qdev.name = "vmport", + .qdev.size = sizeof(VMPortState), + .qdev.no_user = 1, + .init = vmport_initfn, +}; + +static void vmport_dev_register(void) +{ + isa_qdev_register(&vmport_info); } +device_init(vmport_dev_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/vmware_vga.c qemu-kvm-0.15.0+noroms/hw/vmware_vga.c --- qemu-kvm-0.14.1+noroms/hw/vmware_vga.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vmware_vga.c 2011-08-09 07:40:29.000000000 -0500 @@ -1280,15 +1280,8 @@ struct pci_vmsvga_state_s *s = DO_UPCAST(struct pci_vmsvga_state_s, card, dev); - pci_config_set_vendor_id(s->card.config, PCI_VENDOR_ID_VMWARE); - pci_config_set_device_id(s->card.config, SVGA_PCI_DEVICE_ID); - pci_config_set_class(s->card.config, PCI_CLASS_DISPLAY_VGA); 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, @@ -1309,11 +1302,6 @@ return 0; } -void pci_vmsvga_init(PCIBus *bus) -{ - pci_create_simple(bus, -1, "vmware-svga"); -} - static PCIDeviceInfo vmsvga_info = { .qdev.name = "vmware-svga", .qdev.size = sizeof(struct pci_vmsvga_state_s), @@ -1321,6 +1309,12 @@ .no_hotplug = 1, .init = pci_vmsvga_initfn, .romfile = "vgabios-vmware.bin", + + .vendor_id = PCI_VENDOR_ID_VMWARE, + .device_id = SVGA_PCI_DEVICE_ID, + .class_id = PCI_CLASS_DISPLAY_VGA, + .subsystem_vendor_id = PCI_VENDOR_ID_VMWARE, + .subsystem_id = SVGA_PCI_DEVICE_ID, }; static void vmsvga_register(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/vmware_vga.h qemu-kvm-0.15.0+noroms/hw/vmware_vga.h --- qemu-kvm-0.14.1+noroms/hw/vmware_vga.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vmware_vga.h 2011-08-09 07:40:29.000000000 -0500 @@ -4,6 +4,16 @@ #include "qemu-common.h" /* vmware_vga.c */ -void pci_vmsvga_init(PCIBus *bus); +static inline bool pci_vmsvga_init(PCIBus *bus) +{ + PCIDevice *dev; + + dev = pci_try_create(bus, -1, "vmware-svga"); + if (!dev || qdev_init(&dev->qdev) < 0) { + return false; + } else { + return true; + } +} #endif diff -Nru qemu-kvm-0.14.1+noroms/hw/vt82c686.c qemu-kvm-0.15.0+noroms/hw/vt82c686.c --- qemu-kvm-0.14.1+noroms/hw/vt82c686.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/vt82c686.c 2011-08-09 07:40:29.000000000 -0500 @@ -156,12 +156,10 @@ typedef struct VT686PMState { PCIDevice dev; - uint16_t pmsts; - uint16_t pmen; - uint16_t pmcntrl; + ACPIPM1EVT pm1a; + ACPIPM1CNT pm1_cnt; APMState apm; - QEMUTimer *tmr_timer; - int64_t tmr_overflow_time; + ACPIPMTimer tmr; PMSMBus smb; uint32_t smb_io_base; } VT686PMState; @@ -174,54 +172,25 @@ 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); + pmsts = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time); + sci_level = (((pmsts & s->pm1a.en) & + (ACPI_BITMASK_RT_CLOCK_ENABLE | + ACPI_BITMASK_POWER_BUTTON_ENABLE | + ACPI_BITMASK_GLOBAL_LOCK_ENABLE | + ACPI_BITMASK_TIMER_ENABLE)) != 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); - } + acpi_pm_tmr_update(&s->tmr, (s->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) && + !(pmsts & ACPI_BITMASK_TIMER_STATUS)); } -static void pm_tmr_timer(void *opaque) +static void pm_tmr_timer(ACPIPMTimer *tmr) { - VT686PMState *s = opaque; + VT686PMState *s = container_of(tmr, VT686PMState, tmr); pm_update_sci(s); } @@ -232,39 +201,15 @@ 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); - } + acpi_pm1_evt_write_sts(&s->pm1a, &s->tmr, val); + pm_update_sci(s); break; case 0x02: - s->pmen = val; + s->pm1a.en = 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; - } - } - } + acpi_pm1_cnt_write(&s->pm1a, &s->pm1_cnt, val); break; default: break; @@ -280,13 +225,13 @@ addr &= 0x0f; switch (addr) { case 0x00: - val = get_pmsts(s); + val = acpi_pm1_evt_get_sts(&s->pm1a, s->tmr.overflow_time); break; case 0x02: - val = s->pmen; + val = s->pm1a.en; break; case 0x04: - val = s->pmcntrl; + val = s->pm1_cnt.cnt; break; default: val = 0; @@ -310,7 +255,7 @@ addr &= 0x0f; switch (addr) { case 0x08: - val = get_pmtmr(s); + val = acpi_pm_tmr_get(&s->tmr); break; default: val = 0; @@ -361,12 +306,12 @@ .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_UINT16(pm1a.sts, VT686PMState), + VMSTATE_UINT16(pm1a.en, VT686PMState), + VMSTATE_UINT16(pm1_cnt.cnt, VT686PMState), VMSTATE_STRUCT(apm, VT686PMState, 0, vmstate_apm, APMState), - VMSTATE_TIMER(tmr_timer, VT686PMState), - VMSTATE_INT64(tmr_overflow_time, VT686PMState), + VMSTATE_TIMER(tmr.timer, VT686PMState), + VMSTATE_INT64(tmr.overflow_time, VT686PMState), VMSTATE_END_OF_LIST() } }; @@ -381,11 +326,6 @@ 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 | @@ -408,6 +348,10 @@ .qdev.desc = "AC97", .qdev.size = sizeof(VT686AC97State), .init = vt82c686b_ac97_initfn, + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_AC97, + .revision = 0x50, + .class_id = PCI_CLASS_MULTIMEDIA_AUDIO, }; static void vt82c686b_ac97_register(void) @@ -422,11 +366,6 @@ 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); @@ -448,6 +387,10 @@ .qdev.desc = "MC97", .qdev.size = sizeof(VT686MC97State), .init = vt82c686b_mc97_initfn, + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_MC97, + .class_id = PCI_CLASS_COMMUNICATION_OTHER, + .revision = 0x30, }; static void vt82c686b_mc97_register(void) @@ -464,11 +407,6 @@ 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); @@ -486,7 +424,8 @@ apm_init(&s->apm, NULL, s); - s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s); + acpi_pm_tmr_init(&s->tmr, pm_tmr_timer); + acpi_pm1_cnt_init(&s->pm1_cnt, NULL); pm_smbus_init(&s->dev.qdev, &s->smb); @@ -516,6 +455,10 @@ .qdev.vmsd = &vmstate_acpi, .init = vt82c686b_pm_initfn, .config_write = pm_write_config, + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_ACPI, + .class_id = PCI_CLASS_BRIDGE_OTHER, + .revision = 0x40, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("smb_io_base", VT686PMState, smb_io_base, 0), DEFINE_PROP_END_OF_LIST(), @@ -550,11 +493,7 @@ 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++) { @@ -585,6 +524,10 @@ .qdev.no_user = 1, .init = vt82c686b_initfn, .config_write = vt82c686b_write_config, + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_ISA_BRIDGE, + .class_id = PCI_CLASS_BRIDGE_ISA, + .revision = 0x40, }; static void vt82c686b_register(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/watchdog.c qemu-kvm-0.15.0+noroms/hw/watchdog.c --- qemu-kvm-0.14.1+noroms/hw/watchdog.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/watchdog.c 2011-08-09 07:40:29.000000000 -0500 @@ -132,7 +132,7 @@ case WDT_PAUSE: /* same as 'stop' command in monitor */ watchdog_mon_event("pause"); - vm_stop(0); + vm_stop(VMSTOP_WATCHDOG); break; case WDT_DEBUG: diff -Nru qemu-kvm-0.14.1+noroms/hw/wdt_i6300esb.c qemu-kvm-0.15.0+noroms/hw/wdt_i6300esb.c --- qemu-kvm-0.14.1+noroms/hw/wdt_i6300esb.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/wdt_i6300esb.c 2011-08-09 07:40:29.000000000 -0500 @@ -129,7 +129,7 @@ i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout); - qemu_mod_timer(d->timer, qemu_get_clock(vm_clock) + timeout); + qemu_mod_timer(d->timer, qemu_get_clock_ns(vm_clock) + timeout); } /* This is called when the guest disables the watchdog. */ @@ -355,31 +355,6 @@ } } -static void i6300esb_map(PCIDevice *dev, int region_num, - pcibus_t addr, pcibus_t size, int type) -{ - static CPUReadMemoryFunc * const mem_read[3] = { - i6300esb_mem_readb, - i6300esb_mem_readw, - i6300esb_mem_readl, - }; - static CPUWriteMemoryFunc * const mem_write[3] = { - i6300esb_mem_writeb, - i6300esb_mem_writew, - i6300esb_mem_writel, - }; - I6300State *d = DO_UPCAST(I6300State, dev, dev); - int io_mem; - - 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, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory (addr, 0x10, io_mem); - /* qemu_register_coalesced_mmio (addr, 0x10); ? */ -} - static const VMStateDescription vmstate_i6300esb = { .name = "i6300esb_wdt", .version_id = sizeof(I6300State), @@ -406,20 +381,27 @@ static int i6300esb_init(PCIDevice *dev) { I6300State *d = DO_UPCAST(I6300State, dev, dev); - uint8_t *pci_conf; + int io_mem; + static CPUReadMemoryFunc * const mem_read[3] = { + i6300esb_mem_readb, + i6300esb_mem_readw, + i6300esb_mem_readl, + }; + static CPUWriteMemoryFunc * const mem_write[3] = { + i6300esb_mem_writeb, + i6300esb_mem_writew, + i6300esb_mem_writel, + }; i6300esb_debug("I6300State = %p\n", d); - d->timer = qemu_new_timer(vm_clock, i6300esb_timer_expired, d); + d->timer = qemu_new_timer_ns(vm_clock, i6300esb_timer_expired, d); 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_register_bar(&d->dev, 0, 0x10, - PCI_BASE_ADDRESS_SPACE_MEMORY, i6300esb_map); + io_mem = cpu_register_io_memory(mem_read, mem_write, d, + DEVICE_NATIVE_ENDIAN); + pci_register_bar_simple(&d->dev, 0, 0x10, 0, io_mem); + /* qemu_register_coalesced_mmio (addr, 0x10); ? */ return 0; } @@ -437,6 +419,9 @@ .config_read = i6300esb_config_read, .config_write = i6300esb_config_write, .init = i6300esb_init, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_ESB_9, + .class_id = PCI_CLASS_SYSTEM_OTHER, }; static void i6300esb_register_devices(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/wdt_ib700.c qemu-kvm-0.15.0+noroms/hw/wdt_ib700.c --- qemu-kvm-0.14.1+noroms/hw/wdt_ib700.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/wdt_ib700.c 2011-08-09 07:40:29.000000000 -0500 @@ -58,7 +58,7 @@ ib700_debug("addr = %x, data = %x\n", addr, data); timeout = (int64_t) time_map[data & 0xF] * get_ticks_per_sec(); - qemu_mod_timer(s->timer, qemu_get_clock (vm_clock) + timeout); + qemu_mod_timer(s->timer, qemu_get_clock_ns (vm_clock) + timeout); } /* A write (of any value) to this register disables the timer. */ @@ -99,7 +99,7 @@ ib700_debug("watchdog init\n"); - s->timer = qemu_new_timer(vm_clock, ib700_timer_expired, s); + s->timer = qemu_new_timer_ns(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); diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_backend.c qemu-kvm-0.15.0+noroms/hw/xen_backend.c --- qemu-kvm-0.14.1+noroms/hw/xen_backend.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_backend.c 2011-08-09 07:40:29.000000000 -0500 @@ -43,7 +43,8 @@ /* ------------------------------------------------------------- */ /* public */ -int xen_xc; +XenXC xen_xc = XC_HANDLER_INITIAL_VALUE; +XenGnttab xen_xcg = XC_HANDLER_INITIAL_VALUE; struct xs_handle *xenstore = NULL; const char *xen_protocol; @@ -58,8 +59,9 @@ char abspath[XEN_BUFSIZE]; snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - if (!xs_write(xenstore, 0, abspath, val, strlen(val))) - return -1; + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) { + return -1; + } return 0; } @@ -94,8 +96,9 @@ int rc = -1; val = xenstore_read_str(base, node); - if (val && 1 == sscanf(val, "%d", ival)) - rc = 0; + if (val && 1 == sscanf(val, "%d", ival)) { + rc = 0; + } qemu_free(val); return rc; } @@ -134,16 +137,16 @@ const char *xenbus_strstate(enum xenbus_state state) { - static const char *const name[] = { - [ XenbusStateUnknown ] = "Unknown", - [ XenbusStateInitialising ] = "Initialising", - [ XenbusStateInitWait ] = "InitWait", - [ XenbusStateInitialised ] = "Initialised", - [ XenbusStateConnected ] = "Connected", - [ XenbusStateClosing ] = "Closing", - [ XenbusStateClosed ] = "Closed", - }; - return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; } int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) @@ -151,10 +154,11 @@ int rc; rc = xenstore_write_be_int(xendev, "state", state); - if (rc < 0) - return rc; + if (rc < 0) { + return rc; + } xen_be_printf(xendev, 1, "backend state: %s -> %s\n", - xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); xendev->be_state = state; return 0; } @@ -166,13 +170,16 @@ struct XenDevice *xendev; QTAILQ_FOREACH(xendev, &xendevs, next) { - if (xendev->dom != dom) - continue; - if (xendev->dev != dev) - continue; - if (strcmp(xendev->type, type) != 0) - continue; - return xendev; + if (xendev->dom != dom) { + continue; + } + if (xendev->dev != dev) { + continue; + } + if (strcmp(xendev->type, type) != 0) { + continue; + } + return xendev; } return NULL; } @@ -187,8 +194,9 @@ char *dom0; xendev = xen_be_find_xendev(type, dom, dev); - if (xendev) - return xendev; + if (xendev) { + return xendev; + } /* init new xendev */ xendev = qemu_mallocz(ops->size); @@ -199,38 +207,39 @@ dom0 = xs_get_domain_path(xenstore, 0); snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d", - dom0, xendev->type, xendev->dom, xendev->dev); + dom0, xendev->type, xendev->dom, xendev->dev); snprintf(xendev->name, sizeof(xendev->name), "%s-%d", - xendev->type, xendev->dev); + xendev->type, xendev->dev); free(dom0); xendev->debug = debug; xendev->local_port = -1; - xendev->evtchndev = xc_evtchn_open(); - if (xendev->evtchndev < 0) { - xen_be_printf(NULL, 0, "can't open evtchn device\n"); - qemu_free(xendev); - return NULL; + xendev->evtchndev = xen_xc_evtchn_open(NULL, 0); + if (xendev->evtchndev == XC_HANDLER_INITIAL_VALUE) { + xen_be_printf(NULL, 0, "can't open evtchn device\n"); + qemu_free(xendev); + return NULL; } fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xc_gnttab_open(); - if (xendev->gnttabdev < 0) { - xen_be_printf(NULL, 0, "can't open gnttab device\n"); - xc_evtchn_close(xendev->evtchndev); - qemu_free(xendev); - return NULL; - } + xendev->gnttabdev = xen_xc_gnttab_open(NULL, 0); + if (xendev->gnttabdev == XC_HANDLER_INITIAL_VALUE) { + xen_be_printf(NULL, 0, "can't open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } } else { - xendev->gnttabdev = -1; + xendev->gnttabdev = XC_HANDLER_INITIAL_VALUE; } QTAILQ_INSERT_TAIL(&xendevs, xendev, next); - if (xendev->ops->alloc) - xendev->ops->alloc(xendev); + if (xendev->ops->alloc) { + xendev->ops->alloc(xendev); + } return xendev; } @@ -251,28 +260,33 @@ xendev = xnext; xnext = xendev->next.tqe_next; - if (xendev->dom != dom) - continue; - if (xendev->dev != dev && dev != -1) - continue; - - if (xendev->ops->free) - xendev->ops->free(xendev); - - if (xendev->fe) { - char token[XEN_BUFSIZE]; - snprintf(token, sizeof(token), "fe:%p", xendev); - xs_unwatch(xenstore, xendev->fe, token); - qemu_free(xendev->fe); - } - - if (xendev->evtchndev >= 0) - xc_evtchn_close(xendev->evtchndev); - if (xendev->gnttabdev >= 0) - xc_gnttab_close(xendev->gnttabdev); + if (xendev->dom != dom) { + continue; + } + if (xendev->dev != dev && dev != -1) { + continue; + } + + if (xendev->ops->free) { + xendev->ops->free(xendev); + } + + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev != XC_HANDLER_INITIAL_VALUE) { + xc_evtchn_close(xendev->evtchndev); + } + if (xendev->gnttabdev != XC_HANDLER_INITIAL_VALUE) { + xc_gnttab_close(xendev->gnttabdev); + } - QTAILQ_REMOVE(&xendevs, xendev, next); - qemu_free(xendev); + QTAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); } return NULL; } @@ -285,14 +299,16 @@ static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) { if (node == NULL || strcmp(node, "online") == 0) { - if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) - xendev->online = 0; + if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) { + xendev->online = 0; + } } if (node) { - xen_be_printf(xendev, 2, "backend update: %s\n", node); - if (xendev->ops->backend_changed) - xendev->ops->backend_changed(xendev, node); + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) { + xendev->ops->backend_changed(xendev, node); + } } } @@ -301,25 +317,29 @@ int fe_state; if (node == NULL || strcmp(node, "state") == 0) { - if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) - fe_state = XenbusStateUnknown; - if (xendev->fe_state != fe_state) - xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", - xenbus_strstate(xendev->fe_state), - xenbus_strstate(fe_state)); - xendev->fe_state = fe_state; + if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) { + fe_state = XenbusStateUnknown; + } + if (xendev->fe_state != fe_state) { + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + } + xendev->fe_state = fe_state; } if (node == NULL || strcmp(node, "protocol") == 0) { - qemu_free(xendev->protocol); - xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); - if (xendev->protocol) - xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) { + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } } if (node) { - xen_be_printf(xendev, 2, "frontend update: %s\n", node); - if (xendev->ops->frontend_changed) - xendev->ops->frontend_changed(xendev, node); + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) { + xendev->ops->frontend_changed(xendev, node); + } } } @@ -340,28 +360,28 @@ int be_state; if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { - xen_be_printf(xendev, 0, "reading backend state failed\n"); - return -1; + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; } if (be_state != XenbusStateInitialising) { - xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", - xenbus_strstate(be_state)); - return -1; + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; } xendev->fe = xenstore_read_be_str(xendev, "frontend"); if (xendev->fe == NULL) { - xen_be_printf(xendev, 0, "reading frontend path failed\n"); - return -1; + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; } /* setup frontend watch */ snprintf(token, sizeof(token), "fe:%p", xendev); if (!xs_watch(xenstore, xendev->fe, token)) { - xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", - xendev->fe); - return -1; + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; } xen_be_set_state(xendev, XenbusStateInitialising); @@ -383,15 +403,16 @@ int rc = 0; if (!xendev->online) { - xen_be_printf(xendev, 1, "not online\n"); - return -1; + xen_be_printf(xendev, 1, "not online\n"); + return -1; } - if (xendev->ops->init) - rc = xendev->ops->init(xendev); + if (xendev->ops->init) { + rc = xendev->ops->init(xendev); + } if (rc != 0) { - xen_be_printf(xendev, 1, "init() failed\n"); - return rc; + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; } xenstore_write_be_str(xendev, "hotplug-status", "connected"); @@ -411,20 +432,21 @@ int rc = 0; if (xendev->fe_state != XenbusStateInitialised && - xendev->fe_state != XenbusStateConnected) { - if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { - xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); - } else { - xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); - return -1; - } + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } } - if (xendev->ops->connect) - rc = xendev->ops->connect(xendev); + if (xendev->ops->connect) { + rc = xendev->ops->connect(xendev); + } if (rc != 0) { - xen_be_printf(xendev, 0, "connect() failed\n"); - return rc; + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; } xen_be_set_state(xendev, XenbusStateConnected); @@ -440,10 +462,12 @@ { if (xendev->be_state != XenbusStateClosing && xendev->be_state != XenbusStateClosed && - xendev->ops->disconnect) - xendev->ops->disconnect(xendev); - if (xendev->be_state != state) + xendev->ops->disconnect) { + xendev->ops->disconnect(xendev); + } + if (xendev->be_state != state) { xen_be_set_state(xendev, state); + } } /* @@ -451,8 +475,9 @@ */ static int xen_be_try_reset(struct XenDevice *xendev) { - if (xendev->fe_state != XenbusStateInitialising) + if (xendev->fe_state != XenbusStateInitialising) { return -1; + } xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); xen_be_set_state(xendev, XenbusStateInitialising); @@ -468,31 +493,32 @@ /* frontend may request shutdown from almost anywhere */ if (xendev->fe_state == XenbusStateClosing || - xendev->fe_state == XenbusStateClosed) { - xen_be_disconnect(xendev, xendev->fe_state); - return; + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev, xendev->fe_state); + return; } /* check for possible backend state transitions */ for (;;) { - switch (xendev->be_state) { - case XenbusStateUnknown: - rc = xen_be_try_setup(xendev); - break; - case XenbusStateInitialising: - rc = xen_be_try_init(xendev); - break; - case XenbusStateInitWait: - rc = xen_be_try_connect(xendev); - break; + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; case XenbusStateClosed: rc = xen_be_try_reset(xendev); break; - default: - rc = -1; - } - if (rc != 0) - break; + default: + rc = -1; + } + if (rc != 0) { + break; + } } } @@ -511,26 +537,28 @@ snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); free(dom0); if (!xs_watch(xenstore, path, token)) { - xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); - return -1; + xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); + return -1; } /* look for backends */ dev = xs_directory(xenstore, 0, path, &cdev); - if (!dev) - return 0; + if (!dev) { + return 0; + } for (j = 0; j < cdev; j++) { - xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); - if (xendev == NULL) - continue; - xen_be_check_state(xendev); + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (xendev == NULL) { + continue; + } + xen_be_check_state(xendev); } free(dev); return 0; } static void xenstore_update_be(char *watch, char *type, int dom, - struct XenDevOps *ops) + struct XenDevOps *ops) { struct XenDevice *xendev; char path[XEN_BUFSIZE], *dom0; @@ -539,25 +567,28 @@ dom0 = xs_get_domain_path(xenstore, 0); len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); free(dom0); - if (strncmp(path, watch, len) != 0) - return; + if (strncmp(path, watch, len) != 0) { + return; + } if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) { - strcpy(path, ""); - if (sscanf(watch+len, "/%u", &dev) != 1) - dev = -1; + strcpy(path, ""); + if (sscanf(watch+len, "/%u", &dev) != 1) { + dev = -1; + } + } + if (dev == -1) { + return; } - if (dev == -1) - return; if (0) { - /* FIXME: detect devices being deleted from xenstore ... */ - xen_be_del_xendev(dom, dev); + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); } xendev = xen_be_get_xendev(type, dom, dev, ops); if (xendev != NULL) { - xen_be_backend_changed(xendev, path); - xen_be_check_state(xendev); + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); } } @@ -567,10 +598,12 @@ unsigned int len; len = strlen(xendev->fe); - if (strncmp(xendev->fe, watch, len) != 0) - return; - if (watch[len] != '/') - return; + if (strncmp(xendev->fe, watch, len) != 0) { + return; + } + if (watch[len] != '/') { + return; + } node = watch + len + 1; xen_be_frontend_changed(xendev, node); @@ -584,14 +617,17 @@ unsigned int dom, count; vec = xs_read_watch(xenstore, &count); - if (vec == NULL) - goto cleanup; + if (vec == NULL) { + goto cleanup; + } if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, - &type, &dom, &ops) == 3) - xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); - if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) - xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + &type, &dom, &ops) == 3) { + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + } + if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) { + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + } cleanup: free(vec); @@ -604,14 +640,15 @@ port = xc_evtchn_pending(xendev->evtchndev); if (port != xendev->local_port) { - xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", - port, xendev->local_port); - return; + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; } xc_evtchn_unmask(xendev->evtchndev, port); - if (xendev->ops->event) - xendev->ops->event(xendev); + if (xendev->ops->event) { + xendev->ops->event(xendev); + } } /* -------------------------------------------------------------------- */ @@ -620,17 +657,17 @@ { xenstore = xs_daemon_open(); if (!xenstore) { - xen_be_printf(NULL, 0, "can't connect to xenstored\n"); - return -1; + xen_be_printf(NULL, 0, "can't connect to xenstored\n"); + return -1; } - if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) - goto err; + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) { + goto err; + } - xen_xc = xc_interface_open(); - if (xen_xc == -1) { - xen_be_printf(NULL, 0, "can't open xen interface\n"); - goto err; + if (xen_xc == XC_HANDLER_INITIAL_VALUE) { + /* Check if xen_init() have been called */ + goto err; } return 0; @@ -649,24 +686,26 @@ int xen_be_bind_evtchn(struct XenDevice *xendev) { - if (xendev->local_port != -1) - return 0; + if (xendev->local_port != -1) { + return 0; + } xendev->local_port = xc_evtchn_bind_interdomain - (xendev->evtchndev, xendev->dom, xendev->remote_port); + (xendev->evtchndev, xendev->dom, xendev->remote_port); if (xendev->local_port == -1) { - xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); - return -1; + xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); + return -1; } xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), - xen_be_evtchn_event, NULL, xendev); + xen_be_evtchn_event, NULL, xendev); return 0; } void xen_be_unbind_evtchn(struct XenDevice *xendev) { - if (xendev->local_port == -1) - return; + if (xendev->local_port == -1) { + return; + } qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); @@ -690,17 +729,21 @@ va_list args; if (xendev) { - if (msg_level > xendev->debug) + if (msg_level > xendev->debug) { return; + } qemu_log("xen be: %s: ", xendev->name); - if (msg_level == 0) + if (msg_level == 0) { fprintf(stderr, "xen be: %s: ", xendev->name); + } } else { - if (msg_level > debug) + if (msg_level > debug) { return; + } qemu_log("xen be core: "); - if (msg_level == 0) + if (msg_level == 0) { fprintf(stderr, "xen be core: "); + } } va_start(args, fmt); qemu_log_vprintf(fmt, args); diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_backend.h qemu-kvm-0.15.0+noroms/hw/xen_backend.h --- qemu-kvm-0.14.1+noroms/hw/xen_backend.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_backend.h 2011-08-09 07:40:29.000000000 -0500 @@ -45,8 +45,8 @@ int remote_port; int local_port; - int evtchndev; - int gnttabdev; + XenEvtchn evtchndev; + XenGnttab gnttabdev; struct XenDevOps *ops; QTAILQ_ENTRY(XenDevice) next; @@ -55,7 +55,7 @@ /* ------------------------------------------------------------- */ /* variables */ -extern int xen_xc; +extern XenXC xen_xc; extern struct xs_handle *xenstore; extern const char *xen_protocol; diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_common.h qemu-kvm-0.15.0+noroms/hw/xen_common.h --- qemu-kvm-0.14.1+noroms/hw/xen_common.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_common.h 2011-08-09 07:40:29.000000000 -0500 @@ -1,6 +1,8 @@ #ifndef QEMU_HW_XEN_COMMON_H #define QEMU_HW_XEN_COMMON_H 1 +#include "config-host.h" + #include #include @@ -13,22 +15,124 @@ #include "qemu-queue.h" /* - * tweaks needed to build with different xen versions - * 0x00030205 -> 3.1.0 - * 0x00030207 -> 3.2.0 - * 0x00030208 -> unstable + * We don't support Xen prior to 3.3.0. */ -#include -#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030205 -# define evtchn_port_or_error_t int -#endif -#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 -# define xc_map_foreign_pages xc_map_foreign_batch + +/* Xen before 4.0 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 +static inline void *xc_map_foreign_bulk(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int *err, + unsigned int num) +{ + return xc_map_foreign_batch(xc_handle, dom, prot, arr, num); +} #endif -#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 -# define xen_mb() mb() -# define xen_rmb() rmb() -# define xen_wmb() wmb() + + +/* Xen before 4.1 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 410 + +typedef int XenXC; +typedef int XenEvtchn; +typedef int XenGnttab; + +# define XC_INTERFACE_FMT "%i" +# define XC_HANDLER_INITIAL_VALUE -1 + +static inline XenEvtchn xen_xc_evtchn_open(void *logger, + unsigned int open_flags) +{ + return xc_evtchn_open(); +} + +static inline XenGnttab xen_xc_gnttab_open(void *logger, + unsigned int open_flags) +{ + return xc_gnttab_open(); +} + +static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, + unsigned int open_flags) +{ + return xc_interface_open(); +} + +static inline int xc_fd(int xen_xc) +{ + return xen_xc; +} + + +static inline int xc_domain_populate_physmap_exact + (XenXC xc_handle, uint32_t domid, unsigned long nr_extents, + unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) +{ + return xc_domain_memory_populate_physmap + (xc_handle, domid, nr_extents, extent_order, mem_flags, extent_start); +} + +static inline int xc_domain_add_to_physmap(int xc_handle, uint32_t domid, + unsigned int space, unsigned long idx, + xen_pfn_t gpfn) +{ + struct xen_add_to_physmap xatp = { + .domid = domid, + .space = space, + .idx = idx, + .gpfn = gpfn, + }; + + return xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp); +} + +static inline struct xs_handle *xs_open(unsigned long flags) +{ + return xs_daemon_open(); +} + +static inline void xs_close(struct xs_handle *xsh) +{ + if (xsh != NULL) { + xs_daemon_close(xsh); + } +} + + +/* Xen 4.1 */ +#else + +typedef xc_interface *XenXC; +typedef xc_evtchn *XenEvtchn; +typedef xc_gnttab *XenGnttab; + +# define XC_INTERFACE_FMT "%p" +# define XC_HANDLER_INITIAL_VALUE NULL + +static inline XenEvtchn xen_xc_evtchn_open(void *logger, + unsigned int open_flags) +{ + return xc_evtchn_open(logger, open_flags); +} + +static inline XenGnttab xen_xc_gnttab_open(void *logger, + unsigned int open_flags) +{ + return xc_gnttab_open(logger, open_flags); +} + +static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, + unsigned int open_flags) +{ + return xc_interface_open(logger, dombuild_logger, open_flags); +} + +/* FIXME There is now way to have the xen fd */ +static inline int xc_fd(xc_interface *xen_xc) +{ + return -1; +} #endif +void destroy_hvm_domain(void); + #endif /* QEMU_HW_XEN_COMMON_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_console.c qemu-kvm-0.15.0+noroms/hw/xen_console.c --- qemu-kvm-0.14.1+noroms/hw/xen_console.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_console.c 2011-08-09 07:40:29.000000000 -0500 @@ -33,7 +33,6 @@ #include #include "hw.h" -#include "sysemu.h" #include "qemu-char.h" #include "xen_backend.h" @@ -180,7 +179,9 @@ static int con_init(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - char *type, *dom; + char *type, *dom, label[32]; + int ret = 0; + const char *output; /* setup */ dom = xs_get_domain_path(xenstore, con->xendev.dom); @@ -190,16 +191,25 @@ type = xenstore_read_str(con->console, "type"); if (!type || strcmp(type, "ioemu") != 0) { xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); - return -1; + ret = -1; + goto out; } - if (!serial_hds[con->xendev.dev]) - xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n", - con->xendev.dev); - else + output = xenstore_read_str(con->console, "output"); + + /* no Xen override, use qemu output device */ + if (output == NULL) { con->chr = serial_hds[con->xendev.dev]; + } else { + snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); + con->chr = qemu_chr_open(label, output, NULL); + } + + xenstore_store_pv_console_info(con->xendev.dev, con->chr); - return 0; +out: + qemu_free(type); + return ret; } static int con_connect(struct XenDevice *xendev) diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_devconfig.c qemu-kvm-0.15.0+noroms/hw/xen_devconfig.c --- qemu-kvm-0.14.1+noroms/hw/xen_devconfig.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_devconfig.c 2011-08-09 07:40:29.000000000 -0500 @@ -96,7 +96,7 @@ { char fe[256], be[256]; int vdev = 202 * 256 + 16 * disk->unit; - int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + int cdrom = disk->media_cd; const char *devtype = cdrom ? "cdrom" : "disk"; const char *mode = cdrom ? "r" : "w"; @@ -126,8 +126,8 @@ char mac[20]; snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", - nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], - nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2], + nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]); xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_disk.c qemu-kvm-0.15.0+noroms/hw/xen_disk.c --- qemu-kvm-0.14.1+noroms/hw/xen_disk.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_disk.c 2011-08-09 07:40:29.000000000 -0500 @@ -120,17 +120,18 @@ struct ioreq *ioreq = NULL; if (QLIST_EMPTY(&blkdev->freelist)) { - if (blkdev->requests_total >= max_requests) - goto out; - /* allocate new struct */ - ioreq = qemu_mallocz(sizeof(*ioreq)); - ioreq->blkdev = blkdev; - blkdev->requests_total++; + if (blkdev->requests_total >= max_requests) { + goto out; + } + /* allocate new struct */ + ioreq = qemu_mallocz(sizeof(*ioreq)); + ioreq->blkdev = blkdev; + blkdev->requests_total++; qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); } else { - /* get one from freelist */ - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); + /* get one from freelist */ + ioreq = QLIST_FIRST(&blkdev->freelist); + QLIST_REMOVE(ioreq, list); qemu_iovec_reset(&ioreq->v); } QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); @@ -173,30 +174,32 @@ int i; xen_be_printf(&blkdev->xendev, 3, - "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", - ioreq->req.operation, ioreq->req.nr_segments, - ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); switch (ioreq->req.operation) { case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ - break; + 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 */ + if (!syncwrite) { + ioreq->presync = ioreq->postsync = 1; + } + /* fall through */ case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ - if (syncwrite) - ioreq->postsync = 1; - break; + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) { + ioreq->postsync = 1; + } + break; default: - xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", - ioreq->req.operation); - goto err; + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; }; if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { @@ -206,29 +209,29 @@ ioreq->start = ioreq->req.sector_number * blkdev->file_blk; for (i = 0; i < ioreq->req.nr_segments; i++) { - if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); - goto err; - } - if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); - goto err; - } - if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); - goto err; - } + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } - ioreq->domids[i] = blkdev->xendev.dom; - ioreq->refs[i] = ioreq->req.seg[i].gref; + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; - len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; qemu_iovec_add(&ioreq->v, (void*)mem, len); } if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); - goto err; + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; } return 0; @@ -239,66 +242,73 @@ static void ioreq_unmap(struct ioreq *ioreq) { - int gnt = ioreq->blkdev->xendev.gnttabdev; + XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; int i; - if (ioreq->v.niov == 0) + if (ioreq->v.niov == 0) { return; + } if (batch_maps) { - if (!ioreq->pages) - return; - if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov) != 0) - xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", - strerror(errno)); - ioreq->blkdev->cnt_map -= ioreq->v.niov; - ioreq->pages = NULL; + if (!ioreq->pages) { + return; + } + if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov) != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + } + ioreq->blkdev->cnt_map -= ioreq->v.niov; + ioreq->pages = NULL; } else { - for (i = 0; i < ioreq->v.niov; i++) { - if (!ioreq->page[i]) - continue; - if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) - xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", - strerror(errno)); - ioreq->blkdev->cnt_map--; - ioreq->page[i] = NULL; - } + for (i = 0; i < ioreq->v.niov; i++) { + if (!ioreq->page[i]) { + continue; + } + if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + } + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } } } static int ioreq_map(struct ioreq *ioreq) { - int gnt = ioreq->blkdev->xendev.gnttabdev; + XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; int i; - if (ioreq->v.niov == 0) + if (ioreq->v.niov == 0) { return 0; + } if (batch_maps) { - ioreq->pages = xc_gnttab_map_grant_refs - (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot); - if (ioreq->pages == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0; i < ioreq->v.niov; i++) - ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + - (uintptr_t)ioreq->v.iov[i].iov_base; - ioreq->blkdev->cnt_map += ioreq->v.niov; + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot); + if (ioreq->pages == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can't map %d grant refs (%s, %d maps)\n", + ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->v.iov[i].iov_base; + } + ioreq->blkdev->cnt_map += ioreq->v.niov; } else { - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = xc_gnttab_map_grant_ref - (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); - if (ioreq->page[i] == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "can't map grant ref %d (%s, %d maps)\n", - ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq_unmap(ioreq); - return -1; - } - ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base; - ioreq->blkdev->cnt_map++; - } + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (ioreq->page[i] == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can't map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base; + ioreq->blkdev->cnt_map++; + } } return 0; } @@ -306,57 +316,59 @@ static int ioreq_runio_qemu_sync(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - int i, rc, len = 0; + int i, rc; off_t pos; - if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) - goto err; - if (ioreq->presync) - bdrv_flush(blkdev->bs); + if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) { + goto err_no_map; + } + if (ioreq->presync) { + bdrv_flush(blkdev->bs); + } switch (ioreq->req.operation) { case BLKIF_OP_READ: - pos = ioreq->start; - for (i = 0; i < ioreq->v.niov; i++) { - rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len / BLOCK_SIZE); - if (rc != 0) { - xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len); - goto err; - } - len += ioreq->v.iov[i].iov_len; - pos += ioreq->v.iov[i].iov_len; - } - break; + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + pos += ioreq->v.iov[i].iov_len; + } + break; case BLKIF_OP_WRITE: case BLKIF_OP_WRITE_BARRIER: - if (!ioreq->req.nr_segments) + 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, - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len / BLOCK_SIZE); - if (rc != 0) { - xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len); - goto err; - } - len += ioreq->v.iov[i].iov_len; - pos += ioreq->v.iov[i].iov_len; - } - break; + } + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + pos += ioreq->v.iov[i].iov_len; + } + break; default: - /* unknown operation (shouldn't happen -- parse catches this) */ - goto err; + /* unknown operation (shouldn't happen -- parse catches this) */ + goto err; } - if (ioreq->postsync) - bdrv_flush(blkdev->bs); + if (ioreq->postsync) { + bdrv_flush(blkdev->bs); + } ioreq->status = BLKIF_RSP_OKAY; ioreq_unmap(ioreq); @@ -364,6 +376,9 @@ return 0; err: + ioreq_unmap(ioreq); +err_no_map: + ioreq_finish(ioreq); ioreq->status = BLKIF_RSP_ERROR; return -1; } @@ -379,8 +394,9 @@ } ioreq->aio_inflight--; - if (ioreq->aio_inflight > 0) + if (ioreq->aio_inflight > 0) { return; + } ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; ioreq_unmap(ioreq); @@ -392,12 +408,14 @@ { struct XenBlkDev *blkdev = ioreq->blkdev; - if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) - goto err; + if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) { + goto err_no_map; + } ioreq->aio_inflight++; - if (ioreq->presync) - bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + if (ioreq->presync) { + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + } switch (ioreq->req.operation) { case BLKIF_OP_READ: @@ -405,28 +423,33 @@ bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, qemu_aio_complete, ioreq); - break; + break; case BLKIF_OP_WRITE: case BLKIF_OP_WRITE_BARRIER: - ioreq->aio_inflight++; - if (!ioreq->req.nr_segments) + if (!ioreq->req.nr_segments) { break; + } + ioreq->aio_inflight++; bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, qemu_aio_complete, ioreq); - break; + break; default: - /* unknown operation (shouldn't happen -- parse catches this) */ - goto err; + /* unknown operation (shouldn't happen -- parse catches this) */ + goto err; } - if (ioreq->postsync) - bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + if (ioreq->postsync) { + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + } qemu_aio_complete(ioreq, 0); return 0; err: + ioreq_unmap(ioreq); +err_no_map: + ioreq_finish(ioreq); ioreq->status = BLKIF_RSP_ERROR; return -1; } @@ -446,36 +469,37 @@ /* Place on the response ring for the relevant domain. */ switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: - dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); - break; + dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + break; case BLKIF_PROTOCOL_X86_32: dst = RING_GET_RESPONSE(&blkdev->rings.x86_32_part, blkdev->rings.x86_32_part.rsp_prod_pvt); - break; + break; case BLKIF_PROTOCOL_X86_64: dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part, blkdev->rings.x86_64_part.rsp_prod_pvt); - break; + break; default: - dst = NULL; + dst = NULL; } memcpy(dst, &resp, sizeof(resp)); blkdev->rings.common.rsp_prod_pvt++; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { - /* - * Tail check for pending requests. Allows frontend to avoid - * notifications if requests are already in flight (lower - * overheads and promotes batching). - */ - RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { - have_requests = 1; + have_requests = 1; } - if (have_requests) - blkdev->more_work++; + if (have_requests) { + blkdev->more_work++; + } return send_notify; } @@ -487,28 +511,29 @@ while (!QLIST_EMPTY(&blkdev->finished)) { ioreq = QLIST_FIRST(&blkdev->finished); - send_notify += blk_send_response_one(ioreq); - ioreq_release(ioreq); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) { + xen_be_send_notify(&blkdev->xendev); } - if (send_notify) - xen_be_send_notify(&blkdev->xendev); } static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) { switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: - memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), - sizeof(ioreq->req)); - break; + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; case BLKIF_PROTOCOL_X86_32: blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc)); - break; + break; case BLKIF_PROTOCOL_X86_64: blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc)); - break; + break; } return 0; } @@ -524,12 +549,14 @@ rp = blkdev->rings.common.sring->req_prod; xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - if (use_aio) + if (use_aio) { blk_send_response_all(blkdev); + } while (rc != rp) { /* pull request from ring */ - if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) { break; + } ioreq = ioreq_start(blkdev); if (ioreq == NULL) { blkdev->more_work++; @@ -540,8 +567,9 @@ /* parse them */ if (ioreq_parse(ioreq) != 0) { - if (blk_send_response_one(ioreq)) + if (blk_send_response_one(ioreq)) { xen_be_send_notify(&blkdev->xendev); + } ioreq_release(ioreq); continue; } @@ -554,11 +582,13 @@ ioreq_runio_qemu_sync(ioreq); } } - if (!use_aio) + if (!use_aio) { blk_send_response_all(blkdev); + } - if (blkdev->more_work && blkdev->requests_inflight < max_requests) + if (blkdev->more_work && blkdev->requests_inflight < max_requests) { qemu_bh_schedule(blkdev->bh); + } } /* ------------------------------------------------------------- */ @@ -577,56 +607,68 @@ QLIST_INIT(&blkdev->finished); QLIST_INIT(&blkdev->freelist); blkdev->bh = qemu_bh_new(blk_bh, blkdev); - if (xen_mode != XEN_EMULATE) + if (xen_mode != XEN_EMULATE) { batch_maps = 1; + } } static int blk_init(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int index, qflags, have_barriers, info = 0; - char *h; /* read xenstore entries */ if (blkdev->params == NULL) { - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); - h = strchr(blkdev->params, ':'); - if (h != NULL) { - blkdev->fileproto = blkdev->params; - blkdev->filename = h+1; - *h = 0; - } else { - blkdev->fileproto = ""; - blkdev->filename = blkdev->params; - } - } - if (blkdev->mode == NULL) - blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); - if (blkdev->type == NULL) - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); - if (blkdev->dev == NULL) - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); - if (blkdev->devtype == NULL) - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + char *h = NULL; + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + if (blkdev->params != NULL) { + h = strchr(blkdev->params, ':'); + } + if (h != NULL) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = ""; + blkdev->filename = blkdev->params; + } + } + if (!strcmp("aio", blkdev->fileproto)) { + blkdev->fileproto = "raw"; + } + if (blkdev->mode == NULL) { + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + } + if (blkdev->type == NULL) { + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + } + if (blkdev->dev == NULL) { + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + } + if (blkdev->devtype == NULL) { + blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + } /* do we have all we need? */ if (blkdev->params == NULL || - blkdev->mode == NULL || - blkdev->type == NULL || - blkdev->dev == NULL) - return -1; + blkdev->mode == NULL || + blkdev->type == NULL || + blkdev->dev == NULL) { + goto out_error; + } /* read-only ? */ if (strcmp(blkdev->mode, "w") == 0) { - qflags = BDRV_O_RDWR; + qflags = BDRV_O_RDWR; } else { - qflags = 0; - info |= VDISK_READONLY; + qflags = 0; + info |= VDISK_READONLY; } /* cdrom ? */ - if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) - info |= VDISK_CDROM; + if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) { + info |= VDISK_CDROM; + } /* init qemu block driver */ index = (blkdev->xendev.dev - 202 * 256) / 16; @@ -635,15 +677,20 @@ /* 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 (bdrv_open(blkdev->bs, blkdev->filename, qflags, - bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { - bdrv_delete(blkdev->bs); - return -1; + if (blkdev->bs) { + if (bdrv_open(blkdev->bs, blkdev->filename, qflags, + bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) { + goto out_error; } } else { /* setup via qemu cmdline -> already setup for us */ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); - blkdev->bs = blkdev->dinfo->bdrv; + blkdev->bs = blkdev->dinfo->bdrv; } blkdev->file_blk = BLOCK_SIZE; blkdev->file_size = bdrv_getlength(blkdev->bs); @@ -651,79 +698,97 @@ xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), blkdev->bs->drv ? blkdev->bs->drv->format_name : "-"); - blkdev->file_size = 0; + blkdev->file_size = 0; } have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," - " size %" PRId64 " (%" PRId64 " MB)\n", - blkdev->type, blkdev->fileproto, blkdev->filename, - blkdev->file_size, blkdev->file_size >> 20); + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); /* fill info */ xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); xenstore_write_be_int(&blkdev->xendev, "info", info); xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); xenstore_write_be_int(&blkdev->xendev, "sectors", - blkdev->file_size / blkdev->file_blk); + blkdev->file_size / blkdev->file_blk); return 0; + +out_error: + qemu_free(blkdev->params); + blkdev->params = NULL; + qemu_free(blkdev->mode); + blkdev->mode = NULL; + qemu_free(blkdev->type); + blkdev->type = NULL; + qemu_free(blkdev->dev); + blkdev->dev = NULL; + qemu_free(blkdev->devtype); + blkdev->devtype = NULL; + return -1; } static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) - return -1; + if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) { + return -1; + } if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) - return -1; + &blkdev->xendev.remote_port) == -1) { + return -1; + } blkdev->protocol = BLKIF_PROTOCOL_NATIVE; if (blkdev->xendev.protocol) { - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_32; - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) + } + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_64; + } } blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, - blkdev->xendev.dom, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - if (!blkdev->sring) - return -1; + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + if (!blkdev->sring) { + return -1; + } blkdev->cnt_map++; switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: { - blkif_sring_t *sring_native = blkdev->sring; - BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); - break; + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; } case BLKIF_PROTOCOL_X86_32: { - blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE); - break; + break; } case BLKIF_PROTOCOL_X86_64: { - blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE); - break; + break; } } xen_be_bind_evtchn(&blkdev->xendev); xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " - "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); return 0; } @@ -737,14 +802,14 @@ bdrv_close(blkdev->bs); bdrv_delete(blkdev->bs); } - blkdev->bs = NULL; + blkdev->bs = NULL; } xen_be_unbind_evtchn(&blkdev->xendev); if (blkdev->sring) { - xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); - blkdev->cnt_map--; - blkdev->sring = NULL; + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; } } @@ -754,10 +819,10 @@ struct ioreq *ioreq; while (!QLIST_EMPTY(&blkdev->freelist)) { - ioreq = QLIST_FIRST(&blkdev->freelist); + ioreq = QLIST_FIRST(&blkdev->freelist); QLIST_REMOVE(ioreq, list); qemu_iovec_destroy(&ioreq->v); - qemu_free(ioreq); + qemu_free(ioreq); } qemu_free(blkdev->params); diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_domainbuild.c qemu-kvm-0.15.0+noroms/hw/xen_domainbuild.c --- qemu-kvm-0.14.1+noroms/hw/xen_domainbuild.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_domainbuild.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,7 +1,6 @@ #include #include "xen_backend.h" #include "xen_domainbuild.h" -#include "sysemu.h" #include "qemu-timer.h" #include "qemu-log.h" @@ -149,7 +148,7 @@ goto quit; } - qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + qemu_mod_timer(xen_poll, qemu_get_clock_ms(rt_clock) + 1000); return; quit: @@ -176,8 +175,9 @@ for (i = 3; i < n; i++) { if (i == fd[0]) continue; - if (i == xen_xc) + if (i == xc_fd(xen_xc)) { continue; + } close(i); } @@ -291,8 +291,8 @@ goto err; } - xen_poll = qemu_new_timer(rt_clock, xen_domain_poll, NULL); - qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + xen_poll = qemu_new_timer_ms(rt_clock, xen_domain_poll, NULL); + qemu_mod_timer(xen_poll, qemu_get_clock_ms(rt_clock) + 1000); return 0; err: diff -Nru qemu-kvm-0.14.1+noroms/hw/xenfb.c qemu-kvm-0.15.0+noroms/hw/xenfb.c --- qemu-kvm-0.14.1+noroms/hw/xenfb.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xenfb.c 2011-08-09 07:40:29.000000000 -0500 @@ -44,7 +44,6 @@ #include #include "hw.h" -#include "sysemu.h" #include "console.h" #include "qemu-char.h" #include "xen_backend.h" @@ -348,13 +347,6 @@ static int input_init(struct XenDevice *xendev) { - struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); - - if (!in->c.ds) { - xen_be_printf(xendev, 1, "ds not set (yet)\n"); - return -1; - } - xenstore_write_be_int(xendev, "feature-abs-pointer", 1); return 0; } @@ -368,6 +360,18 @@ &in->abs_pointer_wanted) == -1) in->abs_pointer_wanted = 0; + if (!in->c.ds) { + char *vfb = xenstore_read_str(NULL, "device/vfb"); + if (vfb == NULL) { + /* there is no vfb, run vkbd on its own */ + in->c.ds = get_displaystate(); + } else { + qemu_free(vfb); + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + } + rc = common_bind(&in->c); if (rc != 0) return rc; diff -Nru qemu-kvm-0.14.1+noroms/hw/xen.h qemu-kvm-0.15.0+noroms/hw/xen.h --- qemu-kvm-0.14.1+noroms/hw/xen.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen.h 2011-08-09 07:40:29.000000000 -0500 @@ -8,6 +8,8 @@ */ #include +#include "qemu-common.h" + /* xen-machine.c */ enum xen_mode { XEN_EMULATE = 0, // xen emulation, using xenner (default) @@ -18,4 +20,35 @@ extern uint32_t xen_domid; extern enum xen_mode xen_mode; +extern int xen_allowed; + +static inline int xen_enabled(void) +{ +#ifdef CONFIG_XEN + return xen_allowed; +#else + return 0; +#endif +} + +int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); +void xen_piix3_set_irq(void *opaque, int irq_num, int level); +void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); +void xen_cmos_set_s3_resume(void *opaque, int irq, int level); + +qemu_irq *xen_interrupt_controller_init(void); + +int xen_init(void); +int xen_hvm_init(void); +void xen_vcpu_init(void); +void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); + +#if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size); +#endif + +#if defined(CONFIG_XEN) && CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 +# define HVM_MAX_VCPUS 32 +#endif + #endif /* QEMU_HW_XEN_H */ diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_machine_pv.c qemu-kvm-0.15.0+noroms/hw/xen_machine_pv.c --- qemu-kvm-0.14.1+noroms/hw/xen_machine_pv.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_machine_pv.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include "hw.h" #include "pc.h" -#include "sysemu.h" #include "boards.h" #include "xen_backend.h" #include "xen_domainbuild.h" @@ -114,6 +113,7 @@ .desc = "Xen Para-virtualized PC", .init = xen_init_pv, .max_cpus = 1, + .default_machine_opts = "accel=xen", }; static void xenpv_machine_init(void) diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_nic.c qemu-kvm-0.15.0+noroms/hw/xen_nic.c --- qemu-kvm-0.14.1+noroms/hw/xen_nic.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xen_nic.c 2011-08-09 07:40:29.000000000 -0500 @@ -74,20 +74,23 @@ resp->status = st; #if 0 - if (txp->flags & NETTXF_extra_info) - RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; + if (txp->flags & NETTXF_extra_info) { + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; + } #endif netdev->tx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); - if (notify) - xen_be_send_notify(&netdev->xendev); + if (notify) { + xen_be_send_notify(&netdev->xendev); + } if (i == netdev->tx_ring.req_cons) { - int more_to_do; - RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); - if (more_to_do) - netdev->tx_work++; + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) { + netdev->tx_work++; + } } } @@ -101,10 +104,11 @@ RING_IDX cons = netdev->tx_ring.req_cons; do { - make_tx_response(netif, txp, NETIF_RSP_ERROR); - if (cons >= end) - break; - txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) { + break; + } + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); } while (1); netdev->tx_ring.req_cons = cons; netif_schedule_work(netif); @@ -122,75 +126,78 @@ void *tmpbuf = NULL; for (;;) { - rc = netdev->tx_ring.req_cons; - rp = netdev->tx_ring.sring->req_prod; - xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - - while ((rc != rp)) { - if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) - break; - memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); - netdev->tx_ring.req_cons = ++rc; + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ + + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) { + break; + } + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; #if 1 - /* should not happen in theory, we don't announce the * - * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ - if (txreq.flags & NETTXF_extra_info) { - xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } - if (txreq.flags & NETTXF_more_data) { - xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } + /* should not happen in theory, we don't announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } #endif - if (txreq.size < 14) { - xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); - net_tx_error(netdev, &txreq, rc); - continue; - } - - if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { - xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } - - xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", - txreq.gref, txreq.offset, txreq.size, txreq.flags, - (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", - (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", - (txreq.flags & NETTXF_more_data) ? " more_data" : "", - (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - - page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - txreq.gref, PROT_READ); - if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (page == NULL) { + xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", txreq.gref); - net_tx_error(netdev, &txreq, rc); - continue; - } - if (txreq.flags & NETTXF_csum_blank) { + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) { /* have read-only mapping -> can't fill checksum in-place */ - if (!tmpbuf) + if (!tmpbuf) { tmpbuf = qemu_malloc(XC_PAGE_SIZE); + } memcpy(tmpbuf, page + txreq.offset, txreq.size); - net_checksum_calculate(tmpbuf, txreq.size); + net_checksum_calculate(tmpbuf, txreq.size); qemu_send_packet(&netdev->nic->nc, tmpbuf, txreq.size); } else { qemu_send_packet(&netdev->nic->nc, page + txreq.offset, txreq.size); } - xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); - net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); - } - if (!netdev->tx_work) - break; - netdev->tx_work = 0; + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) { + break; + } + netdev->tx_work = 0; } qemu_free(tmpbuf); } @@ -198,9 +205,9 @@ /* ------------------------------------------------------------- */ static void net_rx_response(struct XenNetDev *netdev, - netif_rx_request_t *req, int8_t st, - uint16_t offset, uint16_t size, - uint16_t flags) + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) { RING_IDX i = netdev->rx_ring.rsp_prod_pvt; netif_rx_response_t *resp; @@ -211,16 +218,18 @@ resp->flags = flags; resp->id = req->id; resp->status = (int16_t)size; - if (st < 0) - resp->status = (int16_t)st; + if (st < 0) { + resp->status = (int16_t)st; + } xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", - i, resp->status, resp->flags); + i, resp->status, resp->flags); netdev->rx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); - if (notify) - xen_be_send_notify(&netdev->xendev); + if (notify) { + xen_be_send_notify(&netdev->xendev); + } } #define NET_IP_ALIGN 2 @@ -230,17 +239,18 @@ struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; RING_IDX rc, rp; - if (netdev->xendev.be_state != XenbusStateConnected) - return 0; + if (netdev->xendev.be_state != XenbusStateConnected) { + return 0; + } rc = netdev->rx_ring.req_cons; rp = netdev->rx_ring.sring->req_prod; xen_rmb(); if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { - xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", - __FUNCTION__, rc, rp); - return 0; + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; } return 1; } @@ -252,34 +262,35 @@ RING_IDX rc, rp; void *page; - if (netdev->xendev.be_state != XenbusStateConnected) - return -1; + if (netdev->xendev.be_state != XenbusStateConnected) { + return -1; + } rc = netdev->rx_ring.req_cons; rp = netdev->rx_ring.sring->req_prod; xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { - xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); - return -1; + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return -1; } if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { - xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", - (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN); - return -1; + xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", + (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN); + return -1; } memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - rxreq.gref, PROT_WRITE); + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", + xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", rxreq.gref); - net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); - return -1; + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return -1; } memcpy(page + NET_IP_ALIGN, buf, size); xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); @@ -302,15 +313,18 @@ struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); /* read xenstore entries */ - if (netdev->mac == NULL) - netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + if (netdev->mac == NULL) { + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + } /* do we have all we need? */ - if (netdev->mac == NULL) - return -1; + if (netdev->mac == NULL) { + return -1; + } - if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) + if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) { return -1; + } netdev->conf.vlan = qemu_find_vlan(netdev->xendev.dev, 1); netdev->conf.peer = NULL; @@ -334,41 +348,46 @@ int rx_copy; if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", - &netdev->tx_ring_ref) == -1) - return -1; + &netdev->tx_ring_ref) == -1) { + return -1; + } if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", - &netdev->rx_ring_ref) == -1) - return 1; + &netdev->rx_ring_ref) == -1) { + return 1; + } if (xenstore_read_fe_int(&netdev->xendev, "event-channel", - &netdev->xendev.remote_port) == -1) - return -1; + &netdev->xendev.remote_port) == -1) { + return -1; + } - if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) - rx_copy = 0; + if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) { + rx_copy = 0; + } if (rx_copy == 0) { - xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n"); - return -1; + xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n"); + return -1; } netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); - if (!netdev->txs || !netdev->rxs) - return -1; + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) { + return -1; + } BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); xen_be_bind_evtchn(&netdev->xendev); xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " - "remote port %d, local port %d\n", - netdev->tx_ring_ref, netdev->rx_ring_ref, - netdev->xendev.remote_port, netdev->xendev.local_port); + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); net_tx_packets(netdev); return 0; @@ -381,12 +400,12 @@ xen_be_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); - netdev->txs = NULL; + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; } if (netdev->rxs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); - netdev->rxs = NULL; + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; } if (netdev->nic) { qemu_del_vlan_client(&netdev->nic->nc); diff -Nru qemu-kvm-0.14.1+noroms/hw/xen_platform.c qemu-kvm-0.15.0+noroms/hw/xen_platform.c --- qemu-kvm-0.14.1+noroms/hw/xen_platform.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/xen_platform.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,339 @@ +/* + * XEN platform pci device, formerly known as the event channel device + * + * Copyright (c) 2003-2004 Intel Corp. + * Copyright (c) 2006 XenSource + * + * 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 "hw.h" +#include "pc.h" +#include "pci.h" +#include "irq.h" +#include "xen_common.h" +#include "net.h" +#include "xen_backend.h" +#include "rwhandler.h" +#include "trace.h" + +#include + +//#define DEBUG_PLATFORM + +#ifdef DEBUG_PLATFORM +#define DPRINTF(fmt, ...) do { \ + fprintf(stderr, "xen_platform: " fmt, ## __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(fmt, ...) do { } while (0) +#endif + +#define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */ + +typedef struct PCIXenPlatformState { + PCIDevice pci_dev; + uint8_t flags; /* used only for version_id == 2 */ + int drivers_blacklisted; + uint16_t driver_product_version; + + /* Log from guest drivers */ + char log_buffer[4096]; + int log_buffer_off; +} PCIXenPlatformState; + +#define XEN_PLATFORM_IOPORT 0x10 + +/* Send bytes to syslog */ +static void log_writeb(PCIXenPlatformState *s, char val) +{ + if (val == '\n' || s->log_buffer_off == sizeof(s->log_buffer) - 1) { + /* Flush buffer */ + s->log_buffer[s->log_buffer_off] = 0; + trace_xen_platform_log(s->log_buffer); + s->log_buffer_off = 0; + } else { + s->log_buffer[s->log_buffer_off++] = val; + } +} + +/* Xen Platform, Fixed IOPort */ + +static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +{ + PCIXenPlatformState *s = opaque; + + switch (addr - XEN_PLATFORM_IOPORT) { + case 0: + /* TODO: */ + /* Unplug devices. Value is a bitmask of which devices to + unplug, with bit 0 the IDE devices, bit 1 the network + devices, and bit 2 the non-primary-master IDE devices. */ + break; + case 2: + switch (val) { + case 1: + DPRINTF("Citrix Windows PV drivers loaded in guest\n"); + break; + case 0: + DPRINTF("Guest claimed to be running PV product 0?\n"); + break; + default: + DPRINTF("Unknown PV product %d loaded in guest\n", val); + break; + } + s->driver_product_version = val; + break; + } +} + +static void platform_fixed_ioport_writel(void *opaque, uint32_t addr, + uint32_t val) +{ + switch (addr - XEN_PLATFORM_IOPORT) { + case 0: + /* PV driver version */ + break; + } +} + +static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIXenPlatformState *s = opaque; + + switch (addr - XEN_PLATFORM_IOPORT) { + case 0: /* Platform flags */ { + hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ? + HVMMEM_ram_ro : HVMMEM_ram_rw; + if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type, 0xc0, 0x40)) { + DPRINTF("unable to change ro/rw state of ROM memory area!\n"); + } else { + s->flags = val & PFFLAG_ROM_LOCK; + DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n", + (mem_type == HVMMEM_ram_ro ? "ro":"rw")); + } + break; + } + case 2: + log_writeb(s, val); + break; + } +} + +static uint32_t platform_fixed_ioport_readw(void *opaque, uint32_t addr) +{ + PCIXenPlatformState *s = opaque; + + switch (addr - XEN_PLATFORM_IOPORT) { + case 0: + if (s->drivers_blacklisted) { + /* The drivers will recognise this magic number and refuse + * to do anything. */ + return 0xd249; + } else { + /* Magic value so that you can identify the interface. */ + return 0x49d2; + } + default: + return 0xffff; + } +} + +static uint32_t platform_fixed_ioport_readb(void *opaque, uint32_t addr) +{ + PCIXenPlatformState *s = opaque; + + switch (addr - XEN_PLATFORM_IOPORT) { + case 0: + /* Platform flags */ + return s->flags; + case 2: + /* Version number */ + return 1; + default: + return 0xff; + } +} + +static void platform_fixed_ioport_reset(void *opaque) +{ + PCIXenPlatformState *s = opaque; + + platform_fixed_ioport_writeb(s, XEN_PLATFORM_IOPORT, 0); +} + +static void platform_fixed_ioport_init(PCIXenPlatformState* s) +{ + register_ioport_write(XEN_PLATFORM_IOPORT, 16, 4, platform_fixed_ioport_writel, s); + register_ioport_write(XEN_PLATFORM_IOPORT, 16, 2, platform_fixed_ioport_writew, s); + register_ioport_write(XEN_PLATFORM_IOPORT, 16, 1, platform_fixed_ioport_writeb, s); + register_ioport_read(XEN_PLATFORM_IOPORT, 16, 2, platform_fixed_ioport_readw, s); + register_ioport_read(XEN_PLATFORM_IOPORT, 16, 1, platform_fixed_ioport_readb, s); +} + +/* Xen Platform PCI Device */ + +static uint32_t xen_platform_ioport_readb(void *opaque, uint32_t addr) +{ + addr &= 0xff; + + if (addr == 0) { + return platform_fixed_ioport_readb(opaque, XEN_PLATFORM_IOPORT); + } else { + return ~0u; + } +} + +static void xen_platform_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIXenPlatformState *s = opaque; + + addr &= 0xff; + val &= 0xff; + + switch (addr) { + case 0: /* Platform flags */ + platform_fixed_ioport_writeb(opaque, XEN_PLATFORM_IOPORT, val); + break; + case 8: + log_writeb(s, val); + break; + default: + break; + } +} + +static void platform_ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type) +{ + PCIXenPlatformState *d = DO_UPCAST(PCIXenPlatformState, pci_dev, pci_dev); + + register_ioport_write(addr, size, 1, xen_platform_ioport_writeb, d); + register_ioport_read(addr, size, 1, xen_platform_ioport_readb, d); +} + +static uint32_t platform_mmio_read(ReadWriteHandler *handler, pcibus_t addr, int len) +{ + DPRINTF("Warning: attempted read from physical address " + "0x" TARGET_FMT_plx " in xen platform mmio space\n", addr); + + return 0; +} + +static void platform_mmio_write(ReadWriteHandler *handler, pcibus_t addr, + uint32_t val, int len) +{ + DPRINTF("Warning: attempted write of 0x%x to physical " + "address 0x" TARGET_FMT_plx " in xen platform mmio space\n", + val, addr); +} + +static ReadWriteHandler platform_mmio_handler = { + .read = &platform_mmio_read, + .write = &platform_mmio_write, +}; + +static void platform_mmio_map(PCIDevice *d, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + int mmio_io_addr; + + mmio_io_addr = cpu_register_io_memory_simple(&platform_mmio_handler, + DEVICE_NATIVE_ENDIAN); + + cpu_register_physical_memory(addr, size, mmio_io_addr); +} + +static int xen_platform_post_load(void *opaque, int version_id) +{ + PCIXenPlatformState *s = opaque; + + platform_fixed_ioport_writeb(s, XEN_PLATFORM_IOPORT, s->flags); + + return 0; +} + +static const VMStateDescription vmstate_xen_platform = { + .name = "platform", + .version_id = 4, + .minimum_version_id = 4, + .minimum_version_id_old = 4, + .post_load = xen_platform_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(pci_dev, PCIXenPlatformState), + VMSTATE_UINT8(flags, PCIXenPlatformState), + VMSTATE_END_OF_LIST() + } +}; + +static int xen_platform_initfn(PCIDevice *dev) +{ + PCIXenPlatformState *d = DO_UPCAST(PCIXenPlatformState, pci_dev, dev); + uint8_t *pci_conf; + + pci_conf = d->pci_dev.config; + + pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + pci_config_set_prog_interface(pci_conf, 0); + + pci_conf[PCI_INTERRUPT_PIN] = 1; + + pci_register_bar(&d->pci_dev, 0, 0x100, + PCI_BASE_ADDRESS_SPACE_IO, platform_ioport_map); + + /* reserve 16MB mmio address for share memory*/ + pci_register_bar(&d->pci_dev, 1, 0x1000000, + PCI_BASE_ADDRESS_MEM_PREFETCH, platform_mmio_map); + + platform_fixed_ioport_init(d); + + return 0; +} + +static void platform_reset(DeviceState *dev) +{ + PCIXenPlatformState *s = DO_UPCAST(PCIXenPlatformState, pci_dev.qdev, dev); + + platform_fixed_ioport_reset(s); +} + +static PCIDeviceInfo xen_platform_info = { + .init = xen_platform_initfn, + .qdev.name = "xen-platform", + .qdev.desc = "XEN platform pci device", + .qdev.size = sizeof(PCIXenPlatformState), + .qdev.vmsd = &vmstate_xen_platform, + .qdev.reset = platform_reset, + + .vendor_id = PCI_VENDOR_ID_XEN, + .device_id = PCI_DEVICE_ID_XEN_PLATFORM, + .class_id = PCI_CLASS_OTHERS << 8 | 0x80, + .subsystem_vendor_id = PCI_VENDOR_ID_XEN, + .subsystem_id = PCI_DEVICE_ID_XEN_PLATFORM, + .revision = 1, +}; + +static void xen_platform_register(void) +{ + pci_qdev_register(&xen_platform_info); +} + +device_init(xen_platform_register); diff -Nru qemu-kvm-0.14.1+noroms/hw/xics.c qemu-kvm-0.15.0+noroms/hw/xics.c --- qemu-kvm-0.14.1+noroms/hw/xics.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/xics.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,496 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics + * + * Copyright (c) 2010,2011 David Gibson, IBM Corporation. + * + * 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 "hw/spapr.h" +#include "hw/xics.h" + +#include + +/* + * ICP: Presentation layer + */ + +struct icp_server_state { + uint32_t xirr; + uint8_t pending_priority; + uint8_t mfrr; + qemu_irq output; +}; + +#define XISR_MASK 0x00ffffff +#define CPPR_MASK 0xff000000 + +#define XISR(ss) (((ss)->xirr) & XISR_MASK) +#define CPPR(ss) (((ss)->xirr) >> 24) + +struct ics_state; + +struct icp_state { + long nr_servers; + struct icp_server_state *ss; + struct ics_state *ics; +}; + +static void ics_reject(struct ics_state *ics, int nr); +static void ics_resend(struct ics_state *ics); +static void ics_eoi(struct ics_state *ics, int nr); + +static void icp_check_ipi(struct icp_state *icp, int server) +{ + struct icp_server_state *ss = icp->ss + server; + + if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { + return; + } + + if (XISR(ss)) { + ics_reject(icp->ics, XISR(ss)); + } + + ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; + ss->pending_priority = ss->mfrr; + qemu_irq_raise(ss->output); +} + +static void icp_resend(struct icp_state *icp, int server) +{ + struct icp_server_state *ss = icp->ss + server; + + if (ss->mfrr < CPPR(ss)) { + icp_check_ipi(icp, server); + } + ics_resend(icp->ics); +} + +static void icp_set_cppr(struct icp_state *icp, int server, uint8_t cppr) +{ + struct icp_server_state *ss = icp->ss + server; + uint8_t old_cppr; + uint32_t old_xisr; + + old_cppr = CPPR(ss); + ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); + + if (cppr < old_cppr) { + if (XISR(ss) && (cppr <= ss->pending_priority)) { + old_xisr = XISR(ss); + ss->xirr &= ~XISR_MASK; /* Clear XISR */ + qemu_irq_lower(ss->output); + ics_reject(icp->ics, old_xisr); + } + } else { + if (!XISR(ss)) { + icp_resend(icp, server); + } + } +} + +static void icp_set_mfrr(struct icp_state *icp, int nr, uint8_t mfrr) +{ + struct icp_server_state *ss = icp->ss + nr; + + ss->mfrr = mfrr; + if (mfrr < CPPR(ss)) { + icp_check_ipi(icp, nr); + } +} + +static uint32_t icp_accept(struct icp_server_state *ss) +{ + uint32_t xirr; + + qemu_irq_lower(ss->output); + xirr = ss->xirr; + ss->xirr = ss->pending_priority << 24; + return xirr; +} + +static void icp_eoi(struct icp_state *icp, int server, uint32_t xirr) +{ + struct icp_server_state *ss = icp->ss + server; + + ics_eoi(icp->ics, xirr & XISR_MASK); + /* Send EOI -> ICS */ + ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); + if (!XISR(ss)) { + icp_resend(icp, server); + } +} + +static void icp_irq(struct icp_state *icp, int server, int nr, uint8_t priority) +{ + struct icp_server_state *ss = icp->ss + server; + + if ((priority >= CPPR(ss)) + || (XISR(ss) && (ss->pending_priority <= priority))) { + ics_reject(icp->ics, nr); + } else { + if (XISR(ss)) { + ics_reject(icp->ics, XISR(ss)); + } + ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); + ss->pending_priority = priority; + qemu_irq_raise(ss->output); + } +} + +/* + * ICS: Source layer + */ + +struct ics_irq_state { + int server; + uint8_t priority; + uint8_t saved_priority; + /* int pending:1; */ + /* int presented:1; */ + int rejected:1; + int masked_pending:1; +}; + +struct ics_state { + int nr_irqs; + int offset; + qemu_irq *qirqs; + struct ics_irq_state *irqs; + struct icp_state *icp; +}; + +static int ics_valid_irq(struct ics_state *ics, uint32_t nr) +{ + return (nr >= ics->offset) + && (nr < (ics->offset + ics->nr_irqs)); +} + +static void ics_set_irq_msi(void *opaque, int nr, int val) +{ + struct ics_state *ics = (struct ics_state *)opaque; + struct ics_irq_state *irq = ics->irqs + nr; + + if (val) { + if (irq->priority == 0xff) { + irq->masked_pending = 1; + /* masked pending */ ; + } else { + icp_irq(ics->icp, irq->server, nr + ics->offset, irq->priority); + } + } +} + +static void ics_reject_msi(struct ics_state *ics, int nr) +{ + struct ics_irq_state *irq = ics->irqs + nr - ics->offset; + + irq->rejected = 1; +} + +static void ics_resend_msi(struct ics_state *ics) +{ + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + struct ics_irq_state *irq = ics->irqs + i; + + /* FIXME: filter by server#? */ + if (irq->rejected) { + irq->rejected = 0; + if (irq->priority != 0xff) { + icp_irq(ics->icp, irq->server, i + ics->offset, irq->priority); + } + } + } +} + +static void ics_write_xive_msi(struct ics_state *ics, int nr, int server, + uint8_t priority) +{ + struct ics_irq_state *irq = ics->irqs + nr; + + irq->server = server; + irq->priority = priority; + + if (!irq->masked_pending || (priority == 0xff)) { + return; + } + + irq->masked_pending = 0; + icp_irq(ics->icp, server, nr + ics->offset, priority); +} + +static void ics_reject(struct ics_state *ics, int nr) +{ + ics_reject_msi(ics, nr); +} + +static void ics_resend(struct ics_state *ics) +{ + ics_resend_msi(ics); +} + +static void ics_eoi(struct ics_state *ics, int nr) +{ +} + +/* + * Exported functions + */ + +qemu_irq xics_find_qirq(struct icp_state *icp, int irq) +{ + if ((irq < icp->ics->offset) + || (irq >= (icp->ics->offset + icp->ics->nr_irqs))) { + return NULL; + } + + return icp->ics->qirqs[irq - icp->ics->offset]; +} + +static target_ulong h_cppr(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong cppr = args[0]; + + icp_set_cppr(spapr->icp, env->cpu_index, cppr); + return H_SUCCESS; +} + +static target_ulong h_ipi(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong server = args[0]; + target_ulong mfrr = args[1]; + + if (server >= spapr->icp->nr_servers) { + return H_PARAMETER; + } + + icp_set_mfrr(spapr->icp, server, mfrr); + return H_SUCCESS; + +} + +static target_ulong h_xirr(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + uint32_t xirr = icp_accept(spapr->icp->ss + env->cpu_index); + + args[0] = xirr; + return H_SUCCESS; +} + +static target_ulong h_eoi(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong xirr = args[0]; + + icp_eoi(spapr->icp, env->cpu_index, xirr); + return H_SUCCESS; +} + +static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr, server, priority; + + if ((nargs != 3) || (nret != 1)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + server = rtas_ld(args, 1); + priority = rtas_ld(args, 2); + + if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) + || (priority > 0xff)) { + rtas_st(rets, 0, -3); + return; + } + + ics_write_xive_msi(ics, nr - ics->offset, server, priority); + + rtas_st(rets, 0, 0); /* Success */ +} + +static void rtas_get_xive(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 3)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(rets, 0, -3); + return; + } + + rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); + rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); +} + +static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 1)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(rets, 0, -3); + return; + } + + /* This is a NOP for now, since the described PAPR semantics don't + * seem to gel with what Linux does */ +#if 0 + struct ics_irq_state *irq = xics->irqs + (nr - xics->offset); + + irq->saved_priority = irq->priority; + ics_write_xive_msi(xics, nr - xics->offset, irq->server, 0xff); +#endif + + rtas_st(rets, 0, 0); /* Success */ +} + +static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct ics_state *ics = spapr->icp->ics; + uint32_t nr; + + if ((nargs != 1) || (nret != 1)) { + rtas_st(rets, 0, -3); + return; + } + + nr = rtas_ld(args, 0); + + if (!ics_valid_irq(ics, nr)) { + rtas_st(rets, 0, -3); + return; + } + + /* This is a NOP for now, since the described PAPR semantics don't + * seem to gel with what Linux does */ +#if 0 + struct ics_irq_state *irq = xics->irqs + (nr - xics->offset); + + ics_write_xive_msi(xics, nr - xics->offset, + irq->server, irq->saved_priority); +#endif + + rtas_st(rets, 0, 0); /* Success */ +} + +struct icp_state *xics_system_init(int nr_irqs) +{ + CPUState *env; + int max_server_num; + int i; + struct icp_state *icp; + struct ics_state *ics; + + max_server_num = -1; + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index > max_server_num) { + max_server_num = env->cpu_index; + } + } + + icp = qemu_mallocz(sizeof(*icp)); + icp->nr_servers = max_server_num + 1; + icp->ss = qemu_mallocz(icp->nr_servers*sizeof(struct icp_server_state)); + + for (i = 0; i < icp->nr_servers; i++) { + icp->ss[i].mfrr = 0xff; + } + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + struct icp_server_state *ss = &icp->ss[env->cpu_index]; + + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + ss->output = env->irq_inputs[POWER7_INPUT_INT]; + break; + + case PPC_FLAGS_INPUT_970: + ss->output = env->irq_inputs[PPC970_INPUT_INT]; + break; + + default: + hw_error("XICS interrupt model does not support this CPU bus " + "model\n"); + exit(1); + } + } + + ics = qemu_mallocz(sizeof(*ics)); + ics->nr_irqs = nr_irqs; + ics->offset = 16; + ics->irqs = qemu_mallocz(nr_irqs * sizeof(struct ics_irq_state)); + + icp->ics = ics; + ics->icp = icp; + + for (i = 0; i < nr_irqs; i++) { + ics->irqs[i].priority = 0xff; + ics->irqs[i].saved_priority = 0xff; + } + + ics->qirqs = qemu_allocate_irqs(ics_set_irq_msi, ics, nr_irqs); + + spapr_register_hypercall(H_CPPR, h_cppr); + spapr_register_hypercall(H_IPI, h_ipi); + spapr_register_hypercall(H_XIRR, h_xirr); + spapr_register_hypercall(H_EOI, h_eoi); + + spapr_rtas_register("ibm,set-xive", rtas_set_xive); + spapr_rtas_register("ibm,get-xive", rtas_get_xive); + spapr_rtas_register("ibm,int-off", rtas_int_off); + spapr_rtas_register("ibm,int-on", rtas_int_on); + + return icp; +} diff -Nru qemu-kvm-0.14.1+noroms/hw/xics.h qemu-kvm-0.15.0+noroms/hw/xics.h --- qemu-kvm-0.14.1+noroms/hw/xics.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/xics.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,38 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics + * + * Copyright (c) 2010,2011 David Gibson, IBM Corporation. + * + * 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. + * + */ +#if !defined(__XICS_H__) +#define __XICS_H__ + +#define XICS_IPI 0x2 + +struct icp_state; + +qemu_irq xics_find_qirq(struct icp_state *icp, int irq); + +struct icp_state *xics_system_init(int nr_irqs); + +#endif /* __XICS_H__ */ diff -Nru qemu-kvm-0.14.1+noroms/hw/xilinx_axidma.c qemu-kvm-0.15.0+noroms/hw/xilinx_axidma.c --- qemu-kvm-0.14.1+noroms/hw/xilinx_axidma.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/xilinx_axidma.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,509 @@ +/* + * QEMU model of Xilinx AXI-DMA block. + * + * Copyright (c) 2011 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 "qemu-char.h" +#include "qemu-timer.h" +#include "qemu-log.h" +#include "qdev-addr.h" + +#include "xilinx_axidma.h" + +#define D(x) + +#define R_DMACR (0x00 / 4) +#define R_DMASR (0x04 / 4) +#define R_CURDESC (0x08 / 4) +#define R_TAILDESC (0x10 / 4) +#define R_MAX (0x30 / 4) + +enum { + DMACR_RUNSTOP = 1, + DMACR_TAILPTR_MODE = 2, + DMACR_RESET = 4 +}; + +enum { + DMASR_HALTED = 1, + DMASR_IDLE = 2, + DMASR_IOC_IRQ = 1 << 12, + DMASR_DLY_IRQ = 1 << 13, + + DMASR_IRQ_MASK = 7 << 12 +}; + +struct SDesc { + uint64_t nxtdesc; + uint64_t buffer_address; + uint64_t reserved; + uint32_t control; + uint32_t status; + uint32_t app[6]; +}; + +enum { + SDESC_CTRL_EOF = (1 << 26), + SDESC_CTRL_SOF = (1 << 27), + + SDESC_CTRL_LEN_MASK = (1 << 23) - 1 +}; + +enum { + SDESC_STATUS_EOF = (1 << 26), + SDESC_STATUS_SOF_BIT = 27, + SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT), + SDESC_STATUS_COMPLETE = (1 << 31) +}; + +struct AXIStream { + QEMUBH *bh; + ptimer_state *ptimer; + qemu_irq irq; + + int nr; + + struct SDesc desc; + int pos; + unsigned int complete_cnt; + uint32_t regs[R_MAX]; +}; + +struct XilinxAXIDMA { + SysBusDevice busdev; + uint32_t freqhz; + void *dmach; + + struct AXIStream streams[2]; +}; + +/* + * Helper calls to extract info from desriptors and other trivial + * state from regs. + */ +static inline int stream_desc_sof(struct SDesc *d) +{ + return d->control & SDESC_CTRL_SOF; +} + +static inline int stream_desc_eof(struct SDesc *d) +{ + return d->control & SDESC_CTRL_EOF; +} + +static inline int stream_resetting(struct AXIStream *s) +{ + return !!(s->regs[R_DMACR] & DMACR_RESET); +} + +static inline int stream_running(struct AXIStream *s) +{ + return s->regs[R_DMACR] & DMACR_RUNSTOP; +} + +static inline int stream_halted(struct AXIStream *s) +{ + return s->regs[R_DMASR] & DMASR_HALTED; +} + +static inline int stream_idle(struct AXIStream *s) +{ + return !!(s->regs[R_DMASR] & DMASR_IDLE); +} + +static void stream_reset(struct AXIStream *s) +{ + s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ + s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ +} + +/* Map an offset addr into a channel index. */ +static inline int streamid_from_addr(target_phys_addr_t addr) +{ + int sid; + + sid = addr / (0x30); + sid &= 1; + return sid; +} + +#ifdef DEBUG_ENET +static void stream_desc_show(struct SDesc *d) +{ + qemu_log("buffer_addr = " PRIx64 "\n", d->buffer_address); + qemu_log("nxtdesc = " PRIx64 "\n", d->nxtdesc); + qemu_log("control = %x\n", d->control); + qemu_log("status = %x\n", d->status); +} +#endif + +static void stream_desc_load(struct AXIStream *s, target_phys_addr_t addr) +{ + struct SDesc *d = &s->desc; + int i; + + cpu_physical_memory_read(addr, (void *) d, sizeof *d); + + /* Convert from LE into host endianness. */ + d->buffer_address = le64_to_cpu(d->buffer_address); + d->nxtdesc = le64_to_cpu(d->nxtdesc); + d->control = le32_to_cpu(d->control); + d->status = le32_to_cpu(d->status); + for (i = 0; i < ARRAY_SIZE(d->app); i++) { + d->app[i] = le32_to_cpu(d->app[i]); + } +} + +static void stream_desc_store(struct AXIStream *s, target_phys_addr_t addr) +{ + struct SDesc *d = &s->desc; + int i; + + /* Convert from host endianness into LE. */ + d->buffer_address = cpu_to_le64(d->buffer_address); + d->nxtdesc = cpu_to_le64(d->nxtdesc); + d->control = cpu_to_le32(d->control); + d->status = cpu_to_le32(d->status); + for (i = 0; i < ARRAY_SIZE(d->app); i++) { + d->app[i] = cpu_to_le32(d->app[i]); + } + cpu_physical_memory_write(addr, (void *) d, sizeof *d); +} + +static void stream_update_irq(struct AXIStream *s) +{ + unsigned int pending, mask, irq; + + pending = s->regs[R_DMASR] & DMASR_IRQ_MASK; + mask = s->regs[R_DMACR] & DMASR_IRQ_MASK; + + irq = pending & mask; + + qemu_set_irq(s->irq, !!irq); +} + +static void stream_reload_complete_cnt(struct AXIStream *s) +{ + unsigned int comp_th; + comp_th = (s->regs[R_DMACR] >> 16) & 0xff; + s->complete_cnt = comp_th; +} + +static void timer_hit(void *opaque) +{ + struct AXIStream *s = opaque; + + stream_reload_complete_cnt(s); + s->regs[R_DMASR] |= DMASR_DLY_IRQ; + stream_update_irq(s); +} + +static void stream_complete(struct AXIStream *s) +{ + unsigned int comp_delay; + + /* Start the delayed timer. */ + comp_delay = s->regs[R_DMACR] >> 24; + if (comp_delay) { + ptimer_stop(s->ptimer); + ptimer_set_count(s->ptimer, comp_delay); + ptimer_run(s->ptimer, 1); + } + + s->complete_cnt--; + if (s->complete_cnt == 0) { + /* Raise the IOC irq. */ + s->regs[R_DMASR] |= DMASR_IOC_IRQ; + stream_reload_complete_cnt(s); + } +} + +static void stream_process_mem2s(struct AXIStream *s, + struct XilinxDMAConnection *dmach) +{ + uint32_t prev_d; + unsigned char txbuf[16 * 1024]; + unsigned int txlen; + uint32_t app[6]; + + if (!stream_running(s) || stream_idle(s)) { + return; + } + + while (1) { + stream_desc_load(s, s->regs[R_CURDESC]); + + if (s->desc.status & SDESC_STATUS_COMPLETE) { + s->regs[R_DMASR] |= DMASR_IDLE; + break; + } + + if (stream_desc_sof(&s->desc)) { + s->pos = 0; + memcpy(app, s->desc.app, sizeof app); + } + + txlen = s->desc.control & SDESC_CTRL_LEN_MASK; + if ((txlen + s->pos) > sizeof txbuf) { + hw_error("%s: too small internal txbuf! %d\n", __func__, + txlen + s->pos); + } + + cpu_physical_memory_read(s->desc.buffer_address, + txbuf + s->pos, txlen); + s->pos += txlen; + + if (stream_desc_eof(&s->desc)) { + xlx_dma_push_to_client(dmach, txbuf, s->pos, app); + s->pos = 0; + stream_complete(s); + } + + /* Update the descriptor. */ + s->desc.status = txlen | SDESC_STATUS_COMPLETE; + stream_desc_store(s, s->regs[R_CURDESC]); + + /* Advance. */ + prev_d = s->regs[R_CURDESC]; + s->regs[R_CURDESC] = s->desc.nxtdesc; + if (prev_d == s->regs[R_TAILDESC]) { + s->regs[R_DMASR] |= DMASR_IDLE; + break; + } + } +} + +static void stream_process_s2mem(struct AXIStream *s, + unsigned char *buf, size_t len, uint32_t *app) +{ + uint32_t prev_d; + unsigned int rxlen; + int pos = 0; + int sof = 1; + + if (!stream_running(s) || stream_idle(s)) { + return; + } + + while (len) { + stream_desc_load(s, s->regs[R_CURDESC]); + + if (s->desc.status & SDESC_STATUS_COMPLETE) { + s->regs[R_DMASR] |= DMASR_IDLE; + break; + } + + rxlen = s->desc.control & SDESC_CTRL_LEN_MASK; + if (rxlen > len) { + /* It fits. */ + rxlen = len; + } + + cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen); + len -= rxlen; + pos += rxlen; + + /* Update the descriptor. */ + if (!len) { + int i; + + stream_complete(s); + for (i = 0; i < 5; i++) { + s->desc.app[i] = app[i]; + } + s->desc.status |= SDESC_STATUS_EOF; + } + + s->desc.status |= sof << SDESC_STATUS_SOF_BIT; + s->desc.status |= SDESC_STATUS_COMPLETE; + stream_desc_store(s, s->regs[R_CURDESC]); + sof = 0; + + /* Advance. */ + prev_d = s->regs[R_CURDESC]; + s->regs[R_CURDESC] = s->desc.nxtdesc; + if (prev_d == s->regs[R_TAILDESC]) { + s->regs[R_DMASR] |= DMASR_IDLE; + break; + } + } +} + +static +void axidma_push(void *opaque, unsigned char *buf, size_t len, uint32_t *app) +{ + struct XilinxAXIDMA *d = opaque; + struct AXIStream *s = &d->streams[1]; + + if (!app) { + hw_error("No stream app data!\n"); + } + stream_process_s2mem(s, buf, len, app); + stream_update_irq(s); +} + +static uint32_t axidma_readl(void *opaque, target_phys_addr_t addr) +{ + struct XilinxAXIDMA *d = opaque; + struct AXIStream *s; + uint32_t r = 0; + int sid; + + sid = streamid_from_addr(addr); + s = &d->streams[sid]; + + addr = addr % 0x30; + addr >>= 2; + switch (addr) { + case R_DMACR: + /* Simulate one cycles reset delay. */ + s->regs[addr] &= ~DMACR_RESET; + r = s->regs[addr]; + break; + case R_DMASR: + s->regs[addr] &= 0xffff; + s->regs[addr] |= (s->complete_cnt & 0xff) << 16; + s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24; + r = s->regs[addr]; + break; + default: + r = s->regs[addr]; + D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n", + __func__, sid, addr * 4, r)); + break; + } + return r; + +} + +static void +axidma_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct XilinxAXIDMA *d = opaque; + struct AXIStream *s; + int sid; + + sid = streamid_from_addr(addr); + s = &d->streams[sid]; + + addr = addr % 0x30; + addr >>= 2; + switch (addr) { + case R_DMACR: + /* Tailptr mode is always on. */ + value |= DMACR_TAILPTR_MODE; + /* Remember our previous reset state. */ + value |= (s->regs[addr] & DMACR_RESET); + s->regs[addr] = value; + + if (value & DMACR_RESET) { + stream_reset(s); + } + + if ((value & 1) && !stream_resetting(s)) { + /* Start processing. */ + s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE); + } + stream_reload_complete_cnt(s); + break; + + case R_DMASR: + /* Mask away write to clear irq lines. */ + value &= ~(value & DMASR_IRQ_MASK); + s->regs[addr] = value; + break; + + case R_TAILDESC: + s->regs[addr] = value; + s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */ + if (!sid) { + stream_process_mem2s(s, d->dmach); + } + break; + default: + D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", + __func__, sid, addr * 4, value)); + s->regs[addr] = value; + break; + } + stream_update_irq(s); +} + +static CPUReadMemoryFunc * const axidma_read[] = { + &axidma_readl, + &axidma_readl, + &axidma_readl, +}; + +static CPUWriteMemoryFunc * const axidma_write[] = { + &axidma_writel, + &axidma_writel, + &axidma_writel, +}; + +static int xilinx_axidma_init(SysBusDevice *dev) +{ + struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev); + int axidma_regs; + int i; + + sysbus_init_irq(dev, &s->streams[1].irq); + sysbus_init_irq(dev, &s->streams[0].irq); + + if (!s->dmach) { + hw_error("Unconnected DMA channel.\n"); + } + + xlx_dma_connect_dma(s->dmach, s, axidma_push); + + axidma_regs = cpu_register_io_memory(axidma_read, axidma_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4 * 2, axidma_regs); + + for (i = 0; i < 2; i++) { + stream_reset(&s->streams[i]); + s->streams[i].nr = i; + s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]); + s->streams[i].ptimer = ptimer_init(s->streams[i].bh); + ptimer_set_freq(s->streams[i].ptimer, s->freqhz); + } + return 0; +} + +static SysBusDeviceInfo axidma_info = { + .init = xilinx_axidma_init, + .qdev.name = "xilinx,axidma", + .qdev.size = sizeof(struct XilinxAXIDMA), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000), + DEFINE_PROP_PTR("dmach", struct XilinxAXIDMA, dmach), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void xilinx_axidma_register(void) +{ + sysbus_register_withprop(&axidma_info); +} + +device_init(xilinx_axidma_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/xilinx_axidma.h qemu-kvm-0.15.0+noroms/hw/xilinx_axidma.h --- qemu-kvm-0.14.1+noroms/hw/xilinx_axidma.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/xilinx_axidma.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,39 @@ +/* AXI DMA connection. Used until qdev provides a generic way. */ +typedef void (*DMAPushFn)(void *opaque, + unsigned char *buf, size_t len, uint32_t *app); + +struct XilinxDMAConnection { + void *dma; + void *client; + + DMAPushFn to_dma; + DMAPushFn to_client; +}; + +static inline void xlx_dma_connect_client(struct XilinxDMAConnection *dmach, + void *c, DMAPushFn f) +{ + dmach->client = c; + dmach->to_client = f; +} + +static inline void xlx_dma_connect_dma(struct XilinxDMAConnection *dmach, + void *d, DMAPushFn f) +{ + dmach->dma = d; + dmach->to_dma = f; +} + +static inline +void xlx_dma_push_to_dma(struct XilinxDMAConnection *dmach, + uint8_t *buf, size_t len, uint32_t *app) +{ + dmach->to_dma(dmach->dma, buf, len, app); +} +static inline +void xlx_dma_push_to_client(struct XilinxDMAConnection *dmach, + uint8_t *buf, size_t len, uint32_t *app) +{ + dmach->to_client(dmach->client, buf, len, app); +} + diff -Nru qemu-kvm-0.14.1+noroms/hw/xilinx_axienet.c qemu-kvm-0.15.0+noroms/hw/xilinx_axienet.c --- qemu-kvm-0.14.1+noroms/hw/xilinx_axienet.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/hw/xilinx_axienet.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,898 @@ +/* + * QEMU model of Xilinx AXI-Ethernet. + * + * Copyright (c) 2011 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 "qemu-char.h" +#include "qemu-log.h" +#include "net.h" +#include "net/checksum.h" + +#include "xilinx_axidma.h" + +#define DPHY(x) + +/* Advertisement control register. */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ + +struct PHY { + uint32_t regs[32]; + + int link; + + unsigned int (*read)(struct PHY *phy, unsigned int req); + void (*write)(struct PHY *phy, unsigned int req, + unsigned int data); +}; + +static unsigned int tdk_read(struct PHY *phy, unsigned int req) +{ + int regnum; + unsigned r = 0; + + regnum = req & 0x1f; + + switch (regnum) { + case 1: + if (!phy->link) { + break; + } + /* MR1. */ + /* Speeds and modes. */ + r |= (1 << 13) | (1 << 14); + r |= (1 << 11) | (1 << 12); + r |= (1 << 5); /* Autoneg complete. */ + r |= (1 << 3); /* Autoneg able. */ + r |= (1 << 2); /* link. */ + r |= (1 << 1); /* link. */ + break; + case 5: + /* Link partner ability. + We are kind; always agree with whatever best mode + the guest advertises. */ + r = 1 << 14; /* Success. */ + /* Copy advertised modes. */ + r |= phy->regs[4] & (15 << 5); + /* Autoneg support. */ + r |= 1; + break; + case 17: + /* Marvel PHY on many xilinx boards. */ + r = 0x8000; /* 1000Mb */ + break; + case 18: + { + /* Diagnostics reg. */ + int duplex = 0; + int speed_100 = 0; + + if (!phy->link) { + break; + } + + /* Are we advertising 100 half or 100 duplex ? */ + speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); + speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); + + /* Are we advertising 10 duplex or 100 duplex ? */ + duplex = !!(phy->regs[4] & ADVERTISE_100FULL); + duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); + r = (speed_100 << 10) | (duplex << 11); + } + break; + + default: + r = phy->regs[regnum]; + break; + } + DPHY(qemu_log("\n%s %x = reg[%d]\n", __func__, r, regnum)); + return r; +} + +static void +tdk_write(struct PHY *phy, unsigned int req, unsigned int data) +{ + int regnum; + + regnum = req & 0x1f; + DPHY(qemu_log("%s reg[%d] = %x\n", __func__, regnum, data)); + switch (regnum) { + default: + phy->regs[regnum] = data; + break; + } +} + +static void +tdk_init(struct PHY *phy) +{ + phy->regs[0] = 0x3100; + /* PHY Id. */ + phy->regs[2] = 0x0300; + phy->regs[3] = 0xe400; + /* Autonegotiation advertisement reg. */ + phy->regs[4] = 0x01E1; + phy->link = 1; + + phy->read = tdk_read; + phy->write = tdk_write; +} + +struct MDIOBus { + /* bus. */ + int mdc; + int mdio; + + /* decoder. */ + enum { + PREAMBLE, + SOF, + OPC, + ADDR, + REQ, + TURNAROUND, + DATA + } state; + unsigned int drive; + + unsigned int cnt; + unsigned int addr; + unsigned int opc; + unsigned int req; + unsigned int data; + + struct PHY *devs[32]; +}; + +static void +mdio_attach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr) +{ + bus->devs[addr & 0x1f] = phy; +} + +#ifdef USE_THIS_DEAD_CODE +static void +mdio_detach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr) +{ + bus->devs[addr & 0x1f] = NULL; +} +#endif + +static uint16_t mdio_read_req(struct MDIOBus *bus, unsigned int addr, + unsigned int reg) +{ + struct PHY *phy; + uint16_t data; + + phy = bus->devs[addr]; + if (phy && phy->read) { + data = phy->read(phy, reg); + } else { + data = 0xffff; + } + DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data)); + return data; +} + +static void mdio_write_req(struct MDIOBus *bus, unsigned int addr, + unsigned int reg, uint16_t data) +{ + struct PHY *phy; + + DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data)); + phy = bus->devs[addr]; + if (phy && phy->write) { + phy->write(phy, reg, data); + } +} + +#define DENET(x) + +#define R_RAF (0x000 / 4) +enum { + RAF_MCAST_REJ = (1 << 1), + RAF_BCAST_REJ = (1 << 2), + RAF_EMCF_EN = (1 << 12), + RAF_NEWFUNC_EN = (1 << 11) +}; + +#define R_IS (0x00C / 4) +enum { + IS_HARD_ACCESS_COMPLETE = 1, + IS_AUTONEG = (1 << 1), + IS_RX_COMPLETE = (1 << 2), + IS_RX_REJECT = (1 << 3), + IS_TX_COMPLETE = (1 << 5), + IS_RX_DCM_LOCK = (1 << 6), + IS_MGM_RDY = (1 << 7), + IS_PHY_RST_DONE = (1 << 8), +}; + +#define R_IP (0x010 / 4) +#define R_IE (0x014 / 4) +#define R_UAWL (0x020 / 4) +#define R_UAWU (0x024 / 4) +#define R_PPST (0x030 / 4) +enum { + PPST_LINKSTATUS = (1 << 0), + PPST_PHY_LINKSTATUS = (1 << 7), +}; + +#define R_STATS_RX_BYTESL (0x200 / 4) +#define R_STATS_RX_BYTESH (0x204 / 4) +#define R_STATS_TX_BYTESL (0x208 / 4) +#define R_STATS_TX_BYTESH (0x20C / 4) +#define R_STATS_RXL (0x290 / 4) +#define R_STATS_RXH (0x294 / 4) +#define R_STATS_RX_BCASTL (0x2a0 / 4) +#define R_STATS_RX_BCASTH (0x2a4 / 4) +#define R_STATS_RX_MCASTL (0x2a8 / 4) +#define R_STATS_RX_MCASTH (0x2ac / 4) + +#define R_RCW0 (0x400 / 4) +#define R_RCW1 (0x404 / 4) +enum { + RCW1_VLAN = (1 << 27), + RCW1_RX = (1 << 28), + RCW1_FCS = (1 << 29), + RCW1_JUM = (1 << 30), + RCW1_RST = (1 << 31), +}; + +#define R_TC (0x408 / 4) +enum { + TC_VLAN = (1 << 27), + TC_TX = (1 << 28), + TC_FCS = (1 << 29), + TC_JUM = (1 << 30), + TC_RST = (1 << 31), +}; + +#define R_EMMC (0x410 / 4) +enum { + EMMC_LINKSPEED_10MB = (0 << 30), + EMMC_LINKSPEED_100MB = (1 << 30), + EMMC_LINKSPEED_1000MB = (2 << 30), +}; + +#define R_PHYC (0x414 / 4) + +#define R_MC (0x500 / 4) +#define MC_EN (1 << 6) + +#define R_MCR (0x504 / 4) +#define R_MWD (0x508 / 4) +#define R_MRD (0x50c / 4) +#define R_MIS (0x600 / 4) +#define R_MIP (0x620 / 4) +#define R_MIE (0x640 / 4) +#define R_MIC (0x640 / 4) + +#define R_UAW0 (0x700 / 4) +#define R_UAW1 (0x704 / 4) +#define R_FMI (0x708 / 4) +#define R_AF0 (0x710 / 4) +#define R_AF1 (0x714 / 4) +#define R_MAX (0x34 / 4) + +/* Indirect registers. */ +struct TEMAC { + struct MDIOBus mdio_bus; + struct PHY phy; + + void *parent; +}; + +struct XilinxAXIEnet { + SysBusDevice busdev; + qemu_irq irq; + void *dmach; + NICState *nic; + NICConf conf; + + + uint32_t c_rxmem; + uint32_t c_txmem; + uint32_t c_phyaddr; + + struct TEMAC TEMAC; + + /* MII regs. */ + union { + uint32_t regs[4]; + struct { + uint32_t mc; + uint32_t mcr; + uint32_t mwd; + uint32_t mrd; + }; + } mii; + + struct { + uint64_t rx_bytes; + uint64_t tx_bytes; + + uint64_t rx; + uint64_t rx_bcast; + uint64_t rx_mcast; + } stats; + + /* Receive configuration words. */ + uint32_t rcw[2]; + /* Transmit config. */ + uint32_t tc; + uint32_t emmc; + uint32_t phyc; + + /* Unicast Address Word. */ + uint32_t uaw[2]; + /* Unicast address filter used with extended mcast. */ + uint32_t ext_uaw[2]; + uint32_t fmi; + + uint32_t regs[R_MAX]; + + /* Multicast filter addrs. */ + uint32_t maddr[4][2]; + /* 32K x 1 lookup filter. */ + uint32_t ext_mtable[1024]; + + + uint8_t *rxmem; +}; + +static void axienet_rx_reset(struct XilinxAXIEnet *s) +{ + s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN; +} + +static void axienet_tx_reset(struct XilinxAXIEnet *s) +{ + s->tc = TC_JUM | TC_TX | TC_VLAN; +} + +static inline int axienet_rx_resetting(struct XilinxAXIEnet *s) +{ + return s->rcw[1] & RCW1_RST; +} + +static inline int axienet_rx_enabled(struct XilinxAXIEnet *s) +{ + return s->rcw[1] & RCW1_RX; +} + +static inline int axienet_extmcf_enabled(struct XilinxAXIEnet *s) +{ + return !!(s->regs[R_RAF] & RAF_EMCF_EN); +} + +static inline int axienet_newfunc_enabled(struct XilinxAXIEnet *s) +{ + return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN); +} + +static void axienet_reset(struct XilinxAXIEnet *s) +{ + axienet_rx_reset(s); + axienet_tx_reset(s); + + s->regs[R_PPST] = PPST_LINKSTATUS | PPST_PHY_LINKSTATUS; + s->regs[R_IS] = IS_AUTONEG | IS_RX_DCM_LOCK | IS_MGM_RDY | IS_PHY_RST_DONE; + + s->emmc = EMMC_LINKSPEED_100MB; +} + +static void enet_update_irq(struct XilinxAXIEnet *s) +{ + s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE]; + qemu_set_irq(s->irq, !!s->regs[R_IP]); +} + +static uint32_t enet_readl(void *opaque, target_phys_addr_t addr) +{ + struct XilinxAXIEnet *s = opaque; + uint32_t r = 0; + addr >>= 2; + + switch (addr) { + case R_RCW0: + case R_RCW1: + r = s->rcw[addr & 1]; + break; + + case R_TC: + r = s->tc; + break; + + case R_EMMC: + r = s->emmc; + break; + + case R_PHYC: + r = s->phyc; + break; + + case R_MCR: + r = s->mii.regs[addr & 3] | (1 << 7); /* Always ready. */ + break; + + case R_STATS_RX_BYTESL: + case R_STATS_RX_BYTESH: + r = s->stats.rx_bytes >> (32 * (addr & 1)); + break; + + case R_STATS_TX_BYTESL: + case R_STATS_TX_BYTESH: + r = s->stats.tx_bytes >> (32 * (addr & 1)); + break; + + case R_STATS_RXL: + case R_STATS_RXH: + r = s->stats.rx >> (32 * (addr & 1)); + break; + case R_STATS_RX_BCASTL: + case R_STATS_RX_BCASTH: + r = s->stats.rx_bcast >> (32 * (addr & 1)); + break; + case R_STATS_RX_MCASTL: + case R_STATS_RX_MCASTH: + r = s->stats.rx_mcast >> (32 * (addr & 1)); + break; + + case R_MC: + case R_MWD: + case R_MRD: + r = s->mii.regs[addr & 3]; + break; + + case R_UAW0: + case R_UAW1: + r = s->uaw[addr & 1]; + break; + + case R_UAWU: + case R_UAWL: + r = s->ext_uaw[addr & 1]; + break; + + case R_FMI: + r = s->fmi; + break; + + case R_AF0: + case R_AF1: + r = s->maddr[s->fmi & 3][addr & 1]; + break; + + case 0x8000 ... 0x83ff: + r = s->ext_mtable[addr - 0x8000]; + break; + + default: + if (addr < ARRAY_SIZE(s->regs)) { + r = s->regs[addr]; + } + DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n", + __func__, addr * 4, r)); + break; + } + return r; +} + +static void +enet_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + struct XilinxAXIEnet *s = opaque; + struct TEMAC *t = &s->TEMAC; + + addr >>= 2; + switch (addr) { + case R_RCW0: + case R_RCW1: + s->rcw[addr & 1] = value; + if ((addr & 1) && value & RCW1_RST) { + axienet_rx_reset(s); + } + break; + + case R_TC: + s->tc = value; + if (value & TC_RST) { + axienet_tx_reset(s); + } + break; + + case R_EMMC: + s->emmc = value; + break; + + case R_PHYC: + s->phyc = value; + break; + + case R_MC: + value &= ((1 < 7) - 1); + + /* Enable the MII. */ + if (value & MC_EN) { + unsigned int miiclkdiv = value & ((1 << 6) - 1); + if (!miiclkdiv) { + qemu_log("AXIENET: MDIO enabled but MDIOCLK is zero!\n"); + } + } + s->mii.mc = value; + break; + + case R_MCR: { + unsigned int phyaddr = (value >> 24) & 0x1f; + unsigned int regaddr = (value >> 16) & 0x1f; + unsigned int op = (value >> 14) & 3; + unsigned int initiate = (value >> 11) & 1; + + if (initiate) { + if (op == 1) { + mdio_write_req(&t->mdio_bus, phyaddr, regaddr, s->mii.mwd); + } else if (op == 2) { + s->mii.mrd = mdio_read_req(&t->mdio_bus, phyaddr, regaddr); + } else { + qemu_log("AXIENET: invalid MDIOBus OP=%d\n", op); + } + } + s->mii.mcr = value; + break; + } + + case R_MWD: + case R_MRD: + s->mii.regs[addr & 3] = value; + break; + + + case R_UAW0: + case R_UAW1: + s->uaw[addr & 1] = value; + break; + + case R_UAWL: + case R_UAWU: + s->ext_uaw[addr & 1] = value; + break; + + case R_FMI: + s->fmi = value; + break; + + case R_AF0: + case R_AF1: + s->maddr[s->fmi & 3][addr & 1] = value; + break; + + case 0x8000 ... 0x83ff: + s->ext_mtable[addr - 0x8000] = value; + break; + + default: + DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n", + __func__, addr * 4, value)); + if (addr < ARRAY_SIZE(s->regs)) { + s->regs[addr] = value; + } + break; + } + enet_update_irq(s); +} + +static CPUReadMemoryFunc * const enet_read[] = { + &enet_readl, + &enet_readl, + &enet_readl, +}; + +static CPUWriteMemoryFunc * const enet_write[] = { + &enet_writel, + &enet_writel, + &enet_writel, +}; + +static int eth_can_rx(VLANClientState *nc) +{ + struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + + /* RX enabled? */ + return !axienet_rx_resetting(s) && axienet_rx_enabled(s); +} + +static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1) +{ + int match = 1; + + if (memcmp(buf, &f0, 4)) { + match = 0; + } + + if (buf[4] != (f1 & 0xff) || buf[5] != ((f1 >> 8) & 0xff)) { + match = 0; + } + + return match; +} + +static ssize_t eth_rx(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, + 0xff, 0xff, 0xff}; + static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52}; + uint32_t app[6] = {0}; + int promisc = s->fmi & (1 << 31); + int unicast, broadcast, multicast, ip_multicast = 0; + uint32_t csum32; + uint16_t csum16; + int i; + + s = s; + DENET(qemu_log("%s: %zd bytes\n", __func__, size)); + + unicast = ~buf[0] & 0x1; + broadcast = memcmp(buf, sa_bcast, 6) == 0; + multicast = !unicast && !broadcast; + if (multicast && (memcmp(sa_ipmcast, buf, sizeof sa_ipmcast) == 0)) { + ip_multicast = 1; + } + + /* Jumbo or vlan sizes ? */ + if (!(s->rcw[1] & RCW1_JUM)) { + if (size > 1518 && size <= 1522 && !(s->rcw[1] & RCW1_VLAN)) { + return size; + } + } + + /* Basic Address filters. If you want to use the extended filters + you'll generally have to place the ethernet mac into promiscuous mode + to avoid the basic filtering from dropping most frames. */ + if (!promisc) { + if (unicast) { + if (!enet_match_addr(buf, s->uaw[0], s->uaw[1])) { + return size; + } + } else { + if (broadcast) { + /* Broadcast. */ + if (s->regs[R_RAF] & RAF_BCAST_REJ) { + return size; + } + } else { + int drop = 1; + + /* Multicast. */ + if (s->regs[R_RAF] & RAF_MCAST_REJ) { + return size; + } + + for (i = 0; i < 4; i++) { + if (enet_match_addr(buf, s->maddr[i][0], s->maddr[i][1])) { + drop = 0; + break; + } + } + + if (drop) { + return size; + } + } + } + } + + /* Extended mcast filtering enabled? */ + if (axienet_newfunc_enabled(s) && axienet_extmcf_enabled(s)) { + if (unicast) { + if (!enet_match_addr(buf, s->ext_uaw[0], s->ext_uaw[1])) { + return size; + } + } else { + if (broadcast) { + /* Broadcast. ??? */ + if (s->regs[R_RAF] & RAF_BCAST_REJ) { + return size; + } + } else { + int idx, bit; + + /* Multicast. */ + if (!memcmp(buf, sa_ipmcast, 3)) { + return size; + } + + idx = (buf[4] & 0x7f) << 8; + idx |= buf[5]; + + bit = 1 << (idx & 0x1f); + idx >>= 5; + + if (!(s->ext_mtable[idx] & bit)) { + return size; + } + } + } + } + + if (size < 12) { + s->regs[R_IS] |= IS_RX_REJECT; + enet_update_irq(s); + return -1; + } + + if (size > (s->c_rxmem - 4)) { + size = s->c_rxmem - 4; + } + + memcpy(s->rxmem, buf, size); + memset(s->rxmem + size, 0, 4); /* Clear the FCS. */ + + if (s->rcw[1] & RCW1_FCS) { + size += 4; /* fcs is inband. */ + } + + app[0] = 5 << 28; + csum32 = net_checksum_add(size - 14, (uint8_t *)s->rxmem + 14); + /* Fold it once. */ + csum32 = (csum32 & 0xffff) + (csum32 >> 16); + /* And twice to get rid of possible carries. */ + csum16 = (csum32 & 0xffff) + (csum32 >> 16); + app[3] = csum16; + app[4] = size & 0xffff; + + s->stats.rx_bytes += size; + s->stats.rx++; + if (multicast) { + s->stats.rx_mcast++; + app[2] |= 1 | (ip_multicast << 1); + } else if (broadcast) { + s->stats.rx_bcast++; + app[2] |= 1 << 3; + } + + /* Good frame. */ + app[2] |= 1 << 6; + + xlx_dma_push_to_dma(s->dmach, (void *)s->rxmem, size, app); + + s->regs[R_IS] |= IS_RX_COMPLETE; + enet_update_irq(s); + return size; +} + +static void eth_cleanup(VLANClientState *nc) +{ + /* FIXME. */ + struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + qemu_free(s->rxmem); + qemu_free(s); +} + +static void +axienet_stream_push(void *opaque, uint8_t *buf, size_t size, uint32_t *hdr) +{ + struct XilinxAXIEnet *s = opaque; + + /* TX enable ? */ + if (!(s->tc & TC_TX)) { + return; + } + + /* Jumbo or vlan sizes ? */ + if (!(s->tc & TC_JUM)) { + if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) { + return; + } + } + + if (hdr[0] & 1) { + unsigned int start_off = hdr[1] >> 16; + unsigned int write_off = hdr[1] & 0xffff; + uint32_t tmp_csum; + uint16_t csum; + + tmp_csum = net_checksum_add(size - start_off, + (uint8_t *)buf + start_off); + /* Accumulate the seed. */ + tmp_csum += hdr[2] & 0xffff; + + /* Fold the 32bit partial checksum. */ + csum = net_checksum_finish(tmp_csum); + + /* Writeback. */ + buf[write_off] = csum >> 8; + buf[write_off + 1] = csum & 0xff; + } + + qemu_send_packet(&s->nic->nc, buf, size); + + s->stats.tx_bytes += size; + s->regs[R_IS] |= IS_TX_COMPLETE; + enet_update_irq(s); +} + +static NetClientInfo net_xilinx_enet_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = eth_can_rx, + .receive = eth_rx, + .cleanup = eth_cleanup, +}; + +static int xilinx_enet_init(SysBusDevice *dev) +{ + struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), dev); + int enet_regs; + + sysbus_init_irq(dev, &s->irq); + + if (!s->dmach) { + hw_error("Unconnected Xilinx Ethernet MAC.\n"); + } + + xlx_dma_connect_client(s->dmach, s, axienet_stream_push); + + enet_regs = cpu_register_io_memory(enet_read, enet_write, s, + DEVICE_LITTLE_ENDIAN); + sysbus_init_mmio(dev, 0x40000, enet_regs); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + + tdk_init(&s->TEMAC.phy); + mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr); + + s->TEMAC.parent = s; + + s->rxmem = qemu_malloc(s->c_rxmem); + axienet_reset(s); + + return 0; +} + +static SysBusDeviceInfo xilinx_enet_info = { + .init = xilinx_enet_init, + .qdev.name = "xilinx,axienet", + .qdev.size = sizeof(struct XilinxAXIEnet), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("phyaddr", struct XilinxAXIEnet, c_phyaddr, 7), + DEFINE_PROP_UINT32("c_rxmem", struct XilinxAXIEnet, c_rxmem, 0x1000), + DEFINE_PROP_UINT32("c_txmem", struct XilinxAXIEnet, c_txmem, 0x1000), + DEFINE_PROP_PTR("dmach", struct XilinxAXIEnet, dmach), + DEFINE_NIC_PROPERTIES(struct XilinxAXIEnet, conf), + DEFINE_PROP_END_OF_LIST(), + } +}; +static void xilinx_enet_register(void) +{ + sysbus_register_withprop(&xilinx_enet_info); +} + +device_init(xilinx_enet_register) diff -Nru qemu-kvm-0.14.1+noroms/hw/xilinx_ethlite.c qemu-kvm-0.15.0+noroms/hw/xilinx_ethlite.c --- qemu-kvm-0.14.1+noroms/hw/xilinx_ethlite.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xilinx_ethlite.c 2011-08-09 07:40:29.000000000 -0500 @@ -90,13 +90,8 @@ D(qemu_log("%s %x=%x\n", __func__, addr * 4, r)); break; - /* Rx packet data is endian fixed at the way into the rx rams. This - * speeds things up because the ethlite MAC does not have a len - * register. That means the CPU will issue MMIO reads for the entire - * 2k rx buffer even for small packets. - */ default: - r = s->regs[addr]; + r = tswap32(s->regs[addr]); break; } return r; @@ -145,9 +140,8 @@ s->regs[addr] = value; break; - /* Packet data, make sure it stays BE. */ default: - s->regs[addr] = cpu_to_be32(value); + s->regs[addr] = tswap32(value); break; } } @@ -172,7 +166,6 @@ { struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; unsigned int rxbase = s->rxbuf * (0x800 / 4); - int i; /* DA filter. */ if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6)) @@ -186,12 +179,6 @@ D(qemu_log("%s %d rxbase=%x\n", __func__, size, rxbase)); memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); - /* Bring it into host endianess. */ - for (i = 0; i < ((size + 3) / 4); i++) { - uint32_t d = s->regs[rxbase + R_RX_BUF0 + i]; - s->regs[rxbase + R_RX_BUF0 + i] = be32_to_cpu(d); - } - s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; if (s->regs[rxbase + R_RX_CTRL0] & CTRL_I) eth_pulse_irq(s); diff -Nru qemu-kvm-0.14.1+noroms/hw/xilinx.h qemu-kvm-0.15.0+noroms/hw/xilinx.h --- qemu-kvm-0.14.1+noroms/hw/xilinx.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xilinx.h 2011-08-09 07:40:29.000000000 -0500 @@ -48,3 +48,42 @@ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); return dev; } + +static inline DeviceState * +xilinx_axiethernet_create(void *dmach, + NICInfo *nd, target_phys_addr_t base, qemu_irq irq, + int txmem, int rxmem) +{ + DeviceState *dev; + qemu_check_nic_model(nd, "xilinx-axienet"); + + dev = qdev_create(NULL, "xilinx,axienet"); + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint32(dev, "c_rxmem", rxmem); + qdev_prop_set_uint32(dev, "c_txmem", txmem); + qdev_prop_set_ptr(dev, "dmach", dmach); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + + return dev; +} + +static inline DeviceState * +xilinx_axiethernetdma_create(void *dmach, + target_phys_addr_t base, qemu_irq irq, + qemu_irq irq2, int freqhz) +{ + DeviceState *dev = NULL; + + dev = qdev_create(NULL, "xilinx,axidma"); + qdev_prop_set_uint32(dev, "freqhz", freqhz); + qdev_prop_set_ptr(dev, "dmach", dmach); + qdev_init_nofail(dev); + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq2); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, irq); + + return dev; +} diff -Nru qemu-kvm-0.14.1+noroms/hw/xilinx_timer.c qemu-kvm-0.15.0+noroms/hw/xilinx_timer.c --- qemu-kvm-0.14.1+noroms/hw/xilinx_timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xilinx_timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,7 +23,6 @@ */ #include "sysbus.h" -#include "sysemu.h" #include "qemu-timer.h" #define D(x) diff -Nru qemu-kvm-0.14.1+noroms/hw/xio3130_downstream.c qemu-kvm-0.15.0+noroms/hw/xio3130_downstream.c --- qemu-kvm-0.14.1+noroms/hw/xio3130_downstream.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xio3130_downstream.c 2011-08-09 07:40:29.000000000 -0500 @@ -69,9 +69,6 @@ } 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, @@ -182,6 +179,9 @@ .config_write = xio3130_downstream_write_config, .init = xio3130_downstream_initfn, .exit = xio3130_downstream_exitfn, + .vendor_id = PCI_VENDOR_ID_TI, + .device_id = PCI_DEVICE_ID_TI_XIO3130D, + .revision = XIO3130_REVISION, .qdev.props = (Property[]) { DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), diff -Nru qemu-kvm-0.14.1+noroms/hw/xio3130_upstream.c qemu-kvm-0.15.0+noroms/hw/xio3130_upstream.c --- qemu-kvm-0.14.1+noroms/hw/xio3130_upstream.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/xio3130_upstream.c 2011-08-09 07:40:29.000000000 -0500 @@ -65,9 +65,6 @@ } 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, @@ -159,6 +156,9 @@ .config_write = xio3130_upstream_write_config, .init = xio3130_upstream_initfn, .exit = xio3130_upstream_exitfn, + .vendor_id = PCI_VENDOR_ID_TI, + .device_id = PCI_DEVICE_ID_TI_XIO3130U, + .revision = XIO3130_REVISION, .qdev.props = (Property[]) { DEFINE_PROP_UINT8("port", PCIEPort, port, 0), diff -Nru qemu-kvm-0.14.1+noroms/hw/zaurus.c qemu-kvm-0.15.0+noroms/hw/zaurus.c --- qemu-kvm-0.14.1+noroms/hw/zaurus.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/hw/zaurus.c 2011-08-09 07:40:29.000000000 -0500 @@ -16,7 +16,6 @@ * with this program; if not, see . */ #include "hw.h" -#include "pxa.h" #include "sharpsl.h" #include "sysbus.h" @@ -181,17 +180,34 @@ return 0; } +static int scoop_post_load(void *opaque, int version_id) +{ + ScoopInfo *s = (ScoopInfo *) opaque; + int i; + uint32_t level; + + level = s->gpio_level & s->gpio_dir; + + for (i = 0; i < 16; i++) { + qemu_set_irq(s->handler[i], (level >> i) & 1); + } + + s->prev_level = level; + + return 0; +} + static bool is_version_0 (void *opaque, int version_id) { return version_id == 0; } - static const VMStateDescription vmstate_scoop_regs = { .name = "scoop", .version_id = 1, .minimum_version_id = 0, .minimum_version_id_old = 0, + .post_load = scoop_post_load, .fields = (VMStateField []) { VMSTATE_UINT16(status, ScoopInfo), VMSTATE_UINT16(power, ScoopInfo), diff -Nru qemu-kvm-0.14.1+noroms/input.c qemu-kvm-0.15.0+noroms/input.c --- qemu-kvm-0.14.1+noroms/input.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/input.c 2011-08-09 07:40:29.000000000 -0500 @@ -59,7 +59,7 @@ if (is_absolute != current_is_absolute || has_absolute != current_has_absolute) { - notifier_list_notify(&mouse_mode_notifiers); + notifier_list_notify(&mouse_mode_notifiers, NULL); } current_is_absolute = is_absolute; @@ -148,7 +148,7 @@ QEMUPutMouseEntry *entry; QEMUPutMouseEvent *mouse_event; void *mouse_event_opaque; - int width; + int width, height; if (QTAILQ_EMPTY(&mouse_handlers)) { return; @@ -160,16 +160,32 @@ 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; + if (entry->qemu_put_mouse_event_absolute) { + width = 0x7fff; + height = 0x7fff; + } else { + width = graphic_width - 1; + height = graphic_height - 1; + } + + switch (graphic_rotate) { + case 0: + mouse_event(mouse_event_opaque, + dx, dy, dz, buttons_state); + break; + case 90: mouse_event(mouse_event_opaque, width - dy, dx, dz, buttons_state); - } else + break; + case 180: mouse_event(mouse_event_opaque, - dx, dy, dz, buttons_state); + width - dx, height - dy, dz, buttons_state); + break; + case 270: + mouse_event(mouse_event_opaque, + dy, height - dx, dz, buttons_state); + break; + } } } diff -Nru qemu-kvm-0.14.1+noroms/iohandler.c qemu-kvm-0.15.0+noroms/iohandler.c --- qemu-kvm-0.14.1+noroms/iohandler.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/iohandler.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,193 @@ +/* + * QEMU System Emulator - managing I/O handler + * + * 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 "config-host.h" +#include "qemu-common.h" +#include "qemu-char.h" +#include "qemu-queue.h" + +#ifndef _WIN32 +#include +#endif + +typedef struct IOHandlerRecord { + int fd; + IOCanReadHandler *fd_read_poll; + IOHandler *fd_read; + IOHandler *fd_write; + int deleted; + void *opaque; + QLIST_ENTRY(IOHandlerRecord) next; +} IOHandlerRecord; + +static QLIST_HEAD(, IOHandlerRecord) io_handlers = + QLIST_HEAD_INITIALIZER(io_handlers); + + +/* 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; + + 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; + } + 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); +} + +void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds) +{ + IOHandlerRecord *ioh; + + 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, readfds); + if (ioh->fd > *pnfds) + *pnfds = ioh->fd; + } + if (ioh->fd_write) { + FD_SET(ioh->fd, writefds); + if (ioh->fd > *pnfds) + *pnfds = ioh->fd; + } + } +} + +void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int ret) +{ + if (ret > 0) { + IOHandlerRecord *pioh, *ioh; + + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, readfds)) { + ioh->fd_read(ioh->opaque); + } + if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, writefds)) { + ioh->fd_write(ioh->opaque); + } + + /* Do this last in case read/write handlers marked it for deletion */ + if (ioh->deleted) { + QLIST_REMOVE(ioh, next); + qemu_free(ioh); + } + } + } +} + +/* reaping of zombies. right now we're not passing the status to + anyone, but it would be possible to add a callback. */ +#ifndef _WIN32 +typedef struct ChildProcessRecord { + int pid; + QLIST_ENTRY(ChildProcessRecord) next; +} ChildProcessRecord; + +static QLIST_HEAD(, ChildProcessRecord) child_watches = + QLIST_HEAD_INITIALIZER(child_watches); + +static QEMUBH *sigchld_bh; + +static void sigchld_handler(int signal) +{ + qemu_bh_schedule(sigchld_bh); +} + +static void sigchld_bh_handler(void *opaque) +{ + ChildProcessRecord *rec, *next; + + QLIST_FOREACH_SAFE(rec, &child_watches, next, next) { + if (waitpid(rec->pid, NULL, WNOHANG) == rec->pid) { + QLIST_REMOVE(rec, next); + qemu_free(rec); + } + } +} + +static void qemu_init_child_watch(void) +{ + struct sigaction act; + sigchld_bh = qemu_bh_new(sigchld_bh_handler, NULL); + + act.sa_handler = sigchld_handler; + act.sa_flags = SA_NOCLDSTOP; + sigaction(SIGCHLD, &act, NULL); +} + +int qemu_add_child_watch(pid_t pid) +{ + ChildProcessRecord *rec; + + if (!sigchld_bh) { + qemu_init_child_watch(); + } + + QLIST_FOREACH(rec, &child_watches, next) { + if (rec->pid == pid) { + return 1; + } + } + rec = qemu_mallocz(sizeof(ChildProcessRecord)); + rec->pid = pid; + QLIST_INSERT_HEAD(&child_watches, rec, next); + return 0; +} +#endif diff -Nru qemu-kvm-0.14.1+noroms/ioport.c qemu-kvm-0.15.0+noroms/ioport.c --- qemu-kvm-0.14.1+noroms/ioport.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ioport.c 2011-08-09 07:40:29.000000000 -0500 @@ -149,7 +149,8 @@ for(i = start; i < start + length; i += size) { ioport_read_table[bsize][i] = func; if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) - hw_error("register_ioport_read: invalid opaque"); + hw_error("register_ioport_read: invalid opaque for address 0x%x", + i); ioport_opaque[i] = opaque; } return 0; @@ -168,7 +169,8 @@ for(i = start; i < start + length; i += size) { ioport_write_table[bsize][i] = func; if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) - hw_error("register_ioport_write: invalid opaque"); + hw_error("register_ioport_write: invalid opaque for address 0x%x", + i); ioport_opaque[i] = opaque; } return 0; @@ -243,18 +245,25 @@ int i; for(i = start; i < start + length; i++) { - ioport_read_table[0][i] = default_ioport_readb; - ioport_read_table[1][i] = default_ioport_readw; - ioport_read_table[2][i] = default_ioport_readl; - - ioport_write_table[0][i] = default_ioport_writeb; - ioport_write_table[1][i] = default_ioport_writew; - ioport_write_table[2][i] = default_ioport_writel; + ioport_read_table[0][i] = NULL; + ioport_read_table[1][i] = NULL; + ioport_read_table[2][i] = NULL; + + ioport_write_table[0][i] = NULL; + ioport_write_table[1][i] = NULL; + ioport_write_table[2][i] = NULL; ioport_opaque[i] = NULL; } } +bool isa_is_ioport_assigned(pio_addr_t start) +{ + return (ioport_read_table[0][start] || ioport_write_table[0][start] || + ioport_read_table[1][start] || ioport_write_table[1][start] || + ioport_read_table[2][start] || ioport_write_table[2][start]); +} + /***********************************************************/ void cpu_outb(pio_addr_t addr, uint8_t val) diff -Nru qemu-kvm-0.14.1+noroms/ioport.h qemu-kvm-0.15.0+noroms/ioport.h --- qemu-kvm-0.14.1+noroms/ioport.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ioport.h 2011-08-09 07:40:29.000000000 -0500 @@ -43,7 +43,7 @@ int register_ioport_write(pio_addr_t start, int length, int size, IOPortWriteFunc *func, void *opaque); void isa_unassign_ioport(pio_addr_t start, int length); - +bool isa_is_ioport_assigned(pio_addr_t start); void cpu_outb(pio_addr_t addr, uint8_t val); void cpu_outw(pio_addr_t addr, uint16_t val); diff -Nru qemu-kvm-0.14.1+noroms/iov.c qemu-kvm-0.15.0+noroms/iov.c --- qemu-kvm-0.14.1+noroms/iov.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/iov.c 2011-08-09 07:40:29.000000000 -0500 @@ -14,56 +14,61 @@ #include "iov.h" -size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, - const void *buf, size_t size) +size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, + const void *buf, size_t iov_off, size_t size) { - size_t offset; + size_t iovec_off, buf_off; unsigned int i; - offset = 0; - for (i = 0; offset < size && i < iovcnt; i++) { - size_t len; + iovec_off = 0; + buf_off = 0; + for (i = 0; i < iov_cnt && size; i++) { + if (iov_off < (iovec_off + iov[i].iov_len)) { + size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off, size); - len = MIN(iov[i].iov_len, size - offset); + memcpy(iov[i].iov_base + (iov_off - iovec_off), buf + buf_off, len); - memcpy(iov[i].iov_base, buf + offset, len); - offset += len; + buf_off += len; + iov_off += len; + size -= len; + } + iovec_off += iov[i].iov_len; } - return offset; + return buf_off; } -size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, - void *buf, size_t offset, size_t size) +size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + void *buf, size_t iov_off, size_t size) { uint8_t *ptr; - size_t iov_off, buf_off; + size_t iovec_off, buf_off; unsigned int i; ptr = buf; - iov_off = 0; + iovec_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); + for (i = 0; i < iov_cnt && size; i++) { + if (iov_off < (iovec_off + iov[i].iov_len)) { + size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); - memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len); + memcpy(ptr + buf_off, iov[i].iov_base + (iov_off - iovec_off), len); buf_off += len; - offset += len; + iov_off += len; size -= len; } - iov_off += iov[i].iov_len; + iovec_off += iov[i].iov_len; } return buf_off; } -size_t iov_size(const struct iovec *iov, const unsigned int iovcnt) +size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) { size_t len; unsigned int i; len = 0; - for (i = 0; i < iovcnt; i++) { + for (i = 0; i < iov_cnt; i++) { len += iov[i].iov_len; } return len; diff -Nru qemu-kvm-0.14.1+noroms/iov.h qemu-kvm-0.15.0+noroms/iov.h --- qemu-kvm-0.14.1+noroms/iov.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/iov.h 2011-08-09 07:40:29.000000000 -0500 @@ -12,8 +12,8 @@ #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); +size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, + const void *buf, size_t iov_off, size_t size); +size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + void *buf, size_t iov_off, size_t size); +size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); diff -Nru qemu-kvm-0.14.1+noroms/json-lexer.c qemu-kvm-0.15.0+noroms/json-lexer.c --- qemu-kvm-0.14.1+noroms/json-lexer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/json-lexer.c 2011-08-09 07:40:29.000000000 -0500 @@ -18,6 +18,8 @@ #include "qemu-common.h" #include "json-lexer.h" +#define MAX_TOKEN_SIZE (64ULL << 20) + /* * \"([^\\\"]|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*\" * '([^\\']|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*' @@ -28,7 +30,7 @@ */ enum json_lexer_state { - ERROR = 0, + IN_ERROR = 0, IN_DQ_UCODE3, IN_DQ_UCODE2, IN_DQ_UCODE1, @@ -103,7 +105,8 @@ ['u'] = IN_DQ_UCODE0, }, [IN_DQ_STRING] = { - [1 ... 0xFF] = IN_DQ_STRING, + [1 ... 0xBF] = IN_DQ_STRING, + [0xC2 ... 0xF4] = IN_DQ_STRING, ['\\'] = IN_DQ_STRING_ESCAPE, ['"'] = JSON_STRING, }, @@ -142,7 +145,8 @@ ['u'] = IN_SQ_UCODE0, }, [IN_SQ_STRING] = { - [1 ... 0xFF] = IN_SQ_STRING, + [1 ... 0xBF] = IN_SQ_STRING, + [0xC2 ... 0xF4] = IN_SQ_STRING, ['\\'] = IN_SQ_STRING_ESCAPE, ['\''] = JSON_STRING, }, @@ -150,7 +154,7 @@ /* Zero */ [IN_ZERO] = { TERMINAL(JSON_INTEGER), - ['0' ... '9'] = ERROR, + ['0' ... '9'] = IN_ERROR, ['.'] = IN_MANTISSA, }, @@ -272,7 +276,7 @@ lexer->x = lexer->y = 0; } -static int json_lexer_feed_char(JSONLexer *lexer, char ch) +static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) { int char_consumed, new_state; @@ -302,13 +306,42 @@ lexer->token = qstring_new(); new_state = IN_START; break; - case ERROR: - return -EINVAL; + case IN_ERROR: + /* XXX: To avoid having previous bad input leaving the parser in an + * unresponsive state where we consume unpredictable amounts of + * subsequent "good" input, percolate this error state up to the + * tokenizer/parser by forcing a NULL object to be emitted, then + * reset state. + * + * Also note that this handling is required for reliable channel + * negotiation between QMP and the guest agent, since chr(0xFF) + * is placed at the beginning of certain events to ensure proper + * delivery when the channel is in an unknown state. chr(0xFF) is + * never a valid ASCII/UTF-8 sequence, so this should reliably + * induce an error/flush state. + */ + lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y); + QDECREF(lexer->token); + lexer->token = qstring_new(); + new_state = IN_START; + lexer->state = new_state; + return 0; default: break; } lexer->state = new_state; - } while (!char_consumed); + } while (!char_consumed && !flush); + + /* Do not let a single token grow to an arbitrarily large size, + * this is a security consideration. + */ + if (lexer->token->length > MAX_TOKEN_SIZE) { + lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y); + QDECREF(lexer->token); + lexer->token = qstring_new(); + lexer->state = IN_START; + } + return 0; } @@ -319,7 +352,7 @@ for (i = 0; i < size; i++) { int err; - err = json_lexer_feed_char(lexer, buffer[i]); + err = json_lexer_feed_char(lexer, buffer[i], false); if (err < 0) { return err; } @@ -330,7 +363,7 @@ int json_lexer_flush(JSONLexer *lexer) { - return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0); + return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0, true); } void json_lexer_destroy(JSONLexer *lexer) diff -Nru qemu-kvm-0.14.1+noroms/json-lexer.h qemu-kvm-0.15.0+noroms/json-lexer.h --- qemu-kvm-0.14.1+noroms/json-lexer.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/json-lexer.h 2011-08-09 07:40:29.000000000 -0500 @@ -25,6 +25,7 @@ JSON_STRING, JSON_ESCAPE, JSON_SKIP, + JSON_ERROR, } JSONTokenType; typedef struct JSONLexer JSONLexer; diff -Nru qemu-kvm-0.14.1+noroms/json-parser.c qemu-kvm-0.15.0+noroms/json-parser.c --- qemu-kvm-0.14.1+noroms/json-parser.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/json-parser.c 2011-08-09 07:40:29.000000000 -0500 @@ -22,9 +22,11 @@ #include "qbool.h" #include "json-parser.h" #include "json-lexer.h" +#include "qerror.h" typedef struct JSONParserContext { + Error *err; } JSONParserContext; #define BUG_ON(cond) assert(!(cond)) @@ -95,11 +97,15 @@ QObject *token, const char *msg, ...) { va_list ap; + char message[1024]; va_start(ap, msg); - fprintf(stderr, "parse error: "); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); + vsnprintf(message, sizeof(message), msg, ap); va_end(ap); + if (ctxt->err) { + error_free(ctxt->err); + ctxt->err = NULL; + } + error_set(&ctxt->err, QERR_JSON_PARSE_ERROR, message); } /** @@ -269,10 +275,15 @@ */ static int parse_pair(JSONParserContext *ctxt, QDict *dict, QList **tokens, va_list *ap) { - QObject *key, *token = NULL, *value, *peek; + QObject *key = NULL, *token = NULL, *value, *peek; QList *working = qlist_copy(*tokens); peek = qlist_peek(working); + if (peek == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + key = parse_value(ctxt, &working, ap); if (!key || qobject_type(key) != QTYPE_QSTRING) { parse_error(ctxt, peek, "key is not a string in object"); @@ -280,6 +291,11 @@ } token = qlist_pop(working); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + if (!token_is_operator(token, ':')) { parse_error(ctxt, token, "missing : in object pair"); goto out; @@ -315,6 +331,10 @@ QList *working = qlist_copy(*tokens); token = qlist_pop(working); + if (token == NULL) { + goto out; + } + if (!token_is_operator(token, '{')) { goto out; } @@ -324,12 +344,22 @@ dict = qdict_new(); peek = qlist_peek(working); + if (peek == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + if (!token_is_operator(peek, '}')) { if (parse_pair(ctxt, dict, &working, ap) == -1) { goto out; } token = qlist_pop(working); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + while (!token_is_operator(token, '}')) { if (!token_is_operator(token, ',')) { parse_error(ctxt, token, "expected separator in dict"); @@ -343,6 +373,10 @@ } token = qlist_pop(working); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } } qobject_decref(token); token = NULL; @@ -371,6 +405,10 @@ QList *working = qlist_copy(*tokens); token = qlist_pop(working); + if (token == NULL) { + goto out; + } + if (!token_is_operator(token, '[')) { goto out; } @@ -380,6 +418,11 @@ list = qlist_new(); peek = qlist_peek(working); + if (peek == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + if (!token_is_operator(peek, ']')) { QObject *obj; @@ -392,6 +435,11 @@ qlist_append_obj(list, obj); token = qlist_pop(working); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } + while (!token_is_operator(token, ']')) { if (!token_is_operator(token, ',')) { parse_error(ctxt, token, "expected separator in list"); @@ -410,6 +458,10 @@ qlist_append_obj(list, obj); token = qlist_pop(working); + if (token == NULL) { + parse_error(ctxt, NULL, "premature EOI"); + goto out; + } } qobject_decref(token); @@ -438,6 +490,9 @@ QList *working = qlist_copy(*tokens); token = qlist_pop(working); + if (token == NULL) { + goto out; + } if (token_get_type(token) != JSON_KEYWORD) { goto out; @@ -475,6 +530,9 @@ } token = qlist_pop(working); + if (token == NULL) { + goto out; + } if (token_is_escape(token, "%p")) { obj = va_arg(*ap, QObject *); @@ -514,6 +572,10 @@ QList *working = qlist_copy(*tokens); token = qlist_pop(working); + if (token == NULL) { + goto out; + } + switch (token_get_type(token)) { case JSON_STRING: obj = QOBJECT(qstring_from_escaped_str(ctxt, token)); @@ -565,13 +627,24 @@ QObject *json_parser_parse(QList *tokens, va_list *ap) { + return json_parser_parse_err(tokens, ap, NULL); +} + +QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp) +{ JSONParserContext ctxt = {}; - QList *working = qlist_copy(tokens); + QList *working; QObject *result; + if (!tokens) { + return NULL; + } + working = qlist_copy(tokens); result = parse_value(&ctxt, &working, ap); QDECREF(working); + error_propagate(errp, ctxt.err); + return result; } diff -Nru qemu-kvm-0.14.1+noroms/json-parser.h qemu-kvm-0.15.0+noroms/json-parser.h --- qemu-kvm-0.14.1+noroms/json-parser.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/json-parser.h 2011-08-09 07:40:29.000000000 -0500 @@ -16,7 +16,9 @@ #include "qemu-common.h" #include "qlist.h" +#include "error.h" QObject *json_parser_parse(QList *tokens, va_list *ap); +QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp); #endif diff -Nru qemu-kvm-0.14.1+noroms/json-streamer.c qemu-kvm-0.15.0+noroms/json-streamer.c --- qemu-kvm-0.14.1+noroms/json-streamer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/json-streamer.c 2011-08-09 07:40:29.000000000 -0500 @@ -18,6 +18,9 @@ #include "json-lexer.h" #include "json-streamer.h" +#define MAX_TOKEN_SIZE (64ULL << 20) +#define MAX_NESTING (1ULL << 10) + static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTokenType type, int x, int y) { JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); @@ -49,14 +52,44 @@ qdict_put(dict, "x", qint_from_int(x)); qdict_put(dict, "y", qint_from_int(y)); + parser->token_size += token->length; + qlist_append(parser->tokens, dict); - if (parser->brace_count == 0 && - parser->bracket_count == 0) { - parser->emit(parser, parser->tokens); + if (type == JSON_ERROR) { + goto out_emit_bad; + } else if (parser->brace_count < 0 || + parser->bracket_count < 0 || + (parser->brace_count == 0 && + parser->bracket_count == 0)) { + goto out_emit; + } else if (parser->token_size > MAX_TOKEN_SIZE || + parser->bracket_count > MAX_NESTING || + parser->brace_count > MAX_NESTING) { + /* Security consideration, we limit total memory allocated per object + * and the maximum recursion depth that a message can force. + */ + goto out_emit; + } + + return; + +out_emit_bad: + /* clear out token list and tell the parser to emit and error + * indication by passing it a NULL list + */ + QDECREF(parser->tokens); + parser->tokens = NULL; +out_emit: + /* send current list of tokens to parser and reset tokenizer */ + parser->brace_count = 0; + parser->bracket_count = 0; + parser->emit(parser, parser->tokens); + if (parser->tokens) { QDECREF(parser->tokens); - parser->tokens = qlist_new(); } + parser->tokens = qlist_new(); + parser->token_size = 0; } void json_message_parser_init(JSONMessageParser *parser, @@ -66,6 +99,7 @@ parser->brace_count = 0; parser->bracket_count = 0; parser->tokens = qlist_new(); + parser->token_size = 0; json_lexer_init(&parser->lexer, json_message_process_token); } diff -Nru qemu-kvm-0.14.1+noroms/json-streamer.h qemu-kvm-0.15.0+noroms/json-streamer.h --- qemu-kvm-0.14.1+noroms/json-streamer.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/json-streamer.h 2011-08-09 07:40:29.000000000 -0500 @@ -24,6 +24,7 @@ int brace_count; int bracket_count; QList *tokens; + uint64_t token_size; } JSONMessageParser; void json_message_parser_init(JSONMessageParser *parser, diff -Nru qemu-kvm-0.14.1+noroms/kvm/kvm_stat qemu-kvm-0.15.0+noroms/kvm/kvm_stat --- qemu-kvm-0.14.1+noroms/kvm/kvm_stat 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/kvm/kvm_stat 2011-08-09 07:40:29.000000000 -0500 @@ -201,12 +201,63 @@ import re +sys_tracing = '/sys/kernel/debug/tracing' + +class Group(object): + def __init__(self, cpu): + self.events = [] + self.group_leader = None + self.cpu = cpu + def add_event(self, name, event_set, tracepoint, filter = None): + self.events.append(Event(group = self, + name = name, event_set = event_set, + tracepoint = tracepoint, filter = filter)) + if len(self.events) == 1: + self.file = os.fdopen(self.events[0].fd) + def read(self): + bytes = 8 * (1 + len(self.events)) + fmt = 'xxxxxxxx' + 'q' * len(self.events) + return dict(zip([event.name for event in self.events], + struct.unpack(fmt, self.file.read(bytes)))) + +class Event(object): + def __init__(self, group, name, event_set, tracepoint, filter = None): + self.name = name + attr = perf_event_attr() + attr.type = PERF_TYPE_TRACEPOINT + attr.size = ctypes.sizeof(attr) + id_path = os.path.join(sys_tracing, 'events', event_set, + tracepoint, 'id') + id = int(file(id_path).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 + group_leader = -1 + if group.events: + group_leader = group.events[0].fd + fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0) + if fd == -1: + raise Exception('perf_event_open failed') + if filter: + import fcntl + fcntl.ioctl(fd, 0x40082406, filter) + self.fd = fd + def enable(self): + import fcntl + fcntl.ioctl(self.fd, 0x00002400, 0) + def disable(self): + import fcntl + fcntl.ioctl(self.fd, 0x00002401, 0) + class TracepointProvider(object): def __init__(self): - self.base = '/sys/kernel/debug/tracing/events/kvm/' + path = os.path.join(sys_tracing, 'events', 'kvm') fields = [f - for f in os.listdir(self.base) - if os.path.isdir(self.base + '/' + f)] + for f in os.listdir(path) + if os.path.isdir(os.path.join(path, f))] extra = [] for f in fields: if f in filters: @@ -214,10 +265,11 @@ for name, number in values.iteritems(): extra.append(f + '(' + name + ')') fields += extra + self._setup(fields) self.select(fields) def fields(self): return self._fields - def select(self, _fields): + def _setup(self, _fields): self._fields = _fields cpure = r'cpu([0-9]+)' self.cpus = [int(re.match(cpure, x).group(1)) @@ -226,66 +278,59 @@ import resource nfiles = len(self.cpus) * 1000 resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles)) - fds = [] + events = [] self.group_leaders = [] for cpu in self.cpus: - group_leader = -1 - for f in _fields: - fbase, sub = f, None - m = re.match(r'(.*)\((.*)\)', f) + group = Group(cpu) + for name in _fields: + tracepoint = name + filter = None + m = re.match(r'(.*)\((.*)\)', name) 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] + tracepoint, sub = m.groups() + filter = '%s==%d\0' % (filters[tracepoint][0], + filters[tracepoint][1][sub]) + event = group.add_event(name, event_set = 'kvm', + tracepoint = tracepoint, + filter = filter) + self.group_leaders.append(group) + def select(self, fields): + for group in self.group_leaders: + for event in group.events: + if event.name in fields: + event.enable() + else: + event.disable() 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 + from collections import defaultdict + ret = defaultdict(int) + for group in self.group_leaders: + for name, val in group.read().iteritems(): + ret[name] += val return ret class Stats: def __init__(self, provider, fields = None): + self.provider = provider + self.fields_filter = fields + self._update() + def _update(self): def wanted(key): import re - if not fields: + if not self.fields_filter: return True - return re.match(fields, key) != None - self.provider = provider + return re.match(self.fields_filter, key) is not None self.values = dict([(key, None) for key in provider.fields() if wanted(key)]) self.provider.select(self.values.keys()) + def set_fields_filter(self, fields_filter): + self.fields_filter = fields_filter + self._update() def get(self): new = self.provider.read() for key in self.provider.fields(): - oldval = self.values[key] + oldval = self.values.get(key, (0, 0)) newval = new[key] newdelta = None if oldval is not None: @@ -307,6 +352,15 @@ def tui(screen, stats): curses.use_default_colors() curses.noecho() + drilldown = False + fields_filter = stats.fields_filter + def update_drilldown(): + if not fields_filter: + if drilldown: + stats.set_fields_filter(None) + else: + stats.set_fields_filter(r'^[^\(]*$') + update_drilldown() def refresh(sleeptime): screen.erase() screen.addstr(0, 0, 'kvm statistics') @@ -340,6 +394,9 @@ sleeptime = 3 try: c = screen.getkey() + if c == 'x': + drilldown = not drilldown + update_drilldown() if c == 'q': break except KeyboardInterrupt: diff -Nru qemu-kvm-0.14.1+noroms/kvm-all.c qemu-kvm-0.15.0+noroms/kvm-all.c --- qemu-kvm-0.14.1+noroms/kvm-all.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/kvm-all.c 2011-08-09 07:40:29.000000000 -0500 @@ -46,8 +46,6 @@ do { } while (0) #endif -#ifdef OBSOLETE_KVM_IMPL - typedef struct KVMSlot { target_phys_addr_t start_addr; @@ -78,10 +76,18 @@ int pit_in_kernel; int xsave, xcrs; int many_ioeventfds; -}; + int pit_state2; -static KVMState *kvm_state; + int irqchip_inject_ioctl; +#ifdef KVM_CAP_IRQ_ROUTING + struct kvm_irq_routing *irq_routes; + int nr_allocated_irq_routes; +#endif + void *used_gsi_bitmap; + int max_gsi; +}; +KVMState *kvm_state; static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), @@ -89,17 +95,11 @@ 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) { - continue; - } if (s->slots[i].memory_size == 0) { return &s->slots[i]; } @@ -187,14 +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); } -#endif int kvm_irqchip_in_kernel(void) { @@ -206,8 +204,6 @@ return kvm_state->pit_in_kernel; } - -#ifdef OBSOLETE_KVM_IMPL int kvm_init_vcpu(CPUState *env) { KVMState *s = kvm_state; @@ -224,9 +220,11 @@ env->kvm_fd = ret; env->kvm_state = s; + env->kvm_vcpu_dirty = 1; mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { + ret = mmap_size; DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n"); goto err; } @@ -252,51 +250,64 @@ err: return ret; } -#endif /* * dirty pages logging control */ -static int kvm_dirty_pages_log_change(target_phys_addr_t phys_addr, - ram_addr_t size, int flags, int mask) + +static int kvm_mem_flags(KVMState *s, bool log_dirty) +{ + return log_dirty ? KVM_MEM_LOG_DIRTY_PAGES : 0; +} + +static int kvm_slot_dirty_pages_log_change(KVMSlot *mem, bool log_dirty) { KVMState *s = kvm_state; - KVMSlot *mem = kvm_lookup_matching_slot(s, phys_addr, phys_addr + size); + int flags, mask = KVM_MEM_LOG_DIRTY_PAGES; int old_flags; - if (mem == NULL) { - fprintf(stderr, "BUG: %s: invalid parameters " TARGET_FMT_plx "-" - TARGET_FMT_plx "\n", __func__, phys_addr, - (target_phys_addr_t)(phys_addr + size - 1)); - return -EINVAL; - } - old_flags = mem->flags; - flags = (mem->flags & ~mask) | flags; + flags = (mem->flags & ~mask) | kvm_mem_flags(s, log_dirty); mem->flags = flags; /* If nothing changed effectively, no need to issue ioctl */ if (s->migration_log) { flags |= KVM_MEM_LOG_DIRTY_PAGES; } + if (flags == old_flags) { - return 0; + return 0; } return kvm_set_user_memory_region(s, mem); } -int kvm_log_start(target_phys_addr_t phys_addr, ram_addr_t size) +static int kvm_dirty_pages_log_change(target_phys_addr_t phys_addr, + ram_addr_t size, bool log_dirty) { - return kvm_dirty_pages_log_change(phys_addr, size, KVM_MEM_LOG_DIRTY_PAGES, - KVM_MEM_LOG_DIRTY_PAGES); + KVMState *s = kvm_state; + KVMSlot *mem = kvm_lookup_matching_slot(s, phys_addr, phys_addr + size); + + if (mem == NULL) { + fprintf(stderr, "BUG: %s: invalid parameters " TARGET_FMT_plx "-" + TARGET_FMT_plx "\n", __func__, phys_addr, + (target_phys_addr_t)(phys_addr + size - 1)); + return -EINVAL; + } + return kvm_slot_dirty_pages_log_change(mem, log_dirty); } -int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size) +static int kvm_log_start(CPUPhysMemoryClient *client, + 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, true); +} + +static int kvm_log_stop(CPUPhysMemoryClient *client, + target_phys_addr_t phys_addr, ram_addr_t size) +{ + return kvm_dirty_pages_log_change(phys_addr, size, false); } static int kvm_set_migration_log(int enable) @@ -383,7 +394,20 @@ break; } - size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), HOST_LONG_BITS) / 8; + /* XXX bad kernel interface alert + * For dirty bitmap, kernel allocates array of size aligned to + * bits-per-long. But for case when the kernel is 64bits and + * the userspace is 32bits, userspace can't align to the same + * bits-per-long, since sizeof(long) is different between kernel + * and user space. This way, userspace will provide buffer which + * may be 4 bytes less than the kernel will use, resulting in + * userspace memory corruption (which is not detectable by valgrind + * too, in most cases). + * So for now, let's align to 64 instead of HOST_LONG_BITS here, in + * a hope that sizeof(long) wont become >8 any time soon. + */ + size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), + /*HOST_LONG_BITS*/ 64) / 8; if (!d.dirty_bitmap) { d.dirty_bitmap = qemu_malloc(size); } else if (size > allocated_size) { @@ -492,7 +516,6 @@ #endif } -#ifdef OBSOLETE_KVM_IMPL static const KVMCapabilityInfo * kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list) { @@ -504,10 +527,9 @@ } return NULL; } -#endif static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, - ram_addr_t phys_offset) + ram_addr_t phys_offset, bool log_dirty) { KVMState *s = kvm_state; ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; @@ -532,7 +554,8 @@ (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. */ + * identical parameters - update flags and done. */ + kvm_slot_dirty_pages_log_change(mem, log_dirty); return; } @@ -562,7 +585,7 @@ mem->memory_size = old.memory_size; mem->start_addr = old.start_addr; mem->phys_offset = old.phys_offset; - mem->flags = 0; + mem->flags = kvm_mem_flags(s, log_dirty); err = kvm_set_user_memory_region(s, mem); if (err) { @@ -583,12 +606,17 @@ mem->memory_size = start_addr - old.start_addr; mem->start_addr = old.start_addr; mem->phys_offset = old.phys_offset; - mem->flags = 0; + mem->flags = kvm_mem_flags(s, log_dirty); err = kvm_set_user_memory_region(s, mem); if (err) { fprintf(stderr, "%s: error registering prefix slot: %s\n", __func__, strerror(-err)); +#ifdef TARGET_PPC + fprintf(stderr, "%s: This is probably because your kernel's " \ + "PAGE_SIZE is too big. Please try to use 4k " \ + "PAGE_SIZE!\n", __func__); +#endif abort(); } } @@ -602,7 +630,7 @@ 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; + mem->flags = kvm_mem_flags(s, log_dirty); err = kvm_set_user_memory_region(s, mem); if (err) { @@ -625,7 +653,7 @@ mem->memory_size = size; mem->start_addr = start_addr; mem->phys_offset = phys_offset; - mem->flags = 0; + mem->flags = kvm_mem_flags(s, log_dirty); err = kvm_set_user_memory_region(s, mem); if (err) { @@ -637,9 +665,10 @@ static void kvm_client_set_memory(struct CPUPhysMemoryClient *client, target_phys_addr_t start_addr, - ram_addr_t size, ram_addr_t phys_offset) + ram_addr_t size, ram_addr_t phys_offset, + bool log_dirty) { - kvm_set_phys_mem(start_addr, size, phys_offset); + kvm_set_phys_mem(start_addr, size, phys_offset, log_dirty); } static int kvm_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client, @@ -659,14 +688,18 @@ .set_memory = kvm_client_set_memory, .sync_dirty_bitmap = kvm_client_sync_dirty_bitmap, .migration_log = kvm_client_migration_log, + .log_start = kvm_log_start, + .log_stop = kvm_log_stop, }; -void kvm_cpu_register_phys_memory_client(void) +static void kvm_handle_interrupt(CPUState *env, int mask) { - cpu_register_phys_memory_client(&kvm_cpu_phys_memory_client); -} + env->interrupt_request |= mask; -#ifdef OBSOLETE_KVM_IMPL + if (!qemu_cpu_is_self(env)) { + qemu_cpu_kick(env); + } +} int kvm_init(void) { @@ -733,39 +766,37 @@ s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); s->broken_set_mem_region = 1; -#ifdef 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; } -#endif - s->vcpu_events = 0; #ifdef KVM_CAP_VCPU_EVENTS s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); #endif - 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 + s->pit_state2 = 0; +#ifdef KVM_CAP_PIT_STATE2 + s->pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); +#endif + + s->pit_in_kernel = kvm_pit; + ret = kvm_arch_init(s); if (ret < 0) { goto err; @@ -776,6 +807,13 @@ s->many_ioeventfds = kvm_check_many_ioeventfds(); + ret = kvm_create_irqchip(s); + if (ret < 0) { + return ret; + } + + cpu_interrupt_handler = kvm_handle_interrupt; + return 0; err: @@ -791,10 +829,9 @@ return ret; } -#endif -static int kvm_handle_io(uint16_t port, void *data, int direction, int size, - uint32_t count) +static void kvm_handle_io(uint16_t port, void *data, int direction, int size, + uint32_t count) { int i; uint8_t *ptr = data; @@ -828,11 +865,8 @@ ptr += size; } - - return 1; } -#ifdef KVM_CAP_INTERNAL_ERROR_DATA static int kvm_handle_internal_error(CPUState *env, struct kvm_run *run) { fprintf(stderr, "KVM internal error."); @@ -851,7 +885,7 @@ fprintf(stderr, "emulation failure\n"); if (!kvm_arch_stop_on_emulation_error(env)) { cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE); - return 0; + return EXCP_INTERRUPT; } } /* FIXME: Should trigger a qmp message to let management know @@ -859,7 +893,6 @@ */ return -1; } -#endif void kvm_flush_coalesced_mmio_buffer(void) { @@ -878,8 +911,6 @@ } } -#ifdef OBSOLETE_KVM_IMPL - static void do_kvm_cpu_synchronize_state(void *_env) { CPUState *env = _env; @@ -912,60 +943,63 @@ int kvm_cpu_exec(CPUState *env) { struct kvm_run *run = env->kvm_run; - int ret; + int ret, run_ret; DPRINTF("kvm_cpu_exec()\n"); - do { -#ifndef CONFIG_IOTHREAD - if (env->exit_request) { - DPRINTF("interrupt exit requested\n"); - ret = 0; - break; - } -#endif + if (kvm_arch_process_async_events(env)) { + env->exit_request = 0; + return EXCP_HLT; + } - if (kvm_arch_process_irqchip_events(env)) { - ret = 0; - break; - } + cpu_single_env = env; + do { 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); + if (env->exit_request) { + DPRINTF("interrupt exit requested\n"); + /* + * KVM requires us to reenter the kernel after IO exits to complete + * instruction emulation. This self-signal will ensure that we + * leave ASAP again. + */ + qemu_cpu_kick_self(); + } cpu_single_env = NULL; qemu_mutex_unlock_iothread(); - ret = kvm_vcpu_ioctl(env, KVM_RUN, 0); + + run_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) { - cpu_exit(env); - DPRINTF("io window exit\n"); - ret = 0; - break; - } - - if (ret < 0) { - DPRINTF("kvm run failed %s\n", strerror(-ret)); + if (run_ret < 0) { + if (run_ret == -EINTR || run_ret == -EAGAIN) { + DPRINTF("io window exit\n"); + ret = EXCP_INTERRUPT; + break; + } + DPRINTF("kvm run failed %s\n", strerror(-run_ret)); abort(); } - ret = 0; /* exit loop */ switch (run->exit_reason) { case KVM_EXIT_IO: DPRINTF("handle_io\n"); - ret = kvm_handle_io(run->io.port, - (uint8_t *)run + run->io.data_offset, - run->io.direction, - run->io.size, - run->io.count); + kvm_handle_io(run->io.port, + (uint8_t *)run + run->io.data_offset, + run->io.direction, + run->io.size, + run->io.count); + ret = 0; break; case KVM_EXIT_MMIO: DPRINTF("handle_mmio\n"); @@ -973,58 +1007,42 @@ run->mmio.data, run->mmio.len, run->mmio.is_write); - ret = 1; + ret = 0; break; case KVM_EXIT_IRQ_WINDOW_OPEN: DPRINTF("irq_window_open\n"); + ret = EXCP_INTERRUPT; break; case KVM_EXIT_SHUTDOWN: DPRINTF("shutdown\n"); qemu_system_reset_request(); - ret = 1; + ret = EXCP_INTERRUPT; break; case KVM_EXIT_UNKNOWN: fprintf(stderr, "KVM: unknown exit, hardware reason %" PRIx64 "\n", (uint64_t)run->hw.hardware_exit_reason); ret = -1; break; -#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"); -#ifdef KVM_CAP_SET_GUEST_DEBUG - if (kvm_arch_debug(&run->debug.arch)) { - env->exception_index = EXCP_DEBUG; - return 0; - } - /* re-enter, this exception was guest-internal */ - ret = 1; -#endif /* KVM_CAP_SET_GUEST_DEBUG */ - break; default: DPRINTF("kvm_arch_handle_exit\n"); ret = kvm_arch_handle_exit(env, run); break; } - } while (ret > 0); + } 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; + vm_stop(VMSTOP_PANIC); } + env->exit_request = 0; + cpu_single_env = NULL; return ret; } -#endif int kvm_ioctl(KVMState *s, int type, ...) { int ret; @@ -1106,6 +1124,11 @@ return kvm_state->xcrs; } +int kvm_has_pit_state2(void) +{ + return kvm_state->pit_state2; +} + int kvm_has_many_ioeventfds(void) { if (!kvm_enabled()) { @@ -1114,6 +1137,11 @@ return kvm_state->many_ioeventfds; } +int kvm_allows_irq0_override(void) +{ + return !kvm_enabled() || !kvm_irqchip_in_kernel() || kvm_has_gsi_routing(); +} + void kvm_setup_guest_memory(void *start, size_t size) { if (!kvm_has_sync_mmu()) { @@ -1129,10 +1157,6 @@ } #ifdef KVM_CAP_SET_GUEST_DEBUG -#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) { @@ -1204,7 +1228,7 @@ bp->use_count = 1; err = kvm_arch_insert_sw_breakpoint(current_env, bp); if (err) { - free(bp); + qemu_free(bp); return err; } @@ -1328,7 +1352,7 @@ sigmask->len = 8; memcpy(sigmask->sigset, sigset, sizeof(*sigset)); r = kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, sigmask); - free(sigmask); + qemu_free(sigmask); return r; } @@ -1392,7 +1416,6 @@ #endif } -#if defined(KVM_IRQFD) int kvm_set_irqfd(int gsi, int fd, bool assigned) { struct kvm_irqfd irqfd = { @@ -1409,7 +1432,16 @@ return r; return 0; } -#endif + +int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr) +{ + return kvm_arch_on_sigbus_vcpu(env, code, addr); +} + +int kvm_on_sigbus(int code, void *addr) +{ + return kvm_arch_on_sigbus(code, addr); +} #undef PAGE_SIZE #include "qemu-kvm.c" diff -Nru qemu-kvm-0.14.1+noroms/kvm.h qemu-kvm-0.15.0+noroms/kvm.h --- qemu-kvm-0.14.1+noroms/kvm.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/kvm.h 2011-08-09 07:40:29.000000000 -0500 @@ -17,9 +17,6 @@ #include #include "config-host.h" #include "qemu-queue.h" -#ifdef NEED_CPU_H -#include "qemu-kvm.h" -#endif #ifdef CONFIG_KVM #include @@ -33,7 +30,6 @@ #define kvm_enabled() (0) #endif -#ifdef OBSOLETE_KVM_IMPL struct kvm_run; typedef struct KVMCapabilityInfo { @@ -47,7 +43,6 @@ /* external API */ int kvm_init(void); -#endif /* OBSOLETE_KVM_IMPL */ int kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); @@ -56,6 +51,7 @@ int kvm_has_xsave(void); int kvm_has_xcrs(void); int kvm_has_many_ioeventfds(void); +int kvm_has_pit_state2(void); #ifdef NEED_CPU_H int kvm_init_vcpu(CPUState *env); @@ -63,11 +59,6 @@ int kvm_cpu_exec(CPUState *env); #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); - -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); @@ -87,32 +78,33 @@ int kvm_pit_in_kernel(void); +int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr); +int kvm_on_sigbus(int code, void *addr); +#endif /* NEED_CPU_H */ + /* internal API */ struct KVMState; typedef struct KVMState KVMState; +extern KVMState *kvm_state; int kvm_ioctl(KVMState *s, int type, ...); int kvm_vm_ioctl(KVMState *s, int type, ...); +#ifdef NEED_CPU_H int kvm_vcpu_ioctl(CPUState *env, int type, ...); /* 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); +void kvm_arch_pre_run(CPUState *env, struct kvm_run *run); +void 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_process_async_events(CPUState *env); int kvm_arch_get_registers(CPUState *env); @@ -131,8 +123,8 @@ void kvm_arch_reset_vcpu(CPUState *env); -int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr); -int kvm_on_sigbus(int code, void *addr); +int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr); +int kvm_arch_on_sigbus(int code, void *addr); struct kvm_guest_debug; struct kvm_debug_exit_arch; @@ -146,8 +138,6 @@ QTAILQ_HEAD(kvm_sw_breakpoint_head, kvm_sw_breakpoint); -int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info); - struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, target_ulong pc); @@ -169,7 +159,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension); -uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, +uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, uint32_t index, int reg); void kvm_cpu_synchronize_state(CPUState *env); void kvm_cpu_synchronize_post_reset(CPUState *env); @@ -206,32 +196,33 @@ #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); +typedef struct KVMMsiMessage { + uint32_t gsi; + uint32_t addr_lo; + uint32_t addr_hi; + uint32_t data; +} KVMMsiMessage; + int kvm_has_gsi_routing(void); +int kvm_allows_irq0_override(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_msi_message_add(KVMMsiMessage *msg); +int kvm_msi_message_del(KVMMsiMessage *msg); +int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new); + int kvm_commit_irq_routes(void); int kvm_irqchip_in_kernel(void); int kvm_set_irq(int irq, int level, int *status); +#ifdef NEED_CPU_H +#include "qemu-kvm.h" +#endif + #endif diff -Nru qemu-kvm-0.14.1+noroms/kvm-stub.c qemu-kvm-0.15.0+noroms/kvm-stub.c --- qemu-kvm-0.14.1+noroms/kvm-stub.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/kvm-stub.c 2011-08-09 07:40:29.000000000 -0500 @@ -11,9 +11,8 @@ */ #include "qemu-common.h" -#include "sysemu.h" #include "hw/hw.h" -#include "exec-all.h" +#include "cpu.h" #include "gdbstub.h" #include "kvm.h" @@ -33,16 +32,6 @@ 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; @@ -53,11 +42,6 @@ return -ENOSYS; } -int kvm_check_extension(KVMState *s, unsigned int extension) -{ - return 0; -} - int kvm_init(void) { return -ENOSYS; @@ -89,17 +73,17 @@ return 0; } -int kvm_has_vcpu_events(void) +int kvm_has_many_ioeventfds(void) { return 0; } -int kvm_has_robust_singlestep(void) +int kvm_allows_irq0_override(void) { - return 0; + return 1; } -int kvm_has_many_ioeventfds(void) +int kvm_has_pit_state2(void) { return 0; } @@ -110,8 +94,7 @@ int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) { - tb_flush(env); - return 0; + return -ENOSYS; } int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, @@ -157,22 +140,17 @@ return -ENOSYS; } -int kvm_add_msix(uint32_t gsi, uint32_t addr_lo, - uint32_t addr_hi, uint32_t data) +int kvm_msi_message_add(KVMMsiMessage *msg) { return -ENOSYS; } -int kvm_del_msix(uint32_t gsi, uint32_t addr_lo, - uint32_t addr_hi, uint32_t data) +int kvm_msi_message_del(KVMMsiMessage *msg) { 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) +int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new) { return -ENOSYS; } @@ -187,7 +165,18 @@ assert(0); return -ENOSYS; } + +int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr) +{ + return 1; +} + int kvm_on_sigbus(int code, void *addr) { return 1; } + +int kvm_set_irqfd(int gsi, int fd, bool assigned) +{ + return -ENOSYS; +} diff -Nru qemu-kvm-0.14.1+noroms/KVM_VERSION qemu-kvm-0.15.0+noroms/KVM_VERSION --- qemu-kvm-0.14.1+noroms/KVM_VERSION 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/KVM_VERSION 2011-08-09 07:40:29.000000000 -0500 @@ -1 +1 @@ -qemu-kvm-0.14.1 +qemu-kvm-0.15.0 diff -Nru qemu-kvm-0.14.1+noroms/libcacard/cac.c qemu-kvm-0.15.0+noroms/libcacard/cac.c --- qemu-kvm-0.14.1+noroms/libcacard/cac.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/cac.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,403 @@ +/* + * implement the applets for the CAC card. + * + * This code is licensed under the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu-common.h" + +#include "cac.h" +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" + +#define CAC_GET_PROPERTIES 0x56 +#define CAC_GET_ACR 0x4c +#define CAC_READ_BUFFER 0x52 +#define CAC_UPDATE_BUFFER 0x58 +#define CAC_SIGN_DECRYPT 0x42 +#define CAC_GET_CERTIFICATE 0x36 + +/* private data for PKI applets */ +typedef struct CACPKIAppletDataStruct { + unsigned char *cert; + int cert_len; + unsigned char *cert_buffer; + int cert_buffer_len; + unsigned char *sign_buffer; + int sign_buffer_len; + VCardKey *key; +} CACPKIAppletData; + +/* + * CAC applet private data + */ +struct VCardAppletPrivateStruct { + union { + CACPKIAppletData pki_data; + void *reserved; + } u; +}; + +/* + * handle all the APDU's that are common to all CAC applets + */ +static VCardStatus +cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ + int ef; + + switch (apdu->a_ins) { + case VCARD7816_INS_SELECT_FILE: + if (apdu->a_p1 != 0x02) { + /* let the 7816 code handle applet switches */ + return VCARD_NEXT; + } + /* handle file id setting */ + if (apdu->a_Lc != 2) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_INVALID); + return VCARD_DONE; + } + /* CAC 1.0 only supports ef = 0 */ + ef = apdu->a_body[0] | (apdu->a_body[1] << 8); + if (ef != 0) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); + return VCARD_DONE; + } + *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); + return VCARD_DONE; + case VCARD7816_INS_GET_RESPONSE: + case VCARD7816_INS_VERIFY: + /* let the 7816 code handle these */ + return VCARD_NEXT; + case CAC_GET_PROPERTIES: + case CAC_GET_ACR: + /* skip these for now, this will probably be needed */ + *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + return VCARD_DONE; + } + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; +} + +/* + * reset the inter call state between applet selects + */ +static VCardStatus +cac_applet_pki_reset(VCard *card, int channel) +{ + VCardAppletPrivate *applet_private = NULL; + CACPKIAppletData *pki_applet = NULL; + applet_private = vcard_get_current_applet_private(card, channel); + assert(applet_private); + pki_applet = &(applet_private->u.pki_data); + + pki_applet->cert_buffer = NULL; + if (pki_applet->sign_buffer) { + qemu_free(pki_applet->sign_buffer); + pki_applet->sign_buffer = NULL; + } + pki_applet->cert_buffer_len = 0; + pki_applet->sign_buffer_len = 0; + return VCARD_DONE; +} + +static VCardStatus +cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + CACPKIAppletData *pki_applet = NULL; + VCardAppletPrivate *applet_private = NULL; + int size, next; + unsigned char *sign_buffer; + vcard_7816_status_t status; + + applet_private = vcard_get_current_applet_private(card, apdu->a_channel); + assert(applet_private); + pki_applet = &(applet_private->u.pki_data); + + switch (apdu->a_ins) { + case CAC_UPDATE_BUFFER: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); + return VCARD_DONE; + case CAC_GET_CERTIFICATE: + if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + assert(pki_applet->cert != NULL); + size = apdu->a_Le; + if (pki_applet->cert_buffer == NULL) { + pki_applet->cert_buffer = pki_applet->cert; + pki_applet->cert_buffer_len = pki_applet->cert_len; + } + size = MIN(size, pki_applet->cert_buffer_len); + next = MIN(255, pki_applet->cert_buffer_len - size); + *response = vcard_response_new_bytes( + card, pki_applet->cert_buffer, size, + apdu->a_Le, next ? + VCARD7816_SW1_WARNING_CHANGE : + VCARD7816_SW1_SUCCESS, + next); + pki_applet->cert_buffer += size; + pki_applet->cert_buffer_len -= size; + if ((*response == NULL) || (next == 0)) { + pki_applet->cert_buffer = NULL; + } + if (*response == NULL) { + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + return VCARD_DONE; + case CAC_SIGN_DECRYPT: + if (apdu->a_p2 != 0) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + size = apdu->a_Lc; + + sign_buffer = realloc(pki_applet->sign_buffer, + pki_applet->sign_buffer_len+size); + if (sign_buffer == NULL) { + qemu_free(pki_applet->sign_buffer); + pki_applet->sign_buffer = NULL; + pki_applet->sign_buffer_len = 0; + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + return VCARD_DONE; + } + memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size); + size += pki_applet->sign_buffer_len; + switch (apdu->a_p1) { + case 0x80: + /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for + * the rest */ + pki_applet->sign_buffer = sign_buffer; + pki_applet->sign_buffer_len = size; + *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); + return VCARD_DONE; + case 0x00: + /* we now have the whole buffer, do the operation, result will be + * in the sign_buffer */ + status = vcard_emul_rsa_op(card, pki_applet->key, + sign_buffer, size); + if (status != VCARD7816_STATUS_SUCCESS) { + *response = vcard_make_response(status); + break; + } + *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le, + VCARD7816_STATUS_SUCCESS); + if (*response == NULL) { + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + break; + default: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + break; + } + qemu_free(sign_buffer); + pki_applet->sign_buffer = NULL; + pki_applet->sign_buffer_len = 0; + return VCARD_DONE; + case CAC_READ_BUFFER: + /* new CAC call, go ahead and use the old version for now */ + /* TODO: implement */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + } + return cac_common_process_apdu(card, apdu, response); +} + + +static VCardStatus +cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + switch (apdu->a_ins) { + case CAC_UPDATE_BUFFER: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); + return VCARD_DONE; + case CAC_READ_BUFFER: + /* new CAC call, go ahead and use the old version for now */ + /* TODO: implement */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + } + return cac_common_process_apdu(card, apdu, response); +} + + +/* + * TODO: if we ever want to support general CAC middleware, we will need to + * implement the various containers. + */ +static VCardStatus +cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + switch (apdu->a_ins) { + case CAC_READ_BUFFER: + case CAC_UPDATE_BUFFER: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + default: + break; + } + return cac_common_process_apdu(card, apdu, response); +} + +/* + * utilities for creating and destroying the private applet data + */ +static void +cac_delete_pki_applet_private(VCardAppletPrivate *applet_private) +{ + CACPKIAppletData *pki_applet_data = NULL; + if (pki_applet_data == NULL) { + return; + } + pki_applet_data = &(applet_private->u.pki_data); + if (pki_applet_data->cert != NULL) { + qemu_free(pki_applet_data->cert); + } + if (pki_applet_data->sign_buffer != NULL) { + qemu_free(pki_applet_data->sign_buffer); + } + if (pki_applet_data->key != NULL) { + vcard_emul_delete_key(pki_applet_data->key); + } + qemu_free(applet_private); +} + +static VCardAppletPrivate * +cac_new_pki_applet_private(const unsigned char *cert, + int cert_len, VCardKey *key) +{ + CACPKIAppletData *pki_applet_data = NULL; + VCardAppletPrivate *applet_private = NULL; + applet_private = (VCardAppletPrivate *)qemu_malloc(sizeof(VCardAppletPrivate)); + + pki_applet_data = &(applet_private->u.pki_data); + pki_applet_data->cert_buffer = NULL; + pki_applet_data->cert_buffer_len = 0; + pki_applet_data->sign_buffer = NULL; + pki_applet_data->sign_buffer_len = 0; + pki_applet_data->key = NULL; + pki_applet_data->cert = (unsigned char *)qemu_malloc(cert_len+1); + /* + * if we want to support compression, then we simply change the 0 to a 1 + * and compress the cert data with libz + */ + pki_applet_data->cert[0] = 0; /* not compressed */ + memcpy(&pki_applet_data->cert[1], cert, cert_len); + pki_applet_data->cert_len = cert_len+1; + + pki_applet_data->key = key; + return applet_private; +} + + +/* + * create a new cac applet which links to a given cert + */ +static VCardApplet * +cac_new_pki_applet(int i, const unsigned char *cert, + int cert_len, VCardKey *key) +{ + VCardAppletPrivate *applet_private = NULL; + VCardApplet *applet = NULL; + unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; + int pki_aid_len = sizeof(pki_aid); + + pki_aid[pki_aid_len-1] = i; + + applet_private = cac_new_pki_applet_private(cert, cert_len, key); + if (applet_private == NULL) { + goto failure; + } + applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset, + pki_aid, pki_aid_len); + if (applet == NULL) { + goto failure; + } + vcard_set_applet_private(applet, applet_private, + cac_delete_pki_applet_private); + applet_private = NULL; + + return applet; + +failure: + if (applet_private != NULL) { + cac_delete_pki_applet_private(applet_private); + } + return NULL; +} + + +static unsigned char cac_default_container_aid[] = { + 0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 }; +static unsigned char cac_id_aid[] = { + 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 }; +/* + * Initialize the cac card. This is the only public function in this file. All + * the rest are connected through function pointers. + */ +VCardStatus +cac_card_init(VReader *reader, VCard *card, + const char *params, + unsigned char * const *cert, + int cert_len[], + VCardKey *key[] /* adopt the keys*/, + int cert_count) +{ + int i; + VCardApplet *applet; + + /* CAC Cards are VM Cards */ + vcard_set_type(card, VCARD_VM); + + /* create one PKI applet for each cert */ + for (i = 0; i < cert_count; i++) { + applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + } + + /* create a default blank container applet */ + applet = vcard_new_applet(cac_applet_container_process_apdu, + NULL, cac_default_container_aid, + sizeof(cac_default_container_aid)); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + + /* create a default blank container applet */ + applet = vcard_new_applet(cac_applet_id_process_apdu, + NULL, cac_id_aid, + sizeof(cac_id_aid)); + if (applet == NULL) { + goto failure; + } + vcard_add_applet(card, applet); + return VCARD_DONE; + +failure: + return VCARD_FAIL; +} + diff -Nru qemu-kvm-0.14.1+noroms/libcacard/cac.h qemu-kvm-0.15.0+noroms/libcacard/cac.h --- qemu-kvm-0.14.1+noroms/libcacard/cac.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/cac.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,23 @@ +/* + * defines the entry point for the cac card. Only used by cac.c anc + * vcard_emul_type.c + * + * 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 CAC_H +#define CAC_H 1 +#include "vcard.h" +#include "vreader.h" +/* + * Initialize the cac card. This is the only public function in this file. All + * the rest are connected through function pointers. + */ +VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params, + unsigned char * const *cert, int cert_len[], + VCardKey *key[] /* adopt the keys*/, + int cert_count); + +/* not yet implemented */ +VCardStatus cac_is_cac_card(VReader *reader); +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/card_7816.c qemu-kvm-0.15.0+noroms/libcacard/card_7816.c --- qemu-kvm-0.14.1+noroms/libcacard/card_7816.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/card_7816.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,763 @@ +/* + * Implement the 7816 portion of the card spec + * + * This code is licensed under the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu-common.h" + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" + +/* + * set the status bytes based on the status word + */ +static void +vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status) +{ + unsigned char sw1, sw2; + response->b_status = status; /* make sure the status and swX representations + * are consistent */ + sw1 = (status >> 8) & 0xff; + sw2 = status & 0xff; + response->b_sw1 = sw1; + response->b_sw2 = sw2; + response->b_data[response->b_len] = sw1; + response->b_data[response->b_len+1] = sw2; +} + +/* + * set the status bytes in a response buffer + */ +static void +vcard_response_set_status_bytes(VCardResponse *response, + unsigned char sw1, unsigned char sw2) +{ + response->b_status = sw1 << 8 | sw2; + response->b_sw1 = sw1; + response->b_sw2 = sw2; + response->b_data[response->b_len] = sw1; + response->b_data[response->b_len+1] = sw2; +} + +/* + * allocate a VCardResponse structure, plus space for the data buffer, and + * set up everything but the resonse bytes. + */ +VCardResponse * +vcard_response_new_data(unsigned char *buf, int len) +{ + VCardResponse *new_response; + + new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse)); + new_response->b_data = qemu_malloc(len + 2); + memcpy(new_response->b_data, buf, len); + new_response->b_total_len = len+2; + new_response->b_len = len; + new_response->b_type = VCARD_MALLOC; + return new_response; +} + +static VCardResponse * +vcard_init_buffer_response(VCard *card, unsigned char *buf, int len) +{ + VCardResponse *response; + VCardBufferResponse *buffer_response; + + buffer_response = vcard_get_buffer_response(card); + if (buffer_response) { + vcard_set_buffer_response(card, NULL); + vcard_buffer_response_delete(buffer_response); + } + buffer_response = vcard_buffer_response_new(buf, len); + if (buffer_response == NULL) { + return NULL; + } + response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES, + len > 255 ? 0 : len); + if (response == NULL) { + return NULL; + } + vcard_set_buffer_response(card, buffer_response); + return response; +} + +/* + * general buffer to hold results from APDU calls + */ +VCardResponse * +vcard_response_new(VCard *card, unsigned char *buf, + int len, int Le, vcard_7816_status_t status) +{ + VCardResponse *new_response; + + if (len > Le) { + return vcard_init_buffer_response(card, buf, len); + } + new_response = vcard_response_new_data(buf, len); + if (new_response == NULL) { + return NULL; + } + vcard_response_set_status(new_response, status); + return new_response; +} + +/* + * general buffer to hold results from APDU calls + */ +VCardResponse * +vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le, + unsigned char sw1, unsigned char sw2) +{ + VCardResponse *new_response; + + if (len > Le) { + return vcard_init_buffer_response(card, buf, len); + } + new_response = vcard_response_new_data(buf, len); + if (new_response == NULL) { + return NULL; + } + vcard_response_set_status_bytes(new_response, sw1, sw2); + return new_response; +} + +/* + * get a new Reponse buffer that only has a status. + */ +static VCardResponse * +vcard_response_new_status(vcard_7816_status_t status) +{ + VCardResponse *new_response; + + new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse)); + new_response->b_data = &new_response->b_sw1; + new_response->b_len = 0; + new_response->b_total_len = 2; + new_response->b_type = VCARD_MALLOC_STRUCT; + vcard_response_set_status(new_response, status); + return new_response; +} + +/* + * same as above, but specify the status as separate bytes + */ +VCardResponse * +vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2) +{ + VCardResponse *new_response; + + new_response = (VCardResponse *)qemu_malloc(sizeof(VCardResponse)); + new_response->b_data = &new_response->b_sw1; + new_response->b_len = 0; + new_response->b_total_len = 2; + new_response->b_type = VCARD_MALLOC_STRUCT; + vcard_response_set_status_bytes(new_response, sw1, sw2); + return new_response; +} + + +/* + * free the response buffer. The Buffer has a type to handle the buffer + * allocated in other ways than through malloc. + */ +void +vcard_response_delete(VCardResponse *response) +{ + if (response == NULL) { + return; + } + switch (response->b_type) { + case VCARD_MALLOC: + /* everything was malloc'ed */ + if (response->b_data) { + qemu_free(response->b_data); + } + qemu_free(response); + break; + case VCARD_MALLOC_DATA: + /* only the data buffer was malloc'ed */ + if (response->b_data) { + qemu_free(response->b_data); + } + break; + case VCARD_MALLOC_STRUCT: + /* only the structure was malloc'ed */ + qemu_free(response); + break; + case VCARD_STATIC: + break; + } +} + +/* + * decode the class bit and set our generic type field, channel, and + * secure messaging values. + */ +static vcard_7816_status_t +vcard_apdu_set_class(VCardAPDU *apdu) { + apdu->a_channel = 0; + apdu->a_secure_messaging = 0; + apdu->a_type = apdu->a_cla & 0xf0; + apdu->a_gen_type = VCARD_7816_ISO; + + /* parse the class tables 8 & 9 of the 7816-4 Part 4 spec */ + switch (apdu->a_type) { + /* we only support the basic types */ + case 0x00: + case 0x80: + case 0x90: + case 0xa0: + apdu->a_channel = apdu->a_cla & 3; + apdu->a_secure_messaging = apdu->a_cla & 0xe; + break; + case 0xb0: + case 0xc0: + break; + + case 0x10: + case 0x20: + case 0x30: + case 0x40: + case 0x50: + case 0x60: + case 0x70: + /* Reserved for future use */ + apdu->a_gen_type = VCARD_7816_RFU; + break; + case 0xd0: + case 0xe0: + case 0xf0: + default: + apdu->a_gen_type = + (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPIETARY; + break; + } + return VCARD7816_STATUS_SUCCESS; +} + +/* + * set the Le and Lc fiels according to table 5 of the + * 7816-4 part 4 spec + */ +static vcard_7816_status_t +vcard_apdu_set_length(VCardAPDU *apdu) +{ + int L, Le; + + /* process according to table 5 of the 7816-4 Part 4 spec. + * variable names match the variables in the spec */ + L = apdu->a_len-4; /* fixed APDU header */ + apdu->a_Lc = 0; + apdu->a_Le = 0; + apdu->a_body = NULL; + switch (L) { + case 0: + /* 1 minimal apdu */ + return VCARD7816_STATUS_SUCCESS; + case 1: + /* 2S only return values apdu */ + /* zero maps to 256 here */ + apdu->a_Le = apdu->a_header->ah_Le ? + apdu->a_header->ah_Le : 256; + return VCARD7816_STATUS_SUCCESS; + default: + /* if the ah_Le byte is zero and we have more than + * 1 byte in the header, then we must be using extended Le and Lc. + * process the extended now. */ + if (apdu->a_header->ah_Le == 0) { + if (L < 3) { + /* coding error, need at least 3 bytes */ + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; + } + /* calculate the first extended value. Could be either Le or Lc */ + Le = (apdu->a_header->ah_body[0] << 8) + || apdu->a_header->ah_body[1]; + if (L == 3) { + /* 2E extended, return data only */ + /* zero maps to 65536 */ + apdu->a_Le = Le ? Le : 65536; + return VCARD7816_STATUS_SUCCESS; + } + if (Le == 0) { + /* reserved for future use, probably for next time we need + * to extend the lengths */ + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; + } + /* we know that the first extended value is Lc now */ + apdu->a_Lc = Le; + apdu->a_body = &apdu->a_header->ah_body[2]; + if (L == Le+3) { + /* 3E extended, only body parameters */ + return VCARD7816_STATUS_SUCCESS; + } + if (L == Le+5) { + /* 4E extended, parameters and return data */ + Le = (apdu->a_data[apdu->a_len-2] << 8) + || apdu->a_data[apdu->a_len-1]; + apdu->a_Le = Le ? Le : 65536; + return VCARD7816_STATUS_SUCCESS; + } + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; + } + /* not extended */ + apdu->a_Lc = apdu->a_header->ah_Le; + apdu->a_body = &apdu->a_header->ah_body[0]; + if (L == apdu->a_Lc + 1) { + /* 3S only body parameters */ + return VCARD7816_STATUS_SUCCESS; + } + if (L == apdu->a_Lc + 2) { + /* 4S parameters and return data */ + Le = apdu->a_data[apdu->a_len-1]; + apdu->a_Le = Le ? Le : 256; + return VCARD7816_STATUS_SUCCESS; + } + break; + } + return VCARD7816_STATUS_ERROR_WRONG_LENGTH; +} + +/* + * create a new APDU from a raw set of bytes. This will decode all the + * above fields. users of VCARDAPDU's can then depend on the already decoded + * values. + */ +VCardAPDU * +vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status) +{ + VCardAPDU *new_apdu; + + *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; + if (len < 4) { + *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH; + return NULL; + } + + new_apdu = (VCardAPDU *)qemu_malloc(sizeof(VCardAPDU)); + new_apdu->a_data = qemu_malloc(len); + memcpy(new_apdu->a_data, raw_apdu, len); + new_apdu->a_len = len; + *status = vcard_apdu_set_class(new_apdu); + if (*status != VCARD7816_STATUS_SUCCESS) { + qemu_free(new_apdu); + return NULL; + } + *status = vcard_apdu_set_length(new_apdu); + if (*status != VCARD7816_STATUS_SUCCESS) { + qemu_free(new_apdu); + new_apdu = NULL; + } + return new_apdu; +} + +void +vcard_apdu_delete(VCardAPDU *apdu) +{ + if (apdu == NULL) { + return; + } + if (apdu->a_data) { + qemu_free(apdu->a_data); + } + qemu_free(apdu); +} + + +/* + * declare response buffers for all the 7816 defined error codes + */ +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS( + VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS) +VCARD_RESPONSE_NEW_STATIC_STATUS( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL) + +/* + * return a single response code. This function cannot fail. It will always + * return a response. + */ +VCardResponse * +vcard_make_response(vcard_7816_status_t status) +{ + VCardResponse *response = NULL; + + switch (status) { + /* known 7816 response codes */ + case VCARD7816_STATUS_SUCCESS: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_SUCCESS); + case VCARD7816_STATUS_WARNING: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING); + case VCARD7816_STATUS_WARNING_RET_CORUPT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_RET_CORUPT); + case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE); + case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED); + case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID); + case VCARD7816_STATUS_WARNING_CHANGE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_CHANGE); + case VCARD7816_STATUS_WARNING_FILE_FILLED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_WARNING_FILE_FILLED); + case VCARD7816_STATUS_EXC_ERROR: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR); + case VCARD7816_STATUS_EXC_ERROR_CHANGE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR_CHANGE); + case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + case VCARD7816_STATUS_ERROR_WRONG_LENGTH: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_LENGTH); + case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE); + case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED); + case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED); + case VCARD7816_STATUS_ERROR_DATA_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_DATA_INVALID); + case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); + case VCARD7816_STATUS_ERROR_DATA_NO_EF: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_DATA_NO_EF); + case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING); + case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT); + case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); + case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA); + case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); + case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); + case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND); + case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE); + case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT); + case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); + case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT); + case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2); + case VCARD7816_STATUS_ERROR_INS_CODE_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_INS_CODE_INVALID); + case VCARD7816_STATUS_ERROR_CLA_INVALID: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_CLA_INVALID); + case VCARD7816_STATUS_ERROR_GENERAL: + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_ERROR_GENERAL); + default: + /* we don't know this status code, create a response buffer to + * hold it */ + response = vcard_response_new_status(status); + if (response == NULL) { + /* couldn't allocate the buffer, return memmory error */ + return VCARD_RESPONSE_GET_STATIC( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + } + assert(response); + return response; +} + +/* + * Add File card support here if you need it. + */ +static VCardStatus +vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + /* TODO: if we want to support a virtual file system card, we do it here. + * It would probably be a pkcs #15 card type */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; +} + +/* + * VM card (including java cards) + */ +static VCardStatus +vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + int bytes_to_copy, next_byte_count, count; + VCardApplet *current_applet; + VCardBufferResponse *buffer_response; + vcard_7816_status_t status; + + /* parse the class first */ + if (apdu->a_gen_type != VCARD_7816_ISO) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; + } + + /* use a switch so that if we need to support secure channel stuff later, + * we know where to put it */ + switch (apdu->a_secure_messaging) { + case 0x0: /* no SM */ + break; + case 0x4: /* proprietary SM */ + case 0x8: /* header not authenticated */ + case 0xc: /* header authenticated */ + default: + /* for now, don't try to support secure channel stuff in the + * virtual card. */ + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); + return VCARD_DONE; + } + + /* now parse the instruction */ + switch (apdu->a_ins) { + case VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */ + case VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */ + case VCARD7816_INS_GET_CHALLENGE: /* secure channel op */ + case VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */ + case VCARD7816_INS_ERASE_BINARY: /* applet control op */ + case VCARD7816_INS_READ_BINARY: /* applet control op */ + case VCARD7816_INS_WRITE_BINARY: /* applet control op */ + case VCARD7816_INS_UPDATE_BINARY: /* applet control op */ + case VCARD7816_INS_READ_RECORD: /* file op */ + case VCARD7816_INS_WRITE_RECORD: /* file op */ + case VCARD7816_INS_UPDATE_RECORD: /* file op */ + case VCARD7816_INS_APPEND_RECORD: /* file op */ + case VCARD7816_INS_ENVELOPE: + case VCARD7816_INS_PUT_DATA: + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + break; + + case VCARD7816_INS_SELECT_FILE: + if (apdu->a_p1 != 0x04) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); + break; + } + + /* side effect, deselect the current applet if no applet has been found + * */ + current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc); + vcard_select_applet(card, apdu->a_channel, current_applet); + if (current_applet) { + unsigned char *aid; + int aid_len; + aid = vcard_applet_get_aid(current_applet, &aid_len); + *response = vcard_response_new(card, aid, aid_len, apdu->a_Le, + VCARD7816_STATUS_SUCCESS); + } else { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); + } + break; + + case VCARD7816_INS_VERIFY: + if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); + } else { + if (apdu->a_Lc == 0) { + /* handle pin count if possible */ + count = vcard_emul_get_login_count(card); + if (count < 0) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + } else { + if (count > 0xf) { + count = 0xf; + } + *response = vcard_response_new_status_bytes( + VCARD7816_SW1_WARNING_CHANGE, + 0xc0 | count); + if (*response == NULL) { + *response = vcard_make_response( + VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + } + } else { + status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc); + *response = vcard_make_response(status); + } + } + break; + + case VCARD7816_INS_GET_RESPONSE: + buffer_response = vcard_get_buffer_response(card); + if (!buffer_response) { + *response = vcard_make_response( + VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); + /* handle error */ + break; + } + bytes_to_copy = MIN(buffer_response->len, apdu->a_Le); + next_byte_count = MIN(256, buffer_response->len - bytes_to_copy); + *response = vcard_response_new_bytes( + card, buffer_response->current, bytes_to_copy, + apdu->a_Le, + next_byte_count ? + VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS, + next_byte_count); + buffer_response->current += bytes_to_copy; + buffer_response->len -= bytes_to_copy; + if (*response == NULL || (next_byte_count == 0)) { + vcard_set_buffer_response(card, NULL); + vcard_buffer_response_delete(buffer_response); + } + if (*response == NULL) { + *response = + vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); + } + break; + + case VCARD7816_INS_GET_DATA: + *response = + vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + break; + + default: + *response = + vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + break; + } + + /* response should have been set somewhere */ + assert(*response != NULL); + return VCARD_DONE; +} + + +/* + * APDU processing starts here. This routes the card processing stuff to the + * right location. + */ +VCardStatus +vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ + VCardStatus status; + VCardBufferResponse *buffer_response; + + /* first handle any PTS commands, which aren't really APDU's */ + if (apdu->a_type == VCARD_7816_PTS) { + /* the PTS responses aren't really responses either */ + *response = vcard_response_new_data(apdu->a_data, apdu->a_len); + /* PTS responses have no status bytes */ + (*response)->b_total_len = (*response)->b_len; + return VCARD_DONE; + } + buffer_response = vcard_get_buffer_response(card); + if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) { + /* clear out buffer_response, return an error */ + vcard_set_buffer_response(card, NULL); + vcard_buffer_response_delete(buffer_response); + *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR); + return VCARD_DONE; + } + + status = vcard_process_applet_apdu(card, apdu, response); + if (status != VCARD_NEXT) { + return status; + } + switch (vcard_get_type(card)) { + case VCARD_FILE_SYSTEM: + return vcard7816_file_system_process_apdu(card, apdu, response); + case VCARD_VM: + return vcard7816_vm_process_apdu(card, apdu, response); + case VCARD_DIRECT: + /* if we are type direct, then the applet should handle everything */ + assert("VCARD_DIRECT: applet failure"); + break; + } + *response = + vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); + return VCARD_DONE; +} diff -Nru qemu-kvm-0.14.1+noroms/libcacard/card_7816.h qemu-kvm-0.15.0+noroms/libcacard/card_7816.h --- qemu-kvm-0.14.1+noroms/libcacard/card_7816.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/card_7816.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,62 @@ +/* + * Implement the 7816 portion of the card spec + * + * 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 CARD_7816_H +#define CARD_7816_H 1 + +#include "card_7816t.h" +#include "vcardt.h" + +/* + * constructors for VCardResponse's + */ +/* response from a return buffer and a status */ +VCardResponse *vcard_response_new(VCard *card, unsigned char *buf, int len, + int Le, vcard_7816_status_t status); +/* response from a return buffer and status bytes */ +VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf, + int len, int Le, + unsigned char sw1, unsigned char sw2); +/* response from just status bytes */ +VCardResponse *vcard_response_new_status_bytes(unsigned char sw1, + unsigned char sw2); +/* response from just status: NOTE this cannot fail, it will alwyas return a + * valid response, if it can't allocate memory, the response will be + * VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */ +VCardResponse *vcard_make_response(vcard_7816_status_t status); + +/* create a raw response (status has already been encoded */ +VCardResponse *vcard_response_new_data(unsigned char *buf, int len); + + + + +/* + * destructor for VCardResponse. + * Can be called with a NULL response + */ +void vcard_response_delete(VCardResponse *response); + +/* + * constructor for VCardAPDU + */ +VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len, + unsigned short *status); + +/* + * destructor for VCardAPDU + * Can be called with a NULL apdu + */ +void vcard_apdu_delete(VCardAPDU *apdu); + +/* + * APDU processing starts here. This routes the card processing stuff to the + * right location. Always returns a valid response. + */ +VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/card_7816t.h qemu-kvm-0.15.0+noroms/libcacard/card_7816t.h --- qemu-kvm-0.14.1+noroms/libcacard/card_7816t.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/card_7816t.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,165 @@ +/* + * Implement the 7816 portion of the card spec + * + * 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 CARD_7816T_H +#define CARD_7816T_H 1 + +typedef unsigned short vcard_7816_status_t; + +struct VCardResponseStruct { + unsigned char *b_data; + vcard_7816_status_t b_status; + unsigned char b_sw1; + unsigned char b_sw2; + int b_len; + int b_total_len; + enum VCardResponseBufferType { + VCARD_MALLOC, + VCARD_MALLOC_DATA, + VCARD_MALLOC_STRUCT, + VCARD_STATIC + } b_type; +}; + +#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \ +static const VCardResponse VCardResponse##stat = \ + {(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \ + ((stat) & 0xff), 0, 2, VCARD_STATIC}; + +#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \ +static const VCardResponse VCARDResponse##sw1 = \ + {(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \ + (sw1), (sw2), 0, 2, VCARD_STATIC}; + +/* cast away the const, callers need may need to 'free' the + * result, and const implies that they don't */ +#define VCARD_RESPONSE_GET_STATIC(name) \ + ((VCardResponse *)(&VCardResponse##name)) + +typedef enum { + VCARD_7816_ISO, + VCARD_7816_RFU, + VCARD_7816_PTS, + VCARD_7816_PROPIETARY +} VCardAPDUType; + + +/* + * 7816 header. All APDU's have this header. + * They must be laid out in this order. + */ +struct VCardAPDUHeader { + unsigned char ah_cla; + unsigned char ah_ins; + unsigned char ah_p1; + unsigned char ah_p2; + unsigned char ah_Le; + unsigned char ah_body[1]; /* indefinate length */ +}; + +/* + * 7816 APDU structure. The raw bytes are stored in the union and can be + * accessed directly through u.data (which is aliased as a_data). + * + * Names of the fields match the 7816 documentation. + */ +struct VCardAPDUStruct { + int a_len; /* length of the whole buffer, including header */ + int a_Lc; /* 7816 Lc (parameter length) value */ + int a_Le; /* 7816 Le (expected result length) value */ + unsigned char *a_body; /* pointer to the parameter */ + int a_channel; /* decoded channel */ + int a_secure_messaging; /* decoded secure messaging type */ + int a_type; /* decoded type from cla (top nibble of class) */ + VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */ + union { + struct VCardAPDUHeader *header; + unsigned char *data; + } u; +/* give the subfields a unified look */ +#define a_header u.header +#define a_data u.data +#define a_cla a_header->ah_cla /* class */ +#define a_ins a_header->ah_ins /* instruction */ +#define a_p1 a_header->ah_p1 /* parameter 1 */ +#define a_p2 a_header->ah_p2 /* parameter 2 */ +}; + +/* 7816 status codes */ +#define VCARD7816_STATUS_SUCCESS 0x9000 +#define VCARD7816_STATUS_WARNING 0x6200 +#define VCARD7816_STATUS_WARNING_RET_CORUPT 0x6281 +#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE 0x6282 +#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED 0x6283 +#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID 0x6284 +#define VCARD7816_STATUS_WARNING_CHANGE 0x6300 +#define VCARD7816_STATUS_WARNING_FILE_FILLED 0x6381 +#define VCARD7816_STATUS_EXC_ERROR 0x6400 +#define VCARD7816_STATUS_EXC_ERROR_CHANGE 0x6500 +#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE 0x6581 +#define VCARD7816_STATUS_ERROR_WRONG_LENGTH 0x6700 +#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED 0x6800 +#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED 0x6881 +#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED 0x6882 +#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED 0x6900 +#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981 +#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED 0x6982 +#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED 0x6983 +#define VCARD7816_STATUS_ERROR_DATA_INVALID 0x6984 +#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED 0x6985 +#define VCARD7816_STATUS_ERROR_DATA_NO_EF 0x6986 +#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING 0x6987 +#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT 0x6988 +#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS 0x6a00 +#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA 0x6a80 +#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED 0x6a81 +#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND 0x6a82 +#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND 0x6a83 +#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE 0x6a84 +#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT 0x6a85 +#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT 0x6a86 +#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT 0x6a87 +#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND 0x6a88 +#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2 0x6b00 +#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID 0x6d00 +#define VCARD7816_STATUS_ERROR_CLA_INVALID 0x6e00 +#define VCARD7816_STATUS_ERROR_GENERAL 0x6f00 +/* 7816 sw1 codes */ +#define VCARD7816_SW1_SUCCESS 0x90 +#define VCARD7816_SW1_RESPONSE_BYTES 0x61 +#define VCARD7816_SW1_WARNING 0x62 +#define VCARD7816_SW1_WARNING_CHANGE 0x63 +#define VCARD7816_SW1_EXC_ERROR 0x64 +#define VCARD7816_SW1_EXC_ERROR_CHANGE 0x65 +#define VCARD7816_SW1_ERROR_WRONG_LENGTH 0x67 +#define VCARD7816_SW1_CLA_ERROR 0x68 +#define VCARD7816_SW1_COMMAND_ERROR 0x69 +#define VCARD7816_SW1_P1_P2_ERROR 0x6a +#define VCARD7816_SW1_LE_ERROR 0x6c +#define VCARD7816_SW1_INS_ERROR 0x6d +#define VCARD7816_SW1_CLA_NOT_SUPPORTED 0x6e + +/* 7816 Instructions */ +#define VCARD7816_INS_MANAGE_CHANNEL 0x70 +#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82 +#define VCARD7816_INS_GET_CHALLENGE 0x84 +#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88 +#define VCARD7816_INS_ERASE_BINARY 0x0e +#define VCARD7816_INS_READ_BINARY 0xb0 +#define VCARD7816_INS_WRITE_BINARY 0xd0 +#define VCARD7816_INS_UPDATE_BINARY 0xd6 +#define VCARD7816_INS_READ_RECORD 0xb2 +#define VCARD7816_INS_WRITE_RECORD 0xd2 +#define VCARD7816_INS_UPDATE_RECORD 0xdc +#define VCARD7816_INS_APPEND_RECORD 0xe2 +#define VCARD7816_INS_ENVELOPE 0xc2 +#define VCARD7816_INS_PUT_DATA 0xda +#define VCARD7816_INS_GET_DATA 0xca +#define VCARD7816_INS_SELECT_FILE 0xa4 +#define VCARD7816_INS_VERIFY 0x20 +#define VCARD7816_INS_GET_RESPONSE 0xc0 + +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/event.c qemu-kvm-0.15.0+noroms/libcacard/event.c --- qemu-kvm-0.14.1+noroms/libcacard/event.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/event.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,106 @@ +/* + * event queue implementation. + * + * This code is licensed under the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu-common.h" +#include "qemu-thread.h" + +#include "vcard.h" +#include "vreader.h" +#include "vevent.h" + +VEvent * +vevent_new(VEventType type, VReader *reader, VCard *card) +{ + VEvent *new_vevent; + + new_vevent = (VEvent *)qemu_malloc(sizeof(VEvent)); + new_vevent->next = NULL; + new_vevent->type = type; + new_vevent->reader = vreader_reference(reader); + new_vevent->card = vcard_reference(card); + + return new_vevent; +} + +void +vevent_delete(VEvent *vevent) +{ + if (vevent == NULL) { + return; + } + vreader_free(vevent->reader); + vcard_free(vevent->card); + qemu_free(vevent); +} + +/* + * VEvent queue management + */ + +static VEvent *vevent_queue_head; +static VEvent *vevent_queue_tail; +static QemuMutex vevent_queue_lock; +static QemuCond vevent_queue_condition; + +void vevent_queue_init(void) +{ + qemu_mutex_init(&vevent_queue_lock); + qemu_cond_init(&vevent_queue_condition); + vevent_queue_head = vevent_queue_tail = NULL; +} + +void +vevent_queue_vevent(VEvent *vevent) +{ + vevent->next = NULL; + qemu_mutex_lock(&vevent_queue_lock); + if (vevent_queue_head) { + assert(vevent_queue_tail); + vevent_queue_tail->next = vevent; + } else { + vevent_queue_head = vevent; + } + vevent_queue_tail = vevent; + qemu_cond_signal(&vevent_queue_condition); + qemu_mutex_unlock(&vevent_queue_lock); +} + +/* must have lock */ +static VEvent * +vevent_dequeue_vevent(void) +{ + VEvent *vevent = NULL; + if (vevent_queue_head) { + vevent = vevent_queue_head; + vevent_queue_head = vevent->next; + vevent->next = NULL; + } + return vevent; +} + +VEvent *vevent_wait_next_vevent(void) +{ + VEvent *vevent; + + qemu_mutex_lock(&vevent_queue_lock); + while ((vevent = vevent_dequeue_vevent()) == NULL) { + qemu_cond_wait(&vevent_queue_condition, &vevent_queue_lock); + } + qemu_mutex_unlock(&vevent_queue_lock); + return vevent; +} + +VEvent *vevent_get_next_vevent(void) +{ + VEvent *vevent; + + qemu_mutex_lock(&vevent_queue_lock); + vevent = vevent_dequeue_vevent(); + qemu_mutex_unlock(&vevent_queue_lock); + return vevent; +} + diff -Nru qemu-kvm-0.14.1+noroms/libcacard/eventt.h qemu-kvm-0.15.0+noroms/libcacard/eventt.h --- qemu-kvm-0.14.1+noroms/libcacard/eventt.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/eventt.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,29 @@ +/* + * 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 EVENTT_H +#define EVENTT_H 1 +#include "vreadert.h" +#include "vcardt.h" + +typedef struct VEventStruct VEvent; + +typedef enum { + VEVENT_READER_INSERT, + VEVENT_READER_REMOVE, + VEVENT_CARD_INSERT, + VEVENT_CARD_REMOVE, + VEVENT_LAST, +} VEventType; + +struct VEventStruct { + VEvent *next; + VEventType type; + VReader *reader; + VCard *card; +}; +#endif + + diff -Nru qemu-kvm-0.14.1+noroms/libcacard/link_test.c qemu-kvm-0.15.0+noroms/libcacard/link_test.c --- qemu-kvm-0.14.1+noroms/libcacard/link_test.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/link_test.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,22 @@ +/* + * 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 +#include "vcard.h" + +VCardStatus cac_card_init(const char *flags, VCard *card, + const unsigned char *cert[], + int cert_len[], VCardKey *key[] /* adopt the keys*/, + int cert_count); +/* + * this will crash... just test the linkage right now + */ + +main(int argc, char **argv) +{ + VCard *card; /* no constructor yet */ + cac_card_init("", card, NULL, 0, NULL, 0); +} + diff -Nru qemu-kvm-0.14.1+noroms/libcacard/Makefile qemu-kvm-0.15.0+noroms/libcacard/Makefile --- qemu-kvm-0.14.1+noroms/libcacard/Makefile 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/Makefile 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,42 @@ +-include ../config-host.mak +-include $(SRC_PATH)/Makefile.objs +-include $(SRC_PATH)/rules.mak + +$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/libcacard) + +# objects linked against normal qemu binaries, not compiled with libtool +QEMU_OBJS=$(addprefix ../,$(oslib-obj-y) qemu-malloc.o qemu-timer-common.o $(trace-obj-y)) + +# objects linked into a shared library, built with libtool with -fPIC if required +QEMU_OBJS_LIB=$(addsuffix .lo,$(basename $(QEMU_OBJS))) + +QEMU_CFLAGS+=-I../ + +libcacard.lib-y=$(addsuffix .lo,$(basename $(libcacard-y))) + +vscclient: $(libcacard-y) $(QEMU_OBJS) vscclient.o + $(call quiet-command,$(CC) $(libcacard_libs) -lrt -o $@ $^," LINK $@") + +clean: + rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient *.lo .libs/* *.la + rm -Rf .libs + +all: vscclient + +######################################################################### +# Rules for building libcacard standalone library + +ifeq ($(LIBTOOL),) +libcacard.la: + @echo "libtool is missing, please install and rerun configure"; exit 1 + +install-libcacard: + @echo "libtool is missing, please install and rerun configure"; exit 1 +else +libcacard.la: $(libcacard.lib-y) $(QEMU_OBJS_LIB) + $(call quiet-command,libtool --mode=link --quiet --tag=CC $(CC) $(libcacard_libs) -lrt -rpath $(libdir) -o $@ $^," lt LINK $@") + +install-libcacard: libcacard.la + $(INSTALL_DIR) "$(DESTDIR)$(libdir)" + libtool --mode=install $(INSTALL_PROG) libcacard.la "$(DESTDIR)$(libdir)" +endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vcard.c qemu-kvm-0.15.0+noroms/libcacard/vcard.c --- qemu-kvm-0.14.1+noroms/libcacard/vcard.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vcard.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,339 @@ +/* + * implement the Java card standard. + * + * 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 "qemu-common.h" + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816t.h" + +struct VCardAppletStruct { + VCardApplet *next; + VCardProcessAPDU process_apdu; + VCardResetApplet reset_applet; + unsigned char *aid; + int aid_len; + void *applet_private; + VCardAppletPrivateFree applet_private_free; +}; + +struct VCardStruct { + int reference_count; + VCardApplet *applet_list; + VCardApplet *current_applet[MAX_CHANNEL]; + VCardBufferResponse *vcard_buffer_response; + VCardType type; + VCardEmul *vcard_private; + VCardEmulFree vcard_private_free; + VCardGetAtr vcard_get_atr; +}; + +VCardBufferResponse * +vcard_buffer_response_new(unsigned char *buffer, int size) +{ + VCardBufferResponse *new_buffer; + + new_buffer = (VCardBufferResponse *)qemu_malloc(sizeof(VCardBufferResponse)); + new_buffer->buffer = (unsigned char *)qemu_malloc(size); + memcpy(new_buffer->buffer, buffer, size); + new_buffer->buffer_len = size; + new_buffer->current = new_buffer->buffer; + new_buffer->len = size; + return new_buffer; +} + +void +vcard_buffer_response_delete(VCardBufferResponse *buffer_response) +{ + if (buffer_response == NULL) { + return; + } + if (buffer_response->buffer) { + qemu_free(buffer_response->buffer); + } + qemu_free(buffer_response); +} + + +/* + * clean up state after a reset + */ +void +vcard_reset(VCard *card, VCardPower power) +{ + int i; + VCardApplet *applet = NULL; + + if (card->type == VCARD_DIRECT) { + /* select the last applet */ + VCardApplet *current_applet = NULL; + for (current_applet = card->applet_list; current_applet; + current_applet = current_applet->next) { + applet = current_applet; + } + } + for (i = 0; i < MAX_CHANNEL; i++) { + card->current_applet[i] = applet; + } + if (card->vcard_buffer_response) { + vcard_buffer_response_delete(card->vcard_buffer_response); + card->vcard_buffer_response = NULL; + } + vcard_emul_reset(card, power); + if (applet) { + applet->reset_applet(card, 0); + } +} + +/* applet utilities */ + +/* + * applet utilities + */ +/* constructor */ +VCardApplet * +vcard_new_applet(VCardProcessAPDU applet_process_function, + VCardResetApplet applet_reset_function, + unsigned char *aid, int aid_len) +{ + VCardApplet *applet; + + applet = (VCardApplet *)qemu_malloc(sizeof(VCardApplet)); + applet->next = NULL; + applet->applet_private = NULL; + applet->applet_private_free = NULL; + applet->process_apdu = applet_process_function; + applet->reset_applet = applet_reset_function; + + applet->aid = qemu_malloc(aid_len); + memcpy(applet->aid, aid, aid_len); + applet->aid_len = aid_len; + return applet; +} + +/* destructor */ +void +vcard_delete_applet(VCardApplet *applet) +{ + if (applet == NULL) { + return; + } + if (applet->applet_private_free) { + applet->applet_private_free(applet->applet_private); + applet->applet_private = NULL; + } + if (applet->aid) { + qemu_free(applet->aid); + applet->aid = NULL; + } + qemu_free(applet); +} + +/* accessor */ +void +vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private, + VCardAppletPrivateFree private_free) +{ + if (applet->applet_private_free) { + applet->applet_private_free(applet->applet_private); + } + applet->applet_private = private; + applet->applet_private_free = private_free; +} + +VCard * +vcard_new(VCardEmul *private, VCardEmulFree private_free) +{ + VCard *new_card; + int i; + + new_card = (VCard *)qemu_malloc(sizeof(VCard)); + new_card->applet_list = NULL; + for (i = 0; i < MAX_CHANNEL; i++) { + new_card->current_applet[i] = NULL; + } + new_card->vcard_buffer_response = NULL; + new_card->type = VCARD_VM; + new_card->vcard_private = private; + new_card->vcard_private_free = private_free; + new_card->vcard_get_atr = NULL; + new_card->reference_count = 1; + return new_card; +} + +VCard * +vcard_reference(VCard *vcard) +{ + if (vcard == NULL) { + return NULL; + } + vcard->reference_count++; + return vcard; +} + +void +vcard_free(VCard *vcard) +{ + VCardApplet *current_applet = NULL; + VCardApplet *next_applet = NULL; + + if (vcard == NULL) { + return; + } + vcard->reference_count--; + if (vcard->reference_count != 0) { + return; + } + if (vcard->vcard_private_free) { + (*vcard->vcard_private_free)(vcard->vcard_private); + vcard->vcard_private_free = 0; + vcard->vcard_private = 0; + } + for (current_applet = vcard->applet_list; current_applet; + current_applet = next_applet) { + next_applet = current_applet->next; + vcard_delete_applet(current_applet); + } + vcard_buffer_response_delete(vcard->vcard_buffer_response); + qemu_free(vcard); + return; +} + +void +vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len) +{ + if (vcard->vcard_get_atr) { + (*vcard->vcard_get_atr)(vcard, atr, atr_len); + return; + } + vcard_emul_get_atr(vcard, atr, atr_len); +} + +void +vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr) +{ + card->vcard_get_atr = vcard_get_atr; +} + + +VCardStatus +vcard_add_applet(VCard *card, VCardApplet *applet) +{ + applet->next = card->applet_list; + card->applet_list = applet; + /* if our card-type is direct, always call the applet */ + if (card->type == VCARD_DIRECT) { + int i; + + for (i = 0; i < MAX_CHANNEL; i++) { + card->current_applet[i] = applet; + } + } + return VCARD_DONE; +} + +/* + * manage applets + */ +VCardApplet * +vcard_find_applet(VCard *card, unsigned char *aid, int aid_len) +{ + VCardApplet *current_applet; + + for (current_applet = card->applet_list; current_applet; + current_applet = current_applet->next) { + if (current_applet->aid_len != aid_len) { + continue; + } + if (memcmp(current_applet->aid, aid, aid_len) == 0) { + break; + } + } + return current_applet; +} + +unsigned char * +vcard_applet_get_aid(VCardApplet *applet, int *aid_len) +{ + if (applet == NULL) { + return NULL; + } + *aid_len = applet->aid_len; + return applet->aid; +} + + +void +vcard_select_applet(VCard *card, int channel, VCardApplet *applet) +{ + assert(channel < MAX_CHANNEL); + card->current_applet[channel] = applet; + /* reset the applet */ + if (applet && applet->reset_applet) { + applet->reset_applet(card, channel); + } +} + +VCardAppletPrivate * +vcard_get_current_applet_private(VCard *card, int channel) +{ + VCardApplet *applet = card->current_applet[channel]; + + if (applet == NULL) { + return NULL; + } + return applet->applet_private; +} + +VCardStatus +vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response) +{ + if (card->current_applet[apdu->a_channel]) { + return card->current_applet[apdu->a_channel]->process_apdu( + card, apdu, response); + } + return VCARD_NEXT; +} + +/* + * Accessor functions + */ +/* accessor functions for the response buffer */ +VCardBufferResponse * +vcard_get_buffer_response(VCard *card) +{ + return card->vcard_buffer_response; +} + +void +vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer) +{ + card->vcard_buffer_response = buffer; +} + + +/* accessor functions for the type */ +VCardType +vcard_get_type(VCard *card) +{ + return card->type; +} + +void +vcard_set_type(VCard *card, VCardType type) +{ + card->type = type; +} + +/* accessor for private data */ +VCardEmul * +vcard_get_private(VCard *vcard) +{ + return vcard->vcard_private; +} + diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vcard_emul.h qemu-kvm-0.15.0+noroms/libcacard/vcard_emul.h --- qemu-kvm-0.14.1+noroms/libcacard/vcard_emul.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vcard_emul.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,65 @@ +/* + * This is the actual card emulator. + * + * These functions can be implemented in different ways on different platforms + * using the underlying system primitives. For Linux it uses NSS, though direct + * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be + * used. On Windows CAPI could be used. + * + * 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 VCARD_EMUL_H +#define VCARD_EMUL_H 1 + +#include "card_7816t.h" +#include "vcard.h" +#include "vcard_emul_type.h" + +/* + * types + */ +typedef enum { + VCARD_EMUL_OK = 0, + VCARD_EMUL_FAIL, + /* return values by vcard_emul_init */ + VCARD_EMUL_INIT_ALREADY_INITED, +} VCardEmulError; + +/* options are emul specific. call card_emul_parse_args to change a string + * To an options struct */ +typedef struct VCardEmulOptionsStruct VCardEmulOptions; + +/* + * Login functions + */ +/* return the number of login attempts still possible on the card. if unknown, + * return -1 */ +int vcard_emul_get_login_count(VCard *card); +/* login into the card, return the 7816 status word (sw2 || sw1) */ +vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin, + int pin_len); + +/* + * key functions + */ +/* delete a key */ +void vcard_emul_delete_key(VCardKey *key); +/* RSA sign/decrypt with the key, signature happens 'in place' */ +vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key, + unsigned char *buffer, int buffer_size); + +void vcard_emul_reset(VCard *card, VCardPower power); +void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len); + +/* Re-insert of a card that has been removed by force removal */ +VCardEmulError vcard_emul_force_card_insert(VReader *vreader); +/* Force a card removal even if the card is not physically removed */ +VCardEmulError vcard_emul_force_card_remove(VReader *vreader); + +VCardEmulOptions *vcard_emul_options(const char *args); +VCardEmulError vcard_emul_init(const VCardEmulOptions *options); +void vcard_emul_replay_insertion_events(void); +void vcard_emul_usage(void); +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vcard_emul_nss.c qemu-kvm-0.15.0+noroms/libcacard/vcard_emul_nss.c --- qemu-kvm-0.14.1+noroms/libcacard/vcard_emul_nss.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vcard_emul_nss.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,1157 @@ +/* + * This is the actual card emulator. + * + * These functions can be implemented in different ways on different platforms + * using the underlying system primitives. For Linux it uses NSS, though direct + * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be + * used. On Windows CAPI could be used. + * + * 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. + */ + +/* + * NSS headers + */ + +/* avoid including prototypes.h that redefines uint32 */ +#define NO_NSPR_10_SUPPORT + +#include +#include +#include +#include +#include +#include +#include + +#include "qemu-common.h" + +#include "vcard.h" +#include "card_7816t.h" +#include "vcard_emul.h" +#include "vreader.h" +#include "vevent.h" + +struct VCardKeyStruct { + CERTCertificate *cert; + PK11SlotInfo *slot; + SECKEYPrivateKey *key; +}; + + +typedef struct VirtualReaderOptionsStruct VirtualReaderOptions; + +struct VReaderEmulStruct { + PK11SlotInfo *slot; + VCardEmulType default_type; + char *type_params; + PRBool present; + int series; + VCard *saved_vcard; +}; + +/* + * NSS Specific options + */ +struct VirtualReaderOptionsStruct { + char *name; + char *vname; + VCardEmulType card_type; + char *type_params; + char **cert_name; + int cert_count; +}; + +struct VCardEmulOptionsStruct { + void *nss_db; + VirtualReaderOptions *vreader; + int vreader_count; + VCardEmulType hw_card_type; + const char *hw_type_params; + PRBool use_hw; +}; + +static int nss_emul_init; + +/* if we have more that just the slot, define + * VCardEmulStruct here */ + +/* + * allocate the set of arrays for certs, cert_len, key + */ +static PRBool +vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, + VCardKey ***keysp, int cert_count) +{ + *certsp = NULL; + *cert_lenp = NULL; + *keysp = NULL; + *certsp = (unsigned char **)qemu_malloc(sizeof(unsigned char *)*cert_count); + *cert_lenp = (int *)qemu_malloc(sizeof(int)*cert_count); + *keysp = (VCardKey **)qemu_malloc(sizeof(VCardKey *)*cert_count); + return PR_TRUE; +} + +/* + * Emulator specific card information + */ +typedef struct CardEmulCardStruct CardEmulPrivate; + +static VCardEmul * +vcard_emul_new_card(PK11SlotInfo *slot) +{ + PK11_ReferenceSlot(slot); + /* currently we don't need anything other than the slot */ + return (VCardEmul *)slot; +} + +static void +vcard_emul_delete_card(VCardEmul *vcard_emul) +{ + PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul; + if (slot == NULL) { + return; + } + PK11_FreeSlot(slot); +} + +static PK11SlotInfo * +vcard_emul_card_get_slot(VCard *card) +{ + /* note, the card is holding the reference, no need to get another one */ + return (PK11SlotInfo *)vcard_get_private(card); +} + + +/* + * key functions + */ +/* private constructure */ +static VCardKey * +vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert) +{ + VCardKey *key; + + key = (VCardKey *)qemu_malloc(sizeof(VCardKey)); + key->slot = PK11_ReferenceSlot(slot); + key->cert = CERT_DupCertificate(cert); + /* NOTE: if we aren't logged into the token, this could return NULL */ + /* NOTE: the cert is a temp cert, not necessarily the cert in the token, + * use the DER version of this function */ + key->key = PK11_FindKeyByDERCert(slot, cert, NULL); + return key; +} + +/* destructor */ +void +vcard_emul_delete_key(VCardKey *key) +{ + if (!nss_emul_init || (key == NULL)) { + return; + } + if (key->key) { + SECKEY_DestroyPrivateKey(key->key); + key->key = NULL; + } + if (key->cert) { + CERT_DestroyCertificate(key->cert); + } + if (key->slot) { + PK11_FreeSlot(key->slot); + } + return; +} + +/* + * grab the nss key from a VCardKey. If it doesn't exist, try to look it up + */ +static SECKEYPrivateKey * +vcard_emul_get_nss_key(VCardKey *key) +{ + if (key->key) { + return key->key; + } + /* NOTE: if we aren't logged into the token, this could return NULL */ + key->key = PK11_FindPrivateKeyFromCert(key->slot, key->cert, NULL); + return key->key; +} + +/* + * Map NSS errors to 7816 errors + */ +static vcard_7816_status_t +vcard_emul_map_error(int error) +{ + switch (error) { + case SEC_ERROR_TOKEN_NOT_LOGGED_IN: + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; + case SEC_ERROR_BAD_DATA: + case SEC_ERROR_OUTPUT_LEN: + case SEC_ERROR_INPUT_LEN: + case SEC_ERROR_INVALID_ARGS: + case SEC_ERROR_INVALID_ALGORITHM: + case SEC_ERROR_NO_KEY: + case SEC_ERROR_INVALID_KEY: + case SEC_ERROR_DECRYPTION_DISALLOWED: + return VCARD7816_STATUS_ERROR_DATA_INVALID; + case SEC_ERROR_NO_MEMORY: + return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; + } + return VCARD7816_STATUS_EXC_ERROR_CHANGE; +} + +/* RSA sign/decrypt with the key, signature happens 'in place' */ +vcard_7816_status_t +vcard_emul_rsa_op(VCard *card, VCardKey *key, + unsigned char *buffer, int buffer_size) +{ + SECKEYPrivateKey *priv_key; + unsigned signature_len; + SECStatus rv; + + if ((!nss_emul_init) || (key == NULL)) { + /* couldn't get the key, indicate that we aren't logged in */ + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; + } + priv_key = vcard_emul_get_nss_key(key); + + /* + * this is only true of the rsa signature + */ + signature_len = PK11_SignatureLen(priv_key); + if (buffer_size != signature_len) { + return VCARD7816_STATUS_ERROR_DATA_INVALID; + } + rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len, + buffer, buffer_size); + if (rv != SECSuccess) { + return vcard_emul_map_error(PORT_GetError()); + } + assert(buffer_size == signature_len); + return VCARD7816_STATUS_SUCCESS; +} + +/* + * Login functions + */ +/* return the number of login attempts still possible on the card. if unknown, + * return -1 */ +int +vcard_emul_get_login_count(VCard *card) +{ + return -1; +} + +/* login into the card, return the 7816 status word (sw2 || sw1) */ +vcard_7816_status_t +vcard_emul_login(VCard *card, unsigned char *pin, int pin_len) +{ + PK11SlotInfo *slot; + unsigned char *pin_string = NULL; + int i; + SECStatus rv; + + if (!nss_emul_init) { + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; + } + slot = vcard_emul_card_get_slot(card); + /* We depend on the PKCS #11 module internal login state here because we + * create a separate process to handle each guest instance. If we needed + * to handle multiple guests from one process, then we would need to keep + * a lot of extra state in our card structure + * */ + pin_string = qemu_malloc(pin_len+1); + memcpy(pin_string, pin, pin_len); + pin_string[pin_len] = 0; + + /* handle CAC expanded pins correctly */ + for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) { + pin_string[i] = 0; + } + + rv = PK11_Authenticate(slot, PR_FALSE, pin_string); + memset(pin_string, 0, pin_len); /* don't let the pin hang around in memory + to be snooped */ + qemu_free(pin_string); + if (rv == SECSuccess) { + return VCARD7816_STATUS_SUCCESS; + } + /* map the error from port get error */ + return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; +} + +void +vcard_emul_reset(VCard *card, VCardPower power) +{ + PK11SlotInfo *slot; + + if (!nss_emul_init) { + return; + } + + /* + * if we reset the card (either power on or power off), we lose our login + * state + */ + /* TODO: we may also need to send insertion/removal events? */ + slot = vcard_emul_card_get_slot(card); + PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */ + return; +} + + +static VReader * +vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) +{ + VReaderList *reader_list = vreader_get_reader_list(); + VReaderListEntry *current_entry = NULL; + + if (reader_list == NULL) { + return NULL; + } + for (current_entry = vreader_list_get_first(reader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *reader = vreader_list_get_reader(current_entry); + VReaderEmul *reader_emul = vreader_get_private(reader); + if (reader_emul->slot == slot) { + return reader; + } + vreader_free(reader); + } + + return NULL; +} + +/* + * create a new reader emul + */ +static VReaderEmul * +vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) +{ + VReaderEmul *new_reader_emul; + + new_reader_emul = (VReaderEmul *)qemu_malloc(sizeof(VReaderEmul)); + + new_reader_emul->slot = PK11_ReferenceSlot(slot); + new_reader_emul->default_type = type; + new_reader_emul->type_params = strdup(params); + new_reader_emul->present = PR_FALSE; + new_reader_emul->series = 0; + new_reader_emul->saved_vcard = NULL; + return new_reader_emul; +} + +static void +vreader_emul_delete(VReaderEmul *vreader_emul) +{ + if (vreader_emul == NULL) { + return; + } + if (vreader_emul->slot) { + PK11_FreeSlot(vreader_emul->slot); + } + if (vreader_emul->type_params) { + qemu_free(vreader_emul->type_params); + } + qemu_free(vreader_emul); +} + +/* + * TODO: move this to emulater non-specific file + */ +static VCardEmulType +vcard_emul_get_type(VReader *vreader) +{ + VReaderEmul *vreader_emul; + + vreader_emul = vreader_get_private(vreader); + if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) { + return vreader_emul->default_type; + } + + return vcard_emul_type_select(vreader); +} +/* + * TODO: move this to emulater non-specific file + */ +static const char * +vcard_emul_get_type_params(VReader *vreader) +{ + VReaderEmul *vreader_emul; + + vreader_emul = vreader_get_private(vreader); + if (vreader_emul && vreader_emul->type_params) { + return vreader_emul->type_params; + } + + return ""; +} + +/* pull the slot out of the reader private data */ +static PK11SlotInfo * +vcard_emul_reader_get_slot(VReader *vreader) +{ + VReaderEmul *vreader_emul = vreader_get_private(vreader); + if (vreader_emul == NULL) { + return NULL; + } + return vreader_emul->slot; +} + +/* + * Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate + * historical bytes for any software emulated card. The remaining bytes can be + * used to indicate the actual emulator + */ +static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }; + +void +vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len) +{ + int len = MIN(sizeof(nss_atr), *atr_len); + assert(atr != NULL); + + memcpy(atr, nss_atr, len); + *atr_len = len; + return; +} + +/* + * create a new card from certs and keys + */ +static VCard * +vcard_emul_make_card(VReader *reader, + unsigned char * const *certs, int *cert_len, + VCardKey *keys[], int cert_count) +{ + VCardEmul *vcard_emul; + VCard *vcard; + PK11SlotInfo *slot; + VCardEmulType type; + const char *params; + + type = vcard_emul_get_type(reader); + + /* ignore the inserted card */ + if (type == VCARD_EMUL_NONE) { + return NULL; + } + slot = vcard_emul_reader_get_slot(reader); + if (slot == NULL) { + return NULL; + } + + params = vcard_emul_get_type_params(reader); + /* params these can be NULL */ + + vcard_emul = vcard_emul_new_card(slot); + if (vcard_emul == NULL) { + return NULL; + } + vcard = vcard_new(vcard_emul, vcard_emul_delete_card); + if (vcard == NULL) { + vcard_emul_delete_card(vcard_emul); + return NULL; + } + vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count); + return vcard; +} + + +/* + * 'clone' a physical card as a virtual card + */ +static VCard * +vcard_emul_mirror_card(VReader *vreader) +{ + /* + * lookup certs using the C_FindObjects. The Stan Cert handle won't give + * us the real certs until we log in. + */ + PK11GenericObject *firstObj, *thisObj; + int cert_count; + unsigned char **certs; + int *cert_len; + VCardKey **keys; + PK11SlotInfo *slot; + PRBool ret; + + slot = vcard_emul_reader_get_slot(vreader); + if (slot == NULL) { + return NULL; + } + + firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE); + if (firstObj == NULL) { + return NULL; + } + + /* count the certs */ + cert_count = 0; + for (thisObj = firstObj; thisObj; + thisObj = PK11_GetNextGenericObject(thisObj)) { + cert_count++; + } + + if (cert_count == 0) { + PK11_DestroyGenericObjects(firstObj); + return NULL; + } + + /* allocate the arrays */ + ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); + if (ret == PR_FALSE) { + return NULL; + } + + /* fill in the arrays */ + cert_count = 0; + for (thisObj = firstObj; thisObj; + thisObj = PK11_GetNextGenericObject(thisObj)) { + SECItem derCert; + CERTCertificate *cert; + SECStatus rv; + + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj, + CKA_VALUE, &derCert); + if (rv != SECSuccess) { + continue; + } + /* create floating temp cert. This gives us a cert structure even if + * the token isn't logged in */ + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, + NULL, PR_FALSE, PR_TRUE); + SECITEM_FreeItem(&derCert, PR_FALSE); + if (cert == NULL) { + continue; + } + + certs[cert_count] = cert->derCert.data; + cert_len[cert_count] = cert->derCert.len; + keys[cert_count] = vcard_emul_make_key(slot, cert); + cert_count++; + CERT_DestroyCertificate(cert); /* key obj still has a reference */ + } + + /* now create the card */ + return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); +} + +static VCardEmulType default_card_type = VCARD_EMUL_NONE; +static const char *default_type_params = ""; + +/* + * This thread looks for card and reader insertions and puts events on the + * event queue + */ +static void +vcard_emul_event_thread(void *arg) +{ + PK11SlotInfo *slot; + VReader *vreader; + VReaderEmul *vreader_emul; + VCard *vcard; + SECMODModule *module = (SECMODModule *)arg; + + do { + slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); + if (slot == NULL) { + break; + } + vreader = vcard_emul_find_vreader_from_slot(slot); + if (vreader == NULL) { + /* new vreader */ + vreader_emul = vreader_emul_new(slot, default_card_type, + default_type_params); + vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, + vreader_emul_delete); + PK11_FreeSlot(slot); + slot = NULL; + vreader_add_reader(vreader); + vreader_free(vreader); + continue; + } + /* card remove/insert */ + vreader_emul = vreader_get_private(vreader); + if (PK11_IsPresent(slot)) { + int series = PK11_GetSlotSeries(slot); + if (series != vreader_emul->series) { + if (vreader_emul->present) { + vreader_insert_card(vreader, NULL); + } + vcard = vcard_emul_mirror_card(vreader); + vreader_insert_card(vreader, vcard); + vcard_free(vcard); + } + vreader_emul->series = series; + vreader_emul->present = 1; + vreader_free(vreader); + PK11_FreeSlot(slot); + continue; + } + if (vreader_emul->present) { + vreader_insert_card(vreader, NULL); + } + vreader_emul->series = 0; + vreader_emul->present = 0; + PK11_FreeSlot(slot); + vreader_free(vreader); + } while (1); +} + +/* if the card is inserted when we start up, make sure our state is correct */ +static void +vcard_emul_init_series(VReader *vreader, VCard *vcard) +{ + VReaderEmul *vreader_emul = vreader_get_private(vreader); + PK11SlotInfo *slot = vreader_emul->slot; + + vreader_emul->present = PK11_IsPresent(slot); + vreader_emul->series = PK11_GetSlotSeries(slot); + if (vreader_emul->present == 0) { + vreader_insert_card(vreader, NULL); + } +} + +/* + * each module has a separate wait call, create a thread for each module that + * we are using. + */ +static void +vcard_emul_new_event_thread(SECMODModule *module) +{ + PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread, + module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); +} + +static const VCardEmulOptions default_options = { + .nss_db = NULL, + .vreader = NULL, + .vreader_count = 0, + .hw_card_type = VCARD_EMUL_CAC, + .hw_type_params = "", + .use_hw = PR_TRUE +}; + + +/* + * NSS needs the app to supply a password prompt. In our case the only time + * the password is supplied is as part of the Login APDU. The actual password + * is passed in the pw_arg in that case. In all other cases pw_arg should be + * NULL. + */ +static char * +vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg) +{ + /* if it didn't work the first time, don't keep trying */ + if (retries) { + return NULL; + } + /* we are looking up a password when we don't have one in hand */ + if (pw_arg == NULL) { + return NULL; + } + /* TODO: we really should verify that were are using the right slot */ + return PORT_Strdup(pw_arg); +} + +/* Force a card removal even if the card is not physically removed */ +VCardEmulError +vcard_emul_force_card_remove(VReader *vreader) +{ + if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) { + return VCARD_EMUL_FAIL; /* card is already removed */ + } + + /* OK, remove it */ + vreader_insert_card(vreader, NULL); + return VCARD_EMUL_OK; +} + +/* Re-insert of a card that has been removed by force removal */ +VCardEmulError +vcard_emul_force_card_insert(VReader *vreader) +{ + VReaderEmul *vreader_emul; + VCard *vcard; + + if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) { + return VCARD_EMUL_FAIL; /* card is already removed */ + } + vreader_emul = vreader_get_private(vreader); + + /* if it's a softcard, get the saved vcard from the reader emul structure */ + if (vreader_emul->saved_vcard) { + vcard = vcard_reference(vreader_emul->saved_vcard); + } else { + /* it must be a physical card, rebuild it */ + if (!PK11_IsPresent(vreader_emul->slot)) { + /* physical card has been removed, not way to reinsert it */ + return VCARD_EMUL_FAIL; + } + vcard = vcard_emul_mirror_card(vreader); + } + vreader_insert_card(vreader, vcard); + vcard_free(vcard); + + return VCARD_EMUL_OK; +} + + +static PRBool +module_has_removable_hw_slots(SECMODModule *mod) +{ + int i; + PRBool ret = PR_FALSE; + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + + if (!moduleLock) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return ret; + } + SECMOD_GetReadLock(moduleLock); + for (i = 0; i < mod->slotCount; i++) { + PK11SlotInfo *slot = mod->slots[i]; + if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) { + ret = PR_TRUE; + break; + } + } + SECMOD_ReleaseReadLock(moduleLock); + return ret; +} + +/* Previously we returned FAIL if no readers found. This makes + * no sense when using hardware, since there may be no readers connected + * at the time vcard_emul_init is called, but they will be properly + * recognized later. So Instead return FAIL only if no_hw==1 and no + * vcards can be created (indicates error with certificates provided + * or db), or if any other higher level error (NSS error, missing coolkey). */ +static int vcard_emul_init_called; + +VCardEmulError +vcard_emul_init(const VCardEmulOptions *options) +{ + SECStatus rv; + PRBool ret, has_readers = PR_FALSE, need_coolkey_module; + VReader *vreader; + VReaderEmul *vreader_emul; + SECMODListLock *module_lock; + SECMODModuleList *module_list; + SECMODModuleList *mlp; + int i; + + if (vcard_emul_init_called) { + return VCARD_EMUL_INIT_ALREADY_INITED; + } + vcard_emul_init_called = 1; + vreader_init(); + vevent_queue_init(); + + if (options == NULL) { + options = &default_options; + } + + /* first initialize NSS */ + if (options->nss_db) { + rv = NSS_Init(options->nss_db); + } else { + rv = NSS_Init("sql:/etc/pki/nssdb"); + } + if (rv != SECSuccess) { + return VCARD_EMUL_FAIL; + } + /* Set password callback function */ + PK11_SetPasswordFunc(vcard_emul_get_password); + + /* set up soft cards emulated by software certs rather than physical cards + * */ + for (i = 0; i < options->vreader_count; i++) { + int j; + int cert_count; + unsigned char **certs; + int *cert_len; + VCardKey **keys; + PK11SlotInfo *slot; + + slot = PK11_FindSlotByName(options->vreader[i].name); + if (slot == NULL) { + continue; + } + vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type, + options->vreader[i].type_params); + vreader = vreader_new(options->vreader[i].vname, vreader_emul, + vreader_emul_delete); + vreader_add_reader(vreader); + cert_count = options->vreader[i].cert_count; + + ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, + options->vreader[i].cert_count); + if (ret == PR_FALSE) { + continue; + } + cert_count = 0; + for (j = 0; j < options->vreader[i].cert_count; j++) { + /* we should have a better way of identifying certs than by + * nickname here */ + CERTCertificate *cert = PK11_FindCertFromNickname( + options->vreader[i].cert_name[j], + NULL); + if (cert == NULL) { + continue; + } + certs[cert_count] = cert->derCert.data; + cert_len[cert_count] = cert->derCert.len; + keys[cert_count] = vcard_emul_make_key(slot, cert); + /* this is safe because the key is still holding a cert reference */ + CERT_DestroyCertificate(cert); + cert_count++; + } + if (cert_count) { + VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len, + keys, cert_count); + vreader_insert_card(vreader, vcard); + vcard_emul_init_series(vreader, vcard); + /* allow insertion and removal of soft cards */ + vreader_emul->saved_vcard = vcard_reference(vcard); + vcard_free(vcard); + vreader_free(vreader); + has_readers = PR_TRUE; + } + } + + /* if we aren't suppose to use hw, skip looking up hardware tokens */ + if (!options->use_hw) { + nss_emul_init = has_readers; + return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL; + } + + /* make sure we have some PKCS #11 module loaded */ + module_lock = SECMOD_GetDefaultModuleListLock(); + module_list = SECMOD_GetDefaultModuleList(); + need_coolkey_module = !has_readers; + SECMOD_GetReadLock(module_lock); + for (mlp = module_list; mlp; mlp = mlp->next) { + SECMODModule *module = mlp->module; + if (module_has_removable_hw_slots(module)) { + need_coolkey_module = PR_FALSE; + break; + } + } + SECMOD_ReleaseReadLock(module_lock); + + if (need_coolkey_module) { + SECMODModule *module; + module = SECMOD_LoadUserModule( + (char *)"library=libcoolkeypk11.so name=Coolkey", + NULL, PR_FALSE); + if (module == NULL) { + return VCARD_EMUL_FAIL; + } + SECMOD_DestroyModule(module); /* free our reference, Module will still + * be on the list. + * until we destroy it */ + } + + /* now examine all the slots, finding which should be readers */ + /* We should control this with options. For now we mirror out any + * removable hardware slot */ + default_card_type = options->hw_card_type; + default_type_params = strdup(options->hw_type_params); + + SECMOD_GetReadLock(module_lock); + for (mlp = module_list; mlp; mlp = mlp->next) { + SECMODModule *module = mlp->module; + PRBool has_emul_slots = PR_FALSE; + + if (module == NULL) { + continue; + } + + for (i = 0; i < module->slotCount; i++) { + PK11SlotInfo *slot = module->slots[i]; + + /* only map removable HW slots */ + if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) { + continue; + } + vreader_emul = vreader_emul_new(slot, options->hw_card_type, + options->hw_type_params); + vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, + vreader_emul_delete); + vreader_add_reader(vreader); + + has_readers = PR_TRUE; + has_emul_slots = PR_TRUE; + + if (PK11_IsPresent(slot)) { + VCard *vcard; + vcard = vcard_emul_mirror_card(vreader); + vreader_insert_card(vreader, vcard); + vcard_emul_init_series(vreader, vcard); + vcard_free(vcard); + } + } + if (has_emul_slots) { + vcard_emul_new_event_thread(module); + } + } + SECMOD_ReleaseReadLock(module_lock); + nss_emul_init = has_readers; + + return VCARD_EMUL_OK; +} + +/* Recreate card insert events for all readers (user should + * deduce implied reader insert. perhaps do a reader insert as well?) + */ +void +vcard_emul_replay_insertion_events(void) +{ + VReaderListEntry *current_entry; + VReaderListEntry *next_entry = NULL; + VReaderList *list = vreader_get_reader_list(); + + for (current_entry = vreader_list_get_first(list); current_entry; + current_entry = next_entry) { + VReader *vreader = vreader_list_get_reader(current_entry); + next_entry = vreader_list_get_next(current_entry); + vreader_queue_card_event(vreader); + } +} + +/* + * Silly little functions to help parsing our argument string + */ +static char * +copy_string(const char *str, int str_len) +{ + char *new_str; + + new_str = qemu_malloc(str_len+1); + memcpy(new_str, str, str_len); + new_str[str_len] = 0; + return new_str; +} + +static int +count_tokens(const char *str, char token, char token_end) +{ + int count = 0; + + for (; *str; str++) { + if (*str == token) { + count++; + } + if (*str == token_end) { + break; + } + } + return count; +} + +static const char * +strip(const char *str) +{ + for (; *str && isspace(*str); str++) { + } + return str; +} + +static const char * +find_blank(const char *str) +{ + for (; *str && !isspace(*str); str++) { + } + return str; +} + + +/* + * We really want to use some existing argument parsing library here. That + * would give us a consistent look */ +static VCardEmulOptions options; +#define READER_STEP 4 + +VCardEmulOptions * +vcard_emul_options(const char *args) +{ + int reader_count = 0; + VCardEmulOptions *opts; + char type_str[100]; + int type_len; + + /* Allow the future use of allocating the options structure on the fly */ + memcpy(&options, &default_options, sizeof(options)); + opts = &options; + + do { + args = strip(args); /* strip off the leading spaces */ + if (*args == ',') { + continue; + } + /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol) + * cert_2,cert_3...) */ + if (strncmp(args, "soft=", 5) == 0) { + const char *name; + const char *vname; + const char *type_params; + VCardEmulType type; + int name_length, vname_length, type_params_length, count, i; + VirtualReaderOptions *vreaderOpt = NULL; + + args = strip(args + 5); + if (*args != '(') { + continue; + } + name = args; + args = strpbrk(args + 1, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + args = strip(args+1); + name_length = args - name - 2; + vname = args; + args = strpbrk(args + 1, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + vname_length = args - name - 2; + args = strip(args+1); + type_len = strpbrk(args, ",)") - args; + assert(sizeof(type_str) > type_len); + strncpy(type_str, args, type_len); + type_str[type_len] = 0; + type = vcard_emul_type_from_string(type_str); + args = strpbrk(args, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + args = strip(args++); + type_params = args; + args = strpbrk(args + 1, ",)"); + if (*args == 0) { + break; + } + if (*args == ')') { + args++; + continue; + } + type_params_length = args - name; + args = strip(args++); + if (*args == 0) { + break; + } + + if (opts->vreader_count >= reader_count) { + reader_count += READER_STEP; + vreaderOpt = realloc(opts->vreader, + reader_count * sizeof(*vreaderOpt)); + if (vreaderOpt == NULL) { + return opts; /* we're done */ + } + } + opts->vreader = vreaderOpt; + vreaderOpt = &vreaderOpt[opts->vreader_count]; + vreaderOpt->name = copy_string(name, name_length); + vreaderOpt->vname = copy_string(vname, vname_length); + vreaderOpt->card_type = type; + vreaderOpt->type_params = + copy_string(type_params, type_params_length); + count = count_tokens(args, ',', ')'); + vreaderOpt->cert_count = count; + vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *)); + for (i = 0; i < count; i++) { + const char *cert = args + 1; + args = strpbrk(args + 1, ",)"); + vreaderOpt->cert_name[i] = copy_string(cert, args - cert); + } + if (*args == ')') { + args++; + } + opts->vreader_count++; + /* use_hw= */ + } else if (strncmp(args, "use_hw=", 7) == 0) { + args = strip(args+7); + if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') { + opts->use_hw = PR_FALSE; + } else { + opts->use_hw = PR_TRUE; + } + args = find_blank(args); + /* hw_type= */ + } else if (strncmp(args, "hw_type=", 8) == 0) { + args = strip(args+8); + opts->hw_card_type = vcard_emul_type_from_string(args); + args = find_blank(args); + /* hw_params= */ + } else if (strncmp(args, "hw_params=", 10) == 0) { + const char *params; + args = strip(args+10); + params = args; + args = find_blank(args); + opts->hw_type_params = copy_string(params, args-params); + /* db="/data/base/path" */ + } else if (strncmp(args, "db=", 3) == 0) { + const char *db; + args = strip(args+3); + if (*args != '"') { + continue; + } + args++; + db = args; + args = strpbrk(args, "\"\n"); + opts->nss_db = copy_string(db, args-db); + if (*args != 0) { + args++; + } + } else { + args = find_blank(args); + } + } while (*args != 0); + + return opts; +} + +void +vcard_emul_usage(void) +{ + fprintf(stderr, +"emul args: comma separated list of the following arguments\n" +" db={nss_database} (default sql:/etc/pki/nssdb)\n" +" use_hw=[yes|no] (default yes)\n" +" hw_type={card_type_to_emulate} (default CAC)\n" +" hw_param={param_for_card} (default \"\")\n" +" soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n" +" {cert1},{cert2},{cert3} (default none)\n" +"\n" +" {nss_database} The location of the NSS cert & key database\n" +" {card_type_to_emulate} What card interface to present to the guest\n" +" {param_for_card} Card interface specific parameters\n" +" {slot_name} NSS slot that contains the certs\n" +" {vreader_name} Virutal reader name to present to the guest\n" +" {certN} Nickname of the certificate n on the virtual card\n" +"\n" +"These parameters come as a single string separated by blanks or newlines." +"\n" +"Unless use_hw is set to no, all tokens that look like removable hardware\n" +"tokens will be presented to the guest using the emulator specified by\n" +"hw_type, and parameters of hw_param.\n" +"\n" +"If more one or more soft= parameters are specified, these readers will be\n" +"presented to the guest\n"); +} diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vcard_emul_type.c qemu-kvm-0.15.0+noroms/libcacard/vcard_emul_type.c --- qemu-kvm-0.14.1+noroms/libcacard/vcard_emul_type.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vcard_emul_type.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,57 @@ +/* + * This file contains utility functions which abstract the different card + * types. The goal is that new card types can easily be added by simply + * changing this file and vcard_emul_type.h. It is currently not a requirement + * to dynamically add new card types. + * + * 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 +#include "vcardt.h" +#include "vcard_emul_type.h" +#include "cac.h" + +VCardStatus vcard_init(VReader *vreader, VCard *vcard, + VCardEmulType type, const char *params, + unsigned char *const *cert, int cert_len[], + VCardKey *key[], int cert_count) +{ + switch (type) { + case VCARD_EMUL_NONE: + break; + case VCARD_EMUL_CAC: + return cac_card_init(vreader, vcard, params, + cert, cert_len, key, cert_count); + /* add new ones here */ + default: + break; + } + return VCARD_FAIL; +} + +VCardEmulType vcard_emul_type_select(VReader *vreader) +{ +#ifdef notdef + /* since there is only one emulator no need to call this function */ + if (cac_is_cac_card(vreader) == VCARD_DONE) { + return VCARD_EMUL_CAC; + } +#endif + /* return the default */ + return VCARD_EMUL_CAC; +} + +VCardEmulType vcard_emul_type_from_string(const char *type_string) +{ + if (strcasecmp(type_string, "CAC") == 0) { + return VCARD_EMUL_CAC; + } +#ifdef USE_PASSTHRU + if (strcasecmp(type_string, "PASSTHRU") == 0) { + return VCARD_EMUL_PASSTHRU; + } +#endif + return VCARD_EMUL_NONE; +} diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vcard_emul_type.h qemu-kvm-0.15.0+noroms/libcacard/vcard_emul_type.h --- qemu-kvm-0.14.1+noroms/libcacard/vcard_emul_type.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vcard_emul_type.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,32 @@ +/* + * This header file abstracts the different card types. The goal is new card + * types can easily be added by simply changing this file and + * vcard_emul_type.c. It is currently not a requirement to dynamically add new + * card types. + * + * 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 VCARD_EMUL_TYPE_H +#define VCARD_EMUL_TYPE_H 1 +#include "vcardt.h" +#include "vreadert.h" + +/* + * types + */ +typedef enum { + VCARD_EMUL_NONE = 0, + VCARD_EMUL_CAC, + VCARD_EMUL_PASSTHRU +} VCardEmulType; + +/* functions used by the rest of the emulator */ +VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type, + const char *params, unsigned char * const *cert, + int cert_len[], VCardKey *key[], int cert_count); +VCardEmulType vcard_emul_type_select(VReader *vreader); +VCardEmulType vcard_emul_type_from_string(const char *type_string); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vcard.h qemu-kvm-0.15.0+noroms/libcacard/vcard.h --- qemu-kvm-0.14.1+noroms/libcacard/vcard.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vcard.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,86 @@ +/* + * 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 VCARD_H +#define VCARD_H 1 + +#include "vcardt.h" + +/* + * response buffer constructors and destructors. + * + * response buffers are used when we need to return more data than will fit in + * a normal APDU response (nominally 254 bytes). + */ +VCardBufferResponse *vcard_buffer_response_new(unsigned char *buffer, int size); +void vcard_buffer_response_delete(VCardBufferResponse *buffer_response); + + +/* + * clean up state on reset + */ +void vcard_reset(VCard *card, VCardPower power); + +/* + * applet utilities + */ +/* + * Constructor for a VCardApplet + */ +VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function, + VCardResetApplet applet_reset_function, + unsigned char *aid, int aid_len); + +/* + * destructor for a VCardApplet + * Can be called with a NULL applet + */ +void vcard_delete_applet(VCardApplet *applet); + +/* accessor - set the card type specific private data */ +void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private, + VCardAppletPrivateFree private_free); + +/* set type of vcard */ +void vcard_set_type(VCard *card, VCardType type); + +/* + * utilities interacting with the current applet + */ +/* add a new applet to a card */ +VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet); +/* find the applet on the card with the given aid */ +VCardApplet *vcard_find_applet(VCard *card, unsigned char *aid, int aid_len); +/* set the following applet to be current on the given channel */ +void vcard_select_applet(VCard *card, int channel, VCardApplet *applet); +/* get the card type specific private data on the given channel */ +VCardAppletPrivate *vcard_get_current_applet_private(VCard *card, int channel); +/* fetch the applet's id */ +unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len); + +/* process the apdu for the current selected applet/file */ +VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu, + VCardResponse **response); +/* + * VCard utilities + */ +/* constructor */ +VCard *vcard_new(VCardEmul *_private, VCardEmulFree private_free); +/* get a reference */ +VCard *vcard_reference(VCard *); +/* destructor (reference counted) */ +void vcard_free(VCard *); +/* get the atr from the card */ +void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len); +void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr); + +/* accessor functions for the response buffer */ +VCardBufferResponse *vcard_get_buffer_response(VCard *card); +void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer); +/* accessor functions for the type */ +VCardType vcard_get_type(VCard *card); +/* get the private data */ +VCardEmul *vcard_get_private(VCard *card); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vcardt.h qemu-kvm-0.15.0+noroms/libcacard/vcardt.h --- qemu-kvm-0.14.1+noroms/libcacard/vcardt.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vcardt.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,64 @@ +/* + * 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 VCARDT_H +#define VCARDT_H 1 + +/* + * these should come from some common spice header file + */ +#include +#ifndef MIN +#define MIN(x, y) ((x) > (y) ? (y) : (x)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +typedef struct VCardStruct VCard; +typedef struct VCardAPDUStruct VCardAPDU; +typedef struct VCardResponseStruct VCardResponse; +typedef struct VCardBufferResponseStruct VCardBufferResponse; +typedef struct VCardAppletStruct VCardApplet; +typedef struct VCardAppletPrivateStruct VCardAppletPrivate; +typedef struct VCardKeyStruct VCardKey; /* opaque */ +typedef struct VCardEmulStruct VCardEmul; + +#define MAX_CHANNEL 4 + +/* create an ATR with appropriate historical bytes */ +#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \ + 'V', 'C', 'A', 'R', 'D', '_' + + +typedef enum { + VCARD_DONE, + VCARD_NEXT, + VCARD_FAIL +} VCardStatus; + +typedef enum { + VCARD_FILE_SYSTEM, + VCARD_VM, + VCARD_DIRECT +} VCardType; + +typedef enum { + VCARD_POWER_ON, + VCARD_POWER_OFF +} VCardPower; + +typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu, + VCardResponse **response); +typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel); +typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *); +typedef void (*VCardEmulFree) (VCardEmul *); +typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len); + +struct VCardBufferResponseStruct { + unsigned char *buffer; + int buffer_len; + unsigned char *current; + int len; +}; + +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vevent.h qemu-kvm-0.15.0+noroms/libcacard/vevent.h --- qemu-kvm-0.14.1+noroms/libcacard/vevent.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vevent.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,27 @@ +/* + * 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 EVENT_H +#define EVENT_H 1 +#include "eventt.h" +#include "vreadert.h" +#include "vcardt.h" + +VEvent *vevent_new(VEventType type, VReader *reader, VCard *card); +void vevent_delete(VEvent *); + +/* + * VEvent queueing services + */ +void vevent_queue_vevent(VEvent *); +void vevent_queue_init(void); + +/* + * VEvent dequeing services + */ +VEvent *vevent_wait_next_vevent(void); +VEvent *vevent_get_next_vevent(void); + + +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vreader.c qemu-kvm-0.15.0+noroms/libcacard/vreader.c --- qemu-kvm-0.14.1+noroms/libcacard/vreader.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vreader.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,513 @@ +/* + * emulate the reader + * + * 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 "qemu-common.h" +#include "qemu-thread.h" + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" +#include "vreader.h" +#include "vevent.h" + +struct VReaderStruct { + int reference_count; + VCard *card; + char *name; + vreader_id_t id; + QemuMutex lock; + VReaderEmul *reader_private; + VReaderEmulFree reader_private_free; +}; + +/* manage locking */ +static inline void +vreader_lock(VReader *reader) +{ + qemu_mutex_lock(&reader->lock); +} + +static inline void +vreader_unlock(VReader *reader) +{ + qemu_mutex_unlock(&reader->lock); +} + +/* + * vreader constructor + */ +VReader * +vreader_new(const char *name, VReaderEmul *private, + VReaderEmulFree private_free) +{ + VReader *reader; + + reader = (VReader *)qemu_malloc(sizeof(VReader)); + qemu_mutex_init(&reader->lock); + reader->reference_count = 1; + reader->name = name ? strdup(name) : NULL; + reader->card = NULL; + reader->id = (vreader_id_t)-1; + reader->reader_private = private; + reader->reader_private_free = private_free; + return reader; +} + +/* get a reference */ +VReader* +vreader_reference(VReader *reader) +{ + if (reader == NULL) { + return NULL; + } + vreader_lock(reader); + reader->reference_count++; + vreader_unlock(reader); + return reader; +} + +/* free a reference */ +void +vreader_free(VReader *reader) +{ + if (reader == NULL) { + return; + } + vreader_lock(reader); + if (reader->reference_count-- > 1) { + vreader_unlock(reader); + return; + } + vreader_unlock(reader); + if (reader->card) { + vcard_free(reader->card); + } + if (reader->name) { + qemu_free(reader->name); + } + if (reader->reader_private_free) { + reader->reader_private_free(reader->reader_private); + } + qemu_free(reader); + return; +} + +static VCard * +vreader_get_card(VReader *reader) +{ + VCard *card; + + vreader_lock(reader); + card = vcard_reference(reader->card); + vreader_unlock(reader); + return card; +} + +VReaderStatus +vreader_card_is_present(VReader *reader) +{ + VCard *card = vreader_get_card(reader); + + if (card == NULL) { + return VREADER_NO_CARD; + } + vcard_free(card); + return VREADER_OK; +} + +vreader_id_t +vreader_get_id(VReader *reader) +{ + if (reader == NULL) { + return (vreader_id_t)-1; + } + return reader->id; +} + +VReaderStatus +vreader_set_id(VReader *reader, vreader_id_t id) +{ + if (reader == NULL) { + return VREADER_NO_CARD; + } + reader->id = id; + return VREADER_OK; +} + +const char * +vreader_get_name(VReader *reader) +{ + if (reader == NULL) { + return NULL; + } + return reader->name; +} + +VReaderEmul * +vreader_get_private(VReader *reader) +{ + return reader->reader_private; +} + +static VReaderStatus +vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len) +{ + VCard *card = vreader_get_card(reader); + + if (card == NULL) { + return VREADER_NO_CARD; + } + /* + * clean up our state + */ + vcard_reset(card, power); + if (atr) { + vcard_get_atr(card, atr, len); + } + vcard_free(card); /* free our reference */ + return VREADER_OK; +} + +VReaderStatus +vreader_power_on(VReader *reader, unsigned char *atr, int *len) +{ + return vreader_reset(reader, VCARD_POWER_ON, atr, len); +} + +VReaderStatus +vreader_power_off(VReader *reader) +{ + return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0); +} + + +VReaderStatus +vreader_xfr_bytes(VReader *reader, + unsigned char *send_buf, int send_buf_len, + unsigned char *receive_buf, int *receive_buf_len) +{ + VCardAPDU *apdu; + VCardResponse *response = NULL; + VCardStatus card_status; + unsigned short status; + VCard *card = vreader_get_card(reader); + + if (card == NULL) { + return VREADER_NO_CARD; + } + + apdu = vcard_apdu_new(send_buf, send_buf_len, &status); + if (apdu == NULL) { + response = vcard_make_response(status); + card_status = VCARD_DONE; + } else { + card_status = vcard_process_apdu(card, apdu, &response); + } + assert(card_status == VCARD_DONE); + if (card_status == VCARD_DONE) { + int size = MIN(*receive_buf_len, response->b_total_len); + memcpy(receive_buf, response->b_data, size); + *receive_buf_len = size; + } + vcard_response_delete(response); + vcard_apdu_delete(apdu); + vcard_free(card); /* free our reference */ + return VREADER_OK; +} + +struct VReaderListStruct { + VReaderListEntry *head; + VReaderListEntry *tail; +}; + +struct VReaderListEntryStruct { + VReaderListEntry *next; + VReaderListEntry *prev; + VReader *reader; +}; + + +static VReaderListEntry * +vreader_list_entry_new(VReader *reader) +{ + VReaderListEntry *new_reader_list_entry; + + new_reader_list_entry = (VReaderListEntry *) + qemu_malloc(sizeof(VReaderListEntry)); + new_reader_list_entry->next = NULL; + new_reader_list_entry->prev = NULL; + new_reader_list_entry->reader = vreader_reference(reader); + return new_reader_list_entry; +} + +static void +vreader_list_entry_delete(VReaderListEntry *entry) +{ + if (entry == NULL) { + return; + } + vreader_free(entry->reader); + qemu_free(entry); +} + + +static VReaderList * +vreader_list_new(void) +{ + VReaderList *new_reader_list; + + new_reader_list = (VReaderList *)qemu_malloc(sizeof(VReaderList)); + new_reader_list->head = NULL; + new_reader_list->tail = NULL; + return new_reader_list; +} + +void +vreader_list_delete(VReaderList *list) +{ + VReaderListEntry *current_entry; + VReaderListEntry *next_entry = NULL; + for (current_entry = vreader_list_get_first(list); current_entry; + current_entry = next_entry) { + next_entry = vreader_list_get_next(current_entry); + vreader_list_entry_delete(current_entry); + } + list->head = NULL; + list->tail = NULL; + qemu_free(list); +} + + +VReaderListEntry * +vreader_list_get_first(VReaderList *list) +{ + return list ? list->head : NULL; +} + +VReaderListEntry * +vreader_list_get_next(VReaderListEntry *current) +{ + return current ? current->next : NULL; +} + +VReader * +vreader_list_get_reader(VReaderListEntry *entry) +{ + return entry ? vreader_reference(entry->reader) : NULL; +} + +static void +vreader_queue(VReaderList *list, VReaderListEntry *entry) +{ + if (entry == NULL) { + return; + } + entry->next = NULL; + entry->prev = list->tail; + if (list->head) { + list->tail->next = entry; + } else { + list->head = entry; + } + list->tail = entry; +} + +static void +vreader_dequeue(VReaderList *list, VReaderListEntry *entry) +{ + if (entry == NULL) { + return; + } + if (entry->next == NULL) { + list->tail = entry->prev; + } else if (entry->prev == NULL) { + list->head = entry->next; + } else { + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + } + if ((list->tail == NULL) || (list->head == NULL)) { + list->head = list->tail = NULL; + } + entry->next = entry->prev = NULL; +} + +static VReaderList *vreader_list; +static QemuMutex vreader_list_mutex; + +static void +vreader_list_init(void) +{ + vreader_list = vreader_list_new(); + qemu_mutex_init(&vreader_list_mutex); +} + +static void +vreader_list_lock(void) +{ + qemu_mutex_lock(&vreader_list_mutex); +} + +static void +vreader_list_unlock(void) +{ + qemu_mutex_unlock(&vreader_list_mutex); +} + +static VReaderList * +vreader_copy_list(VReaderList *list) +{ + VReaderList *new_list = NULL; + VReaderListEntry *current_entry = NULL; + + new_list = vreader_list_new(); + if (new_list == NULL) { + return NULL; + } + for (current_entry = vreader_list_get_first(list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *reader = vreader_list_get_reader(current_entry); + VReaderListEntry *new_entry = vreader_list_entry_new(reader); + + vreader_free(reader); + vreader_queue(new_list, new_entry); + } + return new_list; +} + +VReaderList * +vreader_get_reader_list(void) +{ + VReaderList *new_reader_list; + + vreader_list_lock(); + new_reader_list = vreader_copy_list(vreader_list); + vreader_list_unlock(); + return new_reader_list; +} + +VReader * +vreader_get_reader_by_id(vreader_id_t id) +{ + VReader *reader = NULL; + VReaderListEntry *current_entry = NULL; + + if (id == (vreader_id_t) -1) { + return NULL; + } + + vreader_list_lock(); + for (current_entry = vreader_list_get_first(vreader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *creader = vreader_list_get_reader(current_entry); + if (creader->id == id) { + reader = creader; + break; + } + vreader_free(creader); + } + vreader_list_unlock(); + return reader; +} + +VReader * +vreader_get_reader_by_name(const char *name) +{ + VReader *reader = NULL; + VReaderListEntry *current_entry = NULL; + + vreader_list_lock(); + for (current_entry = vreader_list_get_first(vreader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + VReader *creader = vreader_list_get_reader(current_entry); + if (strcmp(creader->name, name) == 0) { + reader = creader; + break; + } + vreader_free(creader); + } + vreader_list_unlock(); + return reader; +} + +/* called from card_emul to initialize the readers */ +VReaderStatus +vreader_add_reader(VReader *reader) +{ + VReaderListEntry *reader_entry; + + reader_entry = vreader_list_entry_new(reader); + if (reader_entry == NULL) { + return VREADER_OUT_OF_MEMORY; + } + vreader_list_lock(); + vreader_queue(vreader_list, reader_entry); + vreader_list_unlock(); + vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL)); + return VREADER_OK; +} + + +VReaderStatus +vreader_remove_reader(VReader *reader) +{ + VReaderListEntry *current_entry; + + vreader_list_lock(); + for (current_entry = vreader_list_get_first(vreader_list); current_entry; + current_entry = vreader_list_get_next(current_entry)) { + if (current_entry->reader == reader) { + break; + } + } + vreader_dequeue(vreader_list, current_entry); + vreader_list_unlock(); + vreader_list_entry_delete(current_entry); + vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL)); + return VREADER_OK; +} + +/* + * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader + * state. Separated from vreader_insert_card to allow replaying events + * for a given state. + */ +void +vreader_queue_card_event(VReader *reader) +{ + vevent_queue_vevent(vevent_new( + reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader, + reader->card)); +} + +/* + * insert/remove a new card. for removal, card == NULL + */ +VReaderStatus +vreader_insert_card(VReader *reader, VCard *card) +{ + vreader_lock(reader); + if (reader->card) { + /* decrement reference count */ + vcard_free(reader->card); + reader->card = NULL; + } + reader->card = vcard_reference(card); + vreader_unlock(reader); + vreader_queue_card_event(reader); + return VREADER_OK; +} + +/* + * initialize all the static reader structures + */ +void +vreader_init(void) +{ + vreader_list_init(); +} + diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vreader.h qemu-kvm-0.15.0+noroms/libcacard/vreader.h --- qemu-kvm-0.14.1+noroms/libcacard/vreader.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vreader.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,55 @@ +/* + * 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 VREADER_H +#define VREADER_H 1 + +#include "eventt.h" +#include "vreadert.h" +#include "vcardt.h" + +/* + * calls for reader front end + */ +VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len); +VReaderStatus vreader_power_off(VReader *reader); +VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf, + int send_buf_len, unsigned char *receive_buf, + int *receive_buf_len); + +/* constructor */ +VReader *vreader_new(const char *readerName, VReaderEmul *emul_private, + VReaderEmulFree private_free); +/* get a new reference to a reader */ +VReader *vreader_reference(VReader *reader); +/* "destructor" (readers are reference counted) */ +void vreader_free(VReader *reader); + +/* accessors */ +VReaderEmul *vreader_get_private(VReader *); +VReaderStatus vreader_card_is_present(VReader *reader); +void vreader_queue_card_event(VReader *reader); +const char *vreader_get_name(VReader *reader); +vreader_id_t vreader_get_id(VReader *reader); +VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id); + +/* list operations */ +VReaderList *vreader_get_reader_list(void); +void vreader_list_delete(VReaderList *list); +VReader *vreader_list_get_reader(VReaderListEntry *entry); +VReaderListEntry *vreader_list_get_first(VReaderList *list); +VReaderListEntry *vreader_list_get_next(VReaderListEntry *list); +VReader *vreader_get_reader_by_id(vreader_id_t id); +VReader *vreader_get_reader_by_name(const char *name); + +/* + * list tools for vcard_emul + */ +void vreader_init(void); +VReaderStatus vreader_add_reader(VReader *reader); +VReaderStatus vreader_remove_reader(VReader *reader); +VReaderStatus vreader_insert_card(VReader *reader, VCard *card); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vreadert.h qemu-kvm-0.15.0+noroms/libcacard/vreadert.h --- qemu-kvm-0.14.1+noroms/libcacard/vreadert.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vreadert.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,24 @@ +/* + * 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 VREADERT_H +#define VREADERT_H 1 + +typedef enum { + VREADER_OK = 0, + VREADER_NO_CARD, + VREADER_OUT_OF_MEMORY +} VReaderStatus; + +typedef unsigned int vreader_id_t; +typedef struct VReaderStruct VReader; +typedef struct VReaderListStruct VReaderList; +typedef struct VReaderListEntryStruct VReaderListEntry; + +typedef struct VReaderEmulStruct VReaderEmul; +typedef void (*VReaderEmulFree)(VReaderEmul *); + +#endif + diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vscard_common.h qemu-kvm-0.15.0+noroms/libcacard/vscard_common.h --- qemu-kvm-0.14.1+noroms/libcacard/vscard_common.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vscard_common.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,178 @@ +/* Virtual Smart Card protocol definition + * + * This protocol is between a host using virtual smart card readers, + * and a client providing the smart cards, perhaps by emulating them or by + * access to real cards. + * + * Definitions for this protocol: + * Host - user of the card + * Client - owner of the card + * + * The current implementation passes the raw APDU's from 7816 and additionally + * contains messages to setup and teardown readers, handle insertion and + * removal of cards, negotiate the protocol via capabilities and provide + * for error responses. + * + * Copyright (c) 2011 Red Hat. + * + * 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 VSCARD_COMMON_H +#define VSCARD_COMMON_H + +#include + +#define VERSION_MAJOR_BITS 11 +#define VERSION_MIDDLE_BITS 11 +#define VERSION_MINOR_BITS 10 + +#define MAKE_VERSION(major, middle, minor) \ + ((major << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \ + | (middle << VERSION_MINOR_BITS) \ + | (minor)) + +/* + * IMPORTANT NOTE on VERSION + * + * The version below MUST be changed whenever a change in this file is made. + * + * The last digit, the minor, is for bug fix changes only. + * + * The middle digit is for backward / forward compatible changes, updates + * to the existing messages, addition of fields. + * + * The major digit is for a breaking change of protocol, presumably + * something that cannot be accomodated with the existing protocol. + */ + +#define VSCARD_VERSION MAKE_VERSION(0, 0, 2) + +typedef enum VSCMsgType { + VSC_Init = 1, + VSC_Error, + VSC_ReaderAdd, + VSC_ReaderRemove, + VSC_ATR, + VSC_CardRemove, + VSC_APDU, + VSC_Flush, + VSC_FlushComplete +} VSCMsgType; + +typedef enum VSCErrorCode { + VSC_SUCCESS = 0, + VSC_GENERAL_ERROR = 1, + VSC_CANNOT_ADD_MORE_READERS, + VSC_CARD_ALREAY_INSERTED, +} VSCErrorCode; + +#define VSCARD_UNDEFINED_READER_ID 0xffffffff +#define VSCARD_MINIMAL_READER_ID 0 + +#define VSCARD_MAGIC (*(uint32_t *)"VSCD") + +/* + * Header + * Each message starts with the header. + * type - message type + * reader_id - used by messages that are reader specific + * length - length of payload (not including header, i.e. zero for + * messages containing empty payloads) + */ +typedef struct VSCMsgHeader { + uint32_t type; + uint32_t reader_id; + uint32_t length; + uint8_t data[0]; +} VSCMsgHeader; + +/* + * VSCMsgInit Client <-> Host + * Client sends it on connection, with its own capabilities. + * Host replies with VSCMsgInit filling in its capabilities. + * + * It is not meant to be used for negotiation, i.e. sending more then + * once from any side, but could be used for that in the future. + */ +typedef struct VSCMsgInit { + uint32_t magic; + uint32_t version; + uint32_t capabilities[1]; /* receiver must check length, + array may grow in the future*/ +} VSCMsgInit; + +/* + * VSCMsgError Client <-> Host + * This message is a response to any of: + * Reader Add + * Reader Remove + * Card Remove + * If the operation was successful then VSC_SUCCESS + * is returned, other wise a specific error code. + */ +typedef struct VSCMsgError { + uint32_t code; +} VSCMsgError; + +/* + * VSCMsgReaderAdd Client -> Host + * Host replies with allocated reader id in VSCMsgError with code==SUCCESS. + * + * name - name of the reader on client side, UTF-8 encoded. Only used + * for client presentation (may be translated to the device presented to the + * guest), protocol wise only reader_id is important. + */ +typedef struct VSCMsgReaderAdd { + uint8_t name[0]; +} VSCMsgReaderAdd; + +/* + * VSCMsgReaderRemove Client -> Host + * The client's reader has been removed. + */ +typedef struct VSCMsgReaderRemove { +} VSCMsgReaderRemove; + +/* + * VSCMsgATR Client -> Host + * Answer to reset. Sent for card insertion or card reset. The reset/insertion + * happens on the client side, they do not require any action from the host. + */ +typedef struct VSCMsgATR { + uint8_t atr[0]; +} VSCMsgATR; + +/* + * VSCMsgCardRemove Client -> Host + * The client card has been removed. + */ +typedef struct VSCMsgCardRemove { +} VSCMsgCardRemove; + +/* + * VSCMsgAPDU Client <-> Host + * Main reason of existence. Transfer a single APDU in either direction. + */ +typedef struct VSCMsgAPDU { + uint8_t data[0]; +} VSCMsgAPDU; + +/* + * VSCMsgFlush Host -> Client + * Request client to send a FlushComplete message when it is done + * servicing all outstanding APDUs + */ +typedef struct VSCMsgFlush { +} VSCMsgFlush; + +/* + * VSCMsgFlush Client -> Host + * Client response to Flush after all APDUs have been processed and + * responses sent. + */ +typedef struct VSCMsgFlushComplete { +} VSCMsgFlushComplete; + +#endif /* VSCARD_COMMON_H */ diff -Nru qemu-kvm-0.14.1+noroms/libcacard/vscclient.c qemu-kvm-0.15.0+noroms/libcacard/vscclient.c --- qemu-kvm-0.14.1+noroms/libcacard/vscclient.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/libcacard/vscclient.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,652 @@ +/* + * Tester for VSCARD protocol, client side. + * + * Can be used with ccid-card-passthru. + * + * Copyright (c) 2011 Red Hat. + * Written by Alon Levy. + * + * 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 + +#include "qemu-common.h" +#include "qemu-thread.h" +#include "qemu_socket.h" + +#include "vscard_common.h" + +#include "vreader.h" +#include "vcard_emul.h" +#include "vevent.h" + +int verbose; + +int sock; + +static void +print_byte_array( + uint8_t *arrBytes, + unsigned int nSize +) { + int i; + for (i = 0; i < nSize; i++) { + printf("%02X ", arrBytes[i]); + } + printf("\n"); +} + +static void +print_usage(void) { + printf("vscclient [-c .. -e -d %s] " + " \n", +#ifdef USE_PASSTHRU + " -p"); + printf(" -p use passthrough mode\n"); +#else + ""); +#endif + vcard_emul_usage(); +} + +static QemuMutex write_lock; + +static int +send_msg( + VSCMsgType type, + uint32_t reader_id, + const void *msg, + unsigned int length +) { + int rv; + VSCMsgHeader mhHeader; + + qemu_mutex_lock(&write_lock); + + if (verbose > 10) { + printf("sending type=%d id=%d, len =%d (0x%x)\n", + type, reader_id, length, length); + } + + mhHeader.type = htonl(type); + mhHeader.reader_id = 0; + mhHeader.length = htonl(length); + rv = write(sock, &mhHeader, sizeof(mhHeader)); + if (rv < 0) { + /* Error */ + fprintf(stderr, "write header error\n"); + close(sock); + qemu_mutex_unlock(&write_lock); + return 16; + } + rv = write(sock, msg, length); + if (rv < 0) { + /* Error */ + fprintf(stderr, "write error\n"); + close(sock); + qemu_mutex_unlock(&write_lock); + return 16; + } + qemu_mutex_unlock(&write_lock); + + return 0; +} + +static VReader *pending_reader; +static QemuMutex pending_reader_lock; +static QemuCond pending_reader_condition; + +#define MAX_ATR_LEN 40 +static void * +event_thread(void *arg) +{ + unsigned char atr[MAX_ATR_LEN]; + int atr_len = MAX_ATR_LEN; + VEvent *event = NULL; + unsigned int reader_id; + + + while (1) { + const char *reader_name; + + event = vevent_wait_next_vevent(); + if (event == NULL) { + break; + } + reader_id = vreader_get_id(event->reader); + if (reader_id == VSCARD_UNDEFINED_READER_ID && + event->type != VEVENT_READER_INSERT) { + /* ignore events from readers qemu has rejected */ + /* if qemu is still deciding on this reader, wait to see if need to + * forward this event */ + qemu_mutex_lock(&pending_reader_lock); + if (!pending_reader || (pending_reader != event->reader)) { + /* wasn't for a pending reader, this reader has already been + * rejected by qemu */ + qemu_mutex_unlock(&pending_reader_lock); + vevent_delete(event); + continue; + } + /* this reader hasn't been told it's status from qemu yet, wait for + * that status */ + while (pending_reader != NULL) { + qemu_cond_wait(&pending_reader_condition, &pending_reader_lock); + } + qemu_mutex_unlock(&pending_reader_lock); + /* now recheck the id */ + reader_id = vreader_get_id(event->reader); + if (reader_id == VSCARD_UNDEFINED_READER_ID) { + /* this reader was rejected */ + vevent_delete(event); + continue; + } + /* reader was accepted, now forward the event */ + } + switch (event->type) { + case VEVENT_READER_INSERT: + /* tell qemu to insert a new CCID reader */ + /* wait until qemu has responded to our first reader insert + * before we send a second. That way we won't confuse the responses + * */ + qemu_mutex_lock(&pending_reader_lock); + while (pending_reader != NULL) { + qemu_cond_wait(&pending_reader_condition, &pending_reader_lock); + } + pending_reader = vreader_reference(event->reader); + qemu_mutex_unlock(&pending_reader_lock); + reader_name = vreader_get_name(event->reader); + if (verbose > 10) { + printf(" READER INSERT: %s\n", reader_name); + } + send_msg(VSC_ReaderAdd, + reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */ + NULL, 0 /* TODO reader_name, strlen(reader_name) */); + break; + case VEVENT_READER_REMOVE: + /* future, tell qemu that an old CCID reader has been removed */ + if (verbose > 10) { + printf(" READER REMOVE: %d\n", reader_id); + } + send_msg(VSC_ReaderRemove, reader_id, NULL, 0); + break; + case VEVENT_CARD_INSERT: + /* get the ATR (intended as a response to a power on from the + * reader */ + atr_len = MAX_ATR_LEN; + vreader_power_on(event->reader, atr, &atr_len); + /* ATR call functions as a Card Insert event */ + if (verbose > 10) { + printf(" CARD INSERT %d: ", reader_id); + print_byte_array(atr, atr_len); + } + send_msg(VSC_ATR, reader_id, atr, atr_len); + break; + case VEVENT_CARD_REMOVE: + /* Card removed */ + if (verbose > 10) { + printf(" CARD REMOVE %d:\n", reader_id); + } + send_msg(VSC_CardRemove, reader_id, NULL, 0); + break; + default: + break; + } + vevent_delete(event); + } + return NULL; +} + + +static unsigned int +get_id_from_string(char *string, unsigned int default_id) +{ + unsigned int id = atoi(string); + + /* don't accidentally swith to zero because no numbers have been supplied */ + if ((id == 0) && *string != '0') { + return default_id; + } + return id; +} + +static void +do_command(void) +{ + char inbuf[255]; + char *string; + VCardEmulError error; + static unsigned int default_reader_id; + unsigned int reader_id; + VReader *reader = NULL; + + reader_id = default_reader_id; + string = fgets(inbuf, sizeof(inbuf), stdin); + if (string != NULL) { + if (strncmp(string, "exit", 4) == 0) { + /* remove all the readers */ + VReaderList *list = vreader_get_reader_list(); + VReaderListEntry *reader_entry; + printf("Active Readers:\n"); + for (reader_entry = vreader_list_get_first(list); reader_entry; + reader_entry = vreader_list_get_next(reader_entry)) { + VReader *reader = vreader_list_get_reader(reader_entry); + vreader_id_t reader_id; + reader_id = vreader_get_id(reader); + if (reader_id == -1) { + continue; + } + /* be nice and signal card removal first (qemu probably should + * do this itself) */ + if (vreader_card_is_present(reader) == VREADER_OK) { + send_msg(VSC_CardRemove, reader_id, NULL, 0); + } + send_msg(VSC_ReaderRemove, reader_id, NULL, 0); + } + exit(0); + } else if (strncmp(string, "insert", 6) == 0) { + if (string[6] == ' ') { + reader_id = get_id_from_string(&string[7], reader_id); + } + reader = vreader_get_reader_by_id(reader_id); + if (reader != NULL) { + error = vcard_emul_force_card_insert(reader); + printf("insert %s, returned %d\n", + reader ? vreader_get_name(reader) + : "invalid reader", error); + } else { + printf("no reader by id %d found\n", reader_id); + } + } else if (strncmp(string, "remove", 6) == 0) { + if (string[6] == ' ') { + reader_id = get_id_from_string(&string[7], reader_id); + } + reader = vreader_get_reader_by_id(reader_id); + if (reader != NULL) { + error = vcard_emul_force_card_remove(reader); + printf("remove %s, returned %d\n", + reader ? vreader_get_name(reader) + : "invalid reader", error); + } else { + printf("no reader by id %d found\n", reader_id); + } + } else if (strncmp(string, "select", 6) == 0) { + if (string[6] == ' ') { + reader_id = get_id_from_string(&string[7], + VSCARD_UNDEFINED_READER_ID); + } + if (reader_id != VSCARD_UNDEFINED_READER_ID) { + reader = vreader_get_reader_by_id(reader_id); + } + if (reader) { + printf("Selecting reader %d, %s\n", reader_id, + vreader_get_name(reader)); + default_reader_id = reader_id; + } else { + printf("Reader with id %d not found\n", reader_id); + } + } else if (strncmp(string, "debug", 5) == 0) { + if (string[5] == ' ') { + verbose = get_id_from_string(&string[6], 0); + } + printf("debug level = %d\n", verbose); + } else if (strncmp(string, "list", 4) == 0) { + VReaderList *list = vreader_get_reader_list(); + VReaderListEntry *reader_entry; + printf("Active Readers:\n"); + for (reader_entry = vreader_list_get_first(list); reader_entry; + reader_entry = vreader_list_get_next(reader_entry)) { + VReader *reader = vreader_list_get_reader(reader_entry); + vreader_id_t reader_id; + reader_id = vreader_get_id(reader); + if (reader_id == -1) { + continue; + } + printf("%3d %s %s\n", reader_id, + vreader_card_is_present(reader) == VREADER_OK ? + "CARD_PRESENT" : " ", + vreader_get_name(reader)); + } + printf("Inactive Readers:\n"); + for (reader_entry = vreader_list_get_first(list); reader_entry; + reader_entry = vreader_list_get_next(reader_entry)) { + VReader *reader = vreader_list_get_reader(reader_entry); + vreader_id_t reader_id; + reader_id = vreader_get_id(reader); + if (reader_id != -1) { + continue; + } + + printf("INA %s %s\n", + vreader_card_is_present(reader) == VREADER_OK ? + "CARD_PRESENT" : " ", + vreader_get_name(reader)); + } + } else if (*string != 0) { + printf("valid commands:\n"); + printf("insert [reader_id]\n"); + printf("remove [reader_id]\n"); + printf("select reader_id\n"); + printf("list\n"); + printf("debug [level]\n"); + printf("exit\n"); + } + } + vreader_free(reader); + printf("> "); + fflush(stdout); +} + + +#define APDUBufSize 270 + +/* just for ease of parsing command line arguments. */ +#define MAX_CERTS 100 + +static int +connect_to_qemu( + const char *host, + const char *port +) { + struct addrinfo hints; + struct addrinfo *server; + int ret; + + sock = qemu_socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + /* Error */ + fprintf(stderr, "Error opening socket!\n"); + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + + ret = getaddrinfo(host, port, &hints, &server); + + if (ret != 0) { + /* Error */ + fprintf(stderr, "getaddrinfo failed\n"); + return 5; + } + + if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) { + /* Error */ + fprintf(stderr, "Could not connect\n"); + return 5; + } + if (verbose) { + printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader)); + } + return sock; +} + +static int on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) +{ + uint32_t *capabilities = (incoming->capabilities); + int num_capabilities = + 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); + int i; + int rv; + pthread_t thread_id; + + incoming->version = ntohl(incoming->version); + if (incoming->version != VSCARD_VERSION) { + if (verbose > 0) { + printf("warning: host has version %d, we have %d\n", + verbose, VSCARD_VERSION); + } + } + if (incoming->magic != VSCARD_MAGIC) { + printf("unexpected magic: got %d, expected %d\n", + incoming->magic, VSCARD_MAGIC); + return -1; + } + for (i = 0 ; i < num_capabilities; ++i) { + capabilities[i] = ntohl(capabilities[i]); + } + /* Future: check capabilities */ + /* remove whatever reader might be left in qemu, + * in case of an unclean previous exit. */ + send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0); + /* launch the event_thread. This will trigger reader adds for all the + * existing readers */ + rv = pthread_create(&thread_id, NULL, event_thread, NULL); + if (rv < 0) { + perror("pthread_create"); + return rv; + } + return 0; +} + +int +main( + int argc, + char *argv[] +) { + char *qemu_host; + char *qemu_port; + VSCMsgHeader mhHeader; + VSCMsgError *error_msg; + + int rv; + int dwSendLength; + int dwRecvLength; + uint8_t pbRecvBuffer[APDUBufSize]; + uint8_t pbSendBuffer[APDUBufSize]; + VReaderStatus reader_status; + VReader *reader = NULL; + VCardEmulOptions *command_line_options = NULL; + + char *cert_names[MAX_CERTS]; + char *emul_args = NULL; + int cert_count = 0; + int c; + + while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { + switch (c) { + case 'c': + if (cert_count >= MAX_CERTS) { + printf("too many certificates (max = %d)\n", MAX_CERTS); + exit(5); + } + cert_names[cert_count++] = optarg; + break; + case 'e': + emul_args = optarg; + break; + case 'p': + print_usage(); + exit(4); + break; + case 'd': + verbose = get_id_from_string(optarg, 1); + break; + } + } + + if (argc - optind != 2) { + print_usage(); + exit(4); + } + + if (cert_count > 0) { + char *new_args; + int len, i; + /* if we've given some -c options, we clearly we want do so some + * software emulation. add that emulation now. this is NSS Emulator + * specific */ + if (emul_args == NULL) { + emul_args = (char *)"db=\"/etc/pki/nssdb\""; + } +#define SOFT_STRING ",soft=(,Virtual Reader,CAC,," + /* 2 == close paren & null */ + len = strlen(emul_args) + strlen(SOFT_STRING) + 2; + for (i = 0; i < cert_count; i++) { + len += strlen(cert_names[i])+1; /* 1 == comma */ + } + new_args = qemu_malloc(len); + strcpy(new_args, emul_args); + strcat(new_args, SOFT_STRING); + for (i = 0; i < cert_count; i++) { + strcat(new_args, cert_names[i]); + strcat(new_args, ","); + } + strcat(new_args, ")"); + emul_args = new_args; + } + if (emul_args) { + command_line_options = vcard_emul_options(emul_args); + } + + qemu_host = strdup(argv[argc - 2]); + qemu_port = strdup(argv[argc - 1]); + sock = connect_to_qemu(qemu_host, qemu_port); + + qemu_mutex_init(&write_lock); + qemu_mutex_init(&pending_reader_lock); + qemu_cond_init(&pending_reader_condition); + + vcard_emul_init(command_line_options); + + printf("> "); + fflush(stdout); + + /* Send init message, Host responds (and then we send reader attachments) */ + VSCMsgInit init = { + .version = htonl(VSCARD_VERSION), + .magic = VSCARD_MAGIC, + .capabilities = {0} + }; + send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init)); + + do { + fd_set fds; + + FD_ZERO(&fds); + FD_SET(1, &fds); + FD_SET(sock, &fds); + + /* waiting on input from the socket */ + rv = select(sock+1, &fds, NULL, NULL, NULL); + if (rv < 0) { + /* handle error */ + perror("select"); + return 7; + } + if (FD_ISSET(1, &fds)) { + do_command(); + } + if (!FD_ISSET(sock, &fds)) { + continue; + } + + rv = read(sock, &mhHeader, sizeof(mhHeader)); + if (rv < sizeof(mhHeader)) { + /* Error */ + if (rv < 0) { + perror("header read error\n"); + } else { + fprintf(stderr, "header short read %d\n", rv); + } + return 8; + } + mhHeader.type = ntohl(mhHeader.type); + mhHeader.reader_id = ntohl(mhHeader.reader_id); + mhHeader.length = ntohl(mhHeader.length); + if (verbose) { + printf("Header: type=%d, reader_id=%d length=%d (0x%x)\n", + mhHeader.type, mhHeader.reader_id, mhHeader.length, + mhHeader.length); + } + switch (mhHeader.type) { + case VSC_APDU: + case VSC_Flush: + case VSC_Error: + case VSC_Init: + rv = read(sock, pbSendBuffer, mhHeader.length); + break; + default: + fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); + return 0; + } + switch (mhHeader.type) { + case VSC_APDU: + if (rv < 0) { + /* Error */ + fprintf(stderr, "read error\n"); + close(sock); + return 8; + } + if (verbose) { + printf(" recv APDU: "); + print_byte_array(pbSendBuffer, mhHeader.length); + } + /* Transmit recieved APDU */ + dwSendLength = mhHeader.length; + dwRecvLength = sizeof(pbRecvBuffer); + reader = vreader_get_reader_by_id(mhHeader.reader_id); + reader_status = vreader_xfr_bytes(reader, + pbSendBuffer, dwSendLength, + pbRecvBuffer, &dwRecvLength); + if (reader_status == VREADER_OK) { + mhHeader.length = dwRecvLength; + if (verbose) { + printf(" send response: "); + print_byte_array(pbRecvBuffer, mhHeader.length); + } + send_msg(VSC_APDU, mhHeader.reader_id, + pbRecvBuffer, dwRecvLength); + } else { + rv = reader_status; /* warning: not meaningful */ + send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); + } + vreader_free(reader); + reader = NULL; /* we've freed it, don't use it by accident + again */ + break; + case VSC_Flush: + /* TODO: actually flush */ + send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); + break; + case VSC_Error: + error_msg = (VSCMsgError *) pbSendBuffer; + if (error_msg->code == VSC_SUCCESS) { + qemu_mutex_lock(&pending_reader_lock); + if (pending_reader) { + vreader_set_id(pending_reader, mhHeader.reader_id); + vreader_free(pending_reader); + pending_reader = NULL; + qemu_cond_signal(&pending_reader_condition); + } + qemu_mutex_unlock(&pending_reader_lock); + break; + } + printf("warning: qemu refused to add reader\n"); + if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { + /* clear pending reader, qemu can't handle any more */ + qemu_mutex_lock(&pending_reader_lock); + if (pending_reader) { + pending_reader = NULL; + /* make sure the event loop doesn't hang */ + qemu_cond_signal(&pending_reader_condition); + } + qemu_mutex_unlock(&pending_reader_lock); + } + break; + case VSC_Init: + if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { + return -1; + } + break; + default: + printf("Default\n"); + return 0; + } + } while (rv >= 0); + + return 0; +} diff -Nru qemu-kvm-0.14.1+noroms/libfdt_env.h qemu-kvm-0.15.0+noroms/libfdt_env.h --- qemu-kvm-0.14.1+noroms/libfdt_env.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/libfdt_env.h 2011-08-09 07:40:29.000000000 -0500 @@ -19,13 +19,9 @@ #ifndef _LIBFDT_ENV_H #define _LIBFDT_ENV_H -#include -#include -#include -#include -#include +#include "bswap.h" -#if __BYTE_ORDER == __BIG_ENDIAN +#ifdef HOST_WORDS_BIGENDIAN #define fdt32_to_cpu(x) (x) #define cpu_to_fdt32(x) (x) #define fdt64_to_cpu(x) (x) diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/asm-powerpc/kvm.h qemu-kvm-0.15.0+noroms/linux-headers/asm-powerpc/kvm.h --- qemu-kvm-0.14.1+noroms/linux-headers/asm-powerpc/kvm.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/asm-powerpc/kvm.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,275 @@ +/* + * 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. 2007 + * + * Authors: Hollis Blanchard + */ + +#ifndef __LINUX_KVM_POWERPC_H +#define __LINUX_KVM_POWERPC_H + +#include + +struct kvm_regs { + __u64 pc; + __u64 cr; + __u64 ctr; + __u64 lr; + __u64 xer; + __u64 msr; + __u64 srr0; + __u64 srr1; + __u64 pid; + + __u64 sprg0; + __u64 sprg1; + __u64 sprg2; + __u64 sprg3; + __u64 sprg4; + __u64 sprg5; + __u64 sprg6; + __u64 sprg7; + + __u64 gpr[32]; +}; + +#define KVM_SREGS_E_IMPL_NONE 0 +#define KVM_SREGS_E_IMPL_FSL 1 + +#define KVM_SREGS_E_FSL_PIDn (1 << 0) /* PID1/PID2 */ + +/* + * Feature bits indicate which sections of the sregs struct are valid, + * both in KVM_GET_SREGS and KVM_SET_SREGS. On KVM_SET_SREGS, registers + * corresponding to unset feature bits will not be modified. This allows + * restoring a checkpoint made without that feature, while keeping the + * default values of the new registers. + * + * KVM_SREGS_E_BASE contains: + * CSRR0/1 (refers to SRR2/3 on 40x) + * ESR + * DEAR + * MCSR + * TSR + * TCR + * DEC + * TB + * VRSAVE (USPRG0) + */ +#define KVM_SREGS_E_BASE (1 << 0) + +/* + * KVM_SREGS_E_ARCH206 contains: + * + * PIR + * MCSRR0/1 + * DECAR + * IVPR + */ +#define KVM_SREGS_E_ARCH206 (1 << 1) + +/* + * Contains EPCR, plus the upper half of 64-bit registers + * that are 32-bit on 32-bit implementations. + */ +#define KVM_SREGS_E_64 (1 << 2) + +#define KVM_SREGS_E_SPRG8 (1 << 3) +#define KVM_SREGS_E_MCIVPR (1 << 4) + +/* + * IVORs are used -- contains IVOR0-15, plus additional IVORs + * in combination with an appropriate feature bit. + */ +#define KVM_SREGS_E_IVOR (1 << 5) + +/* + * Contains MAS0-4, MAS6-7, TLBnCFG, MMUCFG. + * Also TLBnPS if MMUCFG[MAVN] = 1. + */ +#define KVM_SREGS_E_ARCH206_MMU (1 << 6) + +/* DBSR, DBCR, IAC, DAC, DVC */ +#define KVM_SREGS_E_DEBUG (1 << 7) + +/* Enhanced debug -- DSRR0/1, SPRG9 */ +#define KVM_SREGS_E_ED (1 << 8) + +/* Embedded Floating Point (SPE) -- IVOR32-34 if KVM_SREGS_E_IVOR */ +#define KVM_SREGS_E_SPE (1 << 9) + +/* External Proxy (EXP) -- EPR */ +#define KVM_SREGS_EXP (1 << 10) + +/* External PID (E.PD) -- EPSC/EPLC */ +#define KVM_SREGS_E_PD (1 << 11) + +/* Processor Control (E.PC) -- IVOR36-37 if KVM_SREGS_E_IVOR */ +#define KVM_SREGS_E_PC (1 << 12) + +/* Page table (E.PT) -- EPTCFG */ +#define KVM_SREGS_E_PT (1 << 13) + +/* Embedded Performance Monitor (E.PM) -- IVOR35 if KVM_SREGS_E_IVOR */ +#define KVM_SREGS_E_PM (1 << 14) + +/* + * Special updates: + * + * Some registers may change even while a vcpu is not running. + * To avoid losing these changes, by default these registers are + * not updated by KVM_SET_SREGS. To force an update, set the bit + * in u.e.update_special corresponding to the register to be updated. + * + * The update_special field is zero on return from KVM_GET_SREGS. + * + * When restoring a checkpoint, the caller can set update_special + * to 0xffffffff to ensure that everything is restored, even new features + * that the caller doesn't know about. + */ +#define KVM_SREGS_E_UPDATE_MCSR (1 << 0) +#define KVM_SREGS_E_UPDATE_TSR (1 << 1) +#define KVM_SREGS_E_UPDATE_DEC (1 << 2) +#define KVM_SREGS_E_UPDATE_DBSR (1 << 3) + +/* + * In KVM_SET_SREGS, reserved/pad fields must be left untouched from a + * previous KVM_GET_REGS. + * + * Unless otherwise indicated, setting any register with KVM_SET_SREGS + * directly sets its value. It does not trigger any special semantics such + * as write-one-to-clear. Calling KVM_SET_SREGS on an unmodified struct + * just received from KVM_GET_SREGS is always a no-op. + */ +struct kvm_sregs { + __u32 pvr; + union { + struct { + __u64 sdr1; + struct { + struct { + __u64 slbe; + __u64 slbv; + } slb[64]; + } ppc64; + struct { + __u32 sr[16]; + __u64 ibat[8]; + __u64 dbat[8]; + } ppc32; + } s; + struct { + union { + struct { /* KVM_SREGS_E_IMPL_FSL */ + __u32 features; /* KVM_SREGS_E_FSL_ */ + __u32 svr; + __u64 mcar; + __u32 hid0; + + /* KVM_SREGS_E_FSL_PIDn */ + __u32 pid1, pid2; + } fsl; + __u8 pad[256]; + } impl; + + __u32 features; /* KVM_SREGS_E_ */ + __u32 impl_id; /* KVM_SREGS_E_IMPL_ */ + __u32 update_special; /* KVM_SREGS_E_UPDATE_ */ + __u32 pir; /* read-only */ + __u64 sprg8; + __u64 sprg9; /* E.ED */ + __u64 csrr0; + __u64 dsrr0; /* E.ED */ + __u64 mcsrr0; + __u32 csrr1; + __u32 dsrr1; /* E.ED */ + __u32 mcsrr1; + __u32 esr; + __u64 dear; + __u64 ivpr; + __u64 mcivpr; + __u64 mcsr; /* KVM_SREGS_E_UPDATE_MCSR */ + + __u32 tsr; /* KVM_SREGS_E_UPDATE_TSR */ + __u32 tcr; + __u32 decar; + __u32 dec; /* KVM_SREGS_E_UPDATE_DEC */ + + /* + * Userspace can read TB directly, but the + * value reported here is consistent with "dec". + * + * Read-only. + */ + __u64 tb; + + __u32 dbsr; /* KVM_SREGS_E_UPDATE_DBSR */ + __u32 dbcr[3]; + __u32 iac[4]; + __u32 dac[2]; + __u32 dvc[2]; + __u8 num_iac; /* read-only */ + __u8 num_dac; /* read-only */ + __u8 num_dvc; /* read-only */ + __u8 pad; + + __u32 epr; /* EXP */ + __u32 vrsave; /* a.k.a. USPRG0 */ + __u32 epcr; /* KVM_SREGS_E_64 */ + + __u32 mas0; + __u32 mas1; + __u64 mas2; + __u64 mas7_3; + __u32 mas4; + __u32 mas6; + + __u32 ivor_low[16]; /* IVOR0-15 */ + __u32 ivor_high[18]; /* IVOR32+, plus room to expand */ + + __u32 mmucfg; /* read-only */ + __u32 eptcfg; /* E.PT, read-only */ + __u32 tlbcfg[4];/* read-only */ + __u32 tlbps[4]; /* read-only */ + + __u32 eplc, epsc; /* E.PD */ + } e; + __u8 pad[1020]; + } u; +}; + +struct kvm_fpu { + __u64 fpr[32]; +}; + +struct kvm_debug_exit_arch { +}; + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { +}; + +#define KVM_REG_MASK 0x001f +#define KVM_REG_EXT_MASK 0xffe0 +#define KVM_REG_GPR 0x0000 +#define KVM_REG_FPR 0x0020 +#define KVM_REG_QPR 0x0040 +#define KVM_REG_FQPR 0x0060 + +#define KVM_INTERRUPT_SET -1U +#define KVM_INTERRUPT_UNSET -2U +#define KVM_INTERRUPT_SET_LEVEL -3U + +#endif /* __LINUX_KVM_POWERPC_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/asm-powerpc/kvm_para.h qemu-kvm-0.15.0+noroms/linux-headers/asm-powerpc/kvm_para.h --- qemu-kvm-0.14.1+noroms/linux-headers/asm-powerpc/kvm_para.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/asm-powerpc/kvm_para.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,53 @@ +/* + * 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 __POWERPC_KVM_PARA_H__ +#define __POWERPC_KVM_PARA_H__ + +#include + +struct kvm_vcpu_arch_shared { + __u64 scratch1; + __u64 scratch2; + __u64 scratch3; + __u64 critical; /* Guest may not get interrupts if == r1 */ + __u64 sprg0; + __u64 sprg1; + __u64 sprg2; + __u64 sprg3; + __u64 srr0; + __u64 srr1; + __u64 dar; + __u64 msr; + __u32 dsisr; + __u32 int_pending; /* Tells the guest if we have an interrupt */ + __u32 sr[16]; +}; + +#define KVM_SC_MAGIC_R0 0x4b564d21 /* "KVM!" */ +#define HC_VENDOR_KVM (42 << 16) +#define HC_EV_SUCCESS 0 +#define HC_EV_UNIMPLEMENTED 12 + +#define KVM_FEATURE_MAGIC_PAGE 1 + +#define KVM_MAGIC_FEAT_SR (1 << 0) + + +#endif /* __POWERPC_KVM_PARA_H__ */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/asm-s390/kvm.h qemu-kvm-0.15.0+noroms/linux-headers/asm-s390/kvm.h --- qemu-kvm-0.14.1+noroms/linux-headers/asm-s390/kvm.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/asm-s390/kvm.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,44 @@ +#ifndef __LINUX_KVM_S390_H +#define __LINUX_KVM_S390_H +/* + * asm-s390/kvm.h - KVM s390 specific structures and definitions + * + * Copyright IBM Corp. 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Carsten Otte + * Christian Borntraeger + */ +#include + +#define __KVM_S390 + +/* for KVM_GET_REGS and KVM_SET_REGS */ +struct kvm_regs { + /* general purpose regs for s390 */ + __u64 gprs[16]; +}; + +/* for KVM_GET_SREGS and KVM_SET_SREGS */ +struct kvm_sregs { + __u32 acrs[16]; + __u64 crs[16]; +}; + +/* for KVM_GET_FPU and KVM_SET_FPU */ +struct kvm_fpu { + __u32 fpc; + __u64 fprs[16]; +}; + +struct kvm_debug_exit_arch { +}; + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { +}; + +#endif diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/asm-s390/kvm_para.h qemu-kvm-0.15.0+noroms/linux-headers/asm-s390/kvm_para.h --- qemu-kvm-0.14.1+noroms/linux-headers/asm-s390/kvm_para.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/asm-s390/kvm_para.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,17 @@ +/* + * asm-s390/kvm_para.h - definition for paravirtual devices on s390 + * + * Copyright IBM Corp. 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Christian Borntraeger + */ + +#ifndef __S390_KVM_PARA_H +#define __S390_KVM_PARA_H + + +#endif /* __S390_KVM_PARA_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/asm-x86/hyperv.h qemu-kvm-0.15.0+noroms/linux-headers/asm-x86/hyperv.h --- qemu-kvm-0.14.1+noroms/linux-headers/asm-x86/hyperv.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/asm-x86/hyperv.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,193 @@ +#ifndef _ASM_X86_HYPERV_H +#define _ASM_X86_HYPERV_H + +#include + +/* + * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent + * is set by CPUID(HvCpuIdFunctionVersionAndFeatures). + */ +#define HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000 +#define HYPERV_CPUID_INTERFACE 0x40000001 +#define HYPERV_CPUID_VERSION 0x40000002 +#define HYPERV_CPUID_FEATURES 0x40000003 +#define HYPERV_CPUID_ENLIGHTMENT_INFO 0x40000004 +#define HYPERV_CPUID_IMPLEMENT_LIMITS 0x40000005 + +#define HYPERV_HYPERVISOR_PRESENT_BIT 0x80000000 +#define HYPERV_CPUID_MIN 0x40000005 +#define HYPERV_CPUID_MAX 0x4000ffff + +/* + * Feature identification. EAX indicates which features are available + * to the partition based upon the current partition privileges. + */ + +/* VP Runtime (HV_X64_MSR_VP_RUNTIME) available */ +#define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) +/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ +#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) +/* + * Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM + * and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available + */ +#define HV_X64_MSR_SYNIC_AVAILABLE (1 << 2) +/* + * Synthetic Timer MSRs (HV_X64_MSR_STIMER0_CONFIG through + * HV_X64_MSR_STIMER3_COUNT) available + */ +#define HV_X64_MSR_SYNTIMER_AVAILABLE (1 << 3) +/* + * APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR) + * are available + */ +#define HV_X64_MSR_APIC_ACCESS_AVAILABLE (1 << 4) +/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) available*/ +#define HV_X64_MSR_HYPERCALL_AVAILABLE (1 << 5) +/* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) available*/ +#define HV_X64_MSR_VP_INDEX_AVAILABLE (1 << 6) +/* Virtual system reset MSR (HV_X64_MSR_RESET) is available*/ +#define HV_X64_MSR_RESET_AVAILABLE (1 << 7) + /* + * Access statistics pages MSRs (HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE, + * HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE, HV_X64_MSR_STATS_VP_RETAIL_PAGE, + * HV_X64_MSR_STATS_VP_INTERNAL_PAGE) available + */ +#define HV_X64_MSR_STAT_PAGES_AVAILABLE (1 << 8) + +/* + * Feature identification: EBX indicates which flags were specified at + * partition creation. The format is the same as the partition creation + * flag structure defined in section Partition Creation Flags. + */ +#define HV_X64_CREATE_PARTITIONS (1 << 0) +#define HV_X64_ACCESS_PARTITION_ID (1 << 1) +#define HV_X64_ACCESS_MEMORY_POOL (1 << 2) +#define HV_X64_ADJUST_MESSAGE_BUFFERS (1 << 3) +#define HV_X64_POST_MESSAGES (1 << 4) +#define HV_X64_SIGNAL_EVENTS (1 << 5) +#define HV_X64_CREATE_PORT (1 << 6) +#define HV_X64_CONNECT_PORT (1 << 7) +#define HV_X64_ACCESS_STATS (1 << 8) +#define HV_X64_DEBUGGING (1 << 11) +#define HV_X64_CPU_POWER_MANAGEMENT (1 << 12) +#define HV_X64_CONFIGURE_PROFILER (1 << 13) + +/* + * Feature identification. EDX indicates which miscellaneous features + * are available to the partition. + */ +/* The MWAIT instruction is available (per section MONITOR / MWAIT) */ +#define HV_X64_MWAIT_AVAILABLE (1 << 0) +/* Guest debugging support is available */ +#define HV_X64_GUEST_DEBUGGING_AVAILABLE (1 << 1) +/* Performance Monitor support is available*/ +#define HV_X64_PERF_MONITOR_AVAILABLE (1 << 2) +/* Support for physical CPU dynamic partitioning events is available*/ +#define HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE (1 << 3) +/* + * Support for passing hypercall input parameter block via XMM + * registers is available + */ +#define HV_X64_HYPERCALL_PARAMS_XMM_AVAILABLE (1 << 4) +/* Support for a virtual guest idle state is available */ +#define HV_X64_GUEST_IDLE_STATE_AVAILABLE (1 << 5) + +/* + * Implementation recommendations. Indicates which behaviors the hypervisor + * recommends the OS implement for optimal performance. + */ + /* + * Recommend using hypercall for address space switches rather + * than MOV to CR3 instruction + */ +#define HV_X64_MWAIT_RECOMMENDED (1 << 0) +/* Recommend using hypercall for local TLB flushes rather + * than INVLPG or MOV to CR3 instructions */ +#define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED (1 << 1) +/* + * Recommend using hypercall for remote TLB flushes rather + * than inter-processor interrupts + */ +#define HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED (1 << 2) +/* + * Recommend using MSRs for accessing APIC registers + * EOI, ICR and TPR rather than their memory-mapped counterparts + */ +#define HV_X64_APIC_ACCESS_RECOMMENDED (1 << 3) +/* Recommend using the hypervisor-provided MSR to initiate a system RESET */ +#define HV_X64_SYSTEM_RESET_RECOMMENDED (1 << 4) +/* + * Recommend using relaxed timing for this partition. If used, + * the VM should disable any watchdog timeouts that rely on the + * timely delivery of external interrupts + */ +#define HV_X64_RELAXED_TIMING_RECOMMENDED (1 << 5) + +/* MSR used to identify the guest OS. */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 + +/* MSR used to setup pages used to communicate with the hypervisor. */ +#define HV_X64_MSR_HYPERCALL 0x40000001 + +/* MSR used to provide vcpu index */ +#define HV_X64_MSR_VP_INDEX 0x40000002 + +/* MSR used to read the per-partition time reference counter */ +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 + +/* Define the virtual APIC registers */ +#define HV_X64_MSR_EOI 0x40000070 +#define HV_X64_MSR_ICR 0x40000071 +#define HV_X64_MSR_TPR 0x40000072 +#define HV_X64_MSR_APIC_ASSIST_PAGE 0x40000073 + +/* Define synthetic interrupt controller model specific registers. */ +#define HV_X64_MSR_SCONTROL 0x40000080 +#define HV_X64_MSR_SVERSION 0x40000081 +#define HV_X64_MSR_SIEFP 0x40000082 +#define HV_X64_MSR_SIMP 0x40000083 +#define HV_X64_MSR_EOM 0x40000084 +#define HV_X64_MSR_SINT0 0x40000090 +#define HV_X64_MSR_SINT1 0x40000091 +#define HV_X64_MSR_SINT2 0x40000092 +#define HV_X64_MSR_SINT3 0x40000093 +#define HV_X64_MSR_SINT4 0x40000094 +#define HV_X64_MSR_SINT5 0x40000095 +#define HV_X64_MSR_SINT6 0x40000096 +#define HV_X64_MSR_SINT7 0x40000097 +#define HV_X64_MSR_SINT8 0x40000098 +#define HV_X64_MSR_SINT9 0x40000099 +#define HV_X64_MSR_SINT10 0x4000009A +#define HV_X64_MSR_SINT11 0x4000009B +#define HV_X64_MSR_SINT12 0x4000009C +#define HV_X64_MSR_SINT13 0x4000009D +#define HV_X64_MSR_SINT14 0x4000009E +#define HV_X64_MSR_SINT15 0x4000009F + + +#define HV_X64_MSR_HYPERCALL_ENABLE 0x00000001 +#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12 +#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK \ + (~((1ull << HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) - 1)) + +/* Declare the various hypercall operations. */ +#define HV_X64_HV_NOTIFY_LONG_SPIN_WAIT 0x0008 + +#define HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE 0x00000001 +#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT 12 +#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \ + (~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1)) + +#define HV_PROCESSOR_POWER_STATE_C0 0 +#define HV_PROCESSOR_POWER_STATE_C1 1 +#define HV_PROCESSOR_POWER_STATE_C2 2 +#define HV_PROCESSOR_POWER_STATE_C3 3 + +/* hypercall status code */ +#define HV_STATUS_SUCCESS 0 +#define HV_STATUS_INVALID_HYPERCALL_CODE 2 +#define HV_STATUS_INVALID_HYPERCALL_INPUT 3 +#define HV_STATUS_INVALID_ALIGNMENT 4 + +#endif diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/asm-x86/kvm.h qemu-kvm-0.15.0+noroms/linux-headers/asm-x86/kvm.h --- qemu-kvm-0.14.1+noroms/linux-headers/asm-x86/kvm.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/asm-x86/kvm.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,324 @@ +#ifndef _ASM_X86_KVM_H +#define _ASM_X86_KVM_H + +/* + * KVM x86 specific structures and definitions + * + */ + +#include +#include + +/* Select x86 specific features in */ +#define __KVM_HAVE_PIT +#define __KVM_HAVE_IOAPIC +#define __KVM_HAVE_DEVICE_ASSIGNMENT +#define __KVM_HAVE_MSI +#define __KVM_HAVE_USER_NMI +#define __KVM_HAVE_GUEST_DEBUG +#define __KVM_HAVE_MSIX +#define __KVM_HAVE_MCE +#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 + +struct kvm_memory_alias { + __u32 slot; /* this has a different namespace than memory slots */ + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; + __u64 target_phys_addr; +}; + +/* for KVM_GET_IRQCHIP and KVM_SET_IRQCHIP */ +struct kvm_pic_state { + __u8 last_irr; /* edge detection */ + __u8 irr; /* interrupt request register */ + __u8 imr; /* interrupt mask register */ + __u8 isr; /* interrupt service register */ + __u8 priority_add; /* highest irq priority */ + __u8 irq_base; + __u8 read_reg_select; + __u8 poll; + __u8 special_mask; + __u8 init_state; + __u8 auto_eoi; + __u8 rotate_on_auto_eoi; + __u8 special_fully_nested_mode; + __u8 init4; /* true if 4 byte init */ + __u8 elcr; /* PIIX edge/trigger selection */ + __u8 elcr_mask; +}; + +#define KVM_IOAPIC_NUM_PINS 24 +struct kvm_ioapic_state { + __u64 base_address; + __u32 ioregsel; + __u32 id; + __u32 irr; + __u32 pad; + union { + __u64 bits; + struct { + __u8 vector; + __u8 delivery_mode:3; + __u8 dest_mode:1; + __u8 delivery_status:1; + __u8 polarity:1; + __u8 remote_irr:1; + __u8 trig_mode:1; + __u8 mask:1; + __u8 reserve:7; + __u8 reserved[4]; + __u8 dest_id; + } fields; + } redirtbl[KVM_IOAPIC_NUM_PINS]; +}; + +#define KVM_IRQCHIP_PIC_MASTER 0 +#define KVM_IRQCHIP_PIC_SLAVE 1 +#define KVM_IRQCHIP_IOAPIC 2 +#define KVM_NR_IRQCHIPS 3 + +/* for KVM_GET_REGS and KVM_SET_REGS */ +struct kvm_regs { + /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */ + __u64 rax, rbx, rcx, rdx; + __u64 rsi, rdi, rsp, rbp; + __u64 r8, r9, r10, r11; + __u64 r12, r13, r14, r15; + __u64 rip, rflags; +}; + +/* for KVM_GET_LAPIC and KVM_SET_LAPIC */ +#define KVM_APIC_REG_SIZE 0x400 +struct kvm_lapic_state { + char regs[KVM_APIC_REG_SIZE]; +}; + +struct kvm_segment { + __u64 base; + __u32 limit; + __u16 selector; + __u8 type; + __u8 present, dpl, db, s, l, g, avl; + __u8 unusable; + __u8 padding; +}; + +struct kvm_dtable { + __u64 base; + __u16 limit; + __u16 padding[3]; +}; + + +/* for KVM_GET_SREGS and KVM_SET_SREGS */ +struct kvm_sregs { + /* out (KVM_GET_SREGS) / in (KVM_SET_SREGS) */ + struct kvm_segment cs, ds, es, fs, gs, ss; + struct kvm_segment tr, ldt; + struct kvm_dtable gdt, idt; + __u64 cr0, cr2, cr3, cr4, cr8; + __u64 efer; + __u64 apic_base; + __u64 interrupt_bitmap[(KVM_NR_INTERRUPTS + 63) / 64]; +}; + +/* for KVM_GET_FPU and KVM_SET_FPU */ +struct kvm_fpu { + __u8 fpr[8][16]; + __u16 fcw; + __u16 fsw; + __u8 ftwx; /* in fxsave format */ + __u8 pad1; + __u16 last_opcode; + __u64 last_ip; + __u64 last_dp; + __u8 xmm[16][16]; + __u32 mxcsr; + __u32 pad2; +}; + +struct kvm_msr_entry { + __u32 index; + __u32 reserved; + __u64 data; +}; + +/* for KVM_GET_MSRS and KVM_SET_MSRS */ +struct kvm_msrs { + __u32 nmsrs; /* number of msrs in entries */ + __u32 pad; + + struct kvm_msr_entry entries[0]; +}; + +/* for KVM_GET_MSR_INDEX_LIST */ +struct kvm_msr_list { + __u32 nmsrs; /* number of msrs in entries */ + __u32 indices[0]; +}; + + +struct kvm_cpuid_entry { + __u32 function; + __u32 eax; + __u32 ebx; + __u32 ecx; + __u32 edx; + __u32 padding; +}; + +/* for KVM_SET_CPUID */ +struct kvm_cpuid { + __u32 nent; + __u32 padding; + struct kvm_cpuid_entry entries[0]; +}; + +struct kvm_cpuid_entry2 { + __u32 function; + __u32 index; + __u32 flags; + __u32 eax; + __u32 ebx; + __u32 ecx; + __u32 edx; + __u32 padding[3]; +}; + +#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX 1 +#define KVM_CPUID_FLAG_STATEFUL_FUNC 2 +#define KVM_CPUID_FLAG_STATE_READ_NEXT 4 + +/* for KVM_SET_CPUID2 */ +struct kvm_cpuid2 { + __u32 nent; + __u32 padding; + struct kvm_cpuid_entry2 entries[0]; +}; + +/* for KVM_GET_PIT and KVM_SET_PIT */ +struct kvm_pit_channel_state { + __u32 count; /* can be 65536 */ + __u16 latched_count; + __u8 count_latched; + __u8 status_latched; + __u8 status; + __u8 read_state; + __u8 write_state; + __u8 write_latch; + __u8 rw_mode; + __u8 mode; + __u8 bcd; + __u8 gate; + __s64 count_load_time; +}; + +struct kvm_debug_exit_arch { + __u32 exception; + __u32 pad; + __u64 pc; + __u64 dr6; + __u64 dr7; +}; + +#define KVM_GUESTDBG_USE_SW_BP 0x00010000 +#define KVM_GUESTDBG_USE_HW_BP 0x00020000 +#define KVM_GUESTDBG_INJECT_DB 0x00040000 +#define KVM_GUESTDBG_INJECT_BP 0x00080000 + +/* for KVM_SET_GUEST_DEBUG */ +struct kvm_guest_debug_arch { + __u64 debugreg[8]; +}; + +struct kvm_pit_state { + struct kvm_pit_channel_state channels[3]; +}; + +#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 + +struct kvm_pit_state2 { + struct kvm_pit_channel_state channels[3]; + __u32 flags; + __u32 reserved[9]; +}; + +struct kvm_reinject_control { + __u8 pit_reinject; + __u8 reserved[31]; +}; + +/* 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 { + struct { + __u8 injected; + __u8 nr; + __u8 has_error_code; + __u8 pad; + __u32 error_code; + } exception; + struct { + __u8 injected; + __u8 nr; + __u8 soft; + __u8 shadow; + } interrupt; + struct { + __u8 injected; + __u8 pending; + __u8 masked; + __u8 pad; + } nmi; + __u32 sipi_vector; + __u32 flags; + __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.14.1+noroms/linux-headers/asm-x86/kvm_para.h qemu-kvm-0.15.0+noroms/linux-headers/asm-x86/kvm_para.h --- qemu-kvm-0.14.1+noroms/linux-headers/asm-x86/kvm_para.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/asm-x86/kvm_para.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,79 @@ +#ifndef _ASM_X86_KVM_PARA_H +#define _ASM_X86_KVM_PARA_H + +#include +#include + +/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It + * should be used to determine that a VM is running under KVM. + */ +#define KVM_CPUID_SIGNATURE 0x40000000 + +/* This CPUID returns a feature bitmap in eax. Before enabling a particular + * paravirtualization, the appropriate feature bit should be checked. + */ +#define KVM_CPUID_FEATURES 0x40000001 +#define KVM_FEATURE_CLOCKSOURCE 0 +#define KVM_FEATURE_NOP_IO_DELAY 1 +#define KVM_FEATURE_MMU_OP 2 +/* This indicates that the new set of kvmclock msrs + * are available. The use of 0x11 and 0x12 is deprecated + */ +#define KVM_FEATURE_CLOCKSOURCE2 3 +#define KVM_FEATURE_ASYNC_PF 4 + +/* The last 8 bits are used to indicate how to interpret the flags field + * in pvclock structure. If no bits are set, all flags are ignored. + */ +#define KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 24 + +#define MSR_KVM_WALL_CLOCK 0x11 +#define MSR_KVM_SYSTEM_TIME 0x12 + +/* Custom MSRs falls in the range 0x4b564d00-0x4b564dff */ +#define MSR_KVM_WALL_CLOCK_NEW 0x4b564d00 +#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 +#define MSR_KVM_ASYNC_PF_EN 0x4b564d02 + +#define KVM_MAX_MMU_OP_BATCH 32 + +#define KVM_ASYNC_PF_ENABLED (1 << 0) +#define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1) + +/* Operations for KVM_HC_MMU_OP */ +#define KVM_MMU_OP_WRITE_PTE 1 +#define KVM_MMU_OP_FLUSH_TLB 2 +#define KVM_MMU_OP_RELEASE_PT 3 + +/* Payload for KVM_HC_MMU_OP */ +struct kvm_mmu_op_header { + __u32 op; + __u32 pad; +}; + +struct kvm_mmu_op_write_pte { + struct kvm_mmu_op_header header; + __u64 pte_phys; + __u64 pte_val; +}; + +struct kvm_mmu_op_flush_tlb { + struct kvm_mmu_op_header header; +}; + +struct kvm_mmu_op_release_pt { + struct kvm_mmu_op_header header; + __u64 pt_phys; +}; + +#define KVM_PV_REASON_PAGE_NOT_PRESENT 1 +#define KVM_PV_REASON_PAGE_READY 2 + +struct kvm_vcpu_pv_apf_data { + __u32 reason; + __u8 pad[60]; + __u32 enabled; +}; + + +#endif /* _ASM_X86_KVM_PARA_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/COPYING qemu-kvm-0.15.0+noroms/linux-headers/COPYING --- qemu-kvm-0.14.1+noroms/linux-headers/COPYING 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/COPYING 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,356 @@ + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + + Linus Torvalds + +---------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/linux/kvm.h qemu-kvm-0.15.0+noroms/linux-headers/linux/kvm.h --- qemu-kvm-0.14.1+noroms/linux-headers/linux/kvm.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/linux/kvm.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,804 @@ +#ifndef __LINUX_KVM_H +#define __LINUX_KVM_H + +/* + * Userspace interface for /dev/kvm - kernel based virtual machine + * + * Note: you must update KVM_API_VERSION if you change this interface. + */ + +#include + +#include +#include + +#define KVM_API_VERSION 12 + +/* *** Deprecated interfaces *** */ + +#define KVM_TRC_SHIFT 16 + +#define KVM_TRC_ENTRYEXIT (1 << KVM_TRC_SHIFT) +#define KVM_TRC_HANDLER (1 << (KVM_TRC_SHIFT + 1)) + +#define KVM_TRC_VMENTRY (KVM_TRC_ENTRYEXIT + 0x01) +#define KVM_TRC_VMEXIT (KVM_TRC_ENTRYEXIT + 0x02) +#define KVM_TRC_PAGE_FAULT (KVM_TRC_HANDLER + 0x01) + +#define KVM_TRC_HEAD_SIZE 12 +#define KVM_TRC_CYCLE_SIZE 8 +#define KVM_TRC_EXTRA_MAX 7 + +#define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02) +#define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03) +#define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04) +#define KVM_TRC_IO_READ (KVM_TRC_HANDLER + 0x05) +#define KVM_TRC_IO_WRITE (KVM_TRC_HANDLER + 0x06) +#define KVM_TRC_CR_READ (KVM_TRC_HANDLER + 0x07) +#define KVM_TRC_CR_WRITE (KVM_TRC_HANDLER + 0x08) +#define KVM_TRC_DR_READ (KVM_TRC_HANDLER + 0x09) +#define KVM_TRC_DR_WRITE (KVM_TRC_HANDLER + 0x0A) +#define KVM_TRC_MSR_READ (KVM_TRC_HANDLER + 0x0B) +#define KVM_TRC_MSR_WRITE (KVM_TRC_HANDLER + 0x0C) +#define KVM_TRC_CPUID (KVM_TRC_HANDLER + 0x0D) +#define KVM_TRC_INTR (KVM_TRC_HANDLER + 0x0E) +#define KVM_TRC_NMI (KVM_TRC_HANDLER + 0x0F) +#define KVM_TRC_VMMCALL (KVM_TRC_HANDLER + 0x10) +#define KVM_TRC_HLT (KVM_TRC_HANDLER + 0x11) +#define KVM_TRC_CLTS (KVM_TRC_HANDLER + 0x12) +#define KVM_TRC_LMSW (KVM_TRC_HANDLER + 0x13) +#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14) +#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15) +#define KVM_TRC_GTLB_WRITE (KVM_TRC_HANDLER + 0x16) +#define KVM_TRC_STLB_WRITE (KVM_TRC_HANDLER + 0x17) +#define KVM_TRC_STLB_INVAL (KVM_TRC_HANDLER + 0x18) +#define KVM_TRC_PPC_INSTR (KVM_TRC_HANDLER + 0x19) + +struct kvm_user_trace_setup { + __u32 buf_size; + __u32 buf_nr; +}; + +#define __KVM_DEPRECATED_MAIN_W_0x06 \ + _IOW(KVMIO, 0x06, struct kvm_user_trace_setup) +#define __KVM_DEPRECATED_MAIN_0x07 _IO(KVMIO, 0x07) +#define __KVM_DEPRECATED_MAIN_0x08 _IO(KVMIO, 0x08) + +#define __KVM_DEPRECATED_VM_R_0x70 _IOR(KVMIO, 0x70, struct kvm_assigned_irq) + +struct kvm_breakpoint { + __u32 enabled; + __u32 padding; + __u64 address; +}; + +struct kvm_debug_guest { + __u32 enabled; + __u32 pad; + struct kvm_breakpoint breakpoints[4]; + __u32 singlestep; +}; + +#define __KVM_DEPRECATED_VCPU_W_0x87 _IOW(KVMIO, 0x87, struct kvm_debug_guest) + +/* *** End of deprecated interfaces *** */ + + +/* for KVM_CREATE_MEMORY_REGION */ +struct kvm_memory_region { + __u32 slot; + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; /* bytes */ +}; + +/* for KVM_SET_USER_MEMORY_REGION */ +struct kvm_userspace_memory_region { + __u32 slot; + __u32 flags; + __u64 guest_phys_addr; + __u64 memory_size; /* bytes */ + __u64 userspace_addr; /* start of the userspace allocated memory */ +}; + +/* 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 { + /* + * ACPI gsi notion of irq. + * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47.. + * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23.. + */ + union { + __u32 irq; + __s32 status; + }; + __u32 level; +}; + + +struct kvm_irqchip { + __u32 chip_id; + __u32 pad; + union { + char dummy[512]; /* reserving space */ +#ifdef __KVM_HAVE_PIT + struct kvm_pic_state pic; +#endif +#ifdef __KVM_HAVE_IOAPIC + struct kvm_ioapic_state ioapic; +#endif + } chip; +}; + +/* for KVM_CREATE_PIT2 */ +struct kvm_pit_config { + __u32 flags; + __u32 pad[15]; +}; + +#define KVM_PIT_SPEAKER_DUMMY 1 + +#define KVM_EXIT_UNKNOWN 0 +#define KVM_EXIT_EXCEPTION 1 +#define KVM_EXIT_IO 2 +#define KVM_EXIT_HYPERCALL 3 +#define KVM_EXIT_DEBUG 4 +#define KVM_EXIT_HLT 5 +#define KVM_EXIT_MMIO 6 +#define KVM_EXIT_IRQ_WINDOW_OPEN 7 +#define KVM_EXIT_SHUTDOWN 8 +#define KVM_EXIT_FAIL_ENTRY 9 +#define KVM_EXIT_INTR 10 +#define KVM_EXIT_SET_TPR 11 +#define KVM_EXIT_TPR_ACCESS 12 +#define KVM_EXIT_S390_SIEIC 13 +#define KVM_EXIT_S390_RESET 14 +#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 +#define KVM_INTERNAL_ERROR_SIMUL_EX 2 + +/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ +struct kvm_run { + /* in */ + __u8 request_interrupt_window; + __u8 padding1[7]; + + /* out */ + __u32 exit_reason; + __u8 ready_for_interrupt_injection; + __u8 if_flag; + __u8 padding2[2]; + + /* in (pre_kvm_run), out (post_kvm_run) */ + __u64 cr8; + __u64 apic_base; + +#ifdef __KVM_S390 + /* the processor status word for s390 */ + __u64 psw_mask; /* psw upper half */ + __u64 psw_addr; /* psw lower half */ +#endif + union { + /* KVM_EXIT_UNKNOWN */ + struct { + __u64 hardware_exit_reason; + } hw; + /* KVM_EXIT_FAIL_ENTRY */ + struct { + __u64 hardware_entry_failure_reason; + } fail_entry; + /* KVM_EXIT_EXCEPTION */ + struct { + __u32 exception; + __u32 error_code; + } ex; + /* KVM_EXIT_IO */ + struct { +#define KVM_EXIT_IO_IN 0 +#define KVM_EXIT_IO_OUT 1 + __u8 direction; + __u8 size; /* bytes */ + __u16 port; + __u32 count; + __u64 data_offset; /* relative to kvm_run start */ + } io; + struct { + struct kvm_debug_exit_arch arch; + } debug; + /* KVM_EXIT_MMIO */ + struct { + __u64 phys_addr; + __u8 data[8]; + __u32 len; + __u8 is_write; + } mmio; + /* KVM_EXIT_HYPERCALL */ + struct { + __u64 nr; + __u64 args[6]; + __u64 ret; + __u32 longmode; + __u32 pad; + } hypercall; + /* KVM_EXIT_TPR_ACCESS */ + struct { + __u64 rip; + __u32 is_write; + __u32 pad; + } tpr_access; + /* KVM_EXIT_S390_SIEIC */ + struct { + __u8 icptcode; + __u16 ipa; + __u32 ipb; + } s390_sieic; + /* KVM_EXIT_S390_RESET */ +#define KVM_S390_RESET_POR 1 +#define KVM_S390_RESET_CLEAR 2 +#define KVM_S390_RESET_SUBSYSTEM 4 +#define KVM_S390_RESET_CPU_INIT 8 +#define KVM_S390_RESET_IPL 16 + __u64 s390_reset_flags; + /* KVM_EXIT_DCR */ + struct { + __u32 dcrn; + __u32 data; + __u8 is_write; + } dcr; + struct { + __u32 suberror; + /* Available with KVM_CAP_INTERNAL_ERROR_DATA: */ + __u32 ndata; + __u64 data[16]; + } internal; + /* KVM_EXIT_OSI */ + struct { + __u64 gprs[32]; + } osi; + /* Fix the size of the union. */ + char padding[256]; + }; +}; + +/* for KVM_REGISTER_COALESCED_MMIO / KVM_UNREGISTER_COALESCED_MMIO */ + +struct kvm_coalesced_mmio_zone { + __u64 addr; + __u32 size; + __u32 pad; +}; + +struct kvm_coalesced_mmio { + __u64 phys_addr; + __u32 len; + __u32 pad; + __u8 data[8]; +}; + +struct kvm_coalesced_mmio_ring { + __u32 first, last; + struct kvm_coalesced_mmio coalesced_mmio[0]; +}; + +#define KVM_COALESCED_MMIO_MAX \ + ((PAGE_SIZE - sizeof(struct kvm_coalesced_mmio_ring)) / \ + sizeof(struct kvm_coalesced_mmio)) + +/* for KVM_TRANSLATE */ +struct kvm_translation { + /* in */ + __u64 linear_address; + + /* out */ + __u64 physical_address; + __u8 valid; + __u8 writeable; + __u8 usermode; + __u8 pad[5]; +}; + +/* for KVM_INTERRUPT */ +struct kvm_interrupt { + /* in */ + __u32 irq; +}; + +/* for KVM_GET_DIRTY_LOG */ +struct kvm_dirty_log { + __u32 slot; + __u32 padding1; + union { + void *dirty_bitmap; /* one bit per page */ + __u64 padding2; + }; +}; + +/* for KVM_SET_SIGNAL_MASK */ +struct kvm_signal_mask { + __u32 len; + __u8 sigset[0]; +}; + +/* for KVM_TPR_ACCESS_REPORTING */ +struct kvm_tpr_access_ctl { + __u32 enabled; + __u32 flags; + __u32 reserved[8]; +}; + +/* for KVM_SET_VAPIC_ADDR */ +struct kvm_vapic_addr { + __u64 vapic_addr; +}; + +/* for KVM_SET_MPSTATE */ + +#define KVM_MP_STATE_RUNNABLE 0 +#define KVM_MP_STATE_UNINITIALIZED 1 +#define KVM_MP_STATE_INIT_RECEIVED 2 +#define KVM_MP_STATE_HALTED 3 +#define KVM_MP_STATE_SIPI_RECEIVED 4 + +struct kvm_mp_state { + __u32 mp_state; +}; + +struct kvm_s390_psw { + __u64 mask; + __u64 addr; +}; + +/* valid values for type in kvm_s390_interrupt */ +#define KVM_S390_SIGP_STOP 0xfffe0000u +#define KVM_S390_PROGRAM_INT 0xfffe0001u +#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u +#define KVM_S390_RESTART 0xfffe0003u +#define KVM_S390_INT_VIRTIO 0xffff2603u +#define KVM_S390_INT_SERVICE 0xffff2401u +#define KVM_S390_INT_EMERGENCY 0xffff1201u + +struct kvm_s390_interrupt { + __u32 type; + __u32 parm; + __u64 parm64; +}; + +/* for KVM_SET_GUEST_DEBUG */ + +#define KVM_GUESTDBG_ENABLE 0x00000001 +#define KVM_GUESTDBG_SINGLESTEP 0x00000002 + +struct kvm_guest_debug { + __u32 control; + __u32 pad; + struct kvm_guest_debug_arch arch; +}; + +enum { + kvm_ioeventfd_flag_nr_datamatch, + kvm_ioeventfd_flag_nr_pio, + kvm_ioeventfd_flag_nr_deassign, + kvm_ioeventfd_flag_nr_max, +}; + +#define KVM_IOEVENTFD_FLAG_DATAMATCH (1 << kvm_ioeventfd_flag_nr_datamatch) +#define KVM_IOEVENTFD_FLAG_PIO (1 << kvm_ioeventfd_flag_nr_pio) +#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign) + +#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 << kvm_ioeventfd_flag_nr_max) - 1) + +struct kvm_ioeventfd { + __u64 datamatch; + __u64 addr; /* legal pio/mmio address */ + __u32 len; /* 1, 2, 4, or 8 bytes */ + __s32 fd; + __u32 flags; + __u8 pad[36]; +}; + +/* for KVM_ENABLE_CAP */ +struct kvm_enable_cap { + /* in */ + __u32 cap; + __u32 flags; + __u64 args[4]; + __u8 pad[64]; +}; + +/* for KVM_PPC_GET_PVINFO */ +struct kvm_ppc_pvinfo { + /* out */ + __u32 flags; + __u32 hcall[4]; + __u8 pad[108]; +}; + +#define KVMIO 0xAE + +/* + * ioctls for /dev/kvm fds: + */ +#define KVM_GET_API_VERSION _IO(KVMIO, 0x00) +#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */ +#define KVM_GET_MSR_INDEX_LIST _IOWR(KVMIO, 0x02, struct kvm_msr_list) + +#define KVM_S390_ENABLE_SIE _IO(KVMIO, 0x06) +/* + * Check if a kvm extension is available. Argument is extension number, + * return is 1 (yes) or 0 (no, sorry). + */ +#define KVM_CHECK_EXTENSION _IO(KVMIO, 0x03) +/* + * Get size for mmap(vcpu_fd) + */ +#define KVM_GET_VCPU_MMAP_SIZE _IO(KVMIO, 0x04) /* in bytes */ +#define KVM_GET_SUPPORTED_CPUID _IOWR(KVMIO, 0x05, struct kvm_cpuid2) +#define KVM_TRACE_ENABLE __KVM_DEPRECATED_MAIN_W_0x06 +#define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07 +#define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08 + +/* + * Extension capability list. + */ +#define KVM_CAP_IRQCHIP 0 +#define KVM_CAP_HLT 1 +#define KVM_CAP_MMU_SHADOW_CACHE_CONTROL 2 +#define KVM_CAP_USER_MEMORY 3 +#define KVM_CAP_SET_TSS_ADDR 4 +#define KVM_CAP_VAPIC 6 +#define KVM_CAP_EXT_CPUID 7 +#define KVM_CAP_CLOCKSOURCE 8 +#define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */ +#define KVM_CAP_NR_MEMSLOTS 10 /* returns max memory slots per vm */ +#define KVM_CAP_PIT 11 +#define KVM_CAP_NOP_IO_DELAY 12 +#define KVM_CAP_PV_MMU 13 +#define KVM_CAP_MP_STATE 14 +#define KVM_CAP_COALESCED_MMIO 15 +#define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */ +#ifdef __KVM_HAVE_DEVICE_ASSIGNMENT +#define KVM_CAP_DEVICE_ASSIGNMENT 17 +#endif +#define KVM_CAP_IOMMU 18 +#ifdef __KVM_HAVE_MSI +#define KVM_CAP_DEVICE_MSI 20 +#endif +/* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ +#define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 +#ifdef __KVM_HAVE_USER_NMI +#define KVM_CAP_USER_NMI 22 +#endif +#ifdef __KVM_HAVE_GUEST_DEBUG +#define KVM_CAP_SET_GUEST_DEBUG 23 +#endif +#ifdef __KVM_HAVE_PIT +#define KVM_CAP_REINJECT_CONTROL 24 +#endif +#ifdef __KVM_HAVE_IOAPIC +#define KVM_CAP_IRQ_ROUTING 25 +#endif +#define KVM_CAP_IRQ_INJECT_STATUS 26 +#ifdef __KVM_HAVE_DEVICE_ASSIGNMENT +#define KVM_CAP_DEVICE_DEASSIGNMENT 27 +#endif +#ifdef __KVM_HAVE_MSIX +#define KVM_CAP_DEVICE_MSIX 28 +#endif +#define KVM_CAP_ASSIGN_DEV_IRQ 29 +/* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ +#define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 +#ifdef __KVM_HAVE_MCE +#define KVM_CAP_MCE 31 +#endif +#define KVM_CAP_IRQFD 32 +#ifdef __KVM_HAVE_PIT +#define KVM_CAP_PIT2 33 +#endif +#define KVM_CAP_SET_BOOT_CPU_ID 34 +#ifdef __KVM_HAVE_PIT_STATE2 +#define KVM_CAP_PIT_STATE2 35 +#endif +#define KVM_CAP_IOEVENTFD 36 +#define KVM_CAP_SET_IDENTITY_MAP_ADDR 37 +#ifdef __KVM_HAVE_XEN_HVM +#define KVM_CAP_XEN_HVM 38 +#endif +#define KVM_CAP_ADJUST_CLOCK 39 +#define KVM_CAP_INTERNAL_ERROR_DATA 40 +#ifdef __KVM_HAVE_VCPU_EVENTS +#define KVM_CAP_VCPU_EVENTS 41 +#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 +#define KVM_CAP_PPC_GET_PVINFO 57 +#define KVM_CAP_PPC_IRQ_LEVEL 58 +#define KVM_CAP_ASYNC_PF 59 +#define KVM_CAP_TSC_CONTROL 60 +#define KVM_CAP_GET_TSC_KHZ 61 +#define KVM_CAP_PPC_BOOKE_SREGS 62 + +#ifdef KVM_CAP_IRQ_ROUTING + +struct kvm_irq_routing_irqchip { + __u32 irqchip; + __u32 pin; +}; + +struct kvm_irq_routing_msi { + __u32 address_lo; + __u32 address_hi; + __u32 data; + __u32 pad; +}; + +/* gsi routing entry types */ +#define KVM_IRQ_ROUTING_IRQCHIP 1 +#define KVM_IRQ_ROUTING_MSI 2 + +struct kvm_irq_routing_entry { + __u32 gsi; + __u32 type; + __u32 flags; + __u32 pad; + union { + struct kvm_irq_routing_irqchip irqchip; + struct kvm_irq_routing_msi msi; + __u32 pad[8]; + } u; +}; + +struct kvm_irq_routing { + __u32 nr; + __u32 flags; + struct kvm_irq_routing_entry entries[0]; +}; + +#endif + +#ifdef KVM_CAP_MCE +/* x86 MCE */ +struct kvm_x86_mce { + __u64 status; + __u64 addr; + __u64 misc; + __u64 mcg_status; + __u8 bank; + __u8 pad1[7]; + __u64 pad2[3]; +}; +#endif + +#ifdef KVM_CAP_XEN_HVM +struct kvm_xen_hvm_config { + __u32 flags; + __u32 msr; + __u64 blob_addr_32; + __u64 blob_addr_64; + __u8 blob_size_32; + __u8 blob_size_64; + __u8 pad2[30]; +}; +#endif + +#define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) + +struct kvm_irqfd { + __u32 fd; + __u32 gsi; + __u32 flags; + __u8 pad[20]; +}; + +struct kvm_clock_data { + __u64 clock; + __u32 flags; + __u32 pad[9]; +}; + +/* + * ioctls for VM fds + */ +#define KVM_SET_MEMORY_REGION _IOW(KVMIO, 0x40, struct kvm_memory_region) +/* + * KVM_CREATE_VCPU receives as a parameter the vcpu slot, and returns + * a vcpu fd. + */ +#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) +#define KVM_GET_DIRTY_LOG _IOW(KVMIO, 0x42, struct kvm_dirty_log) +/* KVM_SET_MEMORY_ALIAS is obsolete: */ +#define KVM_SET_MEMORY_ALIAS _IOW(KVMIO, 0x43, struct kvm_memory_alias) +#define KVM_SET_NR_MMU_PAGES _IO(KVMIO, 0x44) +#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45) +#define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \ + struct kvm_userspace_memory_region) +#define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47) +#define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64) +/* Device model IOC */ +#define KVM_CREATE_IRQCHIP _IO(KVMIO, 0x60) +#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level) +#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip) +#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip) +#define KVM_CREATE_PIT _IO(KVMIO, 0x64) +#define KVM_GET_PIT _IOWR(KVMIO, 0x65, struct kvm_pit_state) +#define KVM_SET_PIT _IOR(KVMIO, 0x66, struct kvm_pit_state) +#define KVM_IRQ_LINE_STATUS _IOWR(KVMIO, 0x67, struct kvm_irq_level) +#define KVM_REGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone) +#define KVM_UNREGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone) +#define KVM_ASSIGN_PCI_DEVICE _IOR(KVMIO, 0x69, \ + struct kvm_assigned_pci_dev) +#define KVM_SET_GSI_ROUTING _IOW(KVMIO, 0x6a, struct kvm_irq_routing) +/* deprecated, replaced by KVM_ASSIGN_DEV_IRQ */ +#define KVM_ASSIGN_IRQ __KVM_DEPRECATED_VM_R_0x70 +#define KVM_ASSIGN_DEV_IRQ _IOW(KVMIO, 0x70, struct kvm_assigned_irq) +#define KVM_REINJECT_CONTROL _IO(KVMIO, 0x71) +#define KVM_DEASSIGN_PCI_DEVICE _IOW(KVMIO, 0x72, \ + struct kvm_assigned_pci_dev) +#define KVM_ASSIGN_SET_MSIX_NR _IOW(KVMIO, 0x73, \ + struct kvm_assigned_msix_nr) +#define KVM_ASSIGN_SET_MSIX_ENTRY _IOW(KVMIO, 0x74, \ + struct kvm_assigned_msix_entry) +#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq) +#define KVM_IRQFD _IOW(KVMIO, 0x76, struct kvm_irqfd) +#define KVM_CREATE_PIT2 _IOW(KVMIO, 0x77, struct kvm_pit_config) +#define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78) +#define KVM_IOEVENTFD _IOW(KVMIO, 0x79, struct kvm_ioeventfd) +#define KVM_XEN_HVM_CONFIG _IOW(KVMIO, 0x7a, struct kvm_xen_hvm_config) +#define KVM_SET_CLOCK _IOW(KVMIO, 0x7b, struct kvm_clock_data) +#define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data) +/* Available with KVM_CAP_PIT_STATE2 */ +#define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2) +#define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2) +/* Available with KVM_CAP_PPC_GET_PVINFO */ +#define KVM_PPC_GET_PVINFO _IOW(KVMIO, 0xa1, struct kvm_ppc_pvinfo) +/* Available with KVM_CAP_TSC_CONTROL */ +#define KVM_SET_TSC_KHZ _IO(KVMIO, 0xa2) +#define KVM_GET_TSC_KHZ _IO(KVMIO, 0xa3) + +/* + * ioctls for vcpu fds + */ +#define KVM_RUN _IO(KVMIO, 0x80) +#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs) +#define KVM_SET_REGS _IOW(KVMIO, 0x82, struct kvm_regs) +#define KVM_GET_SREGS _IOR(KVMIO, 0x83, struct kvm_sregs) +#define KVM_SET_SREGS _IOW(KVMIO, 0x84, struct kvm_sregs) +#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) +#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) +/* KVM_DEBUG_GUEST is no longer supported, use KVM_SET_GUEST_DEBUG instead */ +#define KVM_DEBUG_GUEST __KVM_DEPRECATED_VCPU_W_0x87 +#define KVM_GET_MSRS _IOWR(KVMIO, 0x88, struct kvm_msrs) +#define KVM_SET_MSRS _IOW(KVMIO, 0x89, struct kvm_msrs) +#define KVM_SET_CPUID _IOW(KVMIO, 0x8a, struct kvm_cpuid) +#define KVM_SET_SIGNAL_MASK _IOW(KVMIO, 0x8b, struct kvm_signal_mask) +#define KVM_GET_FPU _IOR(KVMIO, 0x8c, struct kvm_fpu) +#define KVM_SET_FPU _IOW(KVMIO, 0x8d, struct kvm_fpu) +#define KVM_GET_LAPIC _IOR(KVMIO, 0x8e, struct kvm_lapic_state) +#define KVM_SET_LAPIC _IOW(KVMIO, 0x8f, struct kvm_lapic_state) +#define KVM_SET_CPUID2 _IOW(KVMIO, 0x90, struct kvm_cpuid2) +#define KVM_GET_CPUID2 _IOWR(KVMIO, 0x91, struct kvm_cpuid2) +/* Available with KVM_CAP_VAPIC */ +#define KVM_TPR_ACCESS_REPORTING _IOWR(KVMIO, 0x92, struct kvm_tpr_access_ctl) +/* Available with KVM_CAP_VAPIC */ +#define KVM_SET_VAPIC_ADDR _IOW(KVMIO, 0x93, struct kvm_vapic_addr) +/* valid for virtual machine (for floating interrupt)_and_ vcpu */ +#define KVM_S390_INTERRUPT _IOW(KVMIO, 0x94, struct kvm_s390_interrupt) +/* store status for s390 */ +#define KVM_S390_STORE_STATUS_NOADDR (-1ul) +#define KVM_S390_STORE_STATUS_PREFIXED (-2ul) +#define KVM_S390_STORE_STATUS _IOW(KVMIO, 0x95, unsigned long) +/* initial ipl psw for s390 */ +#define KVM_S390_SET_INITIAL_PSW _IOW(KVMIO, 0x96, struct kvm_s390_psw) +/* initial reset for s390 */ +#define KVM_S390_INITIAL_RESET _IO(KVMIO, 0x97) +#define KVM_GET_MP_STATE _IOR(KVMIO, 0x98, struct kvm_mp_state) +#define KVM_SET_MP_STATE _IOW(KVMIO, 0x99, struct kvm_mp_state) +/* Available with KVM_CAP_NMI */ +#define KVM_NMI _IO(KVMIO, 0x9a) +/* Available with KVM_CAP_SET_GUEST_DEBUG */ +#define KVM_SET_GUEST_DEBUG _IOW(KVMIO, 0x9b, struct kvm_guest_debug) +/* MCE for x86 */ +#define KVM_X86_SETUP_MCE _IOW(KVMIO, 0x9c, __u64) +#define KVM_X86_GET_MCE_CAP_SUPPORTED _IOR(KVMIO, 0x9d, __u64) +#define KVM_X86_SET_MCE _IOW(KVMIO, 0x9e, struct kvm_x86_mce) +/* IA64 stack access */ +#define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) +#define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) +/* 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) + +struct kvm_assigned_pci_dev { + __u32 assigned_dev_id; + __u32 busnr; + __u32 devfn; + __u32 flags; + __u32 segnr; + union { + __u32 reserved[11]; + }; +}; + +#define KVM_DEV_IRQ_HOST_INTX (1 << 0) +#define KVM_DEV_IRQ_HOST_MSI (1 << 1) +#define KVM_DEV_IRQ_HOST_MSIX (1 << 2) + +#define KVM_DEV_IRQ_GUEST_INTX (1 << 8) +#define KVM_DEV_IRQ_GUEST_MSI (1 << 9) +#define KVM_DEV_IRQ_GUEST_MSIX (1 << 10) + +#define KVM_DEV_IRQ_HOST_MASK 0x00ff +#define KVM_DEV_IRQ_GUEST_MASK 0xff00 + +struct kvm_assigned_irq { + __u32 assigned_dev_id; + __u32 host_irq; + __u32 guest_irq; + __u32 flags; + union { + struct { + __u32 addr_lo; + __u32 addr_hi; + __u32 data; + } guest_msi; + __u32 reserved[12]; + }; +}; + + +struct kvm_assigned_msix_nr { + __u32 assigned_dev_id; + __u16 entry_nr; + __u16 padding; +}; + +#define KVM_MAX_MSIX_PER_DEV 256 +struct kvm_assigned_msix_entry { + __u32 assigned_dev_id; + __u32 gsi; + __u16 entry; /* The index of entry in the MSI-X table */ + __u16 padding[3]; +}; + +#endif /* __LINUX_KVM_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/linux/kvm_para.h qemu-kvm-0.15.0+noroms/linux-headers/linux/kvm_para.h --- qemu-kvm-0.14.1+noroms/linux-headers/linux/kvm_para.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/linux/kvm_para.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,28 @@ +#ifndef __LINUX_KVM_PARA_H +#define __LINUX_KVM_PARA_H + +/* + * This header file provides a method for making a hypercall to the host + * Architectures should define: + * - kvm_hypercall0, kvm_hypercall1... + * - kvm_arch_para_features + * - kvm_para_available + */ + +/* Return values for hypercalls */ +#define KVM_ENOSYS 1000 +#define KVM_EFAULT EFAULT +#define KVM_E2BIG E2BIG +#define KVM_EPERM EPERM + +#define KVM_HC_VAPIC_POLL_IRQ 1 +#define KVM_HC_MMU_OP 2 +#define KVM_HC_FEATURES 3 +#define KVM_HC_PPC_MAP_MAGIC_PAGE 4 + +/* + * hypercalls use architecture specific + */ +#include + +#endif /* __LINUX_KVM_PARA_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/linux/vhost.h qemu-kvm-0.15.0+noroms/linux-headers/linux/vhost.h --- qemu-kvm-0.14.1+noroms/linux-headers/linux/vhost.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/linux/vhost.h 2011-08-09 07:40:29.000000000 -0500 @@ -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.14.1+noroms/linux-headers/linux/virtio_config.h qemu-kvm-0.15.0+noroms/linux-headers/linux/virtio_config.h --- qemu-kvm-0.14.1+noroms/linux-headers/linux/virtio_config.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/linux/virtio_config.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,54 @@ +#ifndef _LINUX_VIRTIO_CONFIG_H +#define _LINUX_VIRTIO_CONFIG_H +/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. + * + * 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 IBM 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 COPYRIGHT HOLDERS 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 IBM 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. */ + +/* Virtio devices use a standardized configuration space to define their + * features and pass configuration information, but each implementation can + * store and access that space differently. */ +#include + +/* Status byte for guest to report progress, and synchronize features. */ +/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +/* We have found a driver for the device. */ +#define VIRTIO_CONFIG_S_DRIVER 2 +/* Driver has used its parts of the config, and is happy */ +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* We've given up on this device. */ +#define VIRTIO_CONFIG_S_FAILED 0x80 + +/* Some virtio feature bits (currently bits 28 through 31) are reserved for the + * transport being used (eg. virtio_ring), the rest are per-device feature + * bits. */ +#define VIRTIO_TRANSPORT_F_START 28 +#define VIRTIO_TRANSPORT_F_END 32 + +/* Do we get callbacks when the ring is completely used, even if we've + * suppressed them? */ +#define VIRTIO_F_NOTIFY_ON_EMPTY 24 + +#endif /* _LINUX_VIRTIO_CONFIG_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/linux/virtio_ring.h qemu-kvm-0.15.0+noroms/linux-headers/linux/virtio_ring.h --- qemu-kvm-0.14.1+noroms/linux-headers/linux/virtio_ring.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/linux/virtio_ring.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,163 @@ +#ifndef _LINUX_VIRTIO_RING_H +#define _LINUX_VIRTIO_RING_H +/* An interface for efficient virtio implementation, currently for use by KVM + * and lguest, but hopefully others soon. Do NOT change this since it will + * break existing servers and clients. + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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 IBM 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 COPYRIGHT HOLDERS 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 IBM 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. + * + * Copyright Rusty Russell IBM Corporation 2007. */ +#include + +/* This marks a buffer as continuing via the next field. */ +#define VRING_DESC_F_NEXT 1 +/* This marks a buffer as write-only (otherwise read-only). */ +#define VRING_DESC_F_WRITE 2 +/* This means the buffer contains a list of buffer descriptors. */ +#define VRING_DESC_F_INDIRECT 4 + +/* The Host uses this in used->flags to advise the Guest: don't kick me when + * you add a buffer. It's unreliable, so it's simply an optimization. Guest + * will still kick if it's out of buffers. */ +#define VRING_USED_F_NO_NOTIFY 1 +/* The Guest uses this in avail->flags to advise the Host: don't interrupt me + * when you consume a buffer. It's unreliable, so it's simply an + * optimization. */ +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +/* We support indirect buffer descriptors */ +#define VIRTIO_RING_F_INDIRECT_DESC 28 + +/* The Guest publishes the used index for which it expects an interrupt + * at the end of the avail ring. Host should ignore the avail->flags field. */ +/* The Host publishes the avail index for which it expects a kick + * at the end of the used ring. Guest should ignore the used->flags field. */ +#define VIRTIO_RING_F_EVENT_IDX 29 + +/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ +struct vring_desc { + /* Address (guest-physical). */ + __u64 addr; + /* Length. */ + __u32 len; + /* The flags as indicated above. */ + __u16 flags; + /* We chain unused descriptors via this, too */ + __u16 next; +}; + +struct vring_avail { + __u16 flags; + __u16 idx; + __u16 ring[]; +}; + +/* u32 is used here for ids for padding reasons. */ +struct vring_used_elem { + /* Index of start of used descriptor chain. */ + __u32 id; + /* Total length of the descriptor chain which was used (written to) */ + __u32 len; +}; + +struct vring_used { + __u16 flags; + __u16 idx; + struct vring_used_elem ring[]; +}; + +struct vring { + unsigned int num; + + struct vring_desc *desc; + + struct vring_avail *avail; + + struct vring_used *used; +}; + +/* The standard layout for the ring is a continuous chunk of memory which looks + * like this. We assume num is a power of 2. + * + * struct vring + * { + * // The actual descriptors (16 bytes each) + * struct vring_desc desc[num]; + * + * // A ring of available descriptor heads with free-running index. + * __u16 avail_flags; + * __u16 avail_idx; + * __u16 available[num]; + * __u16 used_event_idx; + * + * // Padding to the next align boundary. + * char pad[]; + * + * // A ring of used descriptor heads with free-running index. + * __u16 used_flags; + * __u16 used_idx; + * struct vring_used_elem used[num]; + * __u16 avail_event_idx; + * }; + */ +/* We publish the used event index at the end of the available ring, and vice + * versa. They are at the end for backwards compatibility. */ +#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) +#define vring_avail_event(vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) + +static __inline__ void vring_init(struct vring *vr, unsigned int num, void *p, + unsigned long align) +{ + vr->num = num; + vr->desc = p; + vr->avail = p + num*sizeof(struct vring_desc); + vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + align-1) + & ~(align - 1)); +} + +static __inline__ unsigned vring_size(unsigned int num, unsigned long align) +{ + return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (2 + num) + + align - 1) & ~(align - 1)) + + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num; +} + +/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */ +/* Assuming a given event_idx value from the other size, if + * we have just incremented index from old to new_idx, + * should we trigger an event? */ +static __inline__ int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old) +{ + /* Note: Xen has similar logic for notification hold-off + * in include/xen/interface/io/ring.h with req_event and req_prod + * corresponding to event_idx + 1 and new_idx respectively. + * Note also that req_event and req_prod in Xen start at 1, + * event indexes in virtio start at 0. */ + return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old); +} + +#endif /* _LINUX_VIRTIO_RING_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-headers/README qemu-kvm-0.15.0+noroms/linux-headers/README --- qemu-kvm-0.14.1+noroms/linux-headers/README 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-headers/README 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,2 @@ +Automatically imported Linux kernel headers. +Only use scripts/update-linux-headers.sh to update! diff -Nru qemu-kvm-0.14.1+noroms/linux-user/alpha/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/alpha/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/alpha/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/alpha/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -411,11 +411,25 @@ #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 +#define TARGET_NR_recvmmsg 479 +#define TARGET_NR_fallocate 480 +#define TARGET_NR_timerfd_create 481 +#define TARGET_NR_timerfd_settime 482 +#define TARGET_NR_timerfd_gettime 483 +#define TARGET_NR_signalfd4 484 +#define TARGET_NR_eventfd2 485 +#define TARGET_NR_epoll_create1 486 +#define TARGET_NR_dup3 487 +#define TARGET_NR_pipe2 488 +#define TARGET_NR_inotify_init1 489 +#define TARGET_NR_preadv 490 +#define TARGET_NR_pwritev 491 +#define TARGET_NR_rt_tgsigqueueinfo 492 +#define TARGET_NR_perf_event_open 493 +#define TARGET_NR_fanotify_init 494 +#define TARGET_NR_fanotify_mark 495 +#define TARGET_NR_prlimit64 496 +#define TARGET_NR_name_to_handle_at 497 +#define TARGET_NR_open_by_handle_at 498 +#define TARGET_NR_clock_adjtime 499 +#define TARGET_NR_syncfs 500 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11.c qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11.c --- qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11.c 2011-08-09 07:40:29.000000000 -0500 @@ -144,7 +144,7 @@ #if 0 fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n", - opcode, qregs[REG_PC]); + opcode, qregs[ARM_REG_PC]); #endif fpa11 = GET_FPA11(); diff -Nru qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11_cpdt.c qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11_cpdt.c --- qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11_cpdt.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11_cpdt.c 2011-08-09 07:40:29.000000000 -0500 @@ -33,7 +33,7 @@ FPA11 *fpa11 = GET_FPA11(); fpa11->fType[Fn] = typeSingle; /* FIXME - handle failure of get_user() */ - get_user_u32(fpa11->fpreg[Fn].fSingle, addr); + get_user_u32(float32_val(fpa11->fpreg[Fn].fSingle), addr); } static inline @@ -220,7 +220,7 @@ //printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); pBase = readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) + if (ARM_REG_PC == getRn(opcode)) { pBase += 8; write_back = 0; @@ -256,7 +256,7 @@ SetRoundingMode(ROUND_TO_NEAREST); pBase = readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) + if (ARM_REG_PC == getRn(opcode)) { pBase += 8; write_back = 0; @@ -289,7 +289,7 @@ target_ulong pBase, pAddress, pFinal; pBase = readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) + if (ARM_REG_PC == getRn(opcode)) { pBase += 8; write_back = 0; @@ -322,7 +322,7 @@ target_ulong pBase, pAddress, pFinal; pBase = readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) + if (ARM_REG_PC == getRn(opcode)) { pBase += 8; write_back = 0; diff -Nru qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11_cprt.c qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11_cprt.c --- qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11_cprt.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11_cprt.c 2011-08-09 07:40:29.000000000 -0500 @@ -159,7 +159,7 @@ } /* test for equal condition */ - if (floatx80_eq(Fn,Fm, &fpa11->fp_status)) + if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status)) { flags |= CC_ZERO; } diff -Nru qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11.h qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11.h --- qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpa11.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpa11.h 2011-08-09 07:40:29.000000000 -0500 @@ -111,7 +111,7 @@ cpsr_write(user_registers,x,CPSR_NZCV); } -#define REG_PC 15 +#define ARM_REG_PC 15 unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs); diff -Nru qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpopcode.c qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpopcode.c --- qemu-kvm-0.14.1+noroms/linux-user/arm/nwfpe/fpopcode.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/arm/nwfpe/fpopcode.c 2011-08-09 07:40:29.000000000 -0500 @@ -37,25 +37,25 @@ }; const float64 float64Constant[] = { - 0x0000000000000000ULL, /* double 0.0 */ - 0x3ff0000000000000ULL, /* double 1.0 */ - 0x4000000000000000ULL, /* double 2.0 */ - 0x4008000000000000ULL, /* double 3.0 */ - 0x4010000000000000ULL, /* double 4.0 */ - 0x4014000000000000ULL, /* double 5.0 */ - 0x3fe0000000000000ULL, /* double 0.5 */ - 0x4024000000000000ULL /* double 10.0 */ + const_float64(0x0000000000000000ULL), /* double 0.0 */ + const_float64(0x3ff0000000000000ULL), /* double 1.0 */ + const_float64(0x4000000000000000ULL), /* double 2.0 */ + const_float64(0x4008000000000000ULL), /* double 3.0 */ + const_float64(0x4010000000000000ULL), /* double 4.0 */ + const_float64(0x4014000000000000ULL), /* double 5.0 */ + const_float64(0x3fe0000000000000ULL), /* double 0.5 */ + const_float64(0x4024000000000000ULL) /* double 10.0 */ }; const float32 float32Constant[] = { - 0x00000000, /* single 0.0 */ - 0x3f800000, /* single 1.0 */ - 0x40000000, /* single 2.0 */ - 0x40400000, /* single 3.0 */ - 0x40800000, /* single 4.0 */ - 0x40a00000, /* single 5.0 */ - 0x3f000000, /* single 0.5 */ - 0x41200000 /* single 10.0 */ + const_float32(0x00000000), /* single 0.0 */ + const_float32(0x3f800000), /* single 1.0 */ + const_float32(0x40000000), /* single 2.0 */ + const_float32(0x40400000), /* single 3.0 */ + const_float32(0x40800000), /* single 4.0 */ + const_float32(0x40a00000), /* single 5.0 */ + const_float32(0x3f000000), /* single 0.5 */ + const_float32(0x41200000) /* single 10.0 */ }; unsigned int getRegisterCount(const unsigned int opcode) diff -Nru qemu-kvm-0.14.1+noroms/linux-user/arm/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/arm/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/arm/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/arm/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -365,3 +365,16 @@ #define TARGET_NR_dup3 (358) #define TARGET_NR_pipe2 (359) #define TARGET_NR_inotify_init1 (360) +#define TARGET_NR_preadv (361) +#define TARGET_NR_pwritev (362) +#define TARGET_NR_rt_tgsigqueueinfo (363) +#define TARGET_NR_perf_event_open (364) +#define TARGET_NR_recvmmsg (365) +#define TARGET_NR_accept4 (366) +#define TARGET_NR_fanotify_init (367) +#define TARGET_NR_fanotify_mark (368) +#define TARGET_NR_prlimit64 (369) +#define TARGET_NR_name_to_handle_at (370) +#define TARGET_NR_open_by_handle_at (371) +#define TARGET_NR_clock_adjtime (372) +#define TARGET_NR_syncfs (373) diff -Nru qemu-kvm-0.14.1+noroms/linux-user/cris/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/cris/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/cris/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/cris/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -333,3 +333,5 @@ #define TARGET_NR_dup3 330 #define TARGET_NR_pipe2 331 #define TARGET_NR_inotify_init1 332 +#define TARGET_NR_preadv 333 +#define TARGET_NR_pwritev 334 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/elfload.c qemu-kvm-0.15.0+noroms/linux-user/elfload.c --- qemu-kvm-0.14.1+noroms/linux-user/elfload.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/elfload.c 2011-08-09 07:40:29.000000000 -0500 @@ -103,13 +103,13 @@ typedef target_ulong target_elf_greg_t; #ifdef USE_UID16 -typedef uint16_t target_uid_t; -typedef uint16_t target_gid_t; +typedef target_ushort target_uid_t; +typedef target_ushort target_gid_t; #else -typedef uint32_t target_uid_t; -typedef uint32_t target_gid_t; +typedef target_uint target_uid_t; +typedef target_uint target_gid_t; #endif -typedef int32_t target_pid_t; +typedef target_int target_pid_t; #ifdef TARGET_I386 @@ -339,11 +339,86 @@ #endif +#ifdef TARGET_UNICORE32 + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ((x) == EM_UNICORE32) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_UNICORE32 + +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->UC32_REG_asr = 0x10; + regs->UC32_REG_pc = infop->entry & 0xfffffffe; + regs->UC32_REG_sp = infop->start_stack; + /* FIXME - what to for failure of get_user()? */ + get_user_ual(regs->UC32_REG_02, stack + 8); /* envp */ + get_user_ual(regs->UC32_REG_01, stack + 4); /* envp */ + /* XXX: it seems that r0 is zeroed after ! */ + regs->UC32_REG_00 = 0; +} + +#define ELF_NREG 34 +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)[16] = env->regs[16]; + (*regs)[17] = env->regs[17]; + (*regs)[18] = env->regs[18]; + (*regs)[19] = env->regs[19]; + (*regs)[20] = env->regs[20]; + (*regs)[21] = env->regs[21]; + (*regs)[22] = env->regs[22]; + (*regs)[23] = env->regs[23]; + (*regs)[24] = env->regs[24]; + (*regs)[25] = env->regs[25]; + (*regs)[26] = env->regs[26]; + (*regs)[27] = env->regs[27]; + (*regs)[28] = env->regs[28]; + (*regs)[29] = env->regs[29]; + (*regs)[30] = env->regs[30]; + (*regs)[31] = env->regs[31]; + + (*regs)[32] = cpu_asr_read((CPUState *)env); + (*regs)[33] = env->regs[0]; /* XXX */ +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#define ELF_HWCAP (UC32_HWCAP_CMOV | UC32_HWCAP_UCF64) + +#endif + #ifdef TARGET_SPARC #ifdef TARGET_SPARC64 #define ELF_START_MMAP 0x80000000 - +#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ + | HWCAP_SPARC_MULDIV | HWCAP_SPARC_V9) #ifndef TARGET_ABI32 #define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS ) #else @@ -376,7 +451,8 @@ #else #define ELF_START_MMAP 0x80000000 - +#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ + | HWCAP_SPARC_MULDIV) #define elf_check_arch(x) ( (x) == EM_SPARC ) #define ELF_CLASS ELFCLASS32 @@ -793,6 +869,25 @@ #endif /* TARGET_ALPHA */ +#ifdef TARGET_S390X + +#define ELF_START_MMAP (0x20000000000ULL) + +#define elf_check_arch(x) ( (x) == ELF_ARCH ) + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_S390 + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->psw.addr = infop->entry; + regs->psw.mask = PSW_MASK_64 | PSW_MASK_32; + regs->gprs[15] = infop->start_stack; +} + +#endif /* TARGET_S390X */ + #ifndef ELF_PLATFORM #define ELF_PLATFORM (NULL) #endif @@ -834,7 +929,7 @@ #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 DLINFO_ITEMS 12 +#define DLINFO_ITEMS 13 static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) { @@ -1075,6 +1170,33 @@ } } +#ifdef CONFIG_USE_FDPIC +static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong sp) +{ + uint16_t n; + struct elf32_fdpic_loadseg *loadsegs = info->loadsegs; + + /* elf32_fdpic_loadseg */ + n = info->nsegs; + while (n--) { + sp -= 12; + put_user_u32(loadsegs[n].addr, sp+0); + put_user_u32(loadsegs[n].p_vaddr, sp+4); + put_user_u32(loadsegs[n].p_memsz, sp+8); + } + + /* elf32_fdpic_loadmap */ + sp -= 4; + put_user_u16(0, sp+0); /* version */ + put_user_u16(info->nsegs, sp+2); /* nsegs */ + + info->personality = PER_LINUX_FDPIC; + info->loadmap_addr = sp; + + return sp; +} +#endif + static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct elfhdr *exec, struct image_info *info, @@ -1082,11 +1204,29 @@ { abi_ulong sp; int size; + int i; + abi_ulong u_rand_bytes; + uint8_t k_rand_bytes[16]; abi_ulong u_platform; const char *k_platform; const int n = sizeof(elf_addr_t); sp = p; + +#ifdef CONFIG_USE_FDPIC + /* Needs to be before we load the env/argc/... */ + if (elf_is_fdpic(exec)) { + /* Need 4 byte alignment for these structs */ + sp &= ~3; + sp = loader_build_fdpic_loadmap(info, sp); + info->other_info = interp_info; + if (interp_info) { + interp_info->other_info = info; + sp = loader_build_fdpic_loadmap(interp_info, sp); + } + } +#endif + u_platform = 0; k_platform = ELF_PLATFORM; if (k_platform) { @@ -1096,6 +1236,20 @@ /* FIXME - check return value of memcpy_to_target() for failure */ memcpy_to_target(sp, k_platform, len); } + + /* + * Generate 16 random bytes for userspace PRNG seeding (not + * cryptically secure but it's not the aim of QEMU). + */ + srand((unsigned int) time(NULL)); + for (i = 0; i < 16; i++) { + k_rand_bytes[i] = rand(); + } + sp -= 16; + u_rand_bytes = sp; + /* FIXME - check return value of memcpy_to_target() for failure */ + memcpy_to_target(sp, k_rand_bytes, 16); + /* * Force 16 byte _final_ alignment here for generality. */ @@ -1136,6 +1290,8 @@ 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)); + NEW_AUX_ENT(AT_RANDOM, (abi_ulong) u_rand_bytes); + if (k_platform) NEW_AUX_ENT(AT_PLATFORM, u_platform); #ifdef ARCH_DLINFO @@ -1153,6 +1309,78 @@ return sp; } +static void probe_guest_base(const char *image_name, + abi_ulong loaddr, abi_ulong hiaddr) +{ + /* Probe for a suitable guest base address, if the user has not set + * it explicitly, and set guest_base appropriately. + * In case of error we will print a suitable message and exit. + */ +#if defined(CONFIG_USE_GUEST_BASE) + const char *errmsg; + 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; + } + return; + +exit_perror: + errmsg = strerror(errno); +exit_errmsg: + fprintf(stderr, "%s: %s\n", image_name, errmsg); + exit(-1); +#endif +} + + /* Load an ELF image into the address space. IMAGE_NAME is the filename of the image, to use in error messages. @@ -1197,6 +1425,11 @@ } bswap_phdr(phdr, ehdr->e_phnum); +#ifdef CONFIG_USE_FDPIC + info->nsegs = 0; + info->pt_dynamic_addr = 0; +#endif + /* Find the maximum size of the image and allocate an appropriate amount of memory to handle that. */ loaddr = -1, hiaddr = 0; @@ -1210,6 +1443,9 @@ if (a > hiaddr) { hiaddr = a; } +#ifdef CONFIG_USE_FDPIC + ++info->nsegs; +#endif } } @@ -1230,65 +1466,30 @@ /* 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); + probe_guest_base(image_name, loaddr, hiaddr); + } + load_bias = load_addr - loaddr; - 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; - } +#ifdef CONFIG_USE_FDPIC + { + struct elf32_fdpic_loadseg *loadsegs = info->loadsegs = + qemu_malloc(sizeof(*loadsegs) * info->nsegs); + + for (i = 0; i < ehdr->e_phnum; ++i) { + switch (phdr[i].p_type) { + case PT_DYNAMIC: + info->pt_dynamic_addr = phdr[i].p_vaddr + load_bias; + break; + case PT_LOAD: + loadsegs->addr = phdr[i].p_vaddr + load_bias; + loadsegs->p_vaddr = phdr[i].p_vaddr; + loadsegs->p_memsz = phdr[i].p_memsz; + ++loadsegs; + break; } - 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; +#endif info->load_bias = load_bias; info->load_addr = load_addr; @@ -1479,9 +1680,9 @@ { int i, shnum, nsyms, sym_idx = 0, str_idx = 0; struct elf_shdr *shdr; - char *strings; - struct syminfo *s; - struct elf_sym *syms, *new_syms; + char *strings = NULL; + struct syminfo *s = NULL; + struct elf_sym *new_syms, *syms = NULL; shnum = hdr->e_shnum; i = shnum * sizeof(struct elf_shdr); @@ -1506,24 +1707,19 @@ /* Now know where the strtab and symtab are. Snarf them. */ s = malloc(sizeof(*s)); if (!s) { - return; + goto give_up; } 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; + goto give_up; } 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; + goto give_up; } nsyms = i / sizeof(struct elf_sym); @@ -1546,16 +1742,18 @@ } } + /* No "useful" symbol. */ + if (nsyms == 0) { + goto give_up; + } + /* 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; + goto give_up; } syms = new_syms; @@ -1570,6 +1768,13 @@ s->lookup_symbol = lookup_symbolxx; s->next = syminfos; syminfos = s; + + return; + +give_up: + free(s); + free(strings); + free(syms); } int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, @@ -1690,19 +1895,20 @@ size_t namesz_rounded; int type; size_t datasz; + size_t datasz_rounded; void *data; size_t notesz; }; struct target_elf_siginfo { - int si_signo; /* signal number */ - int si_code; /* extra code */ - int si_errno; /* errno */ + target_int si_signo; /* signal number */ + target_int si_code; /* extra code */ + target_int si_errno; /* errno */ }; struct target_elf_prstatus { struct target_elf_siginfo pr_info; /* Info associated with signal */ - short pr_cursig; /* Current signal */ + target_short pr_cursig; /* Current signal */ target_ulong pr_sigpend; /* XXX */ target_ulong pr_sighold; /* XXX */ target_pid_t pr_pid; @@ -1714,7 +1920,7 @@ struct target_timeval pr_cutime; /* XXX Cumulative user time */ struct target_timeval pr_cstime; /* XXX Cumulative system time */ target_elf_gregset_t pr_reg; /* GP registers */ - int pr_fpvalid; /* XXX */ + target_int pr_fpvalid; /* XXX */ }; #define ELF_PRARGSZ (80) /* Number of chars for args */ @@ -1965,7 +2171,9 @@ note->namesz = namesz; note->namesz_rounded = roundup(namesz, sizeof (int32_t)); note->type = type; - note->datasz = roundup(sz, sizeof (int32_t));; + note->datasz = sz; + note->datasz_rounded = roundup(sz, sizeof (int32_t)); + note->data = data; /* @@ -1973,7 +2181,7 @@ * ELF document. */ note->notesz = sizeof (struct elf_note) + - note->namesz_rounded + note->datasz; + note->namesz_rounded + note->datasz_rounded; } static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine, @@ -2193,7 +2401,7 @@ return (-1); if (dump_write(fd, men->name, men->namesz_rounded) != 0) return (-1); - if (dump_write(fd, men->data, men->datasz) != 0) + if (dump_write(fd, men->data, men->datasz_rounded) != 0) return (-1); return (0); @@ -2409,7 +2617,7 @@ * ELF specification wants data to start at page boundary so * we align it here. */ - offset = roundup(offset, ELF_EXEC_PAGESIZE); + data_offset = offset = roundup(offset, ELF_EXEC_PAGESIZE); /* * Write program headers for memory regions mapped in @@ -2432,6 +2640,7 @@ phdr.p_flags |= PF_X; phdr.p_align = ELF_EXEC_PAGESIZE; + bswap_phdr(&phdr, 1); dump_write(fd, &phdr, sizeof (phdr)); } @@ -2443,8 +2652,6 @@ goto out; /* align data to page boundary */ - data_offset = lseek(fd, 0, SEEK_CUR); - data_offset = TARGET_PAGE_ALIGN(data_offset); if (lseek(fd, data_offset, SEEK_SET) != data_offset) goto out; diff -Nru qemu-kvm-0.14.1+noroms/linux-user/flatload.c qemu-kvm-0.15.0+noroms/linux-user/flatload.c --- qemu-kvm-0.14.1+noroms/linux-user/flatload.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/flatload.c 2011-08-09 07:40:29.000000000 -0500 @@ -41,6 +41,8 @@ #include "qemu.h" #include "flat.h" +#define ntohl(x) be32_to_cpu(x) +#include //#define DEBUG @@ -50,14 +52,6 @@ #define DBG_FLT(...) #endif -#define flat_reloc_valid(reloc, size) ((reloc) <= (size)) -#define flat_old_ram_flag(flag) (flag) -#ifdef TARGET_WORDS_BIGENDIAN -#define flat_get_relocate_addr(relval) (relval) -#else -#define flat_get_relocate_addr(relval) bswap32(relval) -#endif - #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ #define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */ @@ -78,8 +72,6 @@ struct linux_binprm; -#define ntohl(x) be32_to_cpu(x) - /****************************************************************************/ /* * create_flat_tables() parses the env- and arg-strings in new user @@ -387,12 +379,11 @@ 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 */ abi_ulong extra; abi_ulong reloc = 0, rp; int i, rev, relocs = 0; abi_ulong fpos; - abi_ulong start_code, end_code; + abi_ulong start_code; abi_ulong indx_len; hdr = ((struct flat_hdr *) bprm->buf); /* exec-header */ @@ -499,7 +490,6 @@ } reloc = datapos + (ntohl(hdr->reloc_start) - text_len); - memp = realdatastart; } else { @@ -514,7 +504,6 @@ realdatastart = textpos + ntohl(hdr->data_start); datapos = realdatastart + indx_len; reloc = (textpos + ntohl(hdr->reloc_start) + indx_len); - memp = textpos; #ifdef CONFIG_BINFMT_ZFLAT #error code needs checking @@ -560,11 +549,10 @@ /* The main program needs a little extra setup in the task structure */ start_code = textpos + sizeof (struct flat_hdr); - end_code = textpos + text_len; DBG_FLT("%s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x\n", id ? "Lib" : "Load", bprm->filename, - (int) start_code, (int) end_code, + (int) start_code, (int) (textpos + text_len), (int) datapos, (int) (datapos + data_len), (int) (datapos + data_len), @@ -625,6 +613,7 @@ * __start to address 4 so that is okay). */ if (rev > OLD_FLAT_VERSION) { + abi_ulong persistent = 0; for (i = 0; i < relocs; i++) { abi_ulong addr, relval; @@ -633,6 +622,9 @@ relocated first). */ if (get_user_ual(relval, reloc + i * sizeof(abi_ulong))) return -EFAULT; + relval = ntohl(relval); + if (flat_set_persistent(relval, &persistent)) + continue; addr = flat_get_relocate_addr(relval); rp = calc_reloc(addr, libinfo, id, 1); if (rp == RELOC_FAILED) @@ -641,22 +633,20 @@ /* Get the pointer's value. */ if (get_user_ual(addr, rp)) return -EFAULT; + addr = flat_get_addr_from_rp(rp, relval, flags, &persistent); if (addr != 0) { /* * Do the relocation. PIC relocs in the data section are * already in target order */ - -#ifndef TARGET_WORDS_BIGENDIAN if ((flags & FLAT_FLAG_GOTPIC) == 0) - addr = bswap32(addr); -#endif + addr = ntohl(addr); addr = calc_reloc(addr, libinfo, id, 0); if (addr == RELOC_FAILED) return -ENOEXEC; /* Write back the relocated pointer. */ - if (put_user_ual(addr, rp)) + if (flat_put_addr_at_rp(rp, addr, relval)) return -EFAULT; } } @@ -733,8 +723,15 @@ * pedantic and include space for the argv/envp array as it may have * a lot of entries. */ -#define TOP_OF_ARGS (TARGET_PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *)) - stack_len = TOP_OF_ARGS - bprm->p; /* the strings */ + stack_len = 0; + for (i = 0; i < bprm->argc; ++i) { + /* the argv strings */ + stack_len += strlen(bprm->argv[i]); + } + for (i = 0; i < bprm->envc; ++i) { + /* the envp strings */ + stack_len += strlen(bprm->envp[i]); + } stack_len += (bprm->argc + 1) * 4; /* the argv array */ stack_len += (bprm->envc + 1) * 4; /* the envp array */ @@ -775,7 +772,8 @@ stack_len *= sizeof(abi_ulong); if ((sp + stack_len) & 15) sp -= 16 - ((sp + stack_len) & 15); - sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, 1); + sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, + flat_argvp_envp_on_stack()); /* Fake some return addresses to ensure the call chain will * initialise library in order for us. We are required to call diff -Nru qemu-kvm-0.14.1+noroms/linux-user/i386/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/i386/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/i386/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/i386/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -335,3 +335,15 @@ #define TARGET_NR_dup3 330 #define TARGET_NR_pipe2 331 #define TARGET_NR_inotify_init1 332 +#define TARGET_NR_preadv 333 +#define TARGET_NR_pwritev 334 +#define TARGET_NR_rt_tgsigqueueinfo 335 +#define TARGET_NR_perf_event_open 336 +#define TARGET_NR_recvmmsg 337 +#define TARGET_NR_fanotify_init 338 +#define TARGET_NR_fanotify_mark 339 +#define TARGET_NR_prlimit64 340 +#define TARGET_NR_name_to_handle_at 341 +#define TARGET_NR_open_by_handle_at 342 +#define TARGET_NR_clock_adjtime 343 +#define TARGET_NR_syncfs 344 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/ioctls.h qemu-kvm-0.15.0+noroms/linux-user/ioctls.h --- qemu-kvm-0.14.1+noroms/linux-user/ioctls.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/ioctls.h 2011-08-09 07:40:29.000000000 -0500 @@ -59,6 +59,10 @@ IOCTL(KDSKBMODE, 0, TYPE_INT) IOCTL(KDGKBENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbentry))) IOCTL(KDGKBSENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbsentry))) + IOCTL(KDGKBLED, 0, TYPE_INT) + IOCTL(KDSKBLED, 0, TYPE_INT) + IOCTL(KDGETLED, 0, TYPE_INT) + IOCTL(KDSETLED, 0, TYPE_INT) IOCTL(BLKROSET, IOC_W, MK_PTR(TYPE_INT)) IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT)) @@ -112,7 +116,8 @@ IOCTL(SIOCADDMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq))) IOCTL(SIOCDELMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq))) IOCTL(SIOCSIFLINK, 0, TYPE_NULL) - IOCTL(SIOCGIFCONF, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ifconf))) + IOCTL_SPECIAL(SIOCGIFCONF, IOC_W | IOC_R, do_ioctl_ifconf, + MK_PTR(MK_STRUCT(STRUCT_ifconf))) IOCTL(SIOCGIFENCAP, IOC_RW, MK_PTR(TYPE_INT)) IOCTL(SIOCSIFENCAP, IOC_W, MK_PTR(TYPE_INT)) IOCTL(SIOCDARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq))) @@ -121,6 +126,7 @@ IOCTL(SIOCDRARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq))) IOCTL(SIOCSRARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq))) IOCTL(SIOCGRARP, IOC_R, MK_PTR(MK_STRUCT(STRUCT_arpreq))) + IOCTL(SIOCGIWNAME, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_char_ifreq))) IOCTL(CDROMPAUSE, 0, TYPE_NULL) IOCTL(CDROMSTART, 0, TYPE_NULL) @@ -323,6 +329,11 @@ IOCTL(FBIOGET_FSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_fix_screeninfo))) IOCTL(FBIOGET_VSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo))) IOCTL(FBIOPUT_VSCREENINFO, IOC_W, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo))) + IOCTL(FBIOGETCMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_cmap))) + IOCTL(FBIOPUTCMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_cmap))) + IOCTL(FBIOPAN_DISPLAY, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo))) + IOCTL(FBIOGET_CON2FBMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_con2fbmap))) + IOCTL(FBIOPUT_CON2FBMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_con2fbmap))) IOCTL(VT_OPENQRY, IOC_R, MK_PTR(TYPE_INT)) IOCTL(VT_GETSTATE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_vt_stat))) @@ -330,3 +341,7 @@ IOCTL(VT_WAITACTIVE, 0, TYPE_INT) IOCTL(VT_LOCKSWITCH, 0, TYPE_INT) IOCTL(VT_UNLOCKSWITCH, 0, TYPE_INT) + IOCTL(VT_GETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode))) + IOCTL(VT_SETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode))) + IOCTL(VT_RELDISP, 0, TYPE_INT) + IOCTL(VT_DISALLOCATE, 0, TYPE_INT) diff -Nru qemu-kvm-0.14.1+noroms/linux-user/linuxload.c qemu-kvm-0.15.0+noroms/linux-user/linuxload.c --- qemu-kvm-0.14.1+noroms/linux-user/linuxload.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/linuxload.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,22 +26,6 @@ return 0; } -static int in_group_p(gid_t g) -{ - /* return TRUE if we're in the specified group, FALSE otherwise */ - int ngroup; - int i; - gid_t grouplist[NGROUPS]; - - ngroup = getgroups(NGROUPS, grouplist); - for(i = 0; i < ngroup; i++) { - if(grouplist[i] == g) { - return 1; - } - } - return 0; -} - static int count(char ** vec) { int i; @@ -57,7 +41,7 @@ { struct stat st; int mode; - int retval, id_change; + int retval; if(fstat(bprm->fd, &st) < 0) { return(-errno); @@ -73,14 +57,10 @@ bprm->e_uid = geteuid(); bprm->e_gid = getegid(); - id_change = 0; /* Set-uid? */ if(mode & S_ISUID) { bprm->e_uid = st.st_uid; - if(bprm->e_uid != geteuid()) { - id_change = 1; - } } /* Set-gid? */ @@ -91,9 +71,6 @@ */ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { bprm->e_gid = st.st_gid; - if (!in_group_p(bprm->e_gid)) { - id_change = 1; - } } retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE); diff -Nru qemu-kvm-0.14.1+noroms/linux-user/m68k/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/m68k/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/m68k/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/m68k/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -328,3 +328,19 @@ #define TARGET_NR_dup3 326 #define TARGET_NR_pipe2 327 #define TARGET_NR_inotify_init1 328 +#define TARGET_NR_inotify_init1 328 +#define TARGET_NR_preadv 329 +#define TARGET_NR_pwritev 330 +#define TARGET_NR_rt_tgsigqueueinfo 331 +#define TARGET_NR_perf_event_open 332 +#define TARGET_NR_get_thread_area 333 +#define TARGET_NR_set_thread_area 334 +#define TARGET_NR_atomic_cmpxchg_32 335 +#define TARGET_NR_atomic_barrier 336 +#define TARGET_NR_fanotify_init 337 +#define TARGET_NR_fanotify_mark 338 +#define TARGET_NR_prlimit64 339 +#define TARGET_NR_name_to_handle_at 340 +#define TARGET_NR_open_by_handle_at 341 +#define TARGET_NR_clock_adjtime 342 +#define TARGET_NR_syncfs 343 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/main.c qemu-kvm-0.15.0+noroms/linux-user/main.c --- qemu-kvm-0.14.1+noroms/linux-user/main.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/main.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,8 +29,7 @@ #include "qemu.h" #include "qemu-common.h" #include "cache-utils.h" -/* For tb_lock */ -#include "exec-all.h" +#include "cpu.h" #include "tcg.h" #include "qemu-timer.h" #include "envlist.h" @@ -319,11 +318,12 @@ env->regs[R_EDX], env->regs[R_ESI], env->regs[R_EDI], - env->regs[R_EBP]); + env->regs[R_EBP], + 0, 0); break; #ifndef TARGET_ABI32 case EXCP_SYSCALL: - /* linux syscall from syscall intruction */ + /* linux syscall from syscall instruction */ env->regs[R_EAX] = do_syscall(env, env->regs[R_EAX], env->regs[R_EDI], @@ -331,7 +331,8 @@ env->regs[R_EDX], env->regs[10], env->regs[8], - env->regs[9]); + env->regs[9], + 0, 0); env->eip = env->exception_next_eip; break; #endif @@ -455,24 +456,6 @@ #ifdef TARGET_ARM -static void arm_cache_flush(abi_ulong start, abi_ulong last) -{ - abi_ulong addr, last1; - - if (last < start) - return; - addr = start; - for(;;) { - last1 = ((addr + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK) - 1; - if (last1 > last) - last1 = last; - tb_invalidate_page_range(addr, last1 + 1); - if (last1 == last) - break; - addr = last1 + 1; - } -} - /* Handle a jump to the kernel code page. */ static int do_kernel_trap(CPUARMState *env) @@ -717,7 +700,7 @@ } if (n == ARM_NR_cacheflush) { - arm_cache_flush(env->regs[0], env->regs[1]); + /* nop */ } else if (n == ARM_NR_semihosting || n == ARM_NR_thumb_semihosting) { env->regs[0] = do_arm_semihosting (env); @@ -733,7 +716,7 @@ if ( n > ARM_NR_BASE) { switch (n) { case ARM_NR_cacheflush: - arm_cache_flush(env->regs[0], env->regs[1]); + /* nop */ break; case ARM_NR_set_tls: cpu_set_tls(env, env->regs[0]); @@ -753,7 +736,8 @@ env->regs[2], env->regs[3], env->regs[4], - env->regs[5]); + env->regs[5], + 0, 0); } } else { goto error; @@ -816,6 +800,84 @@ #endif +#ifdef TARGET_UNICORE32 + +void cpu_loop(CPUState *env) +{ + int trapnr; + unsigned int n, insn; + target_siginfo_t info; + + for (;;) { + cpu_exec_start(env); + trapnr = uc32_cpu_exec(env); + cpu_exec_end(env); + switch (trapnr) { + case UC32_EXCP_PRIV: + { + /* system call */ + get_user_u32(insn, env->regs[31] - 4); + n = insn & 0xffffff; + + if (n >= UC32_SYSCALL_BASE) { + /* linux syscall */ + n -= UC32_SYSCALL_BASE; + if (n == UC32_SYSCALL_NR_set_tls) { + cpu_set_tls(env, env->regs[0]); + env->regs[0] = 0; + } else { + env->regs[0] = do_syscall(env, + n, + env->regs[0], + env->regs[1], + env->regs[2], + env->regs[3], + env->regs[4], + env->regs[5], + 0, 0); + } + } else { + goto error; + } + } + break; + case UC32_EXCP_TRAP: + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->cp0.c4_faultaddr; + queue_signal(env, info.si_signo, &info); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + 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); + } + } + break; + default: + goto error; + } + process_pending_signals(env); + } + +error: + fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + cpu_dump_state(env, stderr, fprintf, 0); + abort(); +} +#endif + #ifdef TARGET_SPARC #define SPARC64_STACK_BIAS 2047 @@ -959,7 +1021,8 @@ ret = do_syscall (env, env->gregs[1], env->regwptr[0], env->regwptr[1], env->regwptr[2], env->regwptr[3], - env->regwptr[4], env->regwptr[5]); + env->regwptr[4], env->regwptr[5], + 0, 0); if ((abi_ulong)ret >= (abi_ulong)(-515)) { #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) env->xcc |= PSR_CARRY; @@ -1552,7 +1615,7 @@ env->crf[0] &= ~0x1; ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6], env->gpr[7], - env->gpr[8]); + env->gpr[8], 0, 0); if (ret == (uint32_t)(-TARGET_QEMU_ESIGRETURN)) { /* Returning from a successful sigreturn syscall. Avoid corrupting register state. */ @@ -1812,7 +1875,7 @@ MIPS_SYS(sys_getcwd , 2) MIPS_SYS(sys_capget , 2) MIPS_SYS(sys_capset , 2) /* 4205 */ - MIPS_SYS(sys_sigaltstack , 0) + MIPS_SYS(sys_sigaltstack , 2) MIPS_SYS(sys_sendfile , 4) MIPS_SYS(sys_ni_syscall , 0) MIPS_SYS(sys_ni_syscall , 0) @@ -1922,6 +1985,33 @@ MIPS_SYS(sys_epoll_pwait, 6) MIPS_SYS(sys_ioprio_set, 3) MIPS_SYS(sys_ioprio_get, 2) + MIPS_SYS(sys_utimensat, 4) + MIPS_SYS(sys_signalfd, 3) + MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */ + MIPS_SYS(sys_eventfd, 1) + MIPS_SYS(sys_fallocate, 6) /* 4320 */ + MIPS_SYS(sys_timerfd_create, 2) + MIPS_SYS(sys_timerfd_gettime, 2) + MIPS_SYS(sys_timerfd_settime, 4) + MIPS_SYS(sys_signalfd4, 4) + MIPS_SYS(sys_eventfd2, 2) /* 4325 */ + MIPS_SYS(sys_epoll_create1, 1) + MIPS_SYS(sys_dup3, 3) + MIPS_SYS(sys_pipe2, 2) + MIPS_SYS(sys_inotify_init1, 1) + MIPS_SYS(sys_preadv, 6) /* 4330 */ + MIPS_SYS(sys_pwritev, 6) + MIPS_SYS(sys_rt_tgsigqueueinfo, 4) + MIPS_SYS(sys_perf_event_open, 5) + MIPS_SYS(sys_accept4, 4) + MIPS_SYS(sys_recvmmsg, 5) /* 4335 */ + MIPS_SYS(sys_fanotify_init, 2) + MIPS_SYS(sys_fanotify_mark, 6) + MIPS_SYS(sys_prlimit64, 4) + MIPS_SYS(sys_name_to_handle_at, 5) + MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ + MIPS_SYS(sys_clock_adjtime, 2) + MIPS_SYS(sys_syncfs, 1) }; #undef MIPS_SYS @@ -1990,7 +2080,7 @@ syscall_num = env->active_tc.gpr[2] - 4000; env->active_tc.PC += 4; if (syscall_num >= sizeof(mips_syscall_args)) { - ret = -ENOSYS; + ret = -TARGET_ENOSYS; } else { int nb_args; abi_ulong sp_reg; @@ -2013,7 +2103,7 @@ env->active_tc.gpr[5], env->active_tc.gpr[6], env->active_tc.gpr[7], - arg5, arg6/*, arg7, arg8*/); + arg5, arg6, arg7, arg8); } if (ret == -TARGET_QEMU_ESIGRETURN) { /* Returning from a successful sigreturn syscall. @@ -2030,6 +2120,8 @@ break; case EXCP_TLBL: case EXCP_TLBS: + case EXCP_AdEL: + case EXCP_AdES: info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ @@ -2101,7 +2193,8 @@ env->gregs[6], env->gregs[7], env->gregs[0], - env->gregs[1]); + env->gregs[1], + 0, 0); env->gregs[0] = ret; break; case EXCP_INTERRUPT: @@ -2170,7 +2263,8 @@ env->regs[12], env->regs[13], env->pregs[7], - env->pregs[11]); + env->pregs[11], + 0, 0); env->regs[10] = ret; break; case EXCP_DEBUG: @@ -2229,7 +2323,8 @@ env->regs[7], env->regs[8], env->regs[9], - env->regs[10]); + env->regs[10], + 0, 0); env->regs[3] = ret; env->sregs[SR_PC] = env->regs[14]; break; @@ -2258,7 +2353,7 @@ break; default: printf ("Unhandled hw-exception: 0x%x\n", - env->sregs[SR_ESR] & 5); + env->sregs[SR_ESR] & ESR_EC_MASK); cpu_dump_state(env, stderr, fprintf, 0); exit (1); break; @@ -2339,7 +2434,8 @@ env->dregs[3], env->dregs[4], env->dregs[5], - env->aregs[0]); + env->aregs[0], + 0, 0); } break; case EXCP_INTERRUPT: @@ -2449,49 +2545,27 @@ fprintf(stderr, "Machine check exception. Exit\n"); exit(1); break; - case EXCP_ARITH: - 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: + case EXCP_SMP_INTERRUPT: + case EXCP_CLK_INTERRUPT: + case EXCP_DEV_INTERRUPT: fprintf(stderr, "External interrupt. Exit\n"); exit(1); break; - case EXCP_DFAULT: + case EXCP_MMFAULT: 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 + info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); - info._sifields._sigfault._addr = env->ipr[IPR_EXC_ADDR]; + info._sifields._sigfault._addr = env->trap_arg0; queue_signal(env, info.si_signo, &info); break; - case EXCP_DTB_MISS_PAL: - fprintf(stderr, "MMU data TLB miss in PALcode\n"); - exit(1); - break; - case EXCP_ITB_MISS: - fprintf(stderr, "MMU instruction TLB miss\n"); - exit(1); - break; - case EXCP_ITB_ACV: - fprintf(stderr, "MMU instruction access violation\n"); - exit(1); - break; - case EXCP_DTB_MISS_NATIVE: - fprintf(stderr, "MMU data TLB miss\n"); - exit(1); - break; case EXCP_UNALIGN: 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]; + info._sifields._sigfault._addr = env->trap_arg0; queue_signal(env, info.si_signo, &info); break; case EXCP_OPCDEC: @@ -2503,12 +2577,20 @@ info._sifields._sigfault._addr = env->pc; queue_signal(env, info.si_signo, &info); break; + case EXCP_ARITH: + 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_FEN: /* No-op. Linux simply re-enables the FPU. */ break; - case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1): + case EXCP_CALL_PAL: env->lock_addr = -1; - switch ((trapnr >> 6) | 0x80) { + switch (env->error_code) { case 0x80: /* BPT */ info.si_signo = TARGET_SIGTRAP; @@ -2531,7 +2613,8 @@ 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]); + env->ir[IR_A4], env->ir[IR_A5], + 0, 0); if (trapnr == TARGET_NR_sigreturn || trapnr == TARGET_NR_rt_sigreturn) { break; @@ -2599,8 +2682,6 @@ goto do_sigill; } break; - case EXCP_CALL_PALP ... (EXCP_CALL_PALE - 1): - goto do_sigill; case EXCP_DEBUG: info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP); if (info.si_signo) { @@ -2624,14 +2705,96 @@ } #endif /* TARGET_ALPHA */ +#ifdef TARGET_S390X +void cpu_loop(CPUS390XState *env) +{ + int trapnr; + target_siginfo_t info; + + while (1) { + trapnr = cpu_s390x_exec (env); + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + 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); + } + } + break; + case EXCP_SVC: + { + int n = env->int_svc_code; + if (!n) { + /* syscalls > 255 */ + n = env->regs[1]; + } + env->psw.addr += env->int_svc_ilc; + env->regs[2] = do_syscall(env, n, + env->regs[2], + env->regs[3], + env->regs[4], + env->regs[5], + env->regs[6], + env->regs[7], + 0, 0); + } + break; + case EXCP_ADDR: + { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->__excp_addr; + queue_signal(env, info.si_signo, &info); + } + break; + case EXCP_SPEC: + { + fprintf(stderr,"specification exception insn 0x%08x%04x\n", ldl(env->psw.addr), lduw(env->psw.addr + 4)); + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->__excp_addr; + queue_signal(env, info.si_signo, &info); + } + break; + default: + printf ("Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(env, stderr, fprintf, 0); + exit (1); + } + process_pending_signals (env); + } +} + +#endif /* TARGET_S390X */ + +static void version(void) +{ + printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION + ", Copyright (c) 2003-2008 Fabrice Bellard\n"); +} + static void usage(void) { - printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n" - "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n" + version(); + printf("usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n" "Linux CPU emulator (compiled for %s emulation)\n" "\n" "Standard options:\n" "-h print this help\n" + "-version display version information and exit\n" "-g port wait gdb connection to port\n" "-L path set the elf interpreter prefix (default=%s)\n" "-s size set the stack size in bytes (default=%ld)\n" @@ -2708,6 +2871,8 @@ { const char *filename; const char *cpu_model; + const char *log_file = DEBUG_LOGFILE; + const char *log_mask = NULL; struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; struct linux_binprm bprm; @@ -2729,9 +2894,6 @@ qemu_cache_utils_init(envp); - /* init debug */ - cpu_set_log_filename(DEBUG_LOGFILE); - if ((envlist = envlist_create()) == NULL) { (void) fprintf(stderr, "Unable to allocate envlist\n"); exit(1); @@ -2770,22 +2932,15 @@ if (!strcmp(r, "-")) { break; } else if (!strcmp(r, "d")) { - int mask; - const CPULogItem *item; - - if (optind >= argc) + if (optind >= argc) { break; - - r = argv[optind++]; - mask = cpu_str_to_log_mask(r); - 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); + log_mask = argv[optind++]; + } else if (!strcmp(r, "D")) { + if (optind >= argc) { + break; + } + log_file = argv[optind++]; } else if (!strcmp(r, "E")) { r = argv[optind++]; if (envlist_setenv(envlist, r) != 0) @@ -2886,13 +3041,33 @@ singlestep = 1; } else if (!strcmp(r, "strace")) { do_strace = 1; - } else - { + } else if (!strcmp(r, "version")) { + version(); + exit(0); + } else { usage(); } } - if (optind >= argc) + /* init debug */ + cpu_set_log_filename(log_file); + if (log_mask) { + int mask; + const CPULogItem *item; + + mask = cpu_str_to_log_mask(log_mask); + 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); + } + + if (optind >= argc) { usage(); + } filename = argv[optind]; exec_path = argv[optind]; @@ -2916,6 +3091,8 @@ #endif #elif defined(TARGET_ARM) cpu_model = "any"; +#elif defined(TARGET_UNICORE32) + cpu_model = "any"; #elif defined(TARGET_M68K) cpu_model = "any"; #elif defined(TARGET_SPARC) @@ -3218,6 +3395,14 @@ env->regs[i] = regs->uregs[i]; } } +#elif defined(TARGET_UNICORE32) + { + int i; + cpu_asr_write(env, regs->uregs[32], 0xffffffff); + for (i = 0; i < 32; i++) { + env->regs[i] = regs->uregs[i]; + } + } #elif defined(TARGET_SPARC) { int i; @@ -3354,11 +3539,20 @@ env->regs[15] = regs->acr; env->pc = regs->erp; } +#elif defined(TARGET_S390X) + { + int i; + for (i = 0; i < 16; i++) { + env->regs[i] = regs->gprs[i]; + } + env->psw.mask = regs->psw.mask; + env->psw.addr = regs->psw.addr; + } #else #error unsupported target CPU #endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) +#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ diff -Nru qemu-kvm-0.14.1+noroms/linux-user/microblaze/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/microblaze/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/microblaze/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/microblaze/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -364,6 +364,16 @@ #define TARGET_NR_sendmsg 360 /* new */ #define TARGET_NR_recvmsg 361 /* new */ #define TARGET_NR_accept04 362 /* new */ - -#define TARGET_NR_syscalls 363 +#define TARGET_NR_preadv 363 /* new */ +#define TARGET_NR_pwritev 364 /* new */ +#define TARGET_NR_rt_tgsigqueueinfo 365 /* new */ +#define TARGET_NR_perf_event_open 366 /* new */ +#define TARGET_NR_recvmmsg 367 /* new */ +#define TARGET_NR_fanotify_init 368 +#define TARGET_NR_fanotify_mark 369 +#define TARGET_NR_prlimit64 370 +#define TARGET_NR_name_to_handle_at 371 +#define TARGET_NR_open_by_handle_at 372 +#define TARGET_NR_clock_adjtime 373 +#define TARGET_NR_syncfs 374 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/mips/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/mips/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/mips/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/mips/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -332,3 +332,16 @@ #define TARGET_NR_dup3 (TARGET_NR_Linux + 327) #define TARGET_NR_pipe2 (TARGET_NR_Linux + 328) #define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 329) +#define TARGET_NR_preadv (TARGET_NR_Linux + 330) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 331) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 332) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 333) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 334) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 335) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 336) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 337) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 338) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 339) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 340) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 341) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 342) diff -Nru qemu-kvm-0.14.1+noroms/linux-user/mips64/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/mips64/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/mips64/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/mips64/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -291,3 +291,16 @@ #define TARGET_NR_dup3 (TARGET_NR_Linux + 286) #define TARGET_NR_pipe2 (TARGET_NR_Linux + 287) #define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 288) +#define TARGET_NR_preadv (TARGET_NR_Linux + 289) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 290) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 291) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 292) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 293) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 294) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 295) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 296) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 297) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 298) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 299) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 300) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 301) diff -Nru qemu-kvm-0.14.1+noroms/linux-user/mipsn32/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/mipsn32/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/mipsn32/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/mipsn32/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -295,3 +295,17 @@ #define TARGET_NR_dup3 (TARGET_NR_Linux + 290) #define TARGET_NR_pipe2 (TARGET_NR_Linux + 291) #define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 292) +#define TARGET_NR_preadv (TARGET_NR_Linux + 293) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 294) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 295) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 296) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 297) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 298) +#define TARGET_NR_getdents64 (TARGET_NR_Linux + 299) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 300) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 301) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 302) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 303) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 304) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 305) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 306) diff -Nru qemu-kvm-0.14.1+noroms/linux-user/mmap.c qemu-kvm-0.15.0+noroms/linux-user/mmap.c --- qemu-kvm-0.14.1+noroms/linux-user/mmap.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/mmap.c 2011-08-09 07:40:29.000000000 -0500 @@ -216,6 +216,7 @@ unsigned long last_brk; +#ifdef CONFIG_USE_GUEST_BASE /* 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) @@ -249,6 +250,7 @@ mmap_next_start = addr; return last_addr; } +#endif /* * Find and reserve a free memory area of size 'size'. The search @@ -271,9 +273,11 @@ size = HOST_PAGE_ALIGN(size); +#ifdef CONFIG_USE_GUEST_BASE if (RESERVED_VA) { return mmap_find_vma_reserved(start, size); } +#endif addr = start; wrapped = repeat = 0; @@ -350,7 +354,7 @@ } wrapped = 1; /* Don't actually use 0 when wrapping, instead indicate - that we'd truely like an allocation in low memory. */ + that we'd truly like an allocation in low memory. */ addr = (mmap_min_addr > TARGET_PAGE_SIZE ? TARGET_PAGE_ALIGN(mmap_min_addr) : TARGET_PAGE_SIZE); diff -Nru qemu-kvm-0.14.1+noroms/linux-user/ppc/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/ppc/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/ppc/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/ppc/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -332,3 +332,33 @@ #define TARGET_NR_dup3 316 #define TARGET_NR_pipe2 317 #define TARGET_NR_inotify_init1 318 +#define TARGET_NR_perf_event_open 319 +#define TARGET_NR_preadv 320 +#define TARGET_NR_pwritev 321 +#define TARGET_NR_rt_tgsigqueueinfo 322 +#define TARGET_NR_fanotify_init 323 +#define TARGET_NR_fanotify_mark 324 +#define TARGET_NR_prlimit64 325 +#define TARGET_NR_socket 326 +#define TARGET_NR_bind 327 +#define TARGET_NR_connect 328 +#define TARGET_NR_listen 329 +#define TARGET_NR_accept 330 +#define TARGET_NR_getsockname 331 +#define TARGET_NR_getpeername 332 +#define TARGET_NR_socketpair 333 +#define TARGET_NR_send 334 +#define TARGET_NR_sendto 335 +#define TARGET_NR_recv 336 +#define TARGET_NR_recvfrom 337 +#define TARGET_NR_shutdown 338 +#define TARGET_NR_setsockopt 339 +#define TARGET_NR_getsockopt 340 +#define TARGET_NR_sendmsg 341 +#define TARGET_NR_recvmsg 342 +#define TARGET_NR_recvmmsg 343 +#define TARGET_NR_accept4 344 +#define TARGET_NR_name_to_handle_at 345 +#define TARGET_NR_open_by_handle_at 346 +#define TARGET_NR_clock_adjtime 347 +#define TARGET_NR_syncfs 348 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/qemu.h qemu-kvm-0.15.0+noroms/linux-user/qemu.h --- qemu-kvm-0.14.1+noroms/linux-user/qemu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/qemu.h 2011-08-09 07:40:29.000000000 -0500 @@ -51,6 +51,13 @@ abi_ulong arg_start; abi_ulong arg_end; int personality; +#ifdef CONFIG_USE_FDPIC + abi_ulong loadmap_addr; + uint16_t nsegs; + void *loadsegs; + abi_ulong pt_dynamic_addr; + struct image_info *other_info; +#endif }; #ifdef TARGET_I386 @@ -98,6 +105,9 @@ FPA11 fpa; int swi_errno; #endif +#ifdef TARGET_UNICORE32 + int swi_errno; +#endif #if defined(TARGET_I386) && !defined(TARGET_X86_64) abi_ulong target_v86; struct vm86_saved_state vm86_saved_regs; @@ -111,7 +121,7 @@ #ifdef TARGET_M68K int sim_syscalls; #endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) +#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) /* Extra fields for semihosted binaries. */ uint32_t stack_base; uint32_t heap_base; @@ -182,7 +192,8 @@ void syscall_init(void); 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); + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8); void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); extern THREAD CPUState *thread_env; void cpu_loop(CPUState *env); @@ -369,7 +380,7 @@ abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len); /* Functions for accessing guest memory. The tget and tput functions - read/write single values, byteswapping as neccessary. The lock_user + read/write single values, byteswapping as necessary. The lock_user gets a pointer to a contiguous area of guest memory, but does not perform and byteswapping. lock_user may return either a pointer to the guest memory, or a temporary buffer. */ diff -Nru qemu-kvm-0.14.1+noroms/linux-user/s390x/syscall.h qemu-kvm-0.15.0+noroms/linux-user/s390x/syscall.h --- qemu-kvm-0.14.1+noroms/linux-user/s390x/syscall.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/s390x/syscall.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,23 @@ +/* this typedef defines how a Program Status Word looks like */ +typedef struct { + abi_ulong mask; + abi_ulong addr; +} __attribute__ ((aligned(8))) target_psw_t; + +/* + * The pt_regs struct defines the way the registers are stored on + * the stack during a system call. + */ + +#define TARGET_NUM_GPRS 16 + +struct target_pt_regs { + abi_ulong args[1]; + target_psw_t psw; + abi_ulong gprs[TARGET_NUM_GPRS]; + abi_ulong orig_gpr2; + unsigned short ilc; + unsigned short trap; +}; + +#define UNAME_MACHINE "s390x" diff -Nru qemu-kvm-0.14.1+noroms/linux-user/s390x/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/s390x/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/s390x/syscall_nr.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/s390x/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,358 @@ +/* + * This file contains the system call numbers. + */ + +#define TARGET_NR_exit 1 +#define TARGET_NR_fork 2 +#define TARGET_NR_read 3 +#define TARGET_NR_write 4 +#define TARGET_NR_open 5 +#define TARGET_NR_close 6 +#define TARGET_NR_restart_syscall 7 +#define TARGET_NR_creat 8 +#define TARGET_NR_link 9 +#define TARGET_NR_unlink 10 +#define TARGET_NR_execve 11 +#define TARGET_NR_chdir 12 +#define TARGET_NR_mknod 14 +#define TARGET_NR_chmod 15 +#define TARGET_NR_lseek 19 +#define TARGET_NR_getpid 20 +#define TARGET_NR_mount 21 +#define TARGET_NR_umount 22 +#define TARGET_NR_ptrace 26 +#define TARGET_NR_alarm 27 +#define TARGET_NR_pause 29 +#define TARGET_NR_utime 30 +#define TARGET_NR_access 33 +#define TARGET_NR_nice 34 +#define TARGET_NR_sync 36 +#define TARGET_NR_kill 37 +#define TARGET_NR_rename 38 +#define TARGET_NR_mkdir 39 +#define TARGET_NR_rmdir 40 +#define TARGET_NR_dup 41 +#define TARGET_NR_pipe 42 +#define TARGET_NR_times 43 +#define TARGET_NR_brk 45 +#define TARGET_NR_signal 48 +#define TARGET_NR_acct 51 +#define TARGET_NR_umount2 52 +#define TARGET_NR_ioctl 54 +#define TARGET_NR_fcntl 55 +#define TARGET_NR_setpgid 57 +#define TARGET_NR_umask 60 +#define TARGET_NR_chroot 61 +#define TARGET_NR_ustat 62 +#define TARGET_NR_dup2 63 +#define TARGET_NR_getppid 64 +#define TARGET_NR_getpgrp 65 +#define TARGET_NR_setsid 66 +#define TARGET_NR_sigaction 67 +#define TARGET_NR_sigsuspend 72 +#define TARGET_NR_sigpending 73 +#define TARGET_NR_sethostname 74 +#define TARGET_NR_setrlimit 75 +#define TARGET_NR_getrusage 77 +#define TARGET_NR_gettimeofday 78 +#define TARGET_NR_settimeofday 79 +#define TARGET_NR_symlink 83 +#define TARGET_NR_readlink 85 +#define TARGET_NR_uselib 86 +#define TARGET_NR_swapon 87 +#define TARGET_NR_reboot 88 +#define TARGET_NR_readdir 89 +#define TARGET_NR_mmap 90 +#define TARGET_NR_munmap 91 +#define TARGET_NR_truncate 92 +#define TARGET_NR_ftruncate 93 +#define TARGET_NR_fchmod 94 +#define TARGET_NR_getpriority 96 +#define TARGET_NR_setpriority 97 +#define TARGET_NR_statfs 99 +#define TARGET_NR_fstatfs 100 +#define TARGET_NR_socketcall 102 +#define TARGET_NR_syslog 103 +#define TARGET_NR_setitimer 104 +#define TARGET_NR_getitimer 105 +#define TARGET_NR_stat 106 +#define TARGET_NR_lstat 107 +#define TARGET_NR_fstat 108 +#define TARGET_NR_lookup_dcookie 110 +#define TARGET_NR_vhangup 111 +#define TARGET_NR_idle 112 +#define TARGET_NR_wait4 114 +#define TARGET_NR_swapoff 115 +#define TARGET_NR_sysinfo 116 +#define TARGET_NR_ipc 117 +#define TARGET_NR_fsync 118 +#define TARGET_NR_sigreturn 119 +#define TARGET_NR_clone 120 +#define TARGET_NR_setdomainname 121 +#define TARGET_NR_uname 122 +#define TARGET_NR_adjtimex 124 +#define TARGET_NR_mprotect 125 +#define TARGET_NR_sigprocmask 126 +#define TARGET_NR_create_module 127 +#define TARGET_NR_init_module 128 +#define TARGET_NR_delete_module 129 +#define TARGET_NR_get_kernel_syms 130 +#define TARGET_NR_quotactl 131 +#define TARGET_NR_getpgid 132 +#define TARGET_NR_fchdir 133 +#define TARGET_NR_bdflush 134 +#define TARGET_NR_sysfs 135 +#define TARGET_NR_personality 136 +#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ +#define TARGET_NR_getdents 141 +#define TARGET_NR_flock 143 +#define TARGET_NR_msync 144 +#define TARGET_NR_readv 145 +#define TARGET_NR_writev 146 +#define TARGET_NR_getsid 147 +#define TARGET_NR_fdatasync 148 +#define TARGET_NR__sysctl 149 +#define TARGET_NR_mlock 150 +#define TARGET_NR_munlock 151 +#define TARGET_NR_mlockall 152 +#define TARGET_NR_munlockall 153 +#define TARGET_NR_sched_setparam 154 +#define TARGET_NR_sched_getparam 155 +#define TARGET_NR_sched_setscheduler 156 +#define TARGET_NR_sched_getscheduler 157 +#define TARGET_NR_sched_yield 158 +#define TARGET_NR_sched_get_priority_max 159 +#define TARGET_NR_sched_get_priority_min 160 +#define TARGET_NR_sched_rr_get_interval 161 +#define TARGET_NR_nanosleep 162 +#define TARGET_NR_mremap 163 +#define TARGET_NR_query_module 167 +#define TARGET_NR_poll 168 +#define TARGET_NR_nfsservctl 169 +#define TARGET_NR_prctl 172 +#define TARGET_NR_rt_sigreturn 173 +#define TARGET_NR_rt_sigaction 174 +#define TARGET_NR_rt_sigprocmask 175 +#define TARGET_NR_rt_sigpending 176 +#define TARGET_NR_rt_sigtimedwait 177 +#define TARGET_NR_rt_sigqueueinfo 178 +#define TARGET_NR_rt_sigsuspend 179 +#define TARGET_NR_pread64 180 +#define TARGET_NR_pwrite64 181 +#define TARGET_NR_getcwd 183 +#define TARGET_NR_capget 184 +#define TARGET_NR_capset 185 +#define TARGET_NR_sigaltstack 186 +#define TARGET_NR_sendfile 187 +#define TARGET_NR_getpmsg 188 +#define TARGET_NR_putpmsg 189 +#define TARGET_NR_vfork 190 +#define TARGET_NR_pivot_root 217 +#define TARGET_NR_mincore 218 +#define TARGET_NR_madvise 219 +#define TARGET_NR_getdents64 220 +#define TARGET_NR_readahead 222 +#define TARGET_NR_setxattr 224 +#define TARGET_NR_lsetxattr 225 +#define TARGET_NR_fsetxattr 226 +#define TARGET_NR_getxattr 227 +#define TARGET_NR_lgetxattr 228 +#define TARGET_NR_fgetxattr 229 +#define TARGET_NR_listxattr 230 +#define TARGET_NR_llistxattr 231 +#define TARGET_NR_flistxattr 232 +#define TARGET_NR_removexattr 233 +#define TARGET_NR_lremovexattr 234 +#define TARGET_NR_fremovexattr 235 +#define TARGET_NR_gettid 236 +#define TARGET_NR_tkill 237 +#define TARGET_NR_futex 238 +#define TARGET_NR_sched_setaffinity 239 +#define TARGET_NR_sched_getaffinity 240 +#define TARGET_NR_tgkill 241 +/* Number 242 is reserved for tux */ +#define TARGET_NR_io_setup 243 +#define TARGET_NR_io_destroy 244 +#define TARGET_NR_io_getevents 245 +#define TARGET_NR_io_submit 246 +#define TARGET_NR_io_cancel 247 +#define TARGET_NR_exit_group 248 +#define TARGET_NR_epoll_create 249 +#define TARGET_NR_epoll_ctl 250 +#define TARGET_NR_epoll_wait 251 +#define TARGET_NR_set_tid_address 252 +#define TARGET_NR_fadvise64 253 +#define TARGET_NR_timer_create 254 +#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) +#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) +#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) +#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) +#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) +#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) +#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) +#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) +/* Number 263 is reserved for vserver */ +#define TARGET_NR_statfs64 265 +#define TARGET_NR_fstatfs64 266 +#define TARGET_NR_remap_file_pages 267 +/* Number 268 is reserved for new sys_mbind */ +/* Number 269 is reserved for new sys_get_mempolicy */ +/* Number 270 is reserved for new sys_set_mempolicy */ +#define TARGET_NR_mq_open 271 +#define TARGET_NR_mq_unlink 272 +#define TARGET_NR_mq_timedsend 273 +#define TARGET_NR_mq_timedreceive 274 +#define TARGET_NR_mq_notify 275 +#define TARGET_NR_mq_getsetattr 276 +#define TARGET_NR_kexec_load 277 +#define TARGET_NR_add_key 278 +#define TARGET_NR_request_key 279 +#define TARGET_NR_keyctl 280 +#define TARGET_NR_waitid 281 +#define TARGET_NR_ioprio_set 282 +#define TARGET_NR_ioprio_get 283 +#define TARGET_NR_inotify_init 284 +#define TARGET_NR_inotify_add_watch 285 +#define TARGET_NR_inotify_rm_watch 286 +/* Number 287 is reserved for new sys_migrate_pages */ +#define TARGET_NR_openat 288 +#define TARGET_NR_mkdirat 289 +#define TARGET_NR_mknodat 290 +#define TARGET_NR_fchownat 291 +#define TARGET_NR_futimesat 292 +#define TARGET_NR_unlinkat 294 +#define TARGET_NR_renameat 295 +#define TARGET_NR_linkat 296 +#define TARGET_NR_symlinkat 297 +#define TARGET_NR_readlinkat 298 +#define TARGET_NR_fchmodat 299 +#define TARGET_NR_faccessat 300 +#define TARGET_NR_pselect6 301 +#define TARGET_NR_ppoll 302 +#define TARGET_NR_unshare 303 +#define TARGET_NR_set_robust_list 304 +#define TARGET_NR_get_robust_list 305 +#define TARGET_NR_splice 306 +#define TARGET_NR_sync_file_range 307 +#define TARGET_NR_tee 308 +#define TARGET_NR_vmsplice 309 +/* Number 310 is reserved for new sys_move_pages */ +#define TARGET_NR_getcpu 311 +#define TARGET_NR_epoll_pwait 312 +#define TARGET_NR_utimes 313 +#define TARGET_NR_fallocate 314 +#define TARGET_NR_utimensat 315 +#define TARGET_NR_signalfd 316 +#define TARGET_NR_timerfd 317 +#define TARGET_NR_eventfd 318 +#define TARGET_NR_timerfd_create 319 +#define TARGET_NR_timerfd_settime 320 +#define TARGET_NR_timerfd_gettime 321 +#define TARGET_NR_signalfd4 322 +#define TARGET_NR_eventfd2 323 +#define TARGET_NR_inotify_init1 324 +#define TARGET_NR_pipe2 325 +#define TARGET_NR_dup3 326 +#define TARGET_NR_epoll_create1 327 +#define TARGET_NR_preadv 328 +#define TARGET_NR_pwritev 329 +#define TARGET_NR_rt_tgsigqueueinfo 330 +#define TARGET_NR_perf_event_open 331 +#define TARGET_NR_fanotify_init 332 +#define TARGET_NR_fanotify_mark 333 +#define TARGET_NR_prlimit64 334 +#define TARGET_NR_name_to_handle_at 335 +#define TARGET_NR_open_by_handle_at 336 +#define TARGET_NR_clock_adjtime 337 +#define TARGET_NR_syncfs 338 + +/* + * There are some system calls that are not present on 64 bit, some + * have a different name although they do the same (e.g. TARGET_NR_chown32 + * is TARGET_NR_chown on 64 bit). + */ +#ifndef TARGET_S390X + +#define TARGET_NR_time 13 +#define TARGET_NR_lchown 16 +#define TARGET_NR_setuid 23 +#define TARGET_NR_getuid 24 +#define TARGET_NR_stime 25 +#define TARGET_NR_setgid 46 +#define TARGET_NR_getgid 47 +#define TARGET_NR_geteuid 49 +#define TARGET_NR_getegid 50 +#define TARGET_NR_setreuid 70 +#define TARGET_NR_setregid 71 +#define TARGET_NR_getrlimit 76 +#define TARGET_NR_getgroups 80 +#define TARGET_NR_setgroups 81 +#define TARGET_NR_fchown 95 +#define TARGET_NR_ioperm 101 +#define TARGET_NR_setfsuid 138 +#define TARGET_NR_setfsgid 139 +#define TARGET_NR__llseek 140 +#define TARGET_NR__newselect 142 +#define TARGET_NR_setresuid 164 +#define TARGET_NR_getresuid 165 +#define TARGET_NR_setresgid 170 +#define TARGET_NR_getresgid 171 +#define TARGET_NR_chown 182 +#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */ +#define TARGET_NR_mmap2 192 +#define TARGET_NR_truncate64 193 +#define TARGET_NR_ftruncate64 194 +#define TARGET_NR_stat64 195 +#define TARGET_NR_lstat64 196 +#define TARGET_NR_fstat64 197 +#define TARGET_NR_lchown32 198 +#define TARGET_NR_getuid32 199 +#define TARGET_NR_getgid32 200 +#define TARGET_NR_geteuid32 201 +#define TARGET_NR_getegid32 202 +#define TARGET_NR_setreuid32 203 +#define TARGET_NR_setregid32 204 +#define TARGET_NR_getgroups32 205 +#define TARGET_NR_setgroups32 206 +#define TARGET_NR_fchown32 207 +#define TARGET_NR_setresuid32 208 +#define TARGET_NR_getresuid32 209 +#define TARGET_NR_setresgid32 210 +#define TARGET_NR_getresgid32 211 +#define TARGET_NR_chown32 212 +#define TARGET_NR_setuid32 213 +#define TARGET_NR_setgid32 214 +#define TARGET_NR_setfsuid32 215 +#define TARGET_NR_setfsgid32 216 +#define TARGET_NR_fcntl64 221 +#define TARGET_NR_sendfile64 223 +#define TARGET_NR_fadvise64_64 264 +#define TARGET_NR_fstatat64 293 + +#else + +#define TARGET_NR_select 142 +#define TARGET_NR_getrlimit 191 /* SuS compliant getrlimit */ +#define TARGET_NR_lchown 198 +#define TARGET_NR_getuid 199 +#define TARGET_NR_getgid 200 +#define TARGET_NR_geteuid 201 +#define TARGET_NR_getegid 202 +#define TARGET_NR_setreuid 203 +#define TARGET_NR_setregid 204 +#define TARGET_NR_getgroups 205 +#define TARGET_NR_setgroups 206 +#define TARGET_NR_fchown 207 +#define TARGET_NR_setresuid 208 +#define TARGET_NR_getresuid 209 +#define TARGET_NR_setresgid 210 +#define TARGET_NR_getresgid 211 +#define TARGET_NR_chown 212 +#define TARGET_NR_setuid 213 +#define TARGET_NR_setgid 214 +#define TARGET_NR_setfsuid 215 +#define TARGET_NR_setfsgid 216 +#define TARGET_NR_newfstatat 293 + +#endif + diff -Nru qemu-kvm-0.14.1+noroms/linux-user/s390x/target_signal.h qemu-kvm-0.15.0+noroms/linux-user/s390x/target_signal.h --- qemu-kvm-0.14.1+noroms/linux-user/s390x/target_signal.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/s390x/target_signal.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,26 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + int ss_flags; + abi_ulong ss_size; +} target_stack_t; + +/* + * sigaltstack controls + */ +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_SIGSTKSZ 8192 + +static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) +{ + return state->regs[15]; +} + +#endif /* TARGET_SIGNAL_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-user/s390x/termbits.h qemu-kvm-0.15.0+noroms/linux-user/s390x/termbits.h --- qemu-kvm-0.14.1+noroms/linux-user/s390x/termbits.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/s390x/termbits.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,283 @@ +/* + * include/asm-s390/termbits.h + * + * S390 version + * + * Derived from "include/asm-i386/termbits.h" + */ + +#define TARGET_NCCS 19 +struct target_termios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ +}; + +struct target_termios2 { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ + unsigned int c_ispeed; /* input speed */ + unsigned int c_ospeed; /* output speed */ +}; + +struct target_ktermios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ + unsigned int c_ispeed; /* input speed */ + unsigned int c_ospeed; /* output speed */ +}; + +/* c_cc characters */ +#define TARGET_VINTR 0 +#define TARGET_VQUIT 1 +#define TARGET_VERASE 2 +#define TARGET_VKILL 3 +#define TARGET_VEOF 4 +#define TARGET_VTIME 5 +#define TARGET_VMIN 6 +#define TARGET_VSWTC 7 +#define TARGET_VSTART 8 +#define TARGET_VSTOP 9 +#define TARGET_VSUSP 10 +#define TARGET_VEOL 11 +#define TARGET_VREPRINT 12 +#define TARGET_VDISCARD 13 +#define TARGET_VWERASE 14 +#define TARGET_VLNEXT 15 +#define TARGET_VEOL2 16 + +/* c_iflag bits */ +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IUCLC 0001000 +#define TARGET_IXON 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IXOFF 0010000 +#define TARGET_IMAXBEL 0020000 +#define TARGET_IUTF8 0040000 + +/* c_oflag bits */ +#define TARGET_OPOST 0000001 +#define TARGET_OLCUC 0000002 +#define TARGET_ONLCR 0000004 +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 +#define TARGET_OFILL 0000100 +#define TARGET_OFDEL 0000200 +#define TARGET_NLDLY 0000400 +#define TARGET_NL0 0000000 +#define TARGET_NL1 0000400 +#define TARGET_CRDLY 0003000 +#define TARGET_CR0 0000000 +#define TARGET_CR1 0001000 +#define TARGET_CR2 0002000 +#define TARGET_CR3 0003000 +#define TARGET_TABDLY 0014000 +#define TARGET_TAB0 0000000 +#define TARGET_TAB1 0004000 +#define TARGET_TAB2 0010000 +#define TARGET_TAB3 0014000 +#define TARGET_XTABS 0014000 +#define TARGET_BSDLY 0020000 +#define TARGET_BS0 0000000 +#define TARGET_BS1 0020000 +#define TARGET_VTDLY 0040000 +#define TARGET_VT0 0000000 +#define TARGET_VT1 0040000 +#define TARGET_FFDLY 0100000 +#define TARGET_FF0 0000000 +#define TARGET_FF1 0100000 + +/* c_cflag bit meaning */ +#define TARGET_CBAUD 0010017 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 +#define TARGET_EXTA B19200 +#define TARGET_EXTB B38400 +#define TARGET_CSIZE 0000060 +#define TARGET_CS5 0000000 +#define TARGET_CS6 0000020 +#define TARGET_CS7 0000040 +#define TARGET_CS8 0000060 +#define TARGET_CSTOPB 0000100 +#define TARGET_CREAD 0000200 +#define TARGET_PARENB 0000400 +#define TARGET_PARODD 0001000 +#define TARGET_HUPCL 0002000 +#define TARGET_CLOCAL 0004000 +#define TARGET_CBAUDEX 0010000 +#define TARGET_BOTHER 0010000 +#define TARGET_B57600 0010001 +#define TARGET_B115200 0010002 +#define TARGET_B230400 0010003 +#define TARGET_B460800 0010004 +#define TARGET_B500000 0010005 +#define TARGET_B576000 0010006 +#define TARGET_B921600 0010007 +#define TARGET_B1000000 0010010 +#define TARGET_B1152000 0010011 +#define TARGET_B1500000 0010012 +#define TARGET_B2000000 0010013 +#define TARGET_B2500000 0010014 +#define TARGET_B3000000 0010015 +#define TARGET_B3500000 0010016 +#define TARGET_B4000000 0010017 +#define TARGET_CIBAUD 002003600000 /* input baud rate */ +#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ + +#define TARGET_IBSHIFT 16 /* Shift from CBAUD to CIBAUD */ + +/* c_lflag bits */ +#define TARGET_ISIG 0000001 +#define TARGET_ICANON 0000002 +#define TARGET_XCASE 0000004 +#define TARGET_ECHO 0000010 +#define TARGET_ECHOE 0000020 +#define TARGET_ECHOK 0000040 +#define TARGET_ECHONL 0000100 +#define TARGET_NOFLSH 0000200 +#define TARGET_TOSTOP 0000400 +#define TARGET_ECHOCTL 0001000 +#define TARGET_ECHOPRT 0002000 +#define TARGET_ECHOKE 0004000 +#define TARGET_FLUSHO 0010000 +#define TARGET_PENDIN 0040000 +#define TARGET_IEXTEN 0100000 + +/* tcflow() and TCXONC use these */ +#define TARGET_TCOOFF 0 +#define TARGET_TCOON 1 +#define TARGET_TCIOFF 2 +#define TARGET_TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TARGET_TCIFLUSH 0 +#define TARGET_TCOFLUSH 1 +#define TARGET_TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TARGET_TCSANOW 0 +#define TARGET_TCSADRAIN 1 +#define TARGET_TCSAFLUSH 2 + +/* + * include/asm-s390/ioctls.h + * + * S390 version + * + * Derived from "include/asm-i386/ioctls.h" + */ + +/* 0x54 is just a magic number to make these relatively unique ('T') */ + +#define TARGET_TCGETS 0x5401 +#define TARGET_TCSETS 0x5402 +#define TARGET_TCSETSW 0x5403 +#define TARGET_TCSETSF 0x5404 +#define TARGET_TCGETA 0x5405 +#define TARGET_TCSETA 0x5406 +#define TARGET_TCSETAW 0x5407 +#define TARGET_TCSETAF 0x5408 +#define TARGET_TCSBRK 0x5409 +#define TARGET_TCXONC 0x540A +#define TARGET_TCFLSH 0x540B +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E +#define TARGET_TIOCGPGRP 0x540F +#define TARGET_TIOCSPGRP 0x5410 +#define TARGET_TIOCOUTQ 0x5411 +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCGWINSZ 0x5413 +#define TARGET_TIOCSWINSZ 0x5414 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_FIONREAD 0x541B +#define TARGET_TIOCINQ FIONREAD +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +#define TARGET_FIONBIO 0x5421 +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ +#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ +#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TARGET_TCGETS2 _IOR('T',0x2A, struct termios2) +#define TARGET_TCSETS2 _IOW('T',0x2B, struct termios2) +#define TARGET_TCSETSW2 _IOW('T',0x2C, struct termios2) +#define TARGET_TCSETSF2 _IOW('T',0x2D, struct termios2) +#define TARGET_TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TARGET_TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TARGET_TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */ + +#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define TARGET_FIOCLEX 0x5451 +#define TARGET_FIOASYNC 0x5452 +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define TARGET_FIOQSIZE 0x545E + +/* Used for packet mode */ +#define TARGET_TIOCPKT_DATA 0 +#define TARGET_TIOCPKT_FLUSHREAD 1 +#define TARGET_TIOCPKT_FLUSHWRITE 2 +#define TARGET_TIOCPKT_STOP 4 +#define TARGET_TIOCPKT_START 8 +#define TARGET_TIOCPKT_NOSTOP 16 +#define TARGET_TIOCPKT_DOSTOP 32 + +#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ + diff -Nru qemu-kvm-0.14.1+noroms/linux-user/sh4/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/sh4/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/sh4/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/sh4/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -125,7 +125,7 @@ #define TARGET_NR_clone 120 #define TARGET_NR_setdomainname 121 #define TARGET_NR_uname 122 -#define TARGET_NR_modify_ldt 123 +#define TARGET_NR_cacheflush 123 #define TARGET_NR_adjtimex 124 #define TARGET_NR_mprotect 125 #define TARGET_NR_sigprocmask 126 @@ -334,3 +334,35 @@ #define TARGET_NR_dup3 330 #define TARGET_NR_pipe2 331 #define TARGET_NR_inotify_init1 332 +#define TARGET_NR_preadv 333 +#define TARGET_NR_pwritev 334 +#define TARGET_NR_rt_tgsigqueueinfo 335 +#define TARGET_NR_perf_event_open 336 +#define TARGET_NR_fanotify_init 337 +#define TARGET_NR_fanotify_mark 338 +#define TARGET_NR_prlimit64 339 + +/* Non-multiplexed socket family */ +#define TARGET_NR_socket 340 +#define TARGET_NR_bind 341 +#define TARGET_NR_connect 342 +#define TARGET_NR_listen 343 +#define TARGET_NR_accept 344 +#define TARGET_NR_getsockname 345 +#define TARGET_NR_getpeername 346 +#define TARGET_NR_socketpair 347 +#define TARGET_NR_send 348 +#define TARGET_NR_sendto 349 +#define TARGET_NR_recv 350 +#define TARGET_NR_recvfrom 351 +#define TARGET_NR_shutdown 352 +#define TARGET_NR_setsockopt 353 +#define TARGET_NR_getsockopt 354 +#define TARGET_NR_sendmsg 355 +#define TARGET_NR_recvmsg 356 +#define TARGET_NR_recvmmsg 357 +#define TARGET_NR_accept4 358 +#define TARGET_NR_name_to_handle_at 359 +#define TARGET_NR_open_by_handle_at 360 +#define TARGET_NR_clock_adjtime 361 +#define TARGET_NR_syncfs 362 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/signal.c qemu-kvm-0.15.0+noroms/linux-user/signal.c --- qemu-kvm-0.14.1+noroms/linux-user/signal.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/signal.c 2011-08-09 07:40:29.000000000 -0500 @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -391,7 +390,7 @@ target_sig, strsignal(host_sig), "core dumped" ); } - /* The proper exit code for dieing from an uncaught signal is + /* The proper exit code for dying from an uncaught signal is * -. The kernel doesn't allow exit() or _exit() to pass * a negative value. To get the proper exit code we need to * actually die from an uncaught signal. Here the default signal @@ -982,8 +981,8 @@ env->regs[R_ECX] = tswapl(sc->ecx); env->eip = tswapl(sc->eip); - cpu_x86_load_seg(env, R_CS, lduw(&sc->cs) | 3); - cpu_x86_load_seg(env, R_SS, lduw(&sc->ss) | 3); + cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3); + cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3); tmpflags = tswapl(sc->eflags); env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); @@ -1299,7 +1298,7 @@ __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(float64_val(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); @@ -1588,7 +1587,7 @@ return 0; } for (i = 0; i < 32; i++) { - __get_user(env->vfp.regs[i], &vfpframe->ufp.fpregs[i]); + __get_user(float64_val(env->vfp.regs[i]), &vfpframe->ufp.fpregs[i]); } __get_user(fpscr, &vfpframe->ufp.fpscr); vfp_set_fpscr(env, fpscr); @@ -2081,7 +2080,6 @@ uint32_t up_psr, pc, npc; target_sigset_t set; sigset_t host_set; - abi_ulong fpu_save_addr; int err, i; sf_addr = env->regwptr[UREG_FP]; @@ -2121,10 +2119,11 @@ err |= __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]); } - err |= __get_user(fpu_save_addr, &sf->fpu_save); - - //if (fpu_save) - // err |= restore_fpu_state(env, fpu_save); + /* FIXME: implement FPU save/restore: + * __get_user(fpu_save, &sf->fpu_save); + * if (fpu_save) + * err |= restore_fpu_state(env, fpu_save); + */ /* This is pretty much atomic, no amount locking would prevent * the races which exist anyways. @@ -2229,7 +2228,6 @@ target_mc_gregset_t *grp; abi_ulong pc, npc, tstate; abi_ulong fp, i7, w_addr; - unsigned char fenab; int err; unsigned int i; @@ -2294,7 +2292,11 @@ if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), abi_ulong) != 0) goto do_sigsegv; - err |= __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab)); + /* FIXME this does not match how the kernel handles the FPU in + * its sparc64_set_context implementation. In particular the FPU + * is only restored if fenab is non-zero in: + * __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; @@ -3614,6 +3616,339 @@ return -TARGET_ENOSYS; } +#elif defined(TARGET_S390X) + +#define __NUM_GPRS 16 +#define __NUM_FPRS 16 +#define __NUM_ACRS 16 + +#define S390_SYSCALL_SIZE 2 +#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ + +#define _SIGCONTEXT_NSIG 64 +#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ +#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) +#define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS) +#define PSW_ADDR_AMODE 0x0000000000000000UL /* 0x80000000UL for 31-bit */ +#define S390_SYSCALL_OPCODE ((uint16_t)0x0a00) + +typedef struct { + target_psw_t psw; + target_ulong gprs[__NUM_GPRS]; + unsigned int acrs[__NUM_ACRS]; +} target_s390_regs_common; + +typedef struct { + unsigned int fpc; + double fprs[__NUM_FPRS]; +} target_s390_fp_regs; + +typedef struct { + target_s390_regs_common regs; + target_s390_fp_regs fpregs; +} target_sigregs; + +struct target_sigcontext { + target_ulong oldmask[_SIGCONTEXT_NSIG_WORDS]; + target_sigregs *sregs; +}; + +typedef struct { + uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + struct target_sigcontext sc; + target_sigregs sregs; + int signo; + uint8_t retcode[S390_SYSCALL_SIZE]; +} sigframe; + +struct target_ucontext { + target_ulong tuc_flags; + struct target_ucontext *tuc_link; + target_stack_t tuc_stack; + target_sigregs tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +typedef struct { + uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + uint8_t retcode[S390_SYSCALL_SIZE]; + struct target_siginfo info; + struct target_ucontext uc; +} rt_sigframe; + +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUState *env, size_t frame_size) +{ + abi_ulong sp; + + /* Default to using normal stack */ + sp = env->regs[15]; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa_flags & TARGET_SA_ONSTACK) { + if (!sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + + target_sigaltstack_used.ss_size; + } + } + + /* This is the legacy signal stack switching. */ + else if (/* FIXME !user_mode(regs) */ 0 && + !(ka->sa_flags & TARGET_SA_RESTORER) && + ka->sa_restorer) { + sp = (abi_ulong) ka->sa_restorer; + } + + return (sp - frame_size) & -8ul; +} + +static void save_sigregs(CPUState *env, target_sigregs *sregs) +{ + int i; + //save_access_regs(current->thread.acrs); FIXME + + /* Copy a 'clean' PSW mask to the user to avoid leaking + information about whether PER is currently on. */ + __put_user(env->psw.mask, &sregs->regs.psw.mask); + __put_user(env->psw.addr, &sregs->regs.psw.addr); + for (i = 0; i < 16; i++) { + __put_user(env->regs[i], &sregs->regs.gprs[i]); + } + for (i = 0; i < 16; i++) { + __put_user(env->aregs[i], &sregs->regs.acrs[i]); + } + /* + * We have to store the fp registers to current->thread.fp_regs + * to merge them with the emulated registers. + */ + //save_fp_regs(¤t->thread.fp_regs); FIXME + for (i = 0; i < 16; i++) { + __put_user(env->fregs[i].ll, &sregs->fpregs.fprs[i]); + } +} + +static void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUState *env) +{ + sigframe *frame; + abi_ulong frame_addr; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, + (unsigned long long)frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + qemu_log("%s: 1\n", __FUNCTION__); + if (__put_user(set->sig[0], &frame->sc.oldmask[0])) { + goto give_sigsegv; + } + + save_sigregs(env, &frame->sregs); + + __put_user((abi_ulong)(unsigned long)&frame->sregs, + (abi_ulong *)&frame->sc.sregs); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[14] = (unsigned long) + ka->sa_restorer | PSW_ADDR_AMODE; + } else { + env->regs[14] = (unsigned long) + frame->retcode | PSW_ADDR_AMODE; + if (__put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, + (uint16_t *)(frame->retcode))) + goto give_sigsegv; + } + + /* Set up backchain. */ + if (__put_user(env->regs[15], (abi_ulong *) frame)) { + goto give_sigsegv; + } + + /* Set up registers for signal handler */ + env->regs[15] = (target_ulong)(unsigned long) frame; + env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; + + env->regs[2] = sig; //map_signal(sig); + env->regs[3] = (target_ulong)(unsigned long) &frame->sc; + + /* We forgot to include these in the sigcontext. + To avoid breaking binary compatibility, they are passed as args. */ + env->regs[4] = 0; // FIXME: no clue... current->thread.trap_no; + env->regs[5] = 0; // FIXME: no clue... current->thread.prot_addr; + + /* Place signal number on stack to allow backtrace from handler. */ + if (__put_user(env->regs[2], (int *) &frame->signo)) { + goto give_sigsegv; + } + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + qemu_log("%s: give_sigsegv\n", __FUNCTION__); + unlock_user_struct(frame, frame_addr, 1); + force_sig(TARGET_SIGSEGV); +} + +static void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) +{ + int i; + rt_sigframe *frame; + abi_ulong frame_addr; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, + (unsigned long long)frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + qemu_log("%s: 1\n", __FUNCTION__); + if (copy_siginfo_to_user(&frame->info, info)) { + goto give_sigsegv; + } + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + save_sigregs(env, &frame->uc.tuc_mcontext); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user((abi_ulong)set->sig[i], + (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE; + } else { + env->regs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; + if (__put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, + (uint16_t *)(frame->retcode))) { + goto give_sigsegv; + } + } + + /* Set up backchain. */ + if (__put_user(env->regs[15], (abi_ulong *) frame)) { + goto give_sigsegv; + } + + /* Set up registers for signal handler */ + env->regs[15] = (target_ulong)(unsigned long) frame; + env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; + + env->regs[2] = sig; //map_signal(sig); + env->regs[3] = (target_ulong)(unsigned long) &frame->info; + env->regs[4] = (target_ulong)(unsigned long) &frame->uc; + return; + +give_sigsegv: + qemu_log("%s: give_sigsegv\n", __FUNCTION__); + unlock_user_struct(frame, frame_addr, 1); + force_sig(TARGET_SIGSEGV); +} + +static int +restore_sigregs(CPUState *env, target_sigregs *sc) +{ + int err = 0; + int i; + + for (i = 0; i < 16; i++) { + err |= __get_user(env->regs[i], &sc->regs.gprs[i]); + } + + err |= __get_user(env->psw.mask, &sc->regs.psw.mask); + qemu_log("%s: sc->regs.psw.addr 0x%llx env->psw.addr 0x%llx\n", + __FUNCTION__, (unsigned long long)sc->regs.psw.addr, + (unsigned long long)env->psw.addr); + err |= __get_user(env->psw.addr, &sc->regs.psw.addr); + /* FIXME: 31-bit -> | PSW_ADDR_AMODE */ + + for (i = 0; i < 16; i++) { + err |= __get_user(env->aregs[i], &sc->regs.acrs[i]); + } + for (i = 0; i < 16; i++) { + err |= __get_user(env->fregs[i].ll, &sc->fpregs.fprs[i]); + } + + return err; +} + +long do_sigreturn(CPUState *env) +{ + sigframe *frame; + abi_ulong frame_addr = env->regs[15]; + qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, + (unsigned long long)frame_addr); + target_sigset_t target_set; + sigset_t set; + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + if (__get_user(target_set.sig[0], &frame->sc.oldmask[0])) { + goto badframe; + } + + target_to_host_sigset_internal(&set, &target_set); + sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */ + + if (restore_sigregs(env, &frame->sregs)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return env->regs[2]; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} + +long do_rt_sigreturn(CPUState *env) +{ + rt_sigframe *frame; + abi_ulong frame_addr = env->regs[15]; + qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__, + (unsigned long long)frame_addr); + 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); /* ~_BLOCKABLE? */ + + if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0, + get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + unlock_user_struct(frame, frame_addr, 0); + return env->regs[2]; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} + #elif defined(TARGET_PPC) && !defined(TARGET_PPC64) /* FIXME: Many of the structures are defined for both PPC and PPC64, but diff -Nru qemu-kvm-0.14.1+noroms/linux-user/sparc/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/sparc/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/sparc/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/sparc/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -285,3 +285,15 @@ #define TARGET_NR_pipe2 321 #define TARGET_NR_inotify_init1 322 #define TARGET_NR_accept4 323 +#define TARGET_NR_preadv 324 +#define TARGET_NR_pwritev 325 +#define TARGET_NR_rt_tgsigqueueinfo 326 +#define TARGET_NR_perf_event_open 327 +#define TARGET_NR_recvmmsg 328 +#define TARGET_NR_fanotify_init 329 +#define TARGET_NR_fanotify_mark 330 +#define TARGET_NR_prlimit64 331 +#define TARGET_NR_name_to_handle_at 332 +#define TARGET_NR_open_by_handle_at 333 +#define TARGET_NR_clock_adjtime 334 +#define TARGET_NR_syncfs 335 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/sparc64/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/sparc64/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/sparc64/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/sparc64/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -322,3 +322,15 @@ #define TARGET_NR_pipe2 321 #define TARGET_NR_inotify_init1 322 #define TARGET_NR_accept4 323 +#define TARGET_NR_preadv 324 +#define TARGET_NR_pwritev 325 +#define TARGET_NR_rt_tgsigqueueinfo 326 +#define TARGET_NR_perf_event_open 327 +#define TARGET_NR_recvmmsg 328 +#define TARGET_NR_fanotify_init 329 +#define TARGET_NR_fanotify_mark 330 +#define TARGET_NR_prlimit64 331 +#define TARGET_NR_name_to_handle_at 332 +#define TARGET_NR_open_by_handle_at 333 +#define TARGET_NR_clock_adjtime 334 +#define TARGET_NR_syncfs 335 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/strace.c qemu-kvm-0.15.0+noroms/linux-user/strace.c --- qemu-kvm-0.14.1+noroms/linux-user/strace.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/strace.c 2011-08-09 07:40:29.000000000 -0500 @@ -9,6 +9,7 @@ #include #include #include +#include #include "qemu.h" int do_strace=0; @@ -63,6 +64,7 @@ UNUSED static void print_raw_param(const char *, abi_long, int); UNUSED static void print_timeval(abi_ulong, int); UNUSED static void print_number(abi_long, int); +UNUSED static void print_signal(abi_ulong, int); /* * Utility functions @@ -117,6 +119,37 @@ gemu_log("%d",cmd); } +static void +print_signal(abi_ulong arg, int last) +{ + const char *signal_name = NULL; + switch(arg) { + case TARGET_SIGHUP: signal_name = "SIGHUP"; break; + case TARGET_SIGINT: signal_name = "SIGINT"; break; + case TARGET_SIGQUIT: signal_name = "SIGQUIT"; break; + case TARGET_SIGILL: signal_name = "SIGILL"; break; + case TARGET_SIGABRT: signal_name = "SIGABRT"; break; + case TARGET_SIGFPE: signal_name = "SIGFPE"; break; + case TARGET_SIGKILL: signal_name = "SIGKILL"; break; + case TARGET_SIGSEGV: signal_name = "SIGSEGV"; break; + case TARGET_SIGPIPE: signal_name = "SIGPIPE"; break; + case TARGET_SIGALRM: signal_name = "SIGALRM"; break; + case TARGET_SIGTERM: signal_name = "SIGTERM"; break; + case TARGET_SIGUSR1: signal_name = "SIGUSR1"; break; + case TARGET_SIGUSR2: signal_name = "SIGUSR2"; break; + case TARGET_SIGCHLD: signal_name = "SIGCHLD"; break; + case TARGET_SIGCONT: signal_name = "SIGCONT"; break; + case TARGET_SIGSTOP: signal_name = "SIGSTOP"; break; + case TARGET_SIGTTIN: signal_name = "SIGTTIN"; break; + case TARGET_SIGTTOU: signal_name = "SIGTTOU"; break; + } + if (signal_name == NULL) { + print_raw_param("%ld", arg, 1); + return; + } + gemu_log("%s%s", signal_name, get_comma(last)); +} + #ifdef TARGET_NR__newselect static void print_fdset(int n, abi_ulong target_fds_addr) @@ -398,6 +431,7 @@ FLAG_TARGET(MAP_DENYWRITE), FLAG_TARGET(MAP_FIXED), FLAG_TARGET(MAP_GROWSDOWN), + FLAG_TARGET(MAP_EXECUTABLE), #ifdef MAP_LOCKED FLAG_TARGET(MAP_LOCKED), #endif @@ -408,6 +442,9 @@ #ifdef MAP_POPULATE FLAG_TARGET(MAP_POPULATE), #endif +#ifdef TARGET_MAP_UNINITIALIZED + FLAG_TARGET(MAP_UNINITIALIZED), +#endif FLAG_END, }; @@ -423,6 +460,44 @@ FLAG_END, }; +UNUSED static struct flags clone_flags[] = { + FLAG_GENERIC(CLONE_VM), + FLAG_GENERIC(CLONE_FS), + FLAG_GENERIC(CLONE_FILES), + FLAG_GENERIC(CLONE_SIGHAND), + FLAG_GENERIC(CLONE_PTRACE), + FLAG_GENERIC(CLONE_VFORK), + FLAG_GENERIC(CLONE_PARENT), + FLAG_GENERIC(CLONE_THREAD), + FLAG_GENERIC(CLONE_NEWNS), + FLAG_GENERIC(CLONE_SYSVSEM), + FLAG_GENERIC(CLONE_SETTLS), + FLAG_GENERIC(CLONE_PARENT_SETTID), + FLAG_GENERIC(CLONE_CHILD_CLEARTID), + FLAG_GENERIC(CLONE_DETACHED), + FLAG_GENERIC(CLONE_UNTRACED), + FLAG_GENERIC(CLONE_CHILD_SETTID), +#if defined(CLONE_NEWUTS) + FLAG_GENERIC(CLONE_NEWUTS), +#endif +#if defined(CLONE_NEWIPC) + FLAG_GENERIC(CLONE_NEWIPC), +#endif +#if defined(CLONE_NEWUSER) + FLAG_GENERIC(CLONE_NEWUSER), +#endif +#if defined(CLONE_NEWPID) + FLAG_GENERIC(CLONE_NEWPID), +#endif +#if defined(CLONE_NEWNET) + FLAG_GENERIC(CLONE_NEWNET), +#endif +#if defined(CLONE_IO) + FLAG_GENERIC(CLONE_IO), +#endif + FLAG_END, +}; + /* * print_xxx utility functions. These are used to print syscall * parameters in certain format. All of these have parameter @@ -437,14 +512,11 @@ } static void -print_flags(const struct flags *f, abi_long tflags, int last) +print_flags(const struct flags *f, abi_long flags, int last) { const char *sep = ""; - int flags; int n; - flags = (int)tswap32(tflags); - if ((flags == 0) && (f->f_value == 0)) { gemu_log("%s%s", f->f_string, get_comma(last)); return; @@ -461,36 +533,33 @@ if (n > 0) { /* print rest of the flags as numeric */ if (flags != 0) { - gemu_log("%s%#x%s", sep, flags, get_comma(last)); + gemu_log("%s%#x%s", sep, (unsigned int)flags, get_comma(last)); } else { gemu_log("%s", get_comma(last)); } } else { /* no string version of flags found, print them in hex then */ - gemu_log("%#x%s", flags, get_comma(last)); + gemu_log("%#x%s", (unsigned int)flags, get_comma(last)); } } static void -print_at_dirfd(abi_long tdirfd, int last) +print_at_dirfd(abi_long dirfd, int last) { - int dirfd = tswap32(tdirfd); - #ifdef AT_FDCWD if (dirfd == AT_FDCWD) { gemu_log("AT_FDCWD%s", get_comma(last)); return; } #endif - gemu_log("%d%s", dirfd, get_comma(last)); + gemu_log("%d%s", (int)dirfd, get_comma(last)); } static void -print_file_mode(abi_long tmode, int last) +print_file_mode(abi_long mode, int last) { const char *sep = ""; const struct flags *m; - mode_t mode = (mode_t)tswap32(tmode); for (m = &mode_flags[0]; m->f_string != NULL; m++) { if ((m->f_value & mode) == m->f_value) { @@ -504,16 +573,14 @@ mode &= ~S_IFMT; /* print rest of the mode as octal */ if (mode != 0) - gemu_log("%s%#o", sep, mode); + gemu_log("%s%#o", sep, (unsigned int)mode); gemu_log("%s", get_comma(last)); } static void -print_open_flags(abi_long tflags, int last) +print_open_flags(abi_long flags, int last) { - int flags = tswap32(tflags); - print_flags(open_access_flags, flags & TARGET_O_ACCMODE, 1); flags &= ~TARGET_O_ACCMODE; if (flags == 0) { @@ -616,7 +683,7 @@ abi_long arg3, abi_long arg4, abi_long arg5) { print_syscall_prologue(name); - print_raw_param("%d", tswap32(arg0), 0); + print_raw_param("%d", arg0, 0); print_pointer(arg1, 0); print_number(arg2, 1); print_syscall_epilogue(name); @@ -673,6 +740,39 @@ } #endif +#ifdef TARGET_NR_clone +static void +print_clone(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); +#if defined(TARGET_M68K) + print_flags(clone_flags, arg0, 0); + print_raw_param("newsp=0x" TARGET_ABI_FMT_lx, arg1, 1); +#elif defined(TARGET_SH4) || defined(TARGET_ALPHA) + print_flags(clone_flags, arg0, 0); + print_raw_param("child_stack=0x" TARGET_ABI_FMT_lx, arg1, 0); + print_raw_param("parent_tidptr=0x" TARGET_ABI_FMT_lx, arg2, 0); + print_raw_param("child_tidptr=0x" TARGET_ABI_FMT_lx, arg3, 0); + print_raw_param("tls=0x" TARGET_ABI_FMT_lx, arg4, 1); +#elif defined(TARGET_CRIS) + print_raw_param("child_stack=0x" TARGET_ABI_FMT_lx, arg0, 0); + print_flags(clone_flags, arg1, 0); + print_raw_param("parent_tidptr=0x" TARGET_ABI_FMT_lx, arg2, 0); + print_raw_param("tls=0x" TARGET_ABI_FMT_lx, arg3, 0); + print_raw_param("child_tidptr=0x" TARGET_ABI_FMT_lx, arg4, 1); +#else + print_flags(clone_flags, arg0, 0); + print_raw_param("child_stack=0x" TARGET_ABI_FMT_lx, arg1, 0); + print_raw_param("parent_tidptr=0x" TARGET_ABI_FMT_lx, arg2, 0); + print_raw_param("tls=0x" TARGET_ABI_FMT_lx, arg3, 0); + print_raw_param("child_tidptr=0x" TARGET_ABI_FMT_lx, arg4, 1); +#endif + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_creat static void print_creat(const struct syscallname *name, @@ -694,7 +794,7 @@ { print_syscall_prologue(name); print_string(arg0, 0); - print_raw_param("0x" TARGET_ABI_FMT_lx, tswapl(arg1), 1); + print_raw_param("0x" TARGET_ABI_FMT_lx, arg1, 1); print_syscall_epilogue(name); } #endif @@ -738,13 +838,8 @@ print_syscall_prologue(name); print_at_dirfd(arg0, 0); print_string(arg1, 0); -#ifdef USE_UID16 - print_raw_param("%d", tswap16(arg2), 0); - print_raw_param("%d", tswap16(arg3), 0); -#else - print_raw_param("%d", tswap32(arg2), 0); - print_raw_param("%d", tswap32(arg3), 0); -#endif + print_raw_param("%d", arg2, 0); + print_raw_param("%d", arg3, 0); print_flags(at_file_flags, arg4, 1); print_syscall_epilogue(name); } @@ -757,7 +852,7 @@ abi_long arg3, abi_long arg4, abi_long arg5) { print_syscall_prologue(name); - print_raw_param("%d", tswap32(arg0), 0); + print_raw_param("%d", arg0, 0); print_flags(fcntl_flags, arg1, 0); /* * TODO: check flags and print following argument only @@ -814,6 +909,28 @@ } #endif +#ifdef TARGET_NR__llseek +static void +print__llseek(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + const char *whence = "UNKNOWN"; + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_raw_param("%ld", arg1, 0); + print_raw_param("%ld", arg2, 0); + print_pointer(arg3, 0); + switch(arg4) { + case SEEK_SET: whence = "SEEK_SET"; break; + case SEEK_CUR: whence = "SEEK_CUR"; break; + case SEEK_END: whence = "SEEK_END"; break; + } + gemu_log("%s",whence); + print_syscall_epilogue(name); +} +#endif + #if defined(TARGET_NR_stat) || defined(TARGET_NR_stat64) || \ defined(TARGET_NR_lstat) || defined(TARGET_NR_lstat64) static void @@ -838,7 +955,7 @@ abi_long arg3, abi_long arg4, abi_long arg5) { print_syscall_prologue(name); - print_raw_param("%d", tswap32(arg0), 0); + print_raw_param("%d", arg0, 0); print_pointer(arg1, 1); print_syscall_epilogue(name); } @@ -872,20 +989,66 @@ } #endif +#ifdef TARGET_NR_rmdir +static void +print_rmdir(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 0); + print_syscall_epilogue(name); +} +#endif + +#ifdef TARGET_NR_rt_sigaction +static void +print_rt_sigaction(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_signal(arg0, 0); + print_pointer(arg1, 0); + print_pointer(arg2, 1); + print_syscall_epilogue(name); +} +#endif + +#ifdef TARGET_NR_rt_sigprocmask +static void +print_rt_sigprocmask(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + const char *how = "UNKNOWN"; + print_syscall_prologue(name); + switch(arg0) { + case TARGET_SIG_BLOCK: how = "SIG_BLOCK"; break; + case TARGET_SIG_UNBLOCK: how = "SIG_UNBLOCK"; break; + case TARGET_SIG_SETMASK: how = "SIG_SETMASK"; break; + } + gemu_log("%s,",how); + print_pointer(arg1, 0); + print_pointer(arg2, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_mknod static void print_mknod(const struct syscallname *name, abi_long arg0, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) { - int hasdev = (tswapl(arg1) & (S_IFCHR|S_IFBLK)); + int hasdev = (arg1 & (S_IFCHR|S_IFBLK)); print_syscall_prologue(name); print_string(arg0, 0); print_file_mode(arg1, (hasdev == 0)); if (hasdev) { - print_raw_param("makedev(%d", major(tswapl(arg2)), 0); - print_raw_param("%d)", minor(tswapl(arg2)), 1); + print_raw_param("makedev(%d", major(arg2), 0); + print_raw_param("%d)", minor(arg2), 1); } print_syscall_epilogue(name); } @@ -897,15 +1060,15 @@ abi_long arg0, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) { - int hasdev = (tswapl(arg2) & (S_IFCHR|S_IFBLK)); + int hasdev = (arg2 & (S_IFCHR|S_IFBLK)); print_syscall_prologue(name); print_at_dirfd(arg0, 0); print_string(arg1, 0); print_file_mode(arg2, (hasdev == 0)); if (hasdev) { - print_raw_param("makedev(%d", major(tswapl(arg3)), 0); - print_raw_param("%d)", minor(tswapl(arg3)), 1); + print_raw_param("makedev(%d", major(arg3), 0); + print_raw_param("%d)", minor(arg3), 1); } print_syscall_epilogue(name); } @@ -917,7 +1080,7 @@ abi_long arg0, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) { - int is_creat = (tswapl(arg1) & TARGET_O_CREAT); + int is_creat = (arg1 & TARGET_O_CREAT); print_syscall_prologue(name); print_string(arg0, 0); @@ -936,7 +1099,7 @@ abi_long arg0, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) { - int is_creat = (tswap32(arg1) & TARGET_O_CREAT); + int is_creat = (arg1 & TARGET_O_CREAT); print_syscall_prologue(name); print_string(arg0, 0); @@ -953,7 +1116,7 @@ abi_long arg0, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5) { - int is_creat = (tswap32(arg2) & TARGET_O_CREAT); + int is_creat = (arg2 & TARGET_O_CREAT); print_syscall_prologue(name); print_at_dirfd(arg0, 0); @@ -1002,7 +1165,7 @@ print_syscall_prologue(name); print_string(arg0, 0); print_pointer(arg1, 0); - print_raw_param("%u", tswapl(arg2), 1); + print_raw_param("%u", arg2, 1); print_syscall_epilogue(name); } #endif @@ -1017,7 +1180,7 @@ print_at_dirfd(arg0, 0); print_string(arg1, 0); print_pointer(arg2, 0); - print_raw_param("%u", tswapl(arg3), 1); + print_raw_param("%u", arg3, 1); print_syscall_epilogue(name); } #endif @@ -1199,7 +1362,7 @@ } #endif -#ifdef TARGET_NR_mmap +#if defined(TARGET_NR_mmap) || defined(TARGET_NR_mmap2) static void print_mmap(const struct syscallname *name, abi_long arg0, abi_long arg1, abi_long arg2, @@ -1207,11 +1370,11 @@ { print_syscall_prologue(name); print_pointer(arg0, 0); - print_raw_param("%d", tswapl(arg1), 0); + print_raw_param("%d", arg1, 0); print_flags(mmap_prot_flags, arg2, 0); print_flags(mmap_flags, arg3, 0); - print_raw_param("%d", tswapl(arg4), 0); - print_raw_param("%#x", tswapl(arg5), 1); + print_raw_param("%d", arg4, 0); + print_raw_param("%#x", arg5, 1); print_syscall_epilogue(name); } #define print_mmap2 print_mmap @@ -1225,7 +1388,7 @@ { print_syscall_prologue(name); print_pointer(arg0, 0); - print_raw_param("%d", tswapl(arg1), 0); + print_raw_param("%d", arg1, 0); print_flags(mmap_prot_flags, arg2, 1); print_syscall_epilogue(name); } @@ -1239,7 +1402,7 @@ { print_syscall_prologue(name); print_pointer(arg0, 0); - print_raw_param("%d", tswapl(arg1), 1); + print_raw_param("%d", arg1, 1); print_syscall_epilogue(name); } #endif @@ -1253,7 +1416,7 @@ return; \ } - int cmd = (int)tswap32(tflag); + int cmd = (int)tflag; #ifdef FUTEX_PRIVATE_FLAG if (cmd & FUTEX_PRIVATE_FLAG) { gemu_log("FUTEX_PRIVATE_FLAG|"); @@ -1287,10 +1450,23 @@ print_syscall_prologue(name); print_pointer(arg0, 0); print_futex_op(arg1, 0); - print_raw_param(",%d", tswapl(arg2), 0); + print_raw_param(",%d", arg2, 0); print_pointer(arg3, 0); /* struct timespec */ print_pointer(arg4, 0); - print_raw_param("%d", tswapl(arg4), 1); + print_raw_param("%d", arg4, 1); + print_syscall_epilogue(name); +} +#endif + +#ifdef TARGET_NR_kill +static void +print_kill(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_raw_param("%d", arg0, 0); + print_signal(arg1, 1); print_syscall_epilogue(name); } #endif diff -Nru qemu-kvm-0.14.1+noroms/linux-user/strace.list qemu-kvm-0.15.0+noroms/linux-user/strace.list --- qemu-kvm-0.14.1+noroms/linux-user/strace.list 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/strace.list 2011-08-09 07:40:29.000000000 -0500 @@ -85,7 +85,7 @@ { TARGET_NR_clock_settime, "clock_settime" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_clone -{ TARGET_NR_clone, "clone" , NULL, NULL, NULL }, +{ TARGET_NR_clone, "clone" , NULL, print_clone, NULL }, #endif #ifdef TARGET_NR_close { TARGET_NR_close, "close" , "%s(%d)", NULL, NULL }, @@ -292,7 +292,7 @@ { TARGET_NR_getpgrp, "getpgrp" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_getpid -{ TARGET_NR_getpid, "getpid" , NULL, NULL, NULL }, +{ TARGET_NR_getpid, "getpid" , "%s()", NULL, NULL }, #endif #ifdef TARGET_NR_getpmsg { TARGET_NR_getpmsg, "getpmsg" , NULL, NULL, NULL }, @@ -418,7 +418,7 @@ { TARGET_NR_keyctl, "keyctl" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_kill -{ TARGET_NR_kill, "kill" , NULL, NULL, NULL }, +{ TARGET_NR_kill, "kill", NULL, print_kill, NULL }, #endif #ifdef TARGET_NR_lchown { TARGET_NR_lchown, "lchown" , NULL, NULL, NULL }, @@ -448,7 +448,7 @@ { TARGET_NR_llistxattr, "llistxattr" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR__llseek -{ TARGET_NR__llseek, "_llseek" , NULL, NULL, NULL }, +{ TARGET_NR__llseek, "_llseek" , NULL, print__llseek, NULL }, #endif #ifdef TARGET_NR_lock { TARGET_NR_lock, "lock" , NULL, NULL, NULL }, @@ -495,6 +495,9 @@ #ifdef TARGET_NR_mkdirat { TARGET_NR_mkdirat, "mkdirat" , NULL, print_mkdirat, NULL }, #endif +#ifdef TARGET_NR_rmdir +{ TARGET_NR_rmdir, "rmdir" , NULL, print_rmdir, NULL }, +#endif #ifdef TARGET_NR_mknod { TARGET_NR_mknod, "mknod" , NULL, print_mknod, NULL }, #endif @@ -1060,13 +1063,13 @@ { TARGET_NR_rmdir, "rmdir" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_rt_sigaction -{ TARGET_NR_rt_sigaction, "rt_sigaction" , NULL, NULL, NULL }, +{ TARGET_NR_rt_sigaction, "rt_sigaction" , NULL, print_rt_sigaction, NULL }, #endif #ifdef TARGET_NR_rt_sigpending { TARGET_NR_rt_sigpending, "rt_sigpending" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_rt_sigprocmask -{ TARGET_NR_rt_sigprocmask, "rt_sigprocmask" , NULL, NULL, NULL }, +{ TARGET_NR_rt_sigprocmask, "rt_sigprocmask" , NULL, print_rt_sigprocmask, NULL }, #endif #ifdef TARGET_NR_rt_sigqueueinfo { TARGET_NR_rt_sigqueueinfo, "rt_sigqueueinfo" , NULL, NULL, NULL }, diff -Nru qemu-kvm-0.14.1+noroms/linux-user/syscall.c qemu-kvm-0.15.0+noroms/linux-user/syscall.c --- qemu-kvm-0.14.1+noroms/linux-user/syscall.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/syscall.c 2011-08-09 07:40:29.000000000 -0500 @@ -59,6 +59,7 @@ //#include #include #include +#include #include #ifdef TARGET_GPROF #include @@ -66,6 +67,9 @@ #ifdef CONFIG_EVENTFD #include #endif +#ifdef CONFIG_EPOLL +#include +#endif #define termios host_termios #define winsize host_winsize @@ -193,7 +197,8 @@ #define __NR_sys_inotify_add_watch __NR_inotify_add_watch #define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch -#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__) +#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__) || \ + defined(__s390x__) #define __NR__llseek __NR_lseek #endif @@ -235,6 +240,12 @@ const struct timespec *,timeout,int *,uaddr2,int,val3) #endif #endif +#define __NR_sys_sched_getaffinity __NR_sched_getaffinity +_syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, + unsigned long *, user_mask_ptr); +#define __NR_sys_sched_setaffinity __NR_sched_setaffinity +_syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, + unsigned long *, user_mask_ptr); static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, @@ -276,7 +287,7 @@ * struct linux kernel uses). */ - bzero(buf, sizeof (*buf)); + memset(buf, 0, sizeof(*buf)); COPY_UTSNAME_FIELD(buf->sysname, uts_buf.sysname); COPY_UTSNAME_FIELD(buf->nodename, uts_buf.nodename); COPY_UTSNAME_FIELD(buf->release, uts_buf.release); @@ -317,7 +328,7 @@ return (fchmodat(dirfd, pathname, mode, 0)); } #endif -#if defined(TARGET_NR_fchownat) && defined(USE_UID16) +#if defined(TARGET_NR_fchownat) static int sys_fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) { @@ -426,7 +437,7 @@ #if defined(TARGET_NR_fchmodat) && defined(__NR_fchmodat) _syscall3(int,sys_fchmodat,int,dirfd,const char *,pathname, mode_t,mode) #endif -#if defined(TARGET_NR_fchownat) && defined(__NR_fchownat) && defined(USE_UID16) +#if defined(TARGET_NR_fchownat) && defined(__NR_fchownat) _syscall5(int,sys_fchownat,int,dirfd,const char *,pathname, uid_t,owner,gid_t,group,int,flags) #endif @@ -529,6 +540,39 @@ #undef TARGET_NR_inotify_rm_watch #endif /* CONFIG_INOTIFY */ +#if defined(TARGET_NR_ppoll) +#ifndef __NR_ppoll +# define __NR_ppoll -1 +#endif +#define __NR_sys_ppoll __NR_ppoll +_syscall5(int, sys_ppoll, struct pollfd *, fds, nfds_t, nfds, + struct timespec *, timeout, const __sigset_t *, sigmask, + size_t, sigsetsize) +#endif + +#if defined(TARGET_NR_pselect6) +#ifndef __NR_pselect6 +# define __NR_pselect6 -1 +#endif +#define __NR_sys_pselect6 __NR_pselect6 +_syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, + fd_set *, exceptfds, struct timespec *, timeout, void *, sig); +#endif + +#if defined(TARGET_NR_prlimit64) +#ifndef __NR_prlimit64 +# define __NR_prlimit64 -1 +#endif +#define __NR_sys_prlimit64 __NR_prlimit64 +/* The glibc rlimit structure may not be that used by the underlying syscall */ +struct host_rlimit64 { + uint64_t rlim_cur; + uint64_t rlim_max; +}; +_syscall4(int, sys_prlimit64, pid_t, pid, int, resource, + const struct host_rlimit64 *, new_limit, + struct host_rlimit64 *, old_limit) +#endif extern int personality(int); extern int flock(int, int); @@ -536,6 +580,17 @@ extern int setfsgid(int); extern int setgroups(int, gid_t *); +/* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ +#ifdef TARGET_ARM +static inline int regpairs_aligned(void *cpu_env) { + return ((((CPUARMState *)cpu_env)->eabi) == 1) ; +} +#elif defined(TARGET_MIPS) +static inline int regpairs_aligned(void *cpu_env) { return 1; } +#else +static inline int regpairs_aligned(void *cpu_env) { return 0; } +#endif + #define ERRNO_TABLE_SIZE 1200 /* target_to_host_errno_table[] is initialized from @@ -689,49 +744,81 @@ static abi_ulong target_brk; static abi_ulong target_original_brk; +static abi_ulong brk_page; void target_set_brk(abi_ulong new_brk) { target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk); + brk_page = HOST_PAGE_ALIGN(target_brk); } +//#define DEBUGF_BRK(message, args...) do { fprintf(stderr, (message), ## args); } while (0) +#define DEBUGF_BRK(message, args...) + /* do_brk() must return target values and target errnos. */ abi_long do_brk(abi_ulong new_brk) { - abi_ulong brk_page; abi_long mapped_addr; int new_alloc_size; - if (!new_brk) + DEBUGF_BRK("do_brk(%#010x) -> ", new_brk); + + if (!new_brk) { + DEBUGF_BRK("%#010x (!new_brk)\n", target_brk); return target_brk; - if (new_brk < target_original_brk) + } + if (new_brk < target_original_brk) { + DEBUGF_BRK("%#010x (new_brk < target_original_brk)\n", target_brk); return target_brk; + } - brk_page = HOST_PAGE_ALIGN(target_brk); - - /* If the new brk is less than this, set it and we're done... */ - if (new_brk < brk_page) { + /* If the new brk is less than the highest page reserved to the + * target heap allocation, set it and we're almost done... */ + if (new_brk <= brk_page) { + /* Heap contents are initialized to zero, as for anonymous + * mapped pages. */ + if (new_brk > target_brk) { + memset(g2h(target_brk), 0, new_brk - target_brk); + } target_brk = new_brk; + DEBUGF_BRK("%#010x (new_brk <= brk_page)\n", target_brk); return target_brk; } - /* We need to allocate more memory after the brk... */ - new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1); + /* We need to allocate more memory after the brk... Note that + * we don't use MAP_FIXED because that will map over the top of + * any existing mapping (like the one with the host libc or qemu + * itself); instead we treat "mapped but at wrong address" as + * a failure and unmap again. + */ + new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page); mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size, PROT_READ|PROT_WRITE, - MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0)); + MAP_ANON|MAP_PRIVATE, 0, 0)); + + if (mapped_addr == brk_page) { + target_brk = new_brk; + brk_page = HOST_PAGE_ALIGN(target_brk); + DEBUGF_BRK("%#010x (mapped_addr == brk_page)\n", target_brk); + return target_brk; + } else if (mapped_addr != -1) { + /* Mapped but at wrong address, meaning there wasn't actually + * enough space for this brk. + */ + target_munmap(mapped_addr, new_alloc_size); + mapped_addr = -1; + DEBUGF_BRK("%#010x (mapped_addr != -1)\n", target_brk); + } + else { + DEBUGF_BRK("%#010x (otherwise)\n", target_brk); + } #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; - } + return -TARGET_ENOMEM; #endif - - if (!is_error(mapped_addr)) { - target_brk = new_brk; - } + /* For everything else, return the previous break. */ return target_brk; } @@ -767,6 +854,20 @@ return 0; } +static inline abi_ulong copy_from_user_fdset_ptr(fd_set *fds, fd_set **fds_ptr, + abi_ulong target_fds_addr, + int n) +{ + if (target_fds_addr) { + if (copy_from_user_fdset(fds, target_fds_addr, n)) + return -TARGET_EFAULT; + *fds_ptr = fds; + } else { + *fds_ptr = NULL; + } + return 0; +} + static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr, const fd_set *fds, int n) @@ -844,18 +945,68 @@ static inline rlim_t target_to_host_rlim(target_ulong target_rlim) { - if (target_rlim == TARGET_RLIM_INFINITY) - return RLIM_INFINITY; + target_ulong target_rlim_swap; + rlim_t result; + + target_rlim_swap = tswapl(target_rlim); + if (target_rlim_swap == TARGET_RLIM_INFINITY || target_rlim_swap != (rlim_t)target_rlim_swap) + result = RLIM_INFINITY; else - return tswapl(target_rlim); + result = target_rlim_swap; + + return result; } static inline target_ulong host_to_target_rlim(rlim_t rlim) { + target_ulong target_rlim_swap; + target_ulong result; + if (rlim == RLIM_INFINITY || rlim != (target_long)rlim) - return TARGET_RLIM_INFINITY; + target_rlim_swap = TARGET_RLIM_INFINITY; else - return tswapl(rlim); + target_rlim_swap = rlim; + result = tswapl(target_rlim_swap); + + return result; +} + +static inline int target_to_host_resource(int code) +{ + switch (code) { + case TARGET_RLIMIT_AS: + return RLIMIT_AS; + case TARGET_RLIMIT_CORE: + return RLIMIT_CORE; + case TARGET_RLIMIT_CPU: + return RLIMIT_CPU; + case TARGET_RLIMIT_DATA: + return RLIMIT_DATA; + case TARGET_RLIMIT_FSIZE: + return RLIMIT_FSIZE; + case TARGET_RLIMIT_LOCKS: + return RLIMIT_LOCKS; + case TARGET_RLIMIT_MEMLOCK: + return RLIMIT_MEMLOCK; + case TARGET_RLIMIT_MSGQUEUE: + return RLIMIT_MSGQUEUE; + case TARGET_RLIMIT_NICE: + return RLIMIT_NICE; + case TARGET_RLIMIT_NOFILE: + return RLIMIT_NOFILE; + case TARGET_RLIMIT_NPROC: + return RLIMIT_NPROC; + case TARGET_RLIMIT_RSS: + return RLIMIT_RSS; + case TARGET_RLIMIT_RTPRIO: + return RLIMIT_RTPRIO; + case TARGET_RLIMIT_SIGPENDING: + return RLIMIT_SIGPENDING; + case TARGET_RLIMIT_STACK: + return RLIMIT_STACK; + default: + return code; + } } static inline abi_long copy_from_user_timeval(struct timeval *tv, @@ -932,6 +1083,7 @@ } #endif +#if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) /* do_select() must return target values and target errnos. */ static abi_long do_select(int n, abi_ulong rfd_addr, abi_ulong wfd_addr, @@ -942,26 +1094,17 @@ struct timeval tv, *tv_ptr; abi_long ret; - if (rfd_addr) { - if (copy_from_user_fdset(&rfds, rfd_addr, n)) - return -TARGET_EFAULT; - rfds_ptr = &rfds; - } else { - rfds_ptr = NULL; + ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); + if (ret) { + return ret; } - if (wfd_addr) { - if (copy_from_user_fdset(&wfds, wfd_addr, n)) - return -TARGET_EFAULT; - wfds_ptr = &wfds; - } else { - wfds_ptr = NULL; + ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); + if (ret) { + return ret; } - if (efd_addr) { - if (copy_from_user_fdset(&efds, efd_addr, n)) - return -TARGET_EFAULT; - efds_ptr = &efds; - } else { - efds_ptr = NULL; + ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); + if (ret) { + return ret; } if (target_tv_addr) { @@ -988,6 +1131,7 @@ return ret; } +#endif static abi_long do_pipe2(int host_pipe[], int flags) { @@ -1448,7 +1592,7 @@ return -TARGET_EFAULT; if (len < 0) return -TARGET_EINVAL; - lv = sizeof(int); + lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; @@ -1485,7 +1629,7 @@ return -TARGET_EFAULT; if (len < 0) return -TARGET_EINVAL; - lv = sizeof(int); + lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; @@ -1860,7 +2004,7 @@ ret = get_errno(recvfrom(fd, host_msg, len, flags, addr, &addrlen)); } else { addr = NULL; /* To keep compiler quiet. */ - ret = get_errno(recv(fd, host_msg, len, flags)); + ret = get_errno(qemu_recv(fd, host_msg, len, flags)); } if (!is_error(ret)) { if (target_addr) { @@ -2952,7 +3096,6 @@ #endif /* kernel structure types definitions */ -#define IFNAMSIZ 16 #define STRUCT(name, ...) STRUCT_ ## name, #define STRUCT_SPECIAL(name) STRUCT_ ## name, @@ -3077,6 +3220,100 @@ } #endif +static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, abi_long cmd, abi_long arg) +{ + const argtype *arg_type = ie->arg_type; + int target_size; + void *argptr; + int ret; + struct ifconf *host_ifconf; + uint32_t outbufsz; + const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) }; + int target_ifreq_size; + int nb_ifreq; + int free_buf = 0; + int i; + int target_ifc_len; + abi_long target_ifc_buf; + int host_ifc_len; + char *host_ifc_buf; + + assert(arg_type[0] == TYPE_PTR); + assert(ie->access == IOC_RW); + + arg_type++; + target_size = thunk_type_size(arg_type, 0); + + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) + return -TARGET_EFAULT; + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + + host_ifconf = (struct ifconf *)(unsigned long)buf_temp; + target_ifc_len = host_ifconf->ifc_len; + target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf; + + target_ifreq_size = thunk_type_size(ifreq_arg_type, 0); + nb_ifreq = target_ifc_len / target_ifreq_size; + host_ifc_len = nb_ifreq * sizeof(struct ifreq); + + outbufsz = sizeof(*host_ifconf) + host_ifc_len; + 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. + */ + host_ifconf = malloc(outbufsz); + if (!host_ifconf) { + return -TARGET_ENOMEM; + } + memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf)); + free_buf = 1; + } + host_ifc_buf = (char*)host_ifconf + sizeof(*host_ifconf); + + host_ifconf->ifc_len = host_ifc_len; + host_ifconf->ifc_buf = host_ifc_buf; + + ret = get_errno(ioctl(fd, ie->host_cmd, host_ifconf)); + if (!is_error(ret)) { + /* convert host ifc_len to target ifc_len */ + + nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq); + target_ifc_len = nb_ifreq * target_ifreq_size; + host_ifconf->ifc_len = target_ifc_len; + + /* restore target ifc_buf */ + + host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf; + + /* copy struct ifconf to target user */ + + argptr = lock_user(VERIFY_WRITE, arg, target_size, 0); + if (!argptr) + return -TARGET_EFAULT; + thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET); + unlock_user(argptr, arg, target_size); + + /* copy ifreq[] to target user */ + + argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0); + for (i = 0; i < nb_ifreq ; i++) { + thunk_convert(argptr + i * target_ifreq_size, + host_ifc_buf + i * sizeof(struct ifreq), + ifreq_arg_type, THUNK_TARGET); + } + unlock_user(argptr, target_ifc_buf, target_ifc_len); + } + + if (free_buf) { + free(host_ifconf); + } + + return ret; +} + static IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, ...) \ { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, @@ -3638,10 +3875,10 @@ #ifndef TARGET_ABI32 static abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) { - abi_long ret; + abi_long ret = 0; abi_ulong val; int idx; - + switch(code) { case TARGET_ARCH_SET_GS: case TARGET_ARCH_SET_FS: @@ -3660,21 +3897,21 @@ idx = R_FS; val = env->segs[idx].base; if (put_user(val, addr, abi_ulong)) - return -TARGET_EFAULT; + ret = -TARGET_EFAULT; break; default: ret = -TARGET_EINVAL; break; } - return 0; + return ret; } #endif #endif /* defined(TARGET_I386) */ -#if defined(CONFIG_USE_NPTL) +#define NEW_STACK_SIZE 0x40000 -#define NEW_STACK_SIZE PTHREAD_STACK_MIN +#if defined(CONFIG_USE_NPTL) static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER; typedef struct { @@ -3718,9 +3955,6 @@ return NULL; } #else -/* this stack is the equivalent of the kernel stack associated with a - thread/process */ -#define NEW_STACK_SIZE 8192 static int clone_func(void *arg) { @@ -4054,7 +4288,31 @@ else return gid; } - +static inline int tswapid(int id) +{ + return tswap16(id); +} +#else /* !USE_UID16 */ +static inline int high2lowuid(int uid) +{ + return uid; +} +static inline int high2lowgid(int gid) +{ + return gid; +} +static inline int low2highuid(int uid) +{ + return uid; +} +static inline int low2highgid(int gid) +{ + return gid; +} +static inline int tswapid(int id) +{ + return tswap32(id); +} #endif /* USE_UID16 */ void syscall_init(void) @@ -4128,13 +4386,10 @@ abi_long arg3, abi_long arg4) { -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; - } -#endif + } return get_errno(truncate64(arg1, target_offset64(arg2, arg3))); } #endif @@ -4145,13 +4400,10 @@ abi_long arg3, abi_long arg4) { -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; - } -#endif + } return get_errno(ftruncate64(arg1, target_offset64(arg2, arg3))); } #endif @@ -4350,7 +4602,8 @@ All errnos that do_syscall() returns must be -TARGET_. */ 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) + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) { abi_long ret; struct stat st; @@ -5360,7 +5613,7 @@ break; case TARGET_NR_setrlimit: { - int resource = arg1; + int resource = target_to_host_resource(arg1); struct target_rlimit *target_rlim; struct rlimit rlim; if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1)) @@ -5373,7 +5626,7 @@ break; case TARGET_NR_getrlimit: { - int resource = arg1; + int resource = target_to_host_resource(arg1); struct target_rlimit *target_rlim; struct rlimit rlim; @@ -5414,7 +5667,7 @@ ret = get_errno(settimeofday(&tv, NULL)); } break; -#ifdef TARGET_NR_select +#if defined(TARGET_NR_select) && !defined(TARGET_S390X) && !defined(TARGET_S390) case TARGET_NR_select: { struct target_sel_arg_struct *sel; @@ -5435,7 +5688,107 @@ #endif #ifdef TARGET_NR_pselect6 case TARGET_NR_pselect6: - goto unimplemented_nowarn; + { + abi_long rfd_addr, wfd_addr, efd_addr, n, ts_addr; + fd_set rfds, wfds, efds; + fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; + struct timespec ts, *ts_ptr; + + /* + * The 6th arg is actually two args smashed together, + * so we cannot use the C library. + */ + sigset_t set; + struct { + sigset_t *set; + size_t size; + } sig, *sig_ptr; + + abi_ulong arg_sigset, arg_sigsize, *arg7; + target_sigset_t *target_sigset; + + n = arg1; + rfd_addr = arg2; + wfd_addr = arg3; + efd_addr = arg4; + ts_addr = arg5; + + ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); + if (ret) { + goto fail; + } + ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); + if (ret) { + goto fail; + } + ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); + if (ret) { + goto fail; + } + + /* + * This takes a timespec, and not a timeval, so we cannot + * use the do_select() helper ... + */ + if (ts_addr) { + if (target_to_host_timespec(&ts, ts_addr)) { + goto efault; + } + ts_ptr = &ts; + } else { + ts_ptr = NULL; + } + + /* Extract the two packed args for the sigset */ + if (arg6) { + sig_ptr = &sig; + sig.size = _NSIG / 8; + + arg7 = lock_user(VERIFY_READ, arg6, sizeof(*arg7) * 2, 1); + if (!arg7) { + goto efault; + } + arg_sigset = tswapl(arg7[0]); + arg_sigsize = tswapl(arg7[1]); + unlock_user(arg7, arg6, 0); + + if (arg_sigset) { + sig.set = &set; + if (arg_sigsize != sizeof(*target_sigset)) { + /* Like the kernel, we enforce correct size sigsets */ + ret = -TARGET_EINVAL; + goto fail; + } + target_sigset = lock_user(VERIFY_READ, arg_sigset, + sizeof(*target_sigset), 1); + if (!target_sigset) { + goto efault; + } + target_to_host_sigset(&set, target_sigset); + unlock_user(target_sigset, arg_sigset, 0); + } else { + sig.set = NULL; + } + } else { + sig_ptr = NULL; + } + + ret = get_errno(sys_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, + ts_ptr, sig_ptr)); + + if (!is_error(ret)) { + if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) + goto efault; + if (wfd_addr && copy_to_user_fdset(wfd_addr, &wfds, n)) + goto efault; + if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) + goto efault; + + if (ts_addr && host_to_target_timespec(ts_addr, &ts)) + goto efault; + } + } + break; #endif case TARGET_NR_symlink: { @@ -5525,7 +5878,9 @@ #endif #ifdef TARGET_NR_mmap case TARGET_NR_mmap: -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || \ + defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) \ + || defined(TARGET_S390X) { abi_ulong *v; abi_ulong v1, v2, v3, v4, v5, v6; @@ -5893,8 +6248,9 @@ #endif #ifdef TARGET_NR_syscall case TARGET_NR_syscall: - ret = do_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); - break; + ret = do_syscall(cpu_env, arg1 & 0xffff, arg2, arg3, arg4, arg5, + arg6, arg7, arg8, 0); + break; #endif case TARGET_NR_wait4: { @@ -6021,6 +6377,8 @@ 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)); +#elif defined(TARGET_S390X) + ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg5, arg4)); #else ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5)); #endif @@ -6109,16 +6467,20 @@ #ifdef TARGET_NR__llseek /* Not on alpha */ case TARGET_NR__llseek: { + int64_t res; #if !defined(__NR_llseek) - ret = get_errno(lseek(arg1, ((uint64_t )arg2 << 32) | arg3, arg5)); - if (put_user_s64(ret, arg4)) - goto efault; + res = lseek(arg1, ((uint64_t)arg2 << 32) | arg3, arg5); + if (res == -1) { + ret = get_errno(res); + } else { + ret = 0; + } #else - int64_t res; ret = get_errno(_llseek(arg1, arg2, arg3, &res, arg5)); - if (put_user_s64(res, arg4)) - goto efault; #endif + if ((ret == 0) && put_user_s64(res, arg4)) { + goto efault; + } } break; #endif @@ -6225,13 +6587,22 @@ } break; #endif /* TARGET_NR_getdents64 */ -#ifdef TARGET_NR__newselect +#if defined(TARGET_NR__newselect) || defined(TARGET_S390X) +#ifdef TARGET_S390X + case TARGET_NR_select: +#else case TARGET_NR__newselect: +#endif ret = do_select(arg1, arg2, arg3, arg4, arg5); break; #endif -#ifdef TARGET_NR_poll +#if defined(TARGET_NR_poll) || defined(TARGET_NR_ppoll) +# ifdef TARGET_NR_poll case TARGET_NR_poll: +# endif +# ifdef TARGET_NR_ppoll + case TARGET_NR_ppoll: +# endif { struct target_pollfd *target_pfd; unsigned int nfds = arg2; @@ -6242,20 +6613,57 @@ target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1); if (!target_pfd) goto efault; + pfd = alloca(sizeof(struct pollfd) * nfds); for(i = 0; i < nfds; i++) { pfd[i].fd = tswap32(target_pfd[i].fd); pfd[i].events = tswap16(target_pfd[i].events); } - ret = get_errno(poll(pfd, nfds, timeout)); + +# ifdef TARGET_NR_ppoll + if (num == TARGET_NR_ppoll) { + struct timespec _timeout_ts, *timeout_ts = &_timeout_ts; + target_sigset_t *target_set; + sigset_t _set, *set = &_set; + + if (arg3) { + if (target_to_host_timespec(timeout_ts, arg3)) { + unlock_user(target_pfd, arg1, 0); + goto efault; + } + } else { + timeout_ts = NULL; + } + + if (arg4) { + target_set = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1); + if (!target_set) { + unlock_user(target_pfd, arg1, 0); + goto efault; + } + target_to_host_sigset(set, target_set); + } else { + set = NULL; + } + + ret = get_errno(sys_ppoll(pfd, nfds, timeout_ts, set, _NSIG/8)); + + if (!is_error(ret) && arg3) { + host_to_target_timespec(arg3, timeout_ts); + } + if (arg4) { + unlock_user(target_set, arg4, 0); + } + } else +# endif + ret = get_errno(poll(pfd, nfds, timeout)); + if (!is_error(ret)) { for(i = 0; i < nfds; i++) { target_pfd[i].revents = tswap16(pfd[i].revents); } - ret += nfds * (sizeof(struct target_pollfd) - - sizeof(struct pollfd)); } - unlock_user(target_pfd, arg1, ret); + unlock_user(target_pfd, arg1, sizeof(struct target_pollfd) * nfds); } break; #endif @@ -6301,6 +6709,56 @@ return value. */ ret = -TARGET_ENOTDIR; break; + case TARGET_NR_sched_getaffinity: + { + unsigned int mask_size; + unsigned long *mask; + + /* + * sched_getaffinity needs multiples of ulong, so need to take + * care of mismatches between target ulong and host ulong sizes. + */ + if (arg2 & (sizeof(abi_ulong) - 1)) { + ret = -TARGET_EINVAL; + break; + } + mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); + + mask = alloca(mask_size); + ret = get_errno(sys_sched_getaffinity(arg1, mask_size, mask)); + + if (!is_error(ret)) { + if (copy_to_user(arg3, mask, ret)) { + goto efault; + } + } + } + break; + case TARGET_NR_sched_setaffinity: + { + unsigned int mask_size; + unsigned long *mask; + + /* + * sched_setaffinity needs multiples of ulong, so need to take + * care of mismatches between target ulong and host ulong sizes. + */ + if (arg2 & (sizeof(abi_ulong) - 1)) { + ret = -TARGET_EINVAL; + break; + } + mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); + + mask = alloca(mask_size); + if (!lock_user_struct(VERIFY_READ, p, arg3, 1)) { + goto efault; + } + memcpy(mask, p, arg2); + unlock_user_struct(p, arg2, 0); + + ret = get_errno(sys_sched_setaffinity(arg1, mask_size, mask)); + } + break; case TARGET_NR_sched_setparam: { struct sched_param *target_schp; @@ -6404,20 +6862,16 @@ #endif #ifdef TARGET_NR_pread case TARGET_NR_pread: -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) + if (regpairs_aligned(cpu_env)) arg4 = arg5; -#endif if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; ret = get_errno(pread(arg1, p, arg3, arg4)); unlock_user(p, arg2, ret); break; case TARGET_NR_pwrite: -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) + if (regpairs_aligned(cpu_env)) arg4 = arg5; -#endif if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) goto efault; ret = get_errno(pwrite(arg1, p, arg3, arg4)); @@ -6451,7 +6905,7 @@ case TARGET_NR_sigaltstack: #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \ defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ - defined(TARGET_M68K) + defined(TARGET_M68K) || defined(TARGET_S390X) ret = do_sigaltstack(arg1, arg2, get_sp_from_cpustate((CPUState *)cpu_env)); break; #else @@ -6477,7 +6931,8 @@ case TARGET_NR_ugetrlimit: { struct rlimit rlim; - ret = get_errno(getrlimit(arg1, &rlim)); + int resource = target_to_host_resource(arg1); + ret = get_errno(getrlimit(resource, &rlim)); if (!is_error(ret)) { struct target_rlimit *target_rlim; if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) @@ -6548,25 +7003,32 @@ ret = host_to_target_stat64(cpu_env, arg3, &st); break; #endif -#ifdef USE_UID16 case TARGET_NR_lchown: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(lchown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); break; +#ifdef TARGET_NR_getuid case TARGET_NR_getuid: ret = get_errno(high2lowuid(getuid())); break; +#endif +#ifdef TARGET_NR_getgid case TARGET_NR_getgid: ret = get_errno(high2lowgid(getgid())); break; +#endif +#ifdef TARGET_NR_geteuid case TARGET_NR_geteuid: ret = get_errno(high2lowuid(geteuid())); break; +#endif +#ifdef TARGET_NR_getegid case TARGET_NR_getegid: ret = get_errno(high2lowgid(getegid())); break; +#endif case TARGET_NR_setreuid: ret = get_errno(setreuid(low2highuid(arg1), low2highuid(arg2))); break; @@ -6576,7 +7038,7 @@ case TARGET_NR_getgroups: { int gidsetsize = arg1; - uint16_t *target_grouplist; + target_id *target_grouplist; gid_t *grouplist; int i; @@ -6589,7 +7051,7 @@ if (!target_grouplist) goto efault; for(i = 0;i < ret; i++) - target_grouplist[i] = tswap16(grouplist[i]); + target_grouplist[i] = tswapid(high2lowgid(grouplist[i])); unlock_user(target_grouplist, arg2, gidsetsize * 2); } } @@ -6597,7 +7059,7 @@ case TARGET_NR_setgroups: { int gidsetsize = arg1; - uint16_t *target_grouplist; + target_id *target_grouplist; gid_t *grouplist; int i; @@ -6608,7 +7070,7 @@ goto fail; } for(i = 0;i < gidsetsize; i++) - grouplist[i] = tswap16(target_grouplist[i]); + grouplist[i] = low2highgid(tswapid(target_grouplist[i])); unlock_user(target_grouplist, arg2, 0); ret = get_errno(setgroups(gidsetsize, grouplist)); } @@ -6684,7 +7146,6 @@ case TARGET_NR_setfsgid: ret = get_errno(setfsgid(arg1)); break; -#endif /* USE_UID16 */ #ifdef TARGET_NR_lchown32 case TARGET_NR_lchown32: @@ -6814,7 +7275,7 @@ case TARGET_NR_osf_sigprocmask: { abi_ulong mask; - int how = arg1; + int how; sigset_t set, oldset; switch(arg1) { @@ -6833,7 +7294,7 @@ } mask = arg2; target_to_host_old_sigset(&set, &mask); - sigprocmask(arg1, &set, &oldset); + sigprocmask(how, &set, &oldset); host_to_target_old_sigset(&mask, &oldset); ret = mask; } @@ -7161,14 +7622,11 @@ #ifdef TARGET_NR_readahead case TARGET_NR_readahead: #if TARGET_ABI_BITS == 32 -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; arg4 = arg5; } -#endif ret = get_errno(readahead(arg1, ((off64_t)arg3 << 32) | arg2, arg4)); #else ret = get_errno(readahead(arg1, arg2, arg3)); @@ -7473,8 +7931,13 @@ #if defined(TARGET_NR_sync_file_range) case TARGET_NR_sync_file_range: #if TARGET_ABI_BITS == 32 +#if defined(TARGET_MIPS) + ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4), + target_offset64(arg5, arg6), arg7)); +#else ret = get_errno(sync_file_range(arg1, target_offset64(arg2, arg3), target_offset64(arg4, arg5), arg6)); +#endif /* !TARGET_MIPS */ #else ret = get_errno(sync_file_range(arg1, arg2, arg3, arg4)); #endif @@ -7492,6 +7955,138 @@ break; #endif #endif +#if defined(CONFIG_EPOLL) +#if defined(TARGET_NR_epoll_create) + case TARGET_NR_epoll_create: + ret = get_errno(epoll_create(arg1)); + break; +#endif +#if defined(TARGET_NR_epoll_create1) && defined(CONFIG_EPOLL_CREATE1) + case TARGET_NR_epoll_create1: + ret = get_errno(epoll_create1(arg1)); + break; +#endif +#if defined(TARGET_NR_epoll_ctl) + case TARGET_NR_epoll_ctl: + { + struct epoll_event ep; + struct epoll_event *epp = 0; + if (arg4) { + struct target_epoll_event *target_ep; + if (!lock_user_struct(VERIFY_READ, target_ep, arg4, 1)) { + goto efault; + } + ep.events = tswap32(target_ep->events); + /* The epoll_data_t union is just opaque data to the kernel, + * so we transfer all 64 bits across and need not worry what + * actual data type it is. + */ + ep.data.u64 = tswap64(target_ep->data.u64); + unlock_user_struct(target_ep, arg4, 0); + epp = &ep; + } + ret = get_errno(epoll_ctl(arg1, arg2, arg3, epp)); + break; + } +#endif + +#if defined(TARGET_NR_epoll_pwait) && defined(CONFIG_EPOLL_PWAIT) +#define IMPLEMENT_EPOLL_PWAIT +#endif +#if defined(TARGET_NR_epoll_wait) || defined(IMPLEMENT_EPOLL_PWAIT) +#if defined(TARGET_NR_epoll_wait) + case TARGET_NR_epoll_wait: +#endif +#if defined(IMPLEMENT_EPOLL_PWAIT) + case TARGET_NR_epoll_pwait: +#endif + { + struct target_epoll_event *target_ep; + struct epoll_event *ep; + int epfd = arg1; + int maxevents = arg3; + int timeout = arg4; + + target_ep = lock_user(VERIFY_WRITE, arg2, + maxevents * sizeof(struct target_epoll_event), 1); + if (!target_ep) { + goto efault; + } + + ep = alloca(maxevents * sizeof(struct epoll_event)); + + switch (num) { +#if defined(IMPLEMENT_EPOLL_PWAIT) + case TARGET_NR_epoll_pwait: + { + target_sigset_t *target_set; + sigset_t _set, *set = &_set; + + if (arg5) { + target_set = lock_user(VERIFY_READ, arg5, + sizeof(target_sigset_t), 1); + if (!target_set) { + unlock_user(target_ep, arg2, 0); + goto efault; + } + target_to_host_sigset(set, target_set); + unlock_user(target_set, arg5, 0); + } else { + set = NULL; + } + + ret = get_errno(epoll_pwait(epfd, ep, maxevents, timeout, set)); + break; + } +#endif +#if defined(TARGET_NR_epoll_wait) + case TARGET_NR_epoll_wait: + ret = get_errno(epoll_wait(epfd, ep, maxevents, timeout)); + break; +#endif + default: + ret = -TARGET_ENOSYS; + } + if (!is_error(ret)) { + int i; + for (i = 0; i < ret; i++) { + target_ep[i].events = tswap32(ep[i].events); + target_ep[i].data.u64 = tswap64(ep[i].data.u64); + } + } + unlock_user(target_ep, arg2, ret * sizeof(struct target_epoll_event)); + break; + } +#endif +#endif +#ifdef TARGET_NR_prlimit64 + case TARGET_NR_prlimit64: + { + /* args: pid, resource number, ptr to new rlimit, ptr to old rlimit */ + struct target_rlimit64 *target_rnew, *target_rold; + struct host_rlimit64 rnew, rold, *rnewp = 0; + if (arg3) { + if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) { + goto efault; + } + rnew.rlim_cur = tswap64(target_rnew->rlim_cur); + rnew.rlim_max = tswap64(target_rnew->rlim_max); + unlock_user_struct(target_rnew, arg3, 0); + rnewp = &rnew; + } + + ret = get_errno(sys_prlimit64(arg1, arg2, rnewp, arg4 ? &rold : 0)); + if (!is_error(ret) && arg4) { + if (!lock_user_struct(VERIFY_WRITE, target_rold, arg4, 1)) { + goto efault; + } + target_rold->rlim_cur = tswap64(rold.rlim_cur); + target_rold->rlim_max = tswap64(rold.rlim_max); + unlock_user_struct(target_rold, arg4, 1); + } + break; + } +#endif default: unimplemented: gemu_log("qemu: Unsupported syscall: %d\n", num); diff -Nru qemu-kvm-0.14.1+noroms/linux-user/syscall_defs.h qemu-kvm-0.15.0+noroms/linux-user/syscall_defs.h --- qemu-kvm-0.14.1+noroms/linux-user/syscall_defs.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/syscall_defs.h 2011-08-09 07:40:29.000000000 -0500 @@ -49,13 +49,17 @@ #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_PPC) || defined(TARGET_MIPS) + || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) /* 16 bit uid wrappers emulation */ #define USE_UID16 +#define target_id uint16_t +#else +#define target_id uint32_t #endif #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ - || defined(TARGET_M68K) || defined(TARGET_CRIS) + || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_UNICORE32) \ + || defined(TARGET_S390X) #define TARGET_IOC_SIZEBITS 14 #define TARGET_IOC_DIRBITS 2 @@ -315,7 +319,11 @@ int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact); -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined (TARGET_SH4) || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) +#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \ + || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined(TARGET_SH4) \ + || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \ + || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \ + || defined(TARGET_S390X) #if defined(TARGET_SPARC) #define TARGET_SA_NOCLDSTOP 8u @@ -685,6 +693,40 @@ #define TARGET_RLIM_INFINITY ((target_ulong)~0UL) #endif +#if defined(TARGET_MIPS) +#define TARGET_RLIMIT_CPU 0 +#define TARGET_RLIMIT_FSIZE 1 +#define TARGET_RLIMIT_DATA 2 +#define TARGET_RLIMIT_STACK 3 +#define TARGET_RLIMIT_CORE 4 +#define TARGET_RLIMIT_RSS 7 +#define TARGET_RLIMIT_NPROC 8 +#define TARGET_RLIMIT_NOFILE 5 +#define TARGET_RLIMIT_MEMLOCK 9 +#define TARGET_RLIMIT_AS 6 +#define TARGET_RLIMIT_LOCKS 10 +#define TARGET_RLIMIT_SIGPENDING 11 +#define TARGET_RLIMIT_MSGQUEUE 12 +#define TARGET_RLIMIT_NICE 13 +#define TARGET_RLIMIT_RTPRIO 14 +#else +#define TARGET_RLIMIT_CPU 0 +#define TARGET_RLIMIT_FSIZE 1 +#define TARGET_RLIMIT_DATA 2 +#define TARGET_RLIMIT_STACK 3 +#define TARGET_RLIMIT_CORE 4 +#define TARGET_RLIMIT_RSS 5 +#define TARGET_RLIMIT_NPROC 6 +#define TARGET_RLIMIT_NOFILE 7 +#define TARGET_RLIMIT_MEMLOCK 8 +#define TARGET_RLIMIT_AS 9 +#define TARGET_RLIMIT_LOCKS 10 +#define TARGET_RLIMIT_SIGPENDING 11 +#define TARGET_RLIMIT_MSGQUEUE 12 +#define TARGET_RLIMIT_NICE 13 +#define TARGET_RLIMIT_RTPRIO 14 +#endif + struct target_pollfd { int fd; /* file descriptor */ short events; /* requested events */ @@ -700,6 +742,10 @@ #define TARGET_KDSKBMODE 0x4b45 #define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */ #define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */ +#define TARGET_KDGKBLED 0x4B64 /* get led flags (not lights) */ +#define TARGET_KDSKBLED 0x4B65 /* set led flags (not lights) */ +#define TARGET_KDGETLED 0x4B31 /* return current led state */ +#define TARGET_KDSETLED 0x4B32 /* set led state [lights, not flags] */ #define TARGET_SIOCATMARK 0x8905 @@ -762,6 +808,9 @@ #define TARGET_SIOCADDDLCI 0x8980 /* Create new DLCI device */ #define TARGET_SIOCDELDLCI 0x8981 /* Delete DLCI device */ +/* From */ + +#define TARGET_SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ /* From */ @@ -917,6 +966,11 @@ #define TARGET_FBIOGET_VSCREENINFO 0x4600 #define TARGET_FBIOPUT_VSCREENINFO 0x4601 #define TARGET_FBIOGET_FSCREENINFO 0x4602 +#define TARGET_FBIOGETCMAP 0x4604 +#define TARGET_FBIOPUTCMAP 0x4605 +#define TARGET_FBIOPAN_DISPLAY 0x4606 +#define TARGET_FBIOGET_CON2FBMAP 0x460F +#define TARGET_FBIOPUT_CON2FBMAP 0x4610 /* vt ioctls */ #define TARGET_VT_OPENQRY 0x5600 @@ -925,6 +979,10 @@ #define TARGET_VT_WAITACTIVE 0x5607 #define TARGET_VT_LOCKSWITCH 0x560b #define TARGET_VT_UNLOCKSWITCH 0x560c +#define TARGET_VT_GETMODE 0x5601 +#define TARGET_VT_SETMODE 0x5602 +#define TARGET_VT_RELDISP 0x5605 +#define TARGET_VT_DISALLOCATE 0x5608 /* from asm/termbits.h */ @@ -999,9 +1057,11 @@ #define TARGET_MAP_NORESERVE 0x4000 /* don't check for reservations */ #define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ #define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ +#define TARGET_MAP_UNINITIALIZED 0x4000000 /* for anonymous mmap, memory could be uninitialized */ #endif -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_CRIS) +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) \ + || defined(TARGET_CRIS) || defined(TARGET_UNICORE32) struct target_stat { unsigned short st_dev; unsigned short __pad1; @@ -1677,6 +1737,27 @@ abi_long __unused[3]; }; +#elif defined(TARGET_S390X) +struct target_stat { + abi_ulong st_dev; + abi_ulong st_ino; + abi_ulong st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad1; + abi_ulong st_rdev; + abi_ulong st_size; + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + abi_ulong st_blksize; + abi_long st_blocks; + abi_ulong __unused[3]; +}; #else #error unsupported CPU #endif @@ -1763,6 +1844,34 @@ abi_long f_frsize; abi_long f_spare[5]; }; +#elif defined(TARGET_S390X) +struct target_statfs { + int32_t f_type; + int32_t f_bsize; + abi_long f_blocks; + abi_long f_bfree; + abi_long f_bavail; + abi_long f_files; + abi_long f_ffree; + kernel_fsid_t f_fsid; + int32_t f_namelen; + int32_t f_frsize; + int32_t f_spare[5]; +}; + +struct target_statfs64 { + int32_t f_type; + int32_t f_bsize; + abi_long f_blocks; + abi_long f_bfree; + abi_long f_bavail; + abi_long f_files; + abi_long f_ffree; + kernel_fsid_t f_fsid; + int32_t f_namelen; + int32_t f_frsize; + int32_t f_spare[5]; +}; #else struct target_statfs { uint32_t f_type; @@ -2205,3 +2314,20 @@ #define FUTEX_CLOCK_REALTIME 256 #define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) +#ifdef CONFIG_EPOLL +typedef union target_epoll_data { + abi_ulong ptr; + abi_ulong fd; + uint32_t u32; + uint64_t u64; +} target_epoll_data_t; + +struct target_epoll_event { + uint32_t events; + target_epoll_data_t data; +}; +#endif +struct target_rlimit64 { + uint64_t rlim_cur; + uint64_t rlim_max; +}; diff -Nru qemu-kvm-0.14.1+noroms/linux-user/syscall_types.h qemu-kvm-0.15.0+noroms/linux-user/syscall_types.h --- qemu-kvm-0.14.1+noroms/linux-user/syscall_types.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/syscall_types.h 2011-08-09 07:40:29.000000000 -0500 @@ -161,11 +161,31 @@ TYPE_INT, /* rotate */ MK_ARRAY(TYPE_INT, 5)) /* reserved */ +STRUCT(fb_cmap, + TYPE_INT, /* start */ + TYPE_INT, /* len */ + TYPE_PTRVOID, /* red */ + TYPE_PTRVOID, /* green */ + TYPE_PTRVOID, /* blue */ + TYPE_PTRVOID) /* transp */ + +STRUCT(fb_con2fbmap, + TYPE_INT, /* console */ + TYPE_INT) /* framebuffer */ + + STRUCT(vt_stat, TYPE_SHORT, /* v_active */ TYPE_SHORT, /* v_signal */ TYPE_SHORT) /* v_state */ +STRUCT(vt_mode, + TYPE_CHAR, /* mode */ + TYPE_CHAR, /* waitv */ + TYPE_SHORT, /* relsig */ + TYPE_SHORT, /* acqsig */ + TYPE_SHORT) /* frsig */ + STRUCT(fiemap_extent, TYPE_ULONGLONG, /* fe_logical */ TYPE_ULONGLONG, /* fe_physical */ diff -Nru qemu-kvm-0.14.1+noroms/linux-user/target_flat.h qemu-kvm-0.15.0+noroms/linux-user/target_flat.h --- qemu-kvm-0.14.1+noroms/linux-user/target_flat.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/target_flat.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,10 @@ +/* If your arch needs to do custom stuff, create your own target_flat.h + * header file in linux-user// + */ +#define flat_argvp_envp_on_stack() 1 +#define flat_reloc_valid(reloc, size) ((reloc) <= (size)) +#define flat_old_ram_flag(flag) (flag) +#define flat_get_relocate_addr(relval) (relval) +#define flat_get_addr_from_rp(rp, relval, flags, persistent) (rp) +#define flat_set_persistent(relval, persistent) (*persistent) +#define flat_put_addr_at_rp(rp, addr, relval) put_user_ual(addr, rp) diff -Nru qemu-kvm-0.14.1+noroms/linux-user/unicore32/syscall.h qemu-kvm-0.15.0+noroms/linux-user/unicore32/syscall.h --- qemu-kvm-0.14.1+noroms/linux-user/unicore32/syscall.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/unicore32/syscall.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#ifndef __UC32_SYSCALL_H__ +#define __UC32_SYSCALL_H__ +struct target_pt_regs { + abi_ulong uregs[34]; +}; + +#define UC32_REG_pc uregs[31] +#define UC32_REG_lr uregs[30] +#define UC32_REG_sp uregs[29] +#define UC32_REG_ip uregs[28] +#define UC32_REG_fp uregs[27] +#define UC32_REG_26 uregs[26] +#define UC32_REG_25 uregs[25] +#define UC32_REG_24 uregs[24] +#define UC32_REG_23 uregs[23] +#define UC32_REG_22 uregs[22] +#define UC32_REG_21 uregs[21] +#define UC32_REG_20 uregs[20] +#define UC32_REG_19 uregs[19] +#define UC32_REG_18 uregs[18] +#define UC32_REG_17 uregs[17] +#define UC32_REG_16 uregs[16] +#define UC32_REG_15 uregs[15] +#define UC32_REG_14 uregs[14] +#define UC32_REG_13 uregs[13] +#define UC32_REG_12 uregs[12] +#define UC32_REG_11 uregs[11] +#define UC32_REG_10 uregs[10] +#define UC32_REG_09 uregs[9] +#define UC32_REG_08 uregs[8] +#define UC32_REG_07 uregs[7] +#define UC32_REG_06 uregs[6] +#define UC32_REG_05 uregs[5] +#define UC32_REG_04 uregs[4] +#define UC32_REG_03 uregs[3] +#define UC32_REG_02 uregs[2] +#define UC32_REG_01 uregs[1] +#define UC32_REG_00 uregs[0] +#define UC32_REG_asr uregs[32] +#define UC32_REG_ORIG_00 uregs[33] + +#define UC32_SYSCALL_BASE 0x900000 +#define UC32_SYSCALL_ARCH_BASE 0xf0000 +#define UC32_SYSCALL_NR_set_tls (UC32_SYSCALL_ARCH_BASE + 5) + +#define UNAME_MACHINE "UniCore-II" + +#endif /* __UC32_SYSCALL_H__ */ diff -Nru qemu-kvm-0.14.1+noroms/linux-user/unicore32/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/unicore32/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/unicore32/syscall_nr.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/unicore32/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,371 @@ +/* + * This file contains the system call numbers for UniCore32 oldabi. + * + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#define TARGET_NR_restart_syscall 0 +#define TARGET_NR_exit 1 +#define TARGET_NR_fork 2 +#define TARGET_NR_read 3 +#define TARGET_NR_write 4 +#define TARGET_NR_open 5 +#define TARGET_NR_close 6 +#define TARGET_NR_waitpid 7 +#define TARGET_NR_creat 8 +#define TARGET_NR_link 9 +#define TARGET_NR_unlink 10 +#define TARGET_NR_execve 11 +#define TARGET_NR_chdir 12 +#define TARGET_NR_time 13 +#define TARGET_NR_mknod 14 +#define TARGET_NR_chmod 15 +#define TARGET_NR_lchown 16 +#define TARGET_NR_break 17 + /* 18 */ +#define TARGET_NR_lseek 19 +#define TARGET_NR_getpid 20 +#define TARGET_NR_mount 21 +#define TARGET_NR_umount 22 +#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 + /* 28 */ +#define TARGET_NR_pause 29 +#define TARGET_NR_utime 30 +#define TARGET_NR_stty 31 +#define TARGET_NR_gtty 32 +#define TARGET_NR_access 33 +#define TARGET_NR_nice 34 +#define TARGET_NR_ftime 35 +#define TARGET_NR_sync 36 +#define TARGET_NR_kill 37 +#define TARGET_NR_rename 38 +#define TARGET_NR_mkdir 39 +#define TARGET_NR_rmdir 40 +#define TARGET_NR_dup 41 +#define TARGET_NR_pipe 42 +#define TARGET_NR_times 43 +#define TARGET_NR_prof 44 +#define TARGET_NR_brk 45 +#define TARGET_NR_setgid 46 +#define TARGET_NR_getgid 47 +#define TARGET_NR_signal 48 +#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 +#define TARGET_NR_ioctl 54 +#define TARGET_NR_fcntl 55 +#define TARGET_NR_mpx 56 +#define TARGET_NR_setpgid 57 +#define TARGET_NR_ulimit 58 + /* 59 */ +#define TARGET_NR_umask 60 +#define TARGET_NR_chroot 61 +#define TARGET_NR_ustat 62 +#define TARGET_NR_dup2 63 +#define TARGET_NR_getppid 64 +#define TARGET_NR_getpgrp 65 +#define TARGET_NR_setsid 66 +#define TARGET_NR_sigaction 67 +#define TARGET_NR_sgetmask 68 +#define TARGET_NR_ssetmask 69 +#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 +#define TARGET_NR_setrlimit 75 +#define TARGET_NR_getrlimit 76 +#define TARGET_NR_getrusage 77 +#define TARGET_NR_gettimeofday 78 +#define TARGET_NR_settimeofday 79 +#define TARGET_NR_getgroups 80 +#define TARGET_NR_setgroups 81 +#define TARGET_NR_select 82 +#define TARGET_NR_symlink 83 + /* 84 */ +#define TARGET_NR_readlink 85 +#define TARGET_NR_uselib 86 +#define TARGET_NR_swapon 87 +#define TARGET_NR_reboot 88 +#define TARGET_NR_readdir 89 +#define TARGET_NR_mmap 90 +#define TARGET_NR_munmap 91 +#define TARGET_NR_truncate 92 +#define TARGET_NR_ftruncate 93 +#define TARGET_NR_fchmod 94 +#define TARGET_NR_fchown 95 +#define TARGET_NR_getpriority 96 +#define TARGET_NR_setpriority 97 +#define TARGET_NR_profil 98 +#define TARGET_NR_statfs 99 +#define TARGET_NR_fstatfs 100 +#define TARGET_NR_ioperm 101 +#define TARGET_NR_socketcall 102 +#define TARGET_NR_syslog 103 +#define TARGET_NR_setitimer 104 +#define TARGET_NR_getitimer 105 +#define TARGET_NR_stat 106 +#define TARGET_NR_lstat 107 +#define TARGET_NR_fstat 108 + /* 109 */ + /* 110 */ +#define TARGET_NR_vhangup 111 +#define TARGET_NR_idle 112 +#define TARGET_NR_syscall 113 +#define TARGET_NR_wait4 114 +#define TARGET_NR_swapoff 115 +#define TARGET_NR_sysinfo 116 +#define TARGET_NR_ipc 117 +#define TARGET_NR_fsync 118 +#define TARGET_NR_sigreturn 119 +#define TARGET_NR_clone 120 +#define TARGET_NR_setdomainname 121 +#define TARGET_NR_uname 122 +#define TARGET_NR_modify_ldt 123 +#define TARGET_NR_adjtimex 124 +#define TARGET_NR_mprotect 125 +#define TARGET_NR_sigprocmask 126 +#define TARGET_NR_create_module 127 +#define TARGET_NR_init_module 128 +#define TARGET_NR_delete_module 129 +#define TARGET_NR_get_kernel_syms 130 +#define TARGET_NR_quotactl 131 +#define TARGET_NR_getpgid 132 +#define TARGET_NR_fchdir 133 +#define TARGET_NR_bdflush 134 +#define TARGET_NR_sysfs 135 +#define TARGET_NR_personality 136 +#define TARGET_NR_afs_syscall 137 +#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 +#define TARGET_NR_flock 143 +#define TARGET_NR_msync 144 +#define TARGET_NR_readv 145 +#define TARGET_NR_writev 146 +#define TARGET_NR_getsid 147 +#define TARGET_NR_fdatasync 148 +#define TARGET_NR__sysctl 149 +#define TARGET_NR_mlock 150 +#define TARGET_NR_munlock 151 +#define TARGET_NR_mlockall 152 +#define TARGET_NR_munlockall 153 +#define TARGET_NR_sched_setparam 154 +#define TARGET_NR_sched_getparam 155 +#define TARGET_NR_sched_setscheduler 156 +#define TARGET_NR_sched_getscheduler 157 +#define TARGET_NR_sched_yield 158 +#define TARGET_NR_sched_get_priority_max 159 +#define TARGET_NR_sched_get_priority_min 160 +#define TARGET_NR_sched_rr_get_interval 161 +#define TARGET_NR_nanosleep 162 +#define TARGET_NR_mremap 163 +#define TARGET_NR_setresuid 164 +#define TARGET_NR_getresuid 165 +#define TARGET_NR_vm86 166 +#define TARGET_NR_query_module 167 +#define TARGET_NR_poll 168 +#define TARGET_NR_nfsservctl 169 +#define TARGET_NR_setresgid 170 +#define TARGET_NR_getresgid 171 +#define TARGET_NR_prctl 172 +#define TARGET_NR_rt_sigreturn 173 +#define TARGET_NR_rt_sigaction 174 +#define TARGET_NR_rt_sigprocmask 175 +#define TARGET_NR_rt_sigpending 176 +#define TARGET_NR_rt_sigtimedwait 177 +#define TARGET_NR_rt_sigqueueinfo 178 +#define TARGET_NR_rt_sigsuspend 179 +#define TARGET_NR_pread 180 +#define TARGET_NR_pwrite 181 +#define TARGET_NR_chown 182 +#define TARGET_NR_getcwd 183 +#define TARGET_NR_capget 184 +#define TARGET_NR_capset 185 +#define TARGET_NR_sigaltstack 186 +#define TARGET_NR_sendfile 187 + /* 188 */ + /* 189 */ +#define TARGET_NR_vfork 190 +#define TARGET_NR_ugetrlimit 191 +#define TARGET_NR_mmap2 192 +#define TARGET_NR_truncate64 193 +#define TARGET_NR_ftruncate64 194 +#define TARGET_NR_stat64 195 +#define TARGET_NR_lstat64 196 +#define TARGET_NR_fstat64 197 +#define TARGET_NR_lchown32 198 +#define TARGET_NR_getuid32 199 +#define TARGET_NR_getgid32 200 +#define TARGET_NR_geteuid32 201 +#define TARGET_NR_getegid32 202 +#define TARGET_NR_setreuid32 203 +#define TARGET_NR_setregid32 204 +#define TARGET_NR_getgroups32 205 +#define TARGET_NR_setgroups32 206 +#define TARGET_NR_fchown32 207 +#define TARGET_NR_setresuid32 208 +#define TARGET_NR_getresuid32 209 +#define TARGET_NR_setresgid32 210 +#define TARGET_NR_getresgid32 211 +#define TARGET_NR_chown32 212 +#define TARGET_NR_setuid32 213 +#define TARGET_NR_setgid32 214 +#define TARGET_NR_setfsuid32 215 +#define TARGET_NR_setfsgid32 216 +#define TARGET_NR_getdents64 217 +#define TARGET_NR_pivot_root 218 +#define TARGET_NR_mincore 219 +#define TARGET_NR_madvise 220 +#define TARGET_NR_fcntl64 221 + /* 222 */ + /* 223 */ +#define TARGET_NR_gettid 224 +#define TARGET_NR_readahead 225 +#define TARGET_NR_setxattr 226 +#define TARGET_NR_lsetxattr 227 +#define TARGET_NR_fsetxattr 228 +#define TARGET_NR_getxattr 229 +#define TARGET_NR_lgetxattr 230 +#define TARGET_NR_fgetxattr 231 +#define TARGET_NR_listxattr 232 +#define TARGET_NR_llistxattr 233 +#define TARGET_NR_flistxattr 234 +#define TARGET_NR_removexattr 235 +#define TARGET_NR_lremovexattr 236 +#define TARGET_NR_fremovexattr 237 +#define TARGET_NR_tkill 238 +#define TARGET_NR_sendfile64 239 +#define TARGET_NR_futex 240 +#define TARGET_NR_sched_setaffinity 241 +#define TARGET_NR_sched_getaffinity 242 +#define TARGET_NR_io_setup 243 +#define TARGET_NR_io_destroy 244 +#define TARGET_NR_io_getevents 245 +#define TARGET_NR_io_submit 246 +#define TARGET_NR_io_cancel 247 +#define TARGET_NR_exit_group 248 +#define TARGET_NR_lookup_dcookie 249 +#define TARGET_NR_epoll_create 250 +#define TARGET_NR_epoll_ctl 251 +#define TARGET_NR_epoll_wait 252 +#define TARGET_NR_remap_file_pages 253 + /* 254 */ + /* 255 */ + /* 256 */ +#define TARGET_NR_set_tid_address 256 +#define TARGET_NR_timer_create 257 +#define TARGET_NR_timer_settime 258 +#define TARGET_NR_timer_gettime 259 +#define TARGET_NR_timer_getoverrun 260 +#define TARGET_NR_timer_delete 261 +#define TARGET_NR_clock_settime 262 +#define TARGET_NR_clock_gettime 263 +#define TARGET_NR_clock_getres 264 +#define TARGET_NR_clock_nanosleep 265 +#define TARGET_NR_statfs64 266 +#define TARGET_NR_fstatfs64 267 +#define TARGET_NR_tgkill 268 +#define TARGET_NR_utimes 269 +#define TARGET_NR_fadvise64_64 270 +#define TARGET_NR_pciconfig_iobase 271 +#define TARGET_NR_pciconfig_read 272 +#define TARGET_NR_pciconfig_write 273 +#define TARGET_NR_mq_open 274 +#define TARGET_NR_mq_unlink 275 +#define TARGET_NR_mq_timedsend 276 +#define TARGET_NR_mq_timedreceive 277 +#define TARGET_NR_mq_notify 278 +#define TARGET_NR_mq_getsetattr 279 +#define TARGET_NR_waitid 280 +#define TARGET_NR_socket 281 +#define TARGET_NR_bind 282 +#define TARGET_NR_connect 283 +#define TARGET_NR_listen 284 +#define TARGET_NR_accept 285 +#define TARGET_NR_getsockname 286 +#define TARGET_NR_getpeername 287 +#define TARGET_NR_socketpair 288 +#define TARGET_NR_send 289 +#define TARGET_NR_sendto 290 +#define TARGET_NR_recv 291 +#define TARGET_NR_recvfrom 292 +#define TARGET_NR_shutdown 293 +#define TARGET_NR_setsockopt 294 +#define TARGET_NR_getsockopt 295 +#define TARGET_NR_sendmsg 296 +#define TARGET_NR_recvmsg 297 +#define TARGET_NR_semop 298 +#define TARGET_NR_semget 299 +#define TARGET_NR_semctl 300 +#define TARGET_NR_msgsnd 301 +#define TARGET_NR_msgrcv 302 +#define TARGET_NR_msgget 303 +#define TARGET_NR_msgctl 304 +#define TARGET_NR_shmat 305 +#define TARGET_NR_shmdt 306 +#define TARGET_NR_shmget 307 +#define TARGET_NR_shmctl 308 +#define TARGET_NR_add_key 309 +#define TARGET_NR_request_key 310 +#define TARGET_NR_keyctl 311 +#define TARGET_NR_semtimedop 312 +#define TARGET_NR_vserver 313 +#define TARGET_NR_ioprio_set 314 +#define TARGET_NR_ioprio_get 315 +#define TARGET_NR_inotify_init 316 +#define TARGET_NR_inotify_add_watch 317 +#define TARGET_NR_inotify_rm_watch 318 +#define TARGET_NR_mbind 319 +#define TARGET_NR_get_mempolicy 320 +#define TARGET_NR_set_mempolicy 321 +#define TARGET_NR_openat 322 +#define TARGET_NR_mkdirat 323 +#define TARGET_NR_mknodat 324 +#define TARGET_NR_fchownat 325 +#define TARGET_NR_futimesat 326 +#define TARGET_NR_fstatat64 327 +#define TARGET_NR_unlinkat 328 +#define TARGET_NR_renameat 329 +#define TARGET_NR_linkat 330 +#define TARGET_NR_symlinkat 331 +#define TARGET_NR_readlinkat 332 +#define TARGET_NR_fchmodat 333 +#define TARGET_NR_faccessat 334 + /* 335 */ + /* 336 */ +#define TARGET_NR_unshare 337 +#define TARGET_NR_set_robust_list 338 +#define TARGET_NR_get_robust_list 339 +#define TARGET_NR_splice 340 +#define TARGET_NR_sync_file_range2 341 +#define TARGET_NR_tee 342 +#define TARGET_NR_vmsplice 343 +#define TARGET_NR_move_pages 344 +#define TARGET_NR_getcpu 345 + /* 346 */ +#define TARGET_NR_kexec_load 347 +#define TARGET_NR_utimensat 348 +#define TARGET_NR_signalfd 349 +#define TARGET_NR_timerfd 350 +#define TARGET_NR_eventfd 351 +#define TARGET_NR_fallocate 352 +#define TARGET_NR_timerfd_settime 353 +#define TARGET_NR_timerfd_gettime 354 +#define TARGET_NR_signalfd4 355 +#define TARGET_NR_eventfd2 356 +#define TARGET_NR_epoll_create1 357 +#define TARGET_NR_dup3 358 +#define TARGET_NR_pipe2 359 +#define TARGET_NR_inotify_init1 360 diff -Nru qemu-kvm-0.14.1+noroms/linux-user/unicore32/target_signal.h qemu-kvm-0.15.0+noroms/linux-user/unicore32/target_signal.h --- qemu-kvm-0.14.1+noroms/linux-user/unicore32/target_signal.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/unicore32/target_signal.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +/* this struct defines a stack used during syscall handling */ +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_ulong ss_flags; + abi_ulong ss_size; +} target_stack_t; + +/* + * sigaltstack controls + */ +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define get_sp_from_cpustate(cpustate) (cpustate->regs[29]) + +#endif /* TARGET_SIGNAL_H */ diff -Nru qemu-kvm-0.14.1+noroms/linux-user/unicore32/termbits.h qemu-kvm-0.15.0+noroms/linux-user/unicore32/termbits.h --- qemu-kvm-0.14.1+noroms/linux-user/unicore32/termbits.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/linux-user/unicore32/termbits.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,2 @@ +/* NOTE: exactly the same as i386 */ +#include "../i386/termbits.h" diff -Nru qemu-kvm-0.14.1+noroms/linux-user/x86_64/syscall_nr.h qemu-kvm-0.15.0+noroms/linux-user/x86_64/syscall_nr.h --- qemu-kvm-0.14.1+noroms/linux-user/x86_64/syscall_nr.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/linux-user/x86_64/syscall_nr.h 2011-08-09 07:40:29.000000000 -0500 @@ -293,3 +293,15 @@ #define TARGET_NR_dup3 292 #define TARGET_NR_pipe2 293 #define TARGET_NR_inotify_init1 294 +#define TARGET_NR_preadv 295 +#define TARGET_NR_pwritev 296 +#define TARGET_NR_rt_tgsigqueueinfo 297 +#define TARGET_NR_perf_event_open 298 +#define TARGET_NR_recvmmsg 299 +#define TARGET_NR_fanotify_init 300 +#define TARGET_NR_fanotify_mark 301 +#define TARGET_NR_prlimit64 302 +#define TARGET_NR_name_to_handle_at 303 +#define TARGET_NR_open_by_handle_at 304 +#define TARGET_NR_clock_adjtime 305 +#define TARGET_NR_syncfs 306 diff -Nru qemu-kvm-0.14.1+noroms/m68k-semi.c qemu-kvm-0.15.0+noroms/m68k-semi.c --- qemu-kvm-0.14.1+noroms/m68k-semi.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/m68k-semi.c 2011-08-09 07:40:29.000000000 -0500 @@ -370,7 +370,7 @@ TaskState *ts = env->opaque; /* Allocate the heap using sbrk. */ if (!ts->heap_limit) { - long ret; + abi_ulong ret; uint32_t size; uint32_t base; @@ -379,8 +379,9 @@ /* Try a big heap, and reduce the size if that fails. */ for (;;) { ret = do_brk(base + size); - if (ret != -1) + if (ret >= (base + size)) { break; + } size >>= 1; } ts->heap_limit = base + size; diff -Nru qemu-kvm-0.14.1+noroms/MAINTAINERS qemu-kvm-0.15.0+noroms/MAINTAINERS --- qemu-kvm-0.14.1+noroms/MAINTAINERS 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/MAINTAINERS 2011-08-09 07:40:29.000000000 -0500 @@ -56,8 +56,8 @@ Guest CPU cores (TCG): ---------------------- Alpha -M: qemu-devel@nongnu.org -S: Orphan +M: Richard Henderson +S: Maintained F: target-alpha/ ARM @@ -70,6 +70,11 @@ S: Maintained F: target-cris/ +LM32 +M: Michael Walle +S: Maintained +F: target-lm32/ + M68K M: Paul Brook S: Maintained @@ -202,6 +207,18 @@ S: Maintained F: hw/etraxfs.c +LM32 Machines +------------- +EVR32 and uclinux BSP +M: Michael Walle +S: Maintained +F: hw/lm32_boards.c + +milkymist +M: Michael Walle +S: Maintained +F: hw/milkymist.c + M68K Machines ------------- an5206 @@ -463,7 +480,7 @@ F: tcg/ia64/ MIPS target -M: Aurelien Jarno +M: Aurelien Jarno S: Maintained F: tcg/mips/ diff -Nru qemu-kvm-0.14.1+noroms/Makefile qemu-kvm-0.15.0+noroms/Makefile --- qemu-kvm-0.14.1+noroms/Makefile 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/Makefile 2011-08-09 07:40:29.000000000 -0500 @@ -88,6 +88,8 @@ endif $(common-obj-y): $(GENERATED_HEADERS) +subdir-libcacard: $(oslib-obj-y) $(trace-obj-y) qemu-malloc.o qemu-timer-common.o + $(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis $(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser @@ -104,6 +106,8 @@ QEMU_CFLAGS+=$(CURL_CFLAGS) +QEMU_CFLAGS+=$(GLIB_CFLAGS) + ui/cocoa.o: ui/cocoa.m ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) @@ -112,46 +116,31 @@ bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS) -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 - -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 - -trace.o: trace.c $(GENERATED_HEADERS) - -trace-dtrace.h: trace-dtrace.dtrace - $(call quiet-command,dtrace -o $@ -h -s $<, " GEN trace-dtrace.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 - -trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) - $(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o") - -simpletrace.o: simpletrace.c $(GENERATED_HEADERS) - version.o: $(SRC_PATH)/version.rc config-host.mak $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@") version-obj-$(CONFIG_WIN32) += version.o ###################################################################### +# Support building shared library libcacard + +.PHONY: libcacard.la install-libcacard +ifeq ($(LIBTOOL),) +libcacard.la: + @echo "libtool is missing, please install and rerun configure"; exit 1 + +install-libcacard: + @echo "libtool is missing, please install and rerun configure"; exit 1 +else +libcacard.la: $(GENERATED_HEADERS) $(oslib-obj-y) qemu-malloc.o qemu-timer-common.o $(addsuffix .lo, $(basename $(trace-obj-y))) + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" libcacard.la,) + +install-libcacard: libcacard.la + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" install-libcacard,) +endif +###################################################################### 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.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS) 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 @@ -164,27 +153,67 @@ 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_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y) qemu-tool.o 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) +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 error.o qerror.o qemu-error.o $(CHECK_PROG_DEPS) + +$(qapi-obj-y): $(GENERATED_HEADERS) +qapi-dir := qapi-generated +test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir) + +$(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h +$(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") +$(qapi-dir)/test-qapi-visit.c: $(qapi-dir)/test-qapi-visit.h +$(qapi-dir)/test-qapi-visit.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") +$(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c +$(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") + +$(qapi-dir)/qga-qapi-types.c: $(qapi-dir)/qga-qapi-types.h +$(qapi-dir)/qga-qapi-types.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") +$(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h +$(qapi-dir)/qga-qapi-visit.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") +$(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") + +test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) +test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o + +test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y) +test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o + +QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o +QGALIB_GEN=$(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c) + +$(QGALIB_GEN): $(GENERATED_HEADERS) +$(QGALIB) qemu-ga.o: $(QGALIB_GEN) $(qapi-obj-y) +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o + +QEMULIBS=libhw32 libhw64 libuser libdis libdis-user 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 fsdev/*.o fsdev/*.d ui/*.o ui/*.d + rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~ + rm -Rf .libs + 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 qapi/*.o qapi/*.d qga/*.o qga/*.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 + rm -rf $(qapi-dir) $(MAKE) -C tests clean - for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \ + for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \ if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ rm -f $$d/qemu-options.def; \ done @@ -193,9 +222,12 @@ 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.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-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps qemu-doc.dvi + rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys + rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp + rm -f 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 \ + for d in $(TARGET_DIRS) $(QEMULIBS); do \ rm -rf $$d || exit 1 ; \ done @@ -207,13 +239,13 @@ 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 \ +pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ +pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ +bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ +mpc8544ds.dtb \ multiboot.bin linuxboot.bin \ -s390-zipl.rom +s390-zipl.rom \ +spapr-rtas.bin slof.bin BLOBS += extboot.bin BLOBS += vapic.bin else @@ -362,10 +394,12 @@ $(datadir)/openbios-sparc32 \ $(datadir)/openbios-sparc64 \ $(datadir)/openbios-ppc \ - $(datadir)/pxe-ne2k_pci.bin \ - $(datadir)/pxe-rtl8139.bin \ - $(datadir)/pxe-pcnet.bin \ - $(datadir)/pxe-e1000.bin \ + $(datadir)/pxe-e1000.rom \ + $(datadir)/pxe-eepro100.rom \ + $(datadir)/pxe-ne2k_pci.rom \ + $(datadir)/pxe-pcnet.rom \ + $(datadir)/pxe-rtl8139.rom \ + $(datadir)/pxe-virtio.rom \ $(datadir)/extboot.bin \ $(docdir)/qemu-doc.html \ $(docdir)/qemu-tech.html \ @@ -374,4 +408,4 @@ $(mandir)/man8/qemu-nbd.8 # Include automatically generated dependency files --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d) +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d) diff -Nru qemu-kvm-0.14.1+noroms/Makefile.objs qemu-kvm-0.15.0+noroms/Makefile.objs --- qemu-kvm-0.14.1+noroms/Makefile.objs 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/Makefile.objs 2011-08-09 07:40:29.000000000 -0500 @@ -2,19 +2,19 @@ # 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 +qobject-obj-y += qerror.o error.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 +oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o +oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-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-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o async.o +block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o block-obj-$(CONFIG_POSIX) += posix-aio-compat.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o block-obj-$(CONFIG_POSIX) += compatfd.o @@ -46,12 +46,14 @@ net-nested-$(CONFIG_VDE) += vde.o net-obj-y += $(addprefix net/, $(net-nested-y)) -ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS),yy) +ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) # 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 +fsdev-nested-y = qemu-fsdev.o +else +fsdev-nested-y = qemu-fsdev-dummy.o endif -fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y)) ###################################################################### @@ -64,7 +66,7 @@ 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 += readline.o console.o cursor.o qemu-error.o common-obj-y += $(oslib-obj-y) common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_POSIX) += os-posix.o @@ -95,12 +97,13 @@ 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 += buffered_file.o migration.o migration-tcp.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 += block-migration.o iohandler.o common-obj-y += pflib.o +common-obj-y += bitmap.o bitops.o common-obj-$(CONFIG_BRLAPI) += baum.o common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o @@ -126,23 +129,24 @@ 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 +ui-obj-$(CONFIG_CURSES) += curses.o +vnc-obj-y += vnc.o d3des.o +vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o +vnc-obj-y += vnc-enc-tight.o vnc-palette.o +vnc-obj-y += vnc-enc-zrle.o +vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o +vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o ifdef CONFIG_VNC_THREAD -ui-obj-y += vnc-jobs-async.o +vnc-obj-y += vnc-jobs-async.o else -ui-obj-y += vnc-jobs-sync.o +vnc-obj-y += vnc-jobs-sync.o endif common-obj-y += $(addprefix ui/, $(ui-obj-y)) +common-obj-$(CONFIG_VNC) += $(addprefix ui/, $(vnc-obj-y)) common-obj-y += iov.o acl.o -common-obj-$(CONFIG_THREAD) += qemu-thread.o -common-obj-$(CONFIG_IOTHREAD) += compatfd.o +#common-obj-$(CONFIG_POSIX) += compatfd.o common-obj-y += notify.o event_notifier.o common-obj-y += qemu-timer.o qemu-timer-common.o @@ -152,8 +156,8 @@ 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 +common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o +common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o xenfb.o xen_disk.o xen_nic.o ###################################################################### # libuser @@ -168,7 +172,8 @@ hw-obj-y = hw-obj-y += loader.o -hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o +hw-obj-$(CONFIG_VIRTIO) += virtio-console.o +hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o hw-obj-y += fw_cfg.o hw-obj-$(CONFIG_PCI) += pci_bridge.o hw-obj-$(CONFIG_PCI) += msix.o msi.o @@ -193,11 +198,17 @@ 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_USB_EHCI) += usb-ehci.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 +hw-obj-$(CONFIG_HPET) += hpet.o +hw-obj-$(CONFIG_APPLESMC) += applesmc.o +hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o +hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o +hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o # PPC devices hw-obj-$(CONFIG_OPENPIC) += openpic.o @@ -238,7 +249,7 @@ hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o # IDE -hw-obj-$(CONFIG_IDE_CORE) += ide/core.o +hw-obj-$(CONFIG_IDE_CORE) += ide/core.o ide/atapi.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 @@ -261,6 +272,7 @@ 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_VMMOUSE) += vmmouse.o hw-obj-$(CONFIG_RC4030) += rc4030.o hw-obj-$(CONFIG_DP8393X) += dp8393x.o @@ -280,9 +292,12 @@ 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 +9pfs-nested-$(CONFIG_VIRTFS) = virtio-9p.o virtio-9p-debug.o +9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o +9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o + +hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y)) + ###################################################################### # libdis @@ -307,6 +322,46 @@ # trace 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 + +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 + +trace.o: trace.c $(GENERATED_HEADERS) + +trace-dtrace.h: trace-dtrace.dtrace + $(call quiet-command,dtrace -o $@ -h -s $<, " GEN trace-dtrace.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 dependency +# 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 + +trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) + $(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o") + +ifeq ($(LIBTOOL),) +trace-dtrace.lo: trace-dtrace.dtrace + @echo "missing libtool. please install and rerun configure."; exit 1 +else +trace-dtrace.lo: trace-dtrace.dtrace + $(call quiet-command,libtool --mode=compile --tag=CC dtrace -o $@ -G -s $<, " lt GEN trace-dtrace.o") +endif + +simpletrace.o: simpletrace.c $(GENERATED_HEADERS) + +ifeq ($(TRACE_BACKEND),dtrace) trace-obj-y = trace-dtrace.o else trace-obj-y = trace.o @@ -316,7 +371,20 @@ endif endif +###################################################################### +# smartcard + +libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o + +###################################################################### +# qapi + +qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o +qapi-nested-y += qmp-registry.o qmp-dispatch.o +qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y)) + vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) +vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS) diff -Nru qemu-kvm-0.14.1+noroms/Makefile.target qemu-kvm-0.15.0+noroms/Makefile.target --- qemu-kvm-0.14.1+noroms/Makefile.target 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/Makefile.target 2011-08-09 07:40:29.000000000 -0500 @@ -3,6 +3,7 @@ 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) +CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y) include ../config-host.mak include config-devices.mak @@ -14,7 +15,10 @@ TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) $(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw) -QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H +ifdef CONFIG_LINUX +QEMU_CFLAGS += -I../linux-headers +endif +QEMU_CFLAGS += -I.. -I$(TARGET_PATH) -DNEED_CPU_H include $(SRC_PATH)/Makefile.objs @@ -37,10 +41,6 @@ LIBS+=-lm endif -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 @@ -75,8 +75,7 @@ libobj-$(CONFIG_NO_CPU_EMULATION) += fake-exec.o libobj-$(CONFIG_CPU_EMULATION) += translate-all.o translate.o libobj-$(CONFIG_CPU_EMULATION) += tcg/tcg.o -libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o -libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o +libobj-y += fpu/softfloat.o libobj-y += op_helper.o helper.o ifeq ($(TARGET_BASE_ARCH), i386) libobj-y += cpuid.o @@ -101,14 +100,12 @@ # HELPER_CFLAGS is used for all the code compiled with static register # variables -op_helper.o cpu-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS) +op_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS) # Note: this is a workaround. The real fix is to avoid compiling -# cpu_signal_handler() in cpu-exec.c. +# cpu_signal_handler() in user-exec.c. signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS) -qemu-kvm-helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) - ######################################################### # Linux user emulator target @@ -116,10 +113,10 @@ $(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) +QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \ elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \ - qemu-malloc.o $(oslib-obj-y) + qemu-malloc.o user-exec.o $(oslib-obj-y) obj-$(TARGET_HAS_BFLT) += flatload.o @@ -157,7 +154,7 @@ LIBS+=-lmx obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \ - gdbstub.o + gdbstub.o user-exec.o obj-i386-y += ioport-user.o @@ -179,7 +176,7 @@ 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 \ - gdbstub.o uaccess.o + gdbstub.o uaccess.o user-exec.o obj-i386-y += ioport-user.o @@ -200,37 +197,46 @@ # need to fix this properly 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-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o obj-y += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o -obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p.o +obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o obj-y += rwhandler.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o obj-$(CONFIG_NO_KVM) += kvm-stub.o - LIBS+=-lz QEMU_CFLAGS += $(VNC_TLS_CFLAGS) QEMU_CFLAGS += $(VNC_SASL_CFLAGS) QEMU_CFLAGS += $(VNC_JPEG_CFLAGS) QEMU_CFLAGS += $(VNC_PNG_CFLAGS) +QEMU_CFLAGS += $(GLIB_CFLAGS) -# xen backend driver support -obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o +# xen support +obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o +obj-$(CONFIG_NO_XEN) += xen-stub.o + +obj-i386-$(CONFIG_XEN) += xen_platform.o # Inter-VM PCI shared memory -obj-$(CONFIG_KVM) += ivshmem.o +CONFIG_IVSHMEM = +ifeq ($(CONFIG_KVM), y) + ifeq ($(CONFIG_PCI), y) + CONFIG_IVSHMEM = y + endif +endif +obj-$(CONFIG_IVSHMEM) += ivshmem.o # Hardware support 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 += cirrus_vga.o sga.o apic.o ioapic.o piix_pci.o +obj-i386-y += vmport.o obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o obj-i386-y += extboot.o obj-i386-y += debugcon.o multiboot.o obj-i386-y += pc_piix.o +obj-i386-$(CONFIG_KVM) += kvmclock.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 @@ -256,11 +262,16 @@ obj-ppc-y += ppc_oldworld.o # NewWorld PowerMac obj-ppc-y += ppc_newworld.o +# IBM pSeries (sPAPR) +ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy) +obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o +obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o +endif # PowerPC 4xx boards 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_mpc8544ds.o +obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o # PowerPC 440 Xilinx ML507 reference board. obj-ppc-y += virtex_ml507.o obj-ppc-$(CONFIG_KVM) += kvm_ppc.o @@ -272,6 +283,28 @@ obj-ppc-y += xilinx_uartlite.o obj-ppc-y += xilinx_ethlite.o +# LM32 boards +obj-lm32-y += lm32_boards.o +obj-lm32-y += milkymist.o + +# LM32 peripherals +obj-lm32-y += lm32_pic.o +obj-lm32-y += lm32_juart.o +obj-lm32-y += lm32_timer.o +obj-lm32-y += lm32_uart.o +obj-lm32-y += lm32_sys.o +obj-lm32-y += milkymist-ac97.o +obj-lm32-y += milkymist-hpdmc.o +obj-lm32-y += milkymist-memcard.o +obj-lm32-y += milkymist-minimac2.o +obj-lm32-y += milkymist-pfpu.o +obj-lm32-y += milkymist-softusb.o +obj-lm32-y += milkymist-sysctl.o +obj-lm32-$(CONFIG_OPENGL) += milkymist-tmu2.o +obj-lm32-y += milkymist-uart.o +obj-lm32-y += milkymist-vgafb.o +obj-lm32-y += framebuffer.o + obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o obj-mips-y += pcspk.o i8254.o obj-mips-y += acpi.o acpi_piix4.o @@ -283,12 +316,15 @@ obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o obj-microblaze-y = petalogix_s3adsp1800_mmu.o +obj-microblaze-y += petalogix_ml605_mmu.o obj-microblaze-y += microblaze_pic_cpu.o obj-microblaze-y += xilinx_intc.o obj-microblaze-y += xilinx_timer.o obj-microblaze-y += xilinx_uartlite.o obj-microblaze-y += xilinx_ethlite.o +obj-microblaze-y += xilinx_axidma.o +obj-microblaze-y += xilinx_axienet.o obj-microblaze-$(CONFIG_FDT) += device_tree.o @@ -342,6 +378,9 @@ 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-arm-y += vexpress.o +obj-arm-y += strongarm.o +obj-arm-y += collie.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 @@ -352,7 +391,8 @@ obj-s390x-y = s390-virtio-bus.o s390-virtio.o -obj-alpha-y = alpha_palcode.o +obj-alpha-y = i8259.o mc146818rtc.o +obj-alpha-y += vga.o cirrus_vga.o ifeq ($(TARGET_ARCH), ia64) firmware.o: firmware.c @@ -372,6 +412,12 @@ endif # CONFIG_SOFTMMU +ifndef CONFIG_LINUX_USER +# libcacard needs qemu-thread support, and besides is only needed by devices +# so not requires with linux-user targets +obj-$(CONFIG_SMARTCARD_NSS) += $(addprefix ../libcacard/, $(libcacard-y)) +endif # CONFIG_LINUX_USER + obj-y += $(addprefix ../, $(trace-obj-y)) obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o @@ -390,7 +436,7 @@ clean: rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o - rm -f *.d */*.d tcg/*.o ide/*.o + rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c ifdef CONFIG_SYSTEMTAP_TRACE rm -f *.stp diff -Nru qemu-kvm-0.14.1+noroms/migration.c qemu-kvm-0.15.0+noroms/migration.c --- qemu-kvm-0.14.1+noroms/migration.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/migration.c 2011-08-09 07:40:29.000000000 -0500 @@ -124,7 +124,7 @@ } current_migration = s; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); return 0; } @@ -276,7 +276,7 @@ { DPRINTF("setting error state\n"); s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); migrate_fd_cleanup(s); } @@ -334,7 +334,7 @@ monitor_resume(s->mon); } s->state = MIG_STATE_ERROR; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); } return ret; @@ -378,7 +378,7 @@ int old_vm_running = vm_running; DPRINTF("done iterating\n"); - vm_stop(0); + vm_stop(VMSTOP_MIGRATE); if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) { if (old_vm_running) { @@ -395,7 +395,7 @@ state = MIG_STATE_ERROR; } s->state = state; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); } } @@ -415,7 +415,7 @@ DPRINTF("cancelling migration\n"); s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); qemu_savevm_state_cancel(s->mon, s->file); migrate_fd_cleanup(s); @@ -429,7 +429,7 @@ if (s->state == MIG_STATE_ACTIVE) { s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers); + notifier_list_notify(&migration_state_notifiers, NULL); migrate_fd_cleanup(s); } qemu_free(s); diff -Nru qemu-kvm-0.14.1+noroms/migration-exec.c qemu-kvm-0.15.0+noroms/migration-exec.c --- qemu-kvm-0.14.1+noroms/migration-exec.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/migration-exec.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,7 +17,6 @@ #include "qemu_socket.h" #include "migration.h" #include "qemu-char.h" -#include "sysemu.h" #include "buffered_file.h" #include "block.h" #include diff -Nru qemu-kvm-0.14.1+noroms/migration-fd.c qemu-kvm-0.15.0+noroms/migration-fd.c --- qemu-kvm-0.14.1+noroms/migration-fd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/migration-fd.c 2011-08-09 07:40:29.000000000 -0500 @@ -16,7 +16,6 @@ #include "migration.h" #include "monitor.h" #include "qemu-char.h" -#include "sysemu.h" #include "buffered_file.h" #include "block.h" #include "qemu_socket.h" diff -Nru qemu-kvm-0.14.1+noroms/migration.h qemu-kvm-0.15.0+noroms/migration.h --- qemu-kvm-0.14.1+noroms/migration.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/migration.h 2011-08-09 07:40:29.000000000 -0500 @@ -139,4 +139,13 @@ void remove_migration_state_change_notifier(Notifier *notify); int get_migration_state(void); +uint64_t ram_bytes_remaining(void); +uint64_t ram_bytes_transferred(void); +uint64_t ram_bytes_total(void); + +int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque); +int ram_load(QEMUFile *f, void *opaque, int version_id); + +extern int incoming_expected; + #endif diff -Nru qemu-kvm-0.14.1+noroms/migration-tcp.c qemu-kvm-0.15.0+noroms/migration-tcp.c --- qemu-kvm-0.14.1+noroms/migration-tcp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/migration-tcp.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,7 +15,6 @@ #include "qemu_socket.h" #include "migration.h" #include "qemu-char.h" -#include "sysemu.h" #include "buffered_file.h" #include "block.h" @@ -139,7 +138,7 @@ { struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); - int s = (unsigned long)opaque; + int s = (intptr_t)opaque; QEMUFile *f; int c; @@ -194,7 +193,7 @@ goto err; qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL, - (void *)(unsigned long)s); + (void *)(intptr_t)s); return 0; diff -Nru qemu-kvm-0.14.1+noroms/migration-unix.c qemu-kvm-0.15.0+noroms/migration-unix.c --- qemu-kvm-0.14.1+noroms/migration-unix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/migration-unix.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,7 +15,6 @@ #include "qemu_socket.h" #include "migration.h" #include "qemu-char.h" -#include "sysemu.h" #include "buffered_file.h" #include "block.h" @@ -147,7 +146,7 @@ { struct sockaddr_un addr; socklen_t addrlen = sizeof(addr); - int s = (unsigned long)opaque; + int s = (intptr_t)opaque; QEMUFile *f; int c; @@ -204,7 +203,7 @@ } qemu_set_fd_handler2(sock, NULL, unix_accept_incoming_migration, NULL, - (void *)(unsigned long)sock); + (void *)(intptr_t)sock); return 0; diff -Nru qemu-kvm-0.14.1+noroms/module.h qemu-kvm-0.15.0+noroms/module.h --- qemu-kvm-0.14.1+noroms/module.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/module.h 2011-08-09 07:40:29.000000000 -0500 @@ -24,12 +24,14 @@ MODULE_INIT_BLOCK, MODULE_INIT_DEVICE, MODULE_INIT_MACHINE, + MODULE_INIT_QAPI, MODULE_INIT_MAX } module_init_type; #define block_init(function) module_init(function, MODULE_INIT_BLOCK) #define device_init(function) module_init(function, MODULE_INIT_DEVICE) #define machine_init(function) module_init(function, MODULE_INIT_MACHINE) +#define qapi_init(function) module_init(function, MODULE_INIT_QAPI) void register_module_init(void (*fn)(void), module_init_type type); diff -Nru qemu-kvm-0.14.1+noroms/monitor.c qemu-kvm-0.15.0+noroms/monitor.c --- qemu-kvm-0.14.1+noroms/monitor.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/monitor.c 2011-08-09 07:40:29.000000000 -0500 @@ -56,7 +56,7 @@ #include "json-streamer.h" #include "json-parser.h" #include "osdep.h" -#include "exec-all.h" +#include "cpu.h" #ifdef CONFIG_SIMPLE_TRACE #include "trace.h" #endif @@ -898,7 +898,7 @@ } monitor_printf(mon, " thread_id=%" PRId64 " ", - qdict_get_int(cpu, "thread_id")); + qdict_get_int(cpu, "thread_id")); monitor_printf(mon, "\n"); } @@ -1041,6 +1041,7 @@ return 0; } +#ifdef CONFIG_VNC static int change_vnc_password(const char *password) { if (!password || !password[0]) { @@ -1087,6 +1088,13 @@ return 0; } +#else +static int do_change_vnc(Monitor *mon, const char *target, const char *arg) +{ + qerror_report(QERR_FEATURE_DISABLED, "vnc"); + return -ENODEV; +} +#endif /** * do_change(): Change a removable medium, or VNC configuration @@ -1152,12 +1160,7 @@ } /* 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; + return vnc_display_password(NULL, password); } qerror_report(QERR_INVALID_PARAMETER, "protocol"); @@ -1196,12 +1199,39 @@ } if (strcmp(protocol, "vnc") == 0) { - rc = vnc_display_pw_expire(NULL, when); - if (rc != 0) { - qerror_report(QERR_SET_PASSWD_FAILED); + return vnc_display_pw_expire(NULL, when); + } + + qerror_report(QERR_INVALID_PARAMETER, "protocol"); + return -1; +} + +static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *fdname = qdict_get_str(qdict, "fdname"); + int skipauth = qdict_get_try_bool(qdict, "skipauth", 0); + CharDriverState *s; + + if (strcmp(protocol, "spice") == 0) { + if (!using_spice) { + /* correct one? spice isn't a device ,,, */ + qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); return -1; } - return 0; + qerror_report(QERR_ADD_CLIENT_FAILED); + return -1; + } else if (strcmp(protocol, "vnc") == 0) { + int fd = monitor_get_fd(mon, fdname); + vnc_display_add_client(NULL, fd, skipauth); + return 0; + } else if ((s = qemu_chr_find(protocol)) != NULL) { + int fd = monitor_get_fd(mon, fdname); + if (qemu_chr_add_client(s, fd) < 0) { + qerror_report(QERR_ADD_CLIENT_FAILED); + return -1; + } + return 0; } qerror_report(QERR_INVALID_PARAMETER, "protocol"); @@ -1280,7 +1310,7 @@ */ static int do_stop(Monitor *mon, const QDict *qdict, QObject **ret_data) { - vm_stop(EXCP_INTERRUPT); + vm_stop(VMSTOP_USER); return 0; } @@ -1452,7 +1482,7 @@ if (l > line_size) l = line_size; if (is_physical) { - cpu_physical_memory_rw(addr, buf, l, 0); + cpu_physical_memory_read(addr, buf, l); } else { env = mon_get_cpu(); if (cpu_memory_rw_debug(env, addr, buf, l, 0) < 0) { @@ -1628,7 +1658,7 @@ l = sizeof(buf); if (l > size) l = size; - cpu_physical_memory_rw(addr, buf, l, 0); + cpu_physical_memory_read(addr, buf, l); if (fwrite(buf, 1, l, f) != l) { monitor_printf(mon, "fwrite() error in do_physical_memory_save\n"); goto exit; @@ -1648,17 +1678,16 @@ static void do_sum(Monitor *mon, const QDict *qdict) { uint32_t addr; - uint8_t buf[1]; uint16_t sum; uint32_t start = qdict_get_int(qdict, "start"); uint32_t size = qdict_get_int(qdict, "size"); sum = 0; for(addr = start; addr < (start + size); addr++) { - cpu_physical_memory_rw(addr, buf, 1, 0); + uint8_t val = ldub_phys(addr); /* BSD sum algorithm ('sum' Unix command) */ sum = (sum >> 1) | (sum << 15); - sum += buf[0]; + sum += val; } monitor_printf(mon, "%05d\n", sum); } @@ -1898,7 +1927,7 @@ kbd_put_keycode(keycode & 0x7f); } /* delayed key up events */ - qemu_mod_timer(key_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) + muldiv64(get_ticks_per_sec(), hold_time, 1000)); } @@ -2049,7 +2078,7 @@ pgd = env->cr[3] & ~0xfff; for(l1 = 0; l1 < 1024; l1++) { - cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4); + cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); pde = le32_to_cpu(pde); if (pde & PG_PRESENT_MASK) { if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { @@ -2057,8 +2086,7 @@ print_pte(mon, (l1 << 22), pde, ~((1 << 21) - 1)); } else { for(l2 = 0; l2 < 1024; l2++) { - cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, - (uint8_t *)&pte, 4); + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); pte = le32_to_cpu(pte); if (pte & PG_PRESENT_MASK) { print_pte(mon, (l1 << 22) + (l2 << 12), @@ -2079,13 +2107,12 @@ pdp_addr = env->cr[3] & ~0x1f; for (l1 = 0; l1 < 4; l1++) { - cpu_physical_memory_read(pdp_addr + l1 * 8, (uint8_t *)&pdpe, 8); + cpu_physical_memory_read(pdp_addr + l1 * 8, &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); + cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); pde = le64_to_cpu(pde); if (pde & PG_PRESENT_MASK) { if (pde & PG_PSE_MASK) { @@ -2095,8 +2122,7 @@ } else { pt_addr = pde & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pt_addr + l3 * 8, - (uint8_t *)&pte, 8); + cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); pte = le64_to_cpu(pte); if (pte & PG_PRESENT_MASK) { print_pte(mon, (l1 << 30 ) + (l2 << 21) @@ -2121,13 +2147,12 @@ pml4_addr = env->cr[3] & 0x3fffffffff000ULL; for (l1 = 0; l1 < 512; l1++) { - cpu_physical_memory_read(pml4_addr + l1 * 8, (uint8_t *)&pml4e, 8); + cpu_physical_memory_read(pml4_addr + l1 * 8, &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); + cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); pdpe = le64_to_cpu(pdpe); if (pdpe & PG_PRESENT_MASK) { if (pdpe & PG_PSE_MASK) { @@ -2137,8 +2162,7 @@ } else { pd_addr = pdpe & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pd_addr + l3 * 8, - (uint8_t *)&pde, 8); + cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); pde = le64_to_cpu(pde); if (pde & PG_PRESENT_MASK) { if (pde & PG_PSE_MASK) { @@ -2151,8 +2175,7 @@ for (l4 = 0; l4 < 512; l4++) { cpu_physical_memory_read(pt_addr + l4 * 8, - (uint8_t *)&pte, - 8); + &pte, 8); pte = le64_to_cpu(pte); if (pte & PG_PRESENT_MASK) { print_pte(mon, (l1 << 39) + @@ -2230,7 +2253,7 @@ last_prot = 0; start = -1; for(l1 = 0; l1 < 1024; l1++) { - cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4); + cpu_physical_memory_read(pgd + l1 * 4, &pde, 4); pde = le32_to_cpu(pde); end = l1 << 22; if (pde & PG_PRESENT_MASK) { @@ -2239,8 +2262,7 @@ mem_print(mon, &start, &last_prot, end, prot); } else { for(l2 = 0; l2 < 1024; l2++) { - cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, - (uint8_t *)&pte, 4); + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); pte = le32_to_cpu(pte); end = (l1 << 22) + (l2 << 12); if (pte & PG_PRESENT_MASK) { @@ -2269,14 +2291,13 @@ last_prot = 0; start = -1; for (l1 = 0; l1 < 4; l1++) { - cpu_physical_memory_read(pdp_addr + l1 * 8, (uint8_t *)&pdpe, 8); + cpu_physical_memory_read(pdp_addr + l1 * 8, &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); + cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8); pde = le64_to_cpu(pde); end = (l1 << 30) + (l2 << 21); if (pde & PG_PRESENT_MASK) { @@ -2287,8 +2308,7 @@ } else { pt_addr = pde & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pt_addr + l3 * 8, - (uint8_t *)&pte, 8); + cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8); pte = le64_to_cpu(pte); end = (l1 << 30) + (l2 << 21) + (l3 << 12); if (pte & PG_PRESENT_MASK) { @@ -2325,14 +2345,13 @@ last_prot = 0; start = -1; for (l1 = 0; l1 < 512; l1++) { - cpu_physical_memory_read(pml4_addr + l1 * 8, (uint8_t *)&pml4e, 8); + cpu_physical_memory_read(pml4_addr + l1 * 8, &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); + cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8); pdpe = le64_to_cpu(pdpe); end = (l1 << 39) + (l2 << 30); if (pdpe & PG_PRESENT_MASK) { @@ -2343,8 +2362,7 @@ } else { pd_addr = pdpe & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { - cpu_physical_memory_read(pd_addr + l3 * 8, - (uint8_t *)&pde, 8); + cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8); pde = le64_to_cpu(pde); end = (l1 << 39) + (l2 << 30) + (l3 << 21); if (pde & PG_PRESENT_MASK) { @@ -2357,8 +2375,7 @@ for (l4 = 0; l4 < 512; l4++) { cpu_physical_memory_read(pt_addr + l4 * 8, - (uint8_t *)&pte, - 8); + &pte, 8); pte = le64_to_cpu(pte); end = (l1 << 39) + (l2 << 30) + (l3 << 21) + (l4 << 12); @@ -2580,19 +2597,21 @@ #endif #if defined(TARGET_I386) -static void do_inject_nmi(Monitor *mon, const QDict *qdict) +static int do_inject_nmi(Monitor *mon, const QDict *qdict, QObject **ret_data) { CPUState *env; - int cpu_index = qdict_get_int(qdict, "cpu_index"); - for (env = first_cpu; env != NULL; env = env->next_cpu) - if (env->cpu_index == cpu_index) { - if (kvm_enabled()) - kvm_inject_interrupt(env, CPU_INTERRUPT_NMI); - else - cpu_interrupt(env, CPU_INTERRUPT_NMI); - break; - } + for (env = first_cpu; env != NULL; env = env->next_cpu) { + cpu_interrupt(env, CPU_INTERRUPT_NMI); + } + + return 0; +} +#else +static int do_inject_nmi(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + qerror_report(QERR_UNSUPPORTED); + return -1; } #endif @@ -2737,12 +2756,15 @@ 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); + int flags = MCE_INJECT_UNCOND_AO; + if (qdict_get_try_bool(qdict, "broadcast", 0)) { + flags |= MCE_INJECT_BROADCAST; + } 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, - broadcast); + if (cenv->cpu_index == cpu_index) { + cpu_x86_inject_mce(mon, cenv, bank, status, mcg_status, addr, misc, + flags); break; } } @@ -2811,7 +2833,7 @@ int saved_vm_running = vm_running; const char *name = qdict_get_str(qdict, "name"); - vm_stop(0); + vm_stop(VMSTOP_LOADVM); if (load_vmstate(name) == 0 && saved_vm_running) { vm_start(); @@ -3485,7 +3507,7 @@ { "asr", offsetof(CPUState, asr) }, #endif /* Segment registers */ - { "sdr1", offsetof(CPUState, sdr1) }, + { "sdr1", offsetof(CPUState, spr[SPR_SDR1]) }, { "sr0", offsetof(CPUState, sr[0]) }, { "sr1", offsetof(CPUState, sr[1]) }, { "sr2", offsetof(CPUState, sr[2]) }, @@ -3502,7 +3524,76 @@ { "sr13", offsetof(CPUState, sr[13]) }, { "sr14", offsetof(CPUState, sr[14]) }, { "sr15", offsetof(CPUState, sr[15]) }, - /* Too lazy to put BATs and SPRs ... */ + /* Too lazy to put BATs... */ + { "pvr", offsetof(CPUState, spr[SPR_PVR]) }, + + { "srr0", offsetof(CPUState, spr[SPR_SRR0]) }, + { "srr1", offsetof(CPUState, spr[SPR_SRR1]) }, + { "sprg0", offsetof(CPUState, spr[SPR_SPRG0]) }, + { "sprg1", offsetof(CPUState, spr[SPR_SPRG1]) }, + { "sprg2", offsetof(CPUState, spr[SPR_SPRG2]) }, + { "sprg3", offsetof(CPUState, spr[SPR_SPRG3]) }, + { "sprg4", offsetof(CPUState, spr[SPR_SPRG4]) }, + { "sprg5", offsetof(CPUState, spr[SPR_SPRG5]) }, + { "sprg6", offsetof(CPUState, spr[SPR_SPRG6]) }, + { "sprg7", offsetof(CPUState, spr[SPR_SPRG7]) }, + { "pid", offsetof(CPUState, spr[SPR_BOOKE_PID]) }, + { "csrr0", offsetof(CPUState, spr[SPR_BOOKE_CSRR0]) }, + { "csrr1", offsetof(CPUState, spr[SPR_BOOKE_CSRR1]) }, + { "esr", offsetof(CPUState, spr[SPR_BOOKE_ESR]) }, + { "dear", offsetof(CPUState, spr[SPR_BOOKE_DEAR]) }, + { "mcsr", offsetof(CPUState, spr[SPR_BOOKE_MCSR]) }, + { "tsr", offsetof(CPUState, spr[SPR_BOOKE_TSR]) }, + { "tcr", offsetof(CPUState, spr[SPR_BOOKE_TCR]) }, + { "vrsave", offsetof(CPUState, spr[SPR_VRSAVE]) }, + { "pir", offsetof(CPUState, spr[SPR_BOOKE_PIR]) }, + { "mcsrr0", offsetof(CPUState, spr[SPR_BOOKE_MCSRR0]) }, + { "mcsrr1", offsetof(CPUState, spr[SPR_BOOKE_MCSRR1]) }, + { "decar", offsetof(CPUState, spr[SPR_BOOKE_DECAR]) }, + { "ivpr", offsetof(CPUState, spr[SPR_BOOKE_IVPR]) }, + { "epcr", offsetof(CPUState, spr[SPR_BOOKE_EPCR]) }, + { "sprg8", offsetof(CPUState, spr[SPR_BOOKE_SPRG8]) }, + { "ivor0", offsetof(CPUState, spr[SPR_BOOKE_IVOR0]) }, + { "ivor1", offsetof(CPUState, spr[SPR_BOOKE_IVOR1]) }, + { "ivor2", offsetof(CPUState, spr[SPR_BOOKE_IVOR2]) }, + { "ivor3", offsetof(CPUState, spr[SPR_BOOKE_IVOR3]) }, + { "ivor4", offsetof(CPUState, spr[SPR_BOOKE_IVOR4]) }, + { "ivor5", offsetof(CPUState, spr[SPR_BOOKE_IVOR5]) }, + { "ivor6", offsetof(CPUState, spr[SPR_BOOKE_IVOR6]) }, + { "ivor7", offsetof(CPUState, spr[SPR_BOOKE_IVOR7]) }, + { "ivor8", offsetof(CPUState, spr[SPR_BOOKE_IVOR8]) }, + { "ivor9", offsetof(CPUState, spr[SPR_BOOKE_IVOR9]) }, + { "ivor10", offsetof(CPUState, spr[SPR_BOOKE_IVOR10]) }, + { "ivor11", offsetof(CPUState, spr[SPR_BOOKE_IVOR11]) }, + { "ivor12", offsetof(CPUState, spr[SPR_BOOKE_IVOR12]) }, + { "ivor13", offsetof(CPUState, spr[SPR_BOOKE_IVOR13]) }, + { "ivor14", offsetof(CPUState, spr[SPR_BOOKE_IVOR14]) }, + { "ivor15", offsetof(CPUState, spr[SPR_BOOKE_IVOR15]) }, + { "ivor32", offsetof(CPUState, spr[SPR_BOOKE_IVOR32]) }, + { "ivor33", offsetof(CPUState, spr[SPR_BOOKE_IVOR33]) }, + { "ivor34", offsetof(CPUState, spr[SPR_BOOKE_IVOR34]) }, + { "ivor35", offsetof(CPUState, spr[SPR_BOOKE_IVOR35]) }, + { "ivor36", offsetof(CPUState, spr[SPR_BOOKE_IVOR36]) }, + { "ivor37", offsetof(CPUState, spr[SPR_BOOKE_IVOR37]) }, + { "mas0", offsetof(CPUState, spr[SPR_BOOKE_MAS0]) }, + { "mas1", offsetof(CPUState, spr[SPR_BOOKE_MAS1]) }, + { "mas2", offsetof(CPUState, spr[SPR_BOOKE_MAS2]) }, + { "mas3", offsetof(CPUState, spr[SPR_BOOKE_MAS3]) }, + { "mas4", offsetof(CPUState, spr[SPR_BOOKE_MAS4]) }, + { "mas6", offsetof(CPUState, spr[SPR_BOOKE_MAS6]) }, + { "mas7", offsetof(CPUState, spr[SPR_BOOKE_MAS7]) }, + { "mmucfg", offsetof(CPUState, spr[SPR_MMUCFG]) }, + { "tlb0cfg", offsetof(CPUState, spr[SPR_BOOKE_TLB0CFG]) }, + { "tlb1cfg", offsetof(CPUState, spr[SPR_BOOKE_TLB1CFG]) }, + { "epr", offsetof(CPUState, spr[SPR_BOOKE_EPR]) }, + { "eplc", offsetof(CPUState, spr[SPR_BOOKE_EPLC]) }, + { "epsc", offsetof(CPUState, spr[SPR_BOOKE_EPSC]) }, + { "svr", offsetof(CPUState, spr[SPR_E500_SVR]) }, + { "mcar", offsetof(CPUState, spr[SPR_Exxx_MCAR]) }, + { "pid1", offsetof(CPUState, spr[SPR_BOOKE_PID1]) }, + { "pid2", offsetof(CPUState, spr[SPR_BOOKE_PID2]) }, + { "hid0", offsetof(CPUState, spr[SPR_HID0]) }, + #elif defined(TARGET_SPARC) { "g0", offsetof(CPUState, gregs[0]) }, { "g1", offsetof(CPUState, gregs[1]) }, @@ -5185,7 +5276,7 @@ Monitor *mon; if (is_first_init) { - key_timer = qemu_new_timer(vm_clock, release_keys, NULL); + key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL); is_first_init = 0; } diff -Nru qemu-kvm-0.14.1+noroms/nbd.c qemu-kvm-0.15.0+noroms/nbd.c --- qemu-kvm-0.14.1+noroms/nbd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/nbd.c 2011-08-09 07:40:29.000000000 -0500 @@ -49,7 +49,7 @@ /* This is all part of the "official" NBD API */ -#define NBD_REPLY_SIZE (4 + 4 + 8) +#define NBD_REPLY_SIZE (4 + 4 + 8) #define NBD_REQUEST_MAGIC 0x25609513 #define NBD_REPLY_MAGIC 0x67446698 @@ -59,11 +59,11 @@ #define NBD_DO_IT _IO(0xab, 3) #define NBD_CLEAR_SOCK _IO(0xab, 4) #define NBD_CLEAR_QUE _IO(0xab, 5) -#define NBD_PRINT_DEBUG _IO(0xab, 6) -#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) +#define NBD_PRINT_DEBUG _IO(0xab, 6) +#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) #define NBD_DISCONNECT _IO(0xab, 8) -#define NBD_OPT_EXPORT_NAME (1 << 0) +#define NBD_OPT_EXPORT_NAME (1 << 0) /* That's all folks */ @@ -78,7 +78,7 @@ ssize_t len; if (do_read) { - len = recv(fd, buffer + offset, size - offset, 0); + len = qemu_recv(fd, buffer + offset, size - offset, 0); } else { len = send(fd, buffer + offset, size - offset, 0); } @@ -107,155 +107,55 @@ return offset; } -int tcp_socket_outgoing(const char *address, uint16_t port) +static void combine_addr(char *buf, size_t len, const char* address, + uint16_t port) { - int s; - struct in_addr in; - struct sockaddr_in addr; - - s = socket(PF_INET, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - if (inet_aton(address, &in) == 0) { - struct hostent *ent; - - ent = gethostbyname(address); - if (ent == NULL) { - goto error; - } - - memcpy(&in, ent->h_addr, sizeof(in)); + /* If the address-part contains a colon, it's an IPv6 IP so needs [] */ + if (strstr(address, ":")) { + snprintf(buf, len, "[%s]:%u", address, port); + } else { + snprintf(buf, len, "%s:%u", address, port); } - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - memcpy(&addr.sin_addr.s_addr, &in, sizeof(in)); - - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; - } - - return s; -error: - closesocket(s); - return -1; } -int tcp_socket_incoming(const char *address, uint16_t port) +int tcp_socket_outgoing(const char *address, uint16_t port) { - int s; - struct in_addr in; - struct sockaddr_in addr; - int opt; - - s = socket(PF_INET, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - if (inet_aton(address, &in) == 0) { - struct hostent *ent; - - ent = gethostbyname(address); - if (ent == NULL) { - goto error; - } - - memcpy(&in, ent->h_addr, sizeof(in)); - } - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - memcpy(&addr.sin_addr.s_addr, &in, sizeof(in)); - - opt = 1; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - (const void *) &opt, sizeof(opt)) == -1) { - goto error; - } - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; - } - - if (listen(s, 128) == -1) { - goto error; - } - - return s; -error: - closesocket(s); - return -1; + char address_and_port[128]; + combine_addr(address_and_port, 128, address, port); + return tcp_socket_outgoing_spec(address_and_port); } -#ifndef _WIN32 -int unix_socket_incoming(const char *path) +int tcp_socket_outgoing_spec(const char *address_and_port) { - int s; - struct sockaddr_un addr; - - s = socket(PF_UNIX, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; - } - - if (listen(s, 128) == -1) { - goto error; - } - - return s; -error: - closesocket(s); - return -1; + return inet_connect(address_and_port, SOCK_STREAM); } -int unix_socket_outgoing(const char *path) +int tcp_socket_incoming(const char *address, uint16_t port) { - int s; - struct sockaddr_un addr; - - s = socket(PF_UNIX, SOCK_STREAM, 0); - if (s == -1) { - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - pstrcpy(addr.sun_path, sizeof(addr.sun_path), path); - - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto error; - } + char address_and_port[128]; + combine_addr(address_and_port, 128, address, port); + return tcp_socket_incoming_spec(address_and_port); +} - return s; -error: - closesocket(s); - return -1; +int tcp_socket_incoming_spec(const char *address_and_port) +{ + char *ostr = NULL; + int olen = 0; + return inet_listen(address_and_port, ostr, olen, SOCK_STREAM, 0); } -#else + int unix_socket_incoming(const char *path) { - errno = ENOTSUP; - return -1; + char *ostr = NULL; + int olen = 0; + + return unix_listen(path, ostr, olen); } int unix_socket_outgoing(const char *path) { - errno = ENOTSUP; - return -1; + return unix_connect(path); } -#endif - /* Basic flow @@ -273,241 +173,241 @@ int nbd_negotiate(int csock, off_t size) { - char buf[8 + 8 + 8 + 128]; + char buf[8 + 8 + 8 + 128]; - /* Negotiate - [ 0 .. 7] passwd ("NBDMAGIC") - [ 8 .. 15] magic (0x00420281861253) - [16 .. 23] size - [24 .. 151] reserved (0) - */ - - TRACE("Beginning negotiation."); - memcpy(buf, "NBDMAGIC", 8); - cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL); - cpu_to_be64w((uint64_t*)(buf + 16), size); - memset(buf + 24, 0, 128); - - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("write failed"); - errno = EINVAL; - return -1; - } + /* Negotiate + [ 0 .. 7] passwd ("NBDMAGIC") + [ 8 .. 15] magic (0x00420281861253) + [16 .. 23] size + [24 .. 151] reserved (0) + */ + + TRACE("Beginning negotiation."); + memcpy(buf, "NBDMAGIC", 8); + cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL); + cpu_to_be64w((uint64_t*)(buf + 16), size); + memset(buf + 24, 0, 128); + + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("write failed"); + errno = EINVAL; + return -1; + } - TRACE("Negotation succeeded."); + TRACE("Negotation succeeded."); - return 0; + return 0; } int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, off_t *size, size_t *blocksize) { - char buf[256]; - uint64_t magic, s; - uint16_t tmp; - - TRACE("Receiving negotation."); - - if (read_sync(csock, buf, 8) != 8) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - - 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] : '.', - qemu_isprint(buf[1]) ? buf[1] : '.', - qemu_isprint(buf[2]) ? buf[2] : '.', - qemu_isprint(buf[3]) ? buf[3] : '.', - qemu_isprint(buf[4]) ? buf[4] : '.', - qemu_isprint(buf[5]) ? buf[5] : '.', - qemu_isprint(buf[6]) ? buf[6] : '.', - qemu_isprint(buf[7]) ? buf[7] : '.'); - - if (memcmp(buf, "NBDMAGIC", 8) != 0) { - LOG("Invalid magic received"); - errno = EINVAL; - return -1; - } - - 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 (!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; - } + char buf[256]; + uint64_t magic, s; + uint16_t tmp; + + TRACE("Receiving negotation."); + + if (read_sync(csock, buf, 8) != 8) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + + 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] : '.', + qemu_isprint(buf[1]) ? buf[1] : '.', + qemu_isprint(buf[2]) ? buf[2] : '.', + qemu_isprint(buf[3]) ? buf[3] : '.', + qemu_isprint(buf[4]) ? buf[4] : '.', + qemu_isprint(buf[5]) ? buf[5] : '.', + qemu_isprint(buf[6]) ? buf[6] : '.', + qemu_isprint(buf[7]) ? buf[7] : '.'); + + if (memcmp(buf, "NBDMAGIC", 8) != 0) { + LOG("Invalid magic received"); + errno = EINVAL; + return -1; + } + + 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 (!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; + } return 0; } #ifndef _WIN32 int nbd_init(int fd, int csock, off_t size, size_t blocksize) { - TRACE("Setting block size to %lu", (unsigned long)blocksize); + TRACE("Setting block size to %lu", (unsigned long)blocksize); - if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { - int serrno = errno; - LOG("Failed setting NBD block size"); - errno = serrno; - return -1; - } + if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) { + int serrno = errno; + LOG("Failed setting NBD block size"); + errno = serrno; + return -1; + } TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize)); - if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) { - int serrno = errno; - LOG("Failed setting size (in blocks)"); - errno = serrno; - return -1; - } - - TRACE("Clearing NBD socket"); - - if (ioctl(fd, NBD_CLEAR_SOCK) == -1) { - int serrno = errno; - LOG("Failed clearing NBD socket"); - errno = serrno; - return -1; - } - - TRACE("Setting NBD socket"); - - if (ioctl(fd, NBD_SET_SOCK, csock) == -1) { - int serrno = errno; - LOG("Failed to set NBD socket"); - errno = serrno; - return -1; - } + if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) { + int serrno = errno; + LOG("Failed setting size (in blocks)"); + errno = serrno; + return -1; + } + + TRACE("Clearing NBD socket"); + + if (ioctl(fd, NBD_CLEAR_SOCK) == -1) { + int serrno = errno; + LOG("Failed clearing NBD socket"); + errno = serrno; + return -1; + } + + TRACE("Setting NBD socket"); - TRACE("Negotiation ended"); + if (ioctl(fd, NBD_SET_SOCK, csock) == -1) { + int serrno = errno; + LOG("Failed to set NBD socket"); + errno = serrno; + return -1; + } - return 0; + TRACE("Negotiation ended"); + + return 0; } int nbd_disconnect(int fd) { - ioctl(fd, NBD_CLEAR_QUE); - ioctl(fd, NBD_DISCONNECT); - ioctl(fd, NBD_CLEAR_SOCK); - return 0; + ioctl(fd, NBD_CLEAR_QUE); + ioctl(fd, NBD_DISCONNECT); + ioctl(fd, NBD_CLEAR_SOCK); + return 0; } int nbd_client(int fd) { - int ret; - int serrno; + int ret; + int serrno; - TRACE("Doing NBD loop"); + TRACE("Doing NBD loop"); - ret = ioctl(fd, NBD_DO_IT); - serrno = errno; + ret = ioctl(fd, NBD_DO_IT); + serrno = errno; - TRACE("NBD loop returned %d: %s", ret, strerror(serrno)); + TRACE("NBD loop returned %d: %s", ret, strerror(serrno)); - TRACE("Clearing NBD queue"); - ioctl(fd, NBD_CLEAR_QUE); + TRACE("Clearing NBD queue"); + ioctl(fd, NBD_CLEAR_QUE); - TRACE("Clearing NBD socket"); - ioctl(fd, NBD_CLEAR_SOCK); + TRACE("Clearing NBD socket"); + ioctl(fd, NBD_CLEAR_SOCK); - errno = serrno; - return ret; + errno = serrno; + return ret; } #else int nbd_init(int fd, int csock, off_t size, size_t blocksize) @@ -531,235 +431,236 @@ int nbd_send_request(int csock, struct nbd_request *request) { - uint8_t buf[4 + 4 + 8 + 8 + 4]; + uint8_t buf[4 + 4 + 8 + 8 + 4]; - cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); - cpu_to_be32w((uint32_t*)(buf + 4), request->type); - cpu_to_be64w((uint64_t*)(buf + 8), request->handle); - cpu_to_be64w((uint64_t*)(buf + 16), request->from); - cpu_to_be32w((uint32_t*)(buf + 24), request->len); - - TRACE("Sending request to client"); - - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("writing to socket failed"); - errno = EINVAL; - return -1; - } - return 0; + cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); + cpu_to_be32w((uint32_t*)(buf + 4), request->type); + cpu_to_be64w((uint64_t*)(buf + 8), request->handle); + cpu_to_be64w((uint64_t*)(buf + 16), request->from); + cpu_to_be32w((uint32_t*)(buf + 24), request->len); + + TRACE("Sending request to client: " + "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", + request->from, request->len, request->handle, request->type); + + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("writing to socket failed"); + errno = EINVAL; + return -1; + } + return 0; } - static int nbd_receive_request(int csock, struct nbd_request *request) { - uint8_t buf[4 + 4 + 8 + 8 + 4]; - uint32_t magic; + uint8_t buf[4 + 4 + 8 + 8 + 4]; + uint32_t magic; - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - - /* Request - [ 0 .. 3] magic (NBD_REQUEST_MAGIC) - [ 4 .. 7] type (0 == READ, 1 == WRITE) - [ 8 .. 15] handle - [16 .. 23] from - [24 .. 27] len - */ - - magic = be32_to_cpup((uint32_t*)buf); - request->type = be32_to_cpup((uint32_t*)(buf + 4)); - request->handle = be64_to_cpup((uint64_t*)(buf + 8)); - request->from = be64_to_cpup((uint64_t*)(buf + 16)); - request->len = be32_to_cpup((uint32_t*)(buf + 24)); - - TRACE("Got request: " - "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", - magic, request->type, request->from, request->len); - - if (magic != NBD_REQUEST_MAGIC) { - LOG("invalid magic (got 0x%x)", magic); - errno = EINVAL; - return -1; - } - return 0; + if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + + /* Request + [ 0 .. 3] magic (NBD_REQUEST_MAGIC) + [ 4 .. 7] type (0 == READ, 1 == WRITE) + [ 8 .. 15] handle + [16 .. 23] from + [24 .. 27] len + */ + + magic = be32_to_cpup((uint32_t*)buf); + request->type = be32_to_cpup((uint32_t*)(buf + 4)); + request->handle = be64_to_cpup((uint64_t*)(buf + 8)); + request->from = be64_to_cpup((uint64_t*)(buf + 16)); + request->len = be32_to_cpup((uint32_t*)(buf + 24)); + + TRACE("Got request: " + "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", + magic, request->type, request->from, request->len); + + if (magic != NBD_REQUEST_MAGIC) { + LOG("invalid magic (got 0x%x)", magic); + errno = EINVAL; + return -1; + } + return 0; } int nbd_receive_reply(int csock, struct nbd_reply *reply) { - uint8_t buf[NBD_REPLY_SIZE]; - uint32_t magic; + uint8_t buf[NBD_REPLY_SIZE]; + uint32_t magic; - memset(buf, 0xAA, sizeof(buf)); + memset(buf, 0xAA, sizeof(buf)); - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("read failed"); - errno = EINVAL; - return -1; - } - - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - - magic = be32_to_cpup((uint32_t*)buf); - reply->error = be32_to_cpup((uint32_t*)(buf + 4)); - reply->handle = be64_to_cpup((uint64_t*)(buf + 8)); - - TRACE("Got reply: " - "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", - magic, reply->error, reply->handle); - - if (magic != NBD_REPLY_MAGIC) { - LOG("invalid magic (got 0x%x)", magic); - errno = EINVAL; - return -1; - } - return 0; + if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + + magic = be32_to_cpup((uint32_t*)buf); + reply->error = be32_to_cpup((uint32_t*)(buf + 4)); + reply->handle = be64_to_cpup((uint64_t*)(buf + 8)); + + TRACE("Got reply: " + "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", + magic, reply->error, reply->handle); + + if (magic != NBD_REPLY_MAGIC) { + LOG("invalid magic (got 0x%x)", magic); + errno = EINVAL; + return -1; + } + return 0; } static int nbd_send_reply(int csock, struct nbd_reply *reply) { - uint8_t buf[4 + 4 + 8]; + uint8_t buf[4 + 4 + 8]; - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); - cpu_to_be32w((uint32_t*)(buf + 4), reply->error); - cpu_to_be64w((uint64_t*)(buf + 8), reply->handle); - - TRACE("Sending response to client"); - - if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { - LOG("writing to socket failed"); - errno = EINVAL; - return -1; - } - return 0; + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); + cpu_to_be32w((uint32_t*)(buf + 4), reply->error); + cpu_to_be64w((uint64_t*)(buf + 8), reply->handle); + + TRACE("Sending response to client"); + + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + LOG("writing to socket failed"); + errno = EINVAL; + return -1; + } + return 0; } 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) { - struct nbd_request request; - struct nbd_reply reply; + struct nbd_request request; + struct nbd_reply reply; - TRACE("Reading request."); + TRACE("Reading request."); - if (nbd_receive_request(csock, &request) == -1) - return -1; + if (nbd_receive_request(csock, &request) == -1) + return -1; + + if (request.len + NBD_REPLY_SIZE > data_size) { + LOG("len (%u) is larger than max len (%u)", + request.len + NBD_REPLY_SIZE, data_size); + errno = EINVAL; + return -1; + } + + if ((request.from + request.len) < request.from) { + LOG("integer overflow detected! " + "you're probably being attacked"); + errno = EINVAL; + return -1; + } - if (request.len + NBD_REPLY_SIZE > data_size) { - LOG("len (%u) is larger than max len (%u)", - request.len + NBD_REPLY_SIZE, data_size); - errno = EINVAL; - return -1; - } - - if ((request.from + request.len) < request.from) { - LOG("integer overflow detected! " - "you're probably being attacked"); - errno = EINVAL; - return -1; - } - - if ((request.from + request.len) > size) { - LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 - ", Offset: %" PRIu64 "\n", + if ((request.from + request.len) > size) { + LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 + ", Offset: %" PRIu64 "\n", request.from, request.len, (uint64_t)size, dev_offset); - LOG("requested operation past EOF--bad client?"); - errno = EINVAL; - return -1; - } - - TRACE("Decoding type"); - - reply.handle = request.handle; - reply.error = 0; - - switch (request.type) { - case NBD_CMD_READ: - TRACE("Request type is READ"); - - if (bdrv_read(bs, (request.from + dev_offset) / 512, - data + NBD_REPLY_SIZE, - request.len / 512) == -1) { - LOG("reading from file failed"); - errno = EINVAL; - return -1; - } - *offset += request.len; - - TRACE("Read %u byte(s)", request.len); - - /* 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 + NBD_REPLY_SIZE) != - request.len + NBD_REPLY_SIZE) { - LOG("writing to socket failed"); - errno = EINVAL; - return -1; - } - break; - case NBD_CMD_WRITE: - TRACE("Request type is WRITE"); - - TRACE("Reading %u byte(s)", request.len); - - if (read_sync(csock, data, request.len) != request.len) { - LOG("reading from socket failed"); - errno = EINVAL; - return -1; - } - - if (readonly) { - TRACE("Server is read-only, return error"); - reply.error = 1; - } else { - TRACE("Writing to device"); - - if (bdrv_write(bs, (request.from + dev_offset) / 512, - data, request.len / 512) == -1) { - LOG("writing to file failed"); - errno = EINVAL; - return -1; - } - - *offset += request.len; - } - - if (nbd_send_reply(csock, &reply) == -1) - return -1; - break; - case NBD_CMD_DISC: - TRACE("Request type is DISCONNECT"); - errno = 0; - return 1; - default: - LOG("invalid request type (%u) received", request.type); - errno = EINVAL; - return -1; - } + LOG("requested operation past EOF--bad client?"); + errno = EINVAL; + return -1; + } + + TRACE("Decoding type"); + + reply.handle = request.handle; + reply.error = 0; + + switch (request.type) { + case NBD_CMD_READ: + TRACE("Request type is READ"); + + if (bdrv_read(bs, (request.from + dev_offset) / 512, + data + NBD_REPLY_SIZE, + request.len / 512) == -1) { + LOG("reading from file failed"); + errno = EINVAL; + return -1; + } + *offset += request.len; + + TRACE("Read %u byte(s)", request.len); + + /* 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 + NBD_REPLY_SIZE) != + request.len + NBD_REPLY_SIZE) { + LOG("writing to socket failed"); + errno = EINVAL; + return -1; + } + break; + case NBD_CMD_WRITE: + TRACE("Request type is WRITE"); + + TRACE("Reading %u byte(s)", request.len); + + if (read_sync(csock, data, request.len) != request.len) { + LOG("reading from socket failed"); + errno = EINVAL; + return -1; + } + + if (readonly) { + TRACE("Server is read-only, return error"); + reply.error = 1; + } else { + TRACE("Writing to device"); + + if (bdrv_write(bs, (request.from + dev_offset) / 512, + data, request.len / 512) == -1) { + LOG("writing to file failed"); + errno = EINVAL; + return -1; + } + + *offset += request.len; + } + + if (nbd_send_reply(csock, &reply) == -1) + return -1; + break; + case NBD_CMD_DISC: + TRACE("Request type is DISCONNECT"); + errno = 0; + return 1; + default: + LOG("invalid request type (%u) received", request.type); + errno = EINVAL; + return -1; + } - TRACE("Request/Reply complete"); + TRACE("Request/Reply complete"); - return 0; + return 0; } diff -Nru qemu-kvm-0.14.1+noroms/nbd.h qemu-kvm-0.15.0+noroms/nbd.h --- qemu-kvm-0.14.1+noroms/nbd.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/nbd.h 2011-08-09 07:40:29.000000000 -0500 @@ -22,19 +22,22 @@ #include #include + #include "block_int.h" struct nbd_request { + uint32_t magic; uint32_t type; uint64_t handle; uint64_t from; uint32_t len; -}; +} __attribute__ ((__packed__)); struct nbd_reply { + uint32_t magic; uint32_t error; uint64_t handle; -}; +} __attribute__ ((__packed__)); enum { NBD_CMD_READ = 0, @@ -47,6 +50,8 @@ 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); +int tcp_socket_outgoing_spec(const char *address_and_port); +int tcp_socket_incoming_spec(const char *address_and_port); int unix_socket_outgoing(const char *path); int unix_socket_incoming(const char *path); diff -Nru qemu-kvm-0.14.1+noroms/net/dump.c qemu-kvm-0.15.0+noroms/net/dump.c --- qemu-kvm-0.14.1+noroms/net/dump.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net/dump.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,9 +24,9 @@ #include "dump.h" #include "qemu-common.h" -#include "sysemu.h" #include "qemu-error.h" #include "qemu-log.h" +#include "qemu-timer.h" typedef struct DumpState { VLANClientState nc; @@ -67,7 +67,7 @@ return size; } - ts = muldiv64(qemu_get_clock(vm_clock), 1000000, get_ticks_per_sec()); + ts = muldiv64(qemu_get_clock_ns(vm_clock), 1000000, get_ticks_per_sec()); caplen = size > s->pcap_caplen ? s->pcap_caplen : size; hdr.ts.tv_sec = ts / 1000000; diff -Nru qemu-kvm-0.14.1+noroms/net/slirp.c qemu-kvm-0.15.0+noroms/net/slirp.c --- qemu-kvm-0.14.1+noroms/net/slirp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net/slirp.c 2011-08-09 07:40:29.000000000 -0500 @@ -30,7 +30,6 @@ #endif #include "net.h" #include "monitor.h" -#include "sysemu.h" #include "qemu_socket.h" #include "slirp/libslirp.h" @@ -129,7 +128,7 @@ } static NetClientInfo net_slirp_info = { - .type = NET_CLIENT_TYPE_SLIRP, + .type = NET_CLIENT_TYPE_USER, .size = sizeof(SlirpState), .receive = net_slirp_receive, .cleanup = net_slirp_cleanup, @@ -241,7 +240,8 @@ nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name); snprintf(nc->info_str, sizeof(nc->info_str), - "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n'); + "net=%s,restrict=%s", inet_ntoa(net), + restricted ? "on" : "off"); s = DO_UPCAST(SlirpState, nc, nc); @@ -615,7 +615,7 @@ } fwd = qemu_malloc(sizeof(struct GuestFwd)); - snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port); + snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port); fwd->hd = qemu_chr_open(buf, p, NULL); if (!fwd->hd) { error_report("could not open guest forwarding device '%s'", buf); @@ -690,6 +690,7 @@ const char *bootfile; const char *smb_export; const char *vsmbsrv; + const char *restrict_opt; char *vnet = NULL; int restricted = 0; int ret; @@ -703,6 +704,18 @@ smb_export = qemu_opt_get(opts, "smb"); vsmbsrv = qemu_opt_get(opts, "smbserver"); + restrict_opt = qemu_opt_get(opts, "restrict"); + if (restrict_opt) { + if (!strcmp(restrict_opt, "on") || + !strcmp(restrict_opt, "yes") || !strcmp(restrict_opt, "y")) { + restricted = 1; + } else if (strcmp(restrict_opt, "off") && + strcmp(restrict_opt, "no") && strcmp(restrict_opt, "n")) { + error_report("invalid option: 'restrict=%s'", restrict_opt); + return -1; + } + } + if (qemu_opt_get(opts, "ip")) { const char *ip = qemu_opt_get(opts, "ip"); int l = strlen(ip) + strlen("/24") + 1; @@ -721,11 +734,6 @@ vnet = qemu_strdup(qemu_opt_get(opts, "net")); } - if (qemu_opt_get(opts, "restrict") && - qemu_opt_get(opts, "restrict")[0] == 'y') { - restricted = 1; - } - qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0); ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost, diff -Nru qemu-kvm-0.14.1+noroms/net/socket.c qemu-kvm-0.15.0+noroms/net/socket.c --- qemu-kvm-0.14.1+noroms/net/socket.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net/socket.c 2011-08-09 07:40:29.000000000 -0500 @@ -76,7 +76,7 @@ uint8_t buf1[4096]; const uint8_t *buf; - size = recv(s->fd, (void *)buf1, sizeof(buf1), 0); + size = qemu_recv(s->fd, buf1, sizeof(buf1), 0); if (size < 0) { err = socket_error(); if (err != EWOULDBLOCK) @@ -138,7 +138,7 @@ NetSocketState *s = opaque; int size; - size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); + size = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0); if (size < 0) return; if (size == 0) { @@ -457,7 +457,7 @@ } else if (err == EINPROGRESS) { break; #ifdef _WIN32 - } else if (err == WSAEALREADY) { + } else if (err == WSAEALREADY || err == WSAEINVAL) { break; #endif } else { @@ -530,7 +530,7 @@ qemu_opt_get(opts, "connect") || qemu_opt_get(opts, "mcast") || qemu_opt_get(opts, "localaddr")) { - error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n"); + error_report("listen=, connect=, mcast= and localaddr= is invalid with fd="); return -1; } @@ -550,7 +550,7 @@ qemu_opt_get(opts, "connect") || qemu_opt_get(opts, "mcast") || qemu_opt_get(opts, "localaddr")) { - error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n"); + error_report("fd=, connect=, mcast= and localaddr= is invalid with listen="); return -1; } @@ -566,7 +566,7 @@ qemu_opt_get(opts, "listen") || qemu_opt_get(opts, "mcast") || qemu_opt_get(opts, "localaddr")) { - error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n"); + error_report("fd=, listen=, mcast= and localaddr= is invalid with connect="); return -1; } diff -Nru qemu-kvm-0.14.1+noroms/net/tap.c qemu-kvm-0.15.0+noroms/net/tap.c --- qemu-kvm-0.14.1+noroms/net/tap.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net/tap.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ #include "config-host.h" -#include #include #include #include diff -Nru qemu-kvm-0.14.1+noroms/net/tap-win32.c qemu-kvm-0.15.0+noroms/net/tap-win32.c --- qemu-kvm-0.14.1+noroms/net/tap-win32.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net/tap-win32.c 2011-08-09 07:40:29.000000000 -0500 @@ -480,7 +480,7 @@ } } - return 0; + return write_size; } static DWORD WINAPI tap_win32_thread_entry(LPVOID param) diff -Nru qemu-kvm-0.14.1+noroms/net/vde.c qemu-kvm-0.15.0+noroms/net/vde.c --- qemu-kvm-0.14.1+noroms/net/vde.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net/vde.c 2011-08-09 07:40:29.000000000 -0500 @@ -31,7 +31,6 @@ #include "qemu-char.h" #include "qemu-common.h" #include "qemu-option.h" -#include "sysemu.h" typedef struct VDEState { VLANClientState nc; diff -Nru qemu-kvm-0.14.1+noroms/net.c qemu-kvm-0.15.0+noroms/net.c --- qemu-kvm-0.14.1+noroms/net.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net.c 2011-08-09 07:40:29.000000000 -0500 @@ -32,10 +32,10 @@ #include "net/vde.h" #include "net/util.h" #include "monitor.h" -#include "sysemu.h" #include "qemu-common.h" #include "qemu_socket.h" #include "hw/qdev.h" +#include "iov.h" static QTAILQ_HEAD(, VLANState) vlans; static QTAILQ_HEAD(, VLANClientState) non_vlan_clients; @@ -93,47 +93,6 @@ return 0; } -int parse_host_src_port(struct sockaddr_in *haddr, - struct sockaddr_in *saddr, - const char *input_str) -{ - char *str = qemu_strdup(input_str); - char *host_str = str; - char *src_str; - const char *src_str2; - char *ptr; - - /* - * Chop off any extra arguments at the end of the string which - * would start with a comma, then fill in the src port information - * if it was provided else use the "any address" and "any port". - */ - if ((ptr = strchr(str,','))) - *ptr = '\0'; - - if ((src_str = strchr(input_str,'@'))) { - *src_str = '\0'; - src_str++; - } - - if (parse_host_port(haddr, host_str) < 0) - goto fail; - - src_str2 = src_str; - if (!src_str || *src_str == '\0') - src_str2 = ":0"; - - if (parse_host_port(saddr, src_str2) < 0) - goto fail; - - free(str); - return(0); - -fail: - free(str); - return -1; -} - int parse_host_port(struct sockaddr_in *saddr, const char *str) { char buf[512]; @@ -411,11 +370,11 @@ } /* no can_receive() handler, they can always receive */ - if (!vc->info->can_receive || vc->info->can_receive(vc)) { - return 1; + if (vc->info->can_receive && !vc->info->can_receive(vc)) { + return 0; } } - return 0; + return 1; } static ssize_t qemu_deliver_packet(VLANClientState *sender, @@ -572,30 +531,13 @@ int iovcnt) { uint8_t buffer[4096]; - size_t offset = 0; - int i; - - for (i = 0; i < iovcnt; i++) { - size_t len; + size_t offset; - len = MIN(sizeof(buffer) - offset, iov[i].iov_len); - memcpy(buffer + offset, iov[i].iov_base, len); - offset += len; - } + offset = iov_to_buf(iov, iovcnt, buffer, 0, sizeof(buffer)); return vc->info->receive(vc, buffer, offset); } -static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt) -{ - size_t offset = 0; - int i; - - for (i = 0; i < iovcnt; i++) - offset += iov[i].iov_len; - return offset; -} - static ssize_t qemu_deliver_packet_iov(VLANClientState *sender, unsigned flags, const struct iovec *iov, @@ -605,7 +547,7 @@ VLANClientState *vc = opaque; if (vc->link_down) { - return calc_iov_length(iov, iovcnt); + return iov_size(iov, iovcnt); } if (vc->info->receive_iov) { @@ -633,7 +575,7 @@ } if (vc->link_down) { - ret = calc_iov_length(iov, iovcnt); + ret = iov_size(iov, iovcnt); continue; } @@ -658,7 +600,7 @@ NetQueue *queue; if (sender->link_down || (!sender->peer && !sender->vlan)) { - return calc_iov_length(iov, iovcnt); + return iov_size(iov, iovcnt); } if (sender->peer) { @@ -768,7 +710,7 @@ return i; } - error_report("qemu: Unsupported NIC model: %s", nd->model); + error_report("Unsupported NIC model: %s", nd->model); return -1; } @@ -834,18 +776,12 @@ nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr")); } - nd->macaddr[0] = 0x52; - nd->macaddr[1] = 0x54; - nd->macaddr[2] = 0x00; - nd->macaddr[3] = 0x12; - nd->macaddr[4] = 0x34; - nd->macaddr[5] = 0x56 + idx; - if (qemu_opt_get(opts, "macaddr") && - net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) { + net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "macaddr")) < 0) { error_report("invalid syntax for ethernet address"); return -1; } + qemu_macaddr_default_if_unset(&nd->macaddr); nd->nvectors = qemu_opt_get_number(opts, "vectors", DEV_NVECTORS_UNSPECIFIED); @@ -888,14 +824,15 @@ const char *type; net_client_init_func init; QemuOptDesc desc[NET_MAX_DESC]; -} net_client_types[] = { - { +} net_client_types[NET_CLIENT_TYPE_MAX] = { + [NET_CLIENT_TYPE_NONE] = { .type = "none", .desc = { NET_COMMON_PARAMS_DESC, { /* end of list */ } }, - }, { + }, + [NET_CLIENT_TYPE_NIC] = { .type = "nic", .init = net_init_nic, .desc = { @@ -924,8 +861,9 @@ }, { /* end of list */ } }, + }, #ifdef CONFIG_SLIRP - }, { + [NET_CLIENT_TYPE_USER] = { .type = "user", .init = net_init_slirp, .desc = { @@ -985,8 +923,9 @@ }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_TAP] = { .type = "tap", .init = net_init_tap, .desc = { @@ -1033,7 +972,8 @@ #endif /* _WIN32 */ { /* end of list */ } }, - }, { + }, + [NET_CLIENT_TYPE_SOCKET] = { .type = "socket", .init = net_init_socket, .desc = { @@ -1061,8 +1001,9 @@ }, { /* end of list */ } }, + }, #ifdef CONFIG_VDE - }, { + [NET_CLIENT_TYPE_VDE] = { .type = "vde", .init = net_init_vde, .desc = { @@ -1086,8 +1027,9 @@ }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_DUMP] = { .type = "dump", .init = net_init_dump, .desc = { @@ -1104,7 +1046,6 @@ { /* end of list */ } }, }, - { /* end of list */ } }; int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) @@ -1152,8 +1093,9 @@ name = qemu_opt_get(opts, "name"); } - for (i = 0; net_client_types[i].type != NULL; i++) { - if (!strcmp(net_client_types[i].type, type)) { + for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) { + if (net_client_types[i].type != NULL && + !strcmp(net_client_types[i].type, type)) { VLANState *vlan = NULL; int ret; @@ -1279,25 +1221,38 @@ return 0; } +static void print_net_client(Monitor *mon, VLANClientState *vc) +{ + monitor_printf(mon, "%s: type=%s,%s\n", vc->name, + net_client_types[vc->info->type].type, vc->info_str); +} + void do_info_network(Monitor *mon) { VLANState *vlan; - VLANClientState *vc; + VLANClientState *vc, *peer; + net_client_type type; QTAILQ_FOREACH(vlan, &vlans, next) { monitor_printf(mon, "VLAN %d devices:\n", vlan->id); QTAILQ_FOREACH(vc, &vlan->clients, next) { - monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str); + monitor_printf(mon, " "); + print_net_client(mon, vc); } } monitor_printf(mon, "Devices not on any VLAN:\n"); QTAILQ_FOREACH(vc, &non_vlan_clients, next) { - monitor_printf(mon, " %s: %s", vc->name, vc->info_str); - if (vc->peer) { - monitor_printf(mon, " peer=%s", vc->peer->name); + peer = vc->peer; + type = vc->info->type; + if (!peer || type == NET_CLIENT_TYPE_NIC) { + monitor_printf(mon, " "); + print_net_client(mon, vc); + } /* else it's a netdev connected to a NIC, printed with the NIC */ + if (peer && type == NET_CLIENT_TYPE_NIC) { + monitor_printf(mon, " \\ "); + print_net_client(mon, peer); } - monitor_printf(mon, "\n"); } } @@ -1328,6 +1283,17 @@ if (vc->info->link_status_changed) { vc->info->link_status_changed(vc); } + + /* Notify peer. Don't update peer link status: this makes it possible to + * disconnect from host network without notifying the guest. + * FIXME: is disconnected link status change operation useful? + * + * Current behaviour is compatible with qemu vlans where there could be + * multiple clients that can still communicate with each other in + * disconnected mode. For now maintain this compatibility. */ + if (vc->peer && vc->peer->info->link_status_changed) { + vc->peer->info->link_status_changed(vc->peer); + } return 0; } @@ -1351,15 +1317,29 @@ { VLANState *vlan; VLANClientState *vc; - int has_nic = 0, has_host_dev = 0; + int i; + + /* Don't warn about the default network setup that you get if + * no command line -net or -netdev options are specified. There + * are two cases that we would otherwise complain about: + * (1) board doesn't support a NIC but the implicit "-net nic" + * requested one + * (2) CONFIG_SLIRP not set, in which case the implicit "-net nic" + * sets up a nic that isn't connected to anything. + */ + if (default_net) { + return; + } QTAILQ_FOREACH(vlan, &vlans, next) { + int has_nic = 0, has_host_dev = 0; + QTAILQ_FOREACH(vc, &vlan->clients, next) { switch (vc->info->type) { case NET_CLIENT_TYPE_NIC: has_nic = 1; break; - case NET_CLIENT_TYPE_SLIRP: + case NET_CLIENT_TYPE_USER: case NET_CLIENT_TYPE_TAP: case NET_CLIENT_TYPE_SOCKET: case NET_CLIENT_TYPE_VDE: @@ -1382,6 +1362,20 @@ vc->name); } } + + /* Check that all NICs requested via -net nic actually got created. + * NICs created via -device don't need to be checked here because + * they are always instantiated. + */ + for (i = 0; i < MAX_NICS; i++) { + NICInfo *nd = &nd_table[i]; + if (nd->used && !nd->instantiated) { + fprintf(stderr, "Warning: requested NIC (%s, model %s) " + "was not created (not supported by this machine?)\n", + nd->name ? nd->name : "anonymous", + nd->model ? nd->model : "unspecified"); + } + } } static int net_init_client(QemuOpts *opts, void *dummy) diff -Nru qemu-kvm-0.14.1+noroms/net-checksum.c qemu-kvm-0.15.0+noroms/net-checksum.c --- qemu-kvm-0.14.1+noroms/net-checksum.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net-checksum.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,86 +0,0 @@ -/* - * IP checksumming functions. - * (c) 2008 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; under version 2 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 "net.h" - -#define PROTO_TCP 6 -#define PROTO_UDP 17 - -uint32_t net_checksum_add(int len, uint8_t *buf) -{ - uint32_t sum = 0; - int i; - - for (i = 0; i < len; i++) { - if (i & 1) - sum += (uint32_t)buf[i]; - else - sum += (uint32_t)buf[i] << 8; - } - return sum; -} - -uint16_t net_checksum_finish(uint32_t sum) -{ - while (sum>>16) - sum = (sum & 0xFFFF)+(sum >> 16); - return ~sum; -} - -uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, - uint8_t *addrs, uint8_t *buf) -{ - uint32_t sum = 0; - - sum += net_checksum_add(length, buf); // payload - sum += net_checksum_add(8, addrs); // src + dst address - sum += proto + length; // protocol & length - return net_checksum_finish(sum); -} - -void net_checksum_calculate(uint8_t *data, int length) -{ - int hlen, plen, proto, csum_offset; - uint16_t csum; - - if ((data[14] & 0xf0) != 0x40) - return; /* not IPv4 */ - hlen = (data[14] & 0x0f) * 4; - plen = (data[16] << 8 | data[17]) - hlen; - proto = data[23]; - - switch (proto) { - case PROTO_TCP: - csum_offset = 16; - break; - case PROTO_UDP: - csum_offset = 6; - break; - default: - return; - } - - if (plen < csum_offset+2) - return; - - data[14+hlen+csum_offset] = 0; - data[14+hlen+csum_offset+1] = 0; - csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen); - data[14+hlen+csum_offset] = csum >> 8; - data[14+hlen+csum_offset+1] = csum & 0xff; -} diff -Nru qemu-kvm-0.14.1+noroms/net.h qemu-kvm-0.15.0+noroms/net.h --- qemu-kvm-0.14.1+noroms/net.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/net.h 2011-08-09 07:40:29.000000000 -0500 @@ -31,11 +31,13 @@ typedef enum { NET_CLIENT_TYPE_NONE, NET_CLIENT_TYPE_NIC, - NET_CLIENT_TYPE_SLIRP, + NET_CLIENT_TYPE_USER, NET_CLIENT_TYPE_TAP, NET_CLIENT_TYPE_SOCKET, NET_CLIENT_TYPE_VDE, - NET_CLIENT_TYPE_DUMP + NET_CLIENT_TYPE_DUMP, + + NET_CLIENT_TYPE_MAX } net_client_type; typedef void (NetPoll)(VLANClientState *, bool enable); @@ -127,13 +129,14 @@ #define MAX_NICS 8 struct NICInfo { - uint8_t macaddr[6]; + MACAddr macaddr; char *model; char *name; char *devaddr; VLANState *vlan; VLANClientState *netdev; - int used; + int used; /* is this slot in nd_table[] being used? */ + int instantiated; /* does this NICInfo correspond to an instantiated NIC? */ int nvectors; }; diff -Nru qemu-kvm-0.14.1+noroms/notify.c qemu-kvm-0.15.0+noroms/notify.c --- qemu-kvm-0.14.1+noroms/notify.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/notify.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,11 +29,11 @@ QTAILQ_REMOVE(&list->notifiers, notifier, node); } -void notifier_list_notify(NotifierList *list) +void notifier_list_notify(NotifierList *list, void *data) { Notifier *notifier, *next; QTAILQ_FOREACH_SAFE(notifier, &list->notifiers, node, next) { - notifier->notify(notifier); + notifier->notify(notifier, data); } } diff -Nru qemu-kvm-0.14.1+noroms/notify.h qemu-kvm-0.15.0+noroms/notify.h --- qemu-kvm-0.14.1+noroms/notify.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/notify.h 2011-08-09 07:40:29.000000000 -0500 @@ -20,7 +20,7 @@ struct Notifier { - void (*notify)(Notifier *notifier); + void (*notify)(Notifier *notifier, void *data); QTAILQ_ENTRY(Notifier) node; }; @@ -38,6 +38,6 @@ void notifier_list_remove(NotifierList *list, Notifier *notifier); -void notifier_list_notify(NotifierList *list); +void notifier_list_notify(NotifierList *list, void *data); #endif diff -Nru qemu-kvm-0.14.1+noroms/osdep.c qemu-kvm-0.15.0+noroms/osdep.c --- qemu-kvm-0.14.1+noroms/osdep.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/osdep.c 2011-08-09 07:40:29.000000000 -0500 @@ -46,7 +46,6 @@ #include "qemu-common.h" #include "trace.h" -#include "sysemu.h" #include "qemu_socket.h" int qemu_madvise(void *addr, size_t len, int advice) diff -Nru qemu-kvm-0.14.1+noroms/osdep.h qemu-kvm-0.15.0+noroms/osdep.h --- qemu-kvm-0.14.1+noroms/osdep.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/osdep.h 2011-08-09 07:40:29.000000000 -0500 @@ -8,9 +8,7 @@ #include #endif -#ifndef _WIN32 #include -#endif #ifndef glue #define xglue(x, y) x ## y @@ -57,6 +55,10 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#endif + #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif @@ -86,6 +88,7 @@ # define QEMU_GNUC_PREREQ(maj, min) 0 #endif +int qemu_daemon(int nochdir, int noclose); void *qemu_memalign(size_t alignment, size_t size); void *qemu_vmalloc(size_t size); void qemu_vfree(void *ptr); @@ -125,6 +128,30 @@ int qemu_madvise(void *addr, size_t len, int advice); +#if defined(__HAIKU__) && defined(__i386__) +#define FMT_pid "%ld" +#else +#define FMT_pid "%d" +#endif + int qemu_create_pidfile(const char *filename); +int qemu_get_thread_id(void); + +#ifdef _WIN32 +static inline void qemu_timersub(const struct timeval *val1, + const struct timeval *val2, + struct timeval *res) +{ + res->tv_sec = val1->tv_sec - val2->tv_sec; + if (val1->tv_usec < val2->tv_usec) { + res->tv_sec--; + res->tv_usec = val1->tv_usec - val2->tv_usec + 1000 * 1000; + } else { + res->tv_usec = val1->tv_usec - val2->tv_usec; + } +} +#else +#define qemu_timersub timersub +#endif #endif diff -Nru qemu-kvm-0.14.1+noroms/oslib-posix.c qemu-kvm-0.15.0+noroms/oslib-posix.c --- qemu-kvm-0.14.1+noroms/oslib-posix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/oslib-posix.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,11 +26,27 @@ * THE SOFTWARE. */ +/* The following block of code temporarily renames the daemon() function so the + compiler does not see the warning associated with it in stdlib.h on OSX */ +#ifdef __APPLE__ +#define daemon qemu_fake_daemon_function +#include +#undef daemon +extern int daemon(int, int); +#endif + #include "config-host.h" #include "sysemu.h" #include "trace.h" #include "qemu_socket.h" + + +int qemu_daemon(int nochdir, int noclose) +{ + return daemon(nochdir, noclose); +} + void *qemu_oom_check(void *ptr) { if (ptr == NULL) { diff -Nru qemu-kvm-0.14.1+noroms/oslib-win32.c qemu-kvm-0.15.0+noroms/oslib-win32.c --- qemu-kvm-0.14.1+noroms/oslib-win32.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/oslib-win32.c 2011-08-09 07:40:29.000000000 -0500 @@ -93,13 +93,6 @@ { } -/* 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) diff -Nru qemu-kvm-0.14.1+noroms/os-posix.c qemu-kvm-0.15.0+noroms/os-posix.c --- qemu-kvm-0.14.1+noroms/os-posix.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/os-posix.c 2011-08-09 07:40:29.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. */ @@ -41,6 +42,7 @@ #ifdef CONFIG_LINUX #include +#include #endif #ifdef CONFIG_EVENTFD @@ -61,14 +63,9 @@ sigaction(SIGPIPE, &act, NULL); } -static void termsig_handler(int signal) +static void termsig_handler(int signal, siginfo_t *info, void *c) { - qemu_system_shutdown_request(); -} - -static void sigchld_handler(int signal) -{ - waitpid(-1, NULL, WNOHANG); + qemu_system_killed(info->si_signo, info->si_pid); } void os_setup_signal_handling(void) @@ -76,14 +73,11 @@ struct sigaction act; memset(&act, 0, sizeof(act)); - act.sa_handler = termsig_handler; + act.sa_sigaction = termsig_handler; + act.sa_flags = SA_SIGINFO; 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. @@ -206,6 +200,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); @@ -375,10 +374,19 @@ if (lockf(fd, F_TLOCK, 0) == -1) { return -1; } - len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); + len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", getpid()); if (write(fd, buffer, len) != len) { return -1; } return 0; } + +int qemu_get_thread_id(void) +{ +#if defined (__linux__) + return syscall(SYS_gettid); +#else + return getpid(); +#endif +} diff -Nru qemu-kvm-0.14.1+noroms/os-win32.c qemu-kvm-0.15.0+noroms/os-win32.c --- qemu-kvm-0.14.1+noroms/os-win32.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/os-win32.c 2011-08-09 07:40:29.000000000 -0500 @@ -140,7 +140,9 @@ int err; WaitObjects *w = &wait_objects; + qemu_mutex_unlock_iothread(); ret = WaitForMultipleObjects(w->num, w->events, FALSE, *timeout); + qemu_mutex_lock_iothread(); 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]); @@ -180,7 +182,7 @@ /* Note: cpu_interrupt() is currently not SMP safe, so we force QEMU to run on a single CPU */ HANDLE h; - DWORD mask, smask; + DWORD_PTR mask, smask; int i; SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE); @@ -256,7 +258,7 @@ if (file == INVALID_HANDLE_VALUE) { return -1; } - len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); + len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", getpid()); ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len, &overlap, NULL); if (ret == 0) { @@ -264,3 +266,8 @@ } return 0; } + +int qemu_get_thread_id(void) +{ + return GetCurrentThreadId(); +} diff -Nru qemu-kvm-0.14.1+noroms/path.c qemu-kvm-0.15.0+noroms/path.c --- qemu-kvm-0.14.1+noroms/path.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/path.c 2011-08-09 07:40:29.000000000 -0500 @@ -38,7 +38,8 @@ return s2[i] == 0; } -static struct pathelem *add_entry(struct pathelem *root, const char *name); +static struct pathelem *add_entry(struct pathelem *root, const char *name, + unsigned char type); static struct pathelem *new_entry(const char *root, struct pathelem *parent, @@ -56,6 +57,15 @@ #define streq(a,b) (strcmp((a), (b)) == 0) +/* Not all systems provide this feature */ +#if defined(DT_DIR) && defined(DT_UNKNOWN) +# define dirent_type(dirent) ((dirent)->d_type) +# define is_dir_maybe(type) ((type) == DT_DIR || (type) == DT_UNKNOWN) +#else +# define dirent_type(dirent) (1) +# define is_dir_maybe(type) (type) +#endif + static struct pathelem *add_dir_maybe(struct pathelem *path) { DIR *dir; @@ -65,7 +75,7 @@ while ((dirent = readdir(dir)) != NULL) { if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){ - path = add_entry(path, dirent->d_name); + path = add_entry(path, dirent->d_name, dirent_type(dirent)); } } closedir(dir); @@ -73,16 +83,22 @@ return path; } -static struct pathelem *add_entry(struct pathelem *root, const char *name) +static struct pathelem *add_entry(struct pathelem *root, const char *name, + unsigned char type) { + struct pathelem **e; + root->num_entries++; root = realloc(root, sizeof(*root) + sizeof(root->entries[0])*root->num_entries); + e = &root->entries[root->num_entries-1]; + + *e = new_entry(root->pathname, root, name); + if (is_dir_maybe(type)) { + *e = add_dir_maybe(*e); + } - root->entries[root->num_entries-1] = new_entry(root->pathname, root, name); - root->entries[root->num_entries-1] - = add_dir_maybe(root->entries[root->num_entries-1]); return root; } diff -Nru qemu-kvm-0.14.1+noroms/pc-bios/mpc8544ds.dts qemu-kvm-0.15.0+noroms/pc-bios/mpc8544ds.dts --- qemu-kvm-0.14.1+noroms/pc-bios/mpc8544ds.dts 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/pc-bios/mpc8544ds.dts 2011-08-09 07:40:29.000000000 -0500 @@ -82,6 +82,12 @@ compatible = "chrp,open-pic"; device_type = "open-pic"; }; + + global-utilities@e0000 { //global utilities block + compatible = "fsl,mpc8544-guts"; + reg = <0xe0000 0x1000>; + fsl,has-rstcr; + }; }; pci0: pci@e0008000 { @@ -119,4 +125,7 @@ chosen { linux,stdout-path = "/soc8544@e0000000/serial@4500"; }; + + hypervisor { + }; }; diff -Nru qemu-kvm-0.14.1+noroms/pc-bios/optionrom/linuxboot.S qemu-kvm-0.15.0+noroms/pc-bios/optionrom/linuxboot.S --- qemu-kvm-0.14.1+noroms/pc-bios/optionrom/linuxboot.S 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/pc-bios/optionrom/linuxboot.S 2011-08-09 07:40:29.000000000 -0500 @@ -22,6 +22,8 @@ #include "optionrom.h" +#define BOOT_ROM_PRODUCT "Linux loader" + BOOT_ROM_START run_linuxboot: diff -Nru qemu-kvm-0.14.1+noroms/pc-bios/optionrom/multiboot.S qemu-kvm-0.15.0+noroms/pc-bios/optionrom/multiboot.S --- qemu-kvm-0.14.1+noroms/pc-bios/optionrom/multiboot.S 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/pc-bios/optionrom/multiboot.S 2011-08-09 07:40:29.000000000 -0500 @@ -20,6 +20,8 @@ #include "optionrom.h" +#define BOOT_ROM_PRODUCT "multiboot loader" + #define MULTIBOOT_MAGIC 0x2badb002 #define GS_PROT_JUMP 0 diff -Nru qemu-kvm-0.14.1+noroms/pc-bios/optionrom/optionrom.h qemu-kvm-0.15.0+noroms/pc-bios/optionrom/optionrom.h --- qemu-kvm-0.14.1+noroms/pc-bios/optionrom/optionrom.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/pc-bios/optionrom/optionrom.h 2011-08-09 07:40:29.000000000 -0500 @@ -97,22 +97,28 @@ #define BOOT_ROM_START \ OPTION_ROM_START \ - push %eax; \ - push %ds; \ - \ - /* setup ds so we can access the IVT */ \ - xor %ax, %ax; \ - mov %ax, %ds; \ - \ - /* install our int 19 handler */ \ - movw $int19_handler, (0x19*4); \ - mov %cs, (0x19*4+2); \ - \ - pop %ds; \ - pop %eax; \ lret; \ - \ - int19_handler:; \ + .org 0x18; \ + .short 0; \ + .short _pnph; \ + _pnph: \ + .ascii "$PnP"; \ + .byte 0x01; \ + .byte ( _pnph_len / 16 ); \ + .short 0x0000; \ + .byte 0x00; \ + .byte 0x00; \ + .long 0x00000000; \ + .short _manufacturer; \ + .short _product; \ + .long 0x00000000; \ + .short 0x0000; \ + .short 0x0000; \ + .short _bev; \ + .short 0x0000; \ + .short 0x0000; \ + .equ _pnph_len, . - _pnph; \ + _bev:; \ /* DS = CS */ \ movw %cs, %ax; \ movw %ax, %ds; @@ -122,5 +128,9 @@ _end: #define BOOT_ROM_END \ + _manufacturer:; \ + .asciz "QEMU"; \ + _product:; \ + .asciz BOOT_ROM_PRODUCT; \ OPTION_ROM_END diff -Nru qemu-kvm-0.14.1+noroms/pc-bios/README qemu-kvm-0.15.0+noroms/pc-bios/README --- qemu-kvm-0.14.1+noroms/pc-bios/README 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/pc-bios/README 2011-08-09 07:40:29.000000000 -0500 @@ -10,19 +10,24 @@ - 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 image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32 - and Sparc64 are built from OpenBIOS SVN revision 1018. + The included image for PowerPC (for 32 and 64 bit PPC CPUs) + is built from OpenBIOS SVN revision 1044 and Sparc32 and Sparc64 + images are built from OpenBIOS SVN revision 1045. -- The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0 +- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware + implementation for certain IBM POWER hardware. The sources are at + https://github.com/dgibson/SLOF, and the image currently in qemu is + built from git tag qemu-slof-20110323. - e1000 8086:100E - eepro100 8086:1209 (also used for 8086:1229 and 8086:2449) - ns8390 1050:0940 - pcnet32 1022:2000 - rtl8139 10ec:8139 - virtio 1af4:1000 +- The PXE roms come from the iPXE project. Built with BANNER_TIME 0. + Sources available at http://ipxe.org. Vendor:Device ID -> ROM mapping: - http://rom-o-matic.net/ + 8086:100e -> pxe-e1000.rom + 8086:1209 -> pxe-eepro100.rom + 1050:0940 -> pxe-ne2k_pci.rom + 1022:2000 -> pxe-pcnet.rom + 10ec:8139 -> pxe-rtl8139.rom + 1af4:1000 -> pxe-virtio.rom - The S390 zipl loader is an addition to the official IBM s390-tools package. That fork is maintained in its own git repository at: diff -Nru qemu-kvm-0.14.1+noroms/pc-bios/spapr-rtas/Makefile qemu-kvm-0.15.0+noroms/pc-bios/spapr-rtas/Makefile --- qemu-kvm-0.14.1+noroms/pc-bios/spapr-rtas/Makefile 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/pc-bios/spapr-rtas/Makefile 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,24 @@ +all: build-all +# Dummy command so that make thinks it has done something + @true + +include ../../config-host.mak +include $(SRC_PATH)/rules.mak + +$(call set-vpath, $(SRC_PATH)/pc-bios/spapr-rtas) + +.PHONY : all clean build-all + +#CFLAGS += -I$(SRC_PATH) +#QEMU_CFLAGS = $(CFLAGS) + +build-all: spapr-rtas.bin + +%.img: %.o + $(call quiet-command,$(CC) -nostdlib -o $@ $<," Building $(TARGET_DIR)$@") + +%.bin: %.img + $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@") + +clean: + rm -f *.o *.d *.img *.bin *~ diff -Nru qemu-kvm-0.14.1+noroms/pc-bios/spapr-rtas/spapr-rtas.S qemu-kvm-0.15.0+noroms/pc-bios/spapr-rtas/spapr-rtas.S --- qemu-kvm-0.14.1+noroms/pc-bios/spapr-rtas/spapr-rtas.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/pc-bios/spapr-rtas/spapr-rtas.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,37 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * Trivial in-partition RTAS implementation, based on a hypercall + * + * Copyright (c) 2010,2011 David Gibson, IBM Corporation. + * + * 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 KVMPPC_HCALL_BASE 0xf000 +#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) + +.globl _start +_start: + mr 4,3 + lis 3,KVMPPC_H_RTAS@h + ori 3,3,KVMPPC_H_RTAS@l + sc 1 + blr diff -Nru qemu-kvm-0.14.1+noroms/poison.h qemu-kvm-0.15.0+noroms/poison.h --- qemu-kvm-0.14.1+noroms/poison.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/poison.h 2011-08-09 07:40:29.000000000 -0500 @@ -10,6 +10,7 @@ #pragma GCC poison TARGET_ALPHA #pragma GCC poison TARGET_ARM #pragma GCC poison TARGET_CRIS +#pragma GCC poison TARGET_LM32 #pragma GCC poison TARGET_M68K #pragma GCC poison TARGET_MIPS #pragma GCC poison TARGET_MIPS64 @@ -36,15 +37,27 @@ #pragma GCC poison CPUState #pragma GCC poison env +#pragma GCC poison lduw_phys +#pragma GCC poison ldl_phys +#pragma GCC poison ldq_phys +#pragma GCC poison stl_phys_notdirty +#pragma GCC poison stq_phys_notdirty +#pragma GCC poison stw_phys +#pragma GCC poison stl_phys +#pragma GCC poison stq_phys + #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 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_0 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_1 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_2 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_3 +#pragma GCC poison CPU_INTERRUPT_TGT_EXT_4 +#pragma GCC poison CPU_INTERRUPT_TGT_INT_0 +#pragma GCC poison CPU_INTERRUPT_TGT_INT_1 +#pragma GCC poison CPU_INTERRUPT_TGT_INT_2 #endif #endif diff -Nru qemu-kvm-0.14.1+noroms/posix-aio-compat.c qemu-kvm-0.15.0+noroms/posix-aio-compat.c --- qemu-kvm-0.14.1+noroms/posix-aio-compat.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/posix-aio-compat.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -323,7 +322,9 @@ while (QTAILQ_EMPTY(&request_list) && !(ret == ETIMEDOUT)) { + idle_threads++; ret = cond_timedwait(&cond, &lock, &ts); + idle_threads--; } if (QTAILQ_EMPTY(&request_list)) @@ -332,7 +333,6 @@ aiocb = QTAILQ_FIRST(&request_list); QTAILQ_REMOVE(&request_list, aiocb, node); aiocb->active = 1; - idle_threads--; mutex_unlock(&lock); switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { @@ -354,13 +354,11 @@ mutex_lock(&lock); aiocb->ret = ret; - idle_threads++; mutex_unlock(&lock); if (kill(pid, aiocb->ev_signo)) die("kill failed"); } - idle_threads--; cur_threads--; mutex_unlock(&lock); @@ -372,7 +370,6 @@ sigset_t set, oldset; cur_threads++; - idle_threads++; /* block all signals */ if (sigfillset(&set)) die("sigfillset"); @@ -456,6 +453,9 @@ } else { ret = -ret; } + + trace_paio_complete(acb, acb->common.opaque, ret); + /* remove the request */ *pacb = acb->next; /* call the callback */ @@ -535,6 +535,8 @@ struct qemu_paiocb *acb = (struct qemu_paiocb *)blockacb; int active = 0; + trace_paio_cancel(acb, acb->common.opaque); + mutex_lock(&lock); if (!acb->active) { QTAILQ_REMOVE(&request_list, acb, node); diff -Nru qemu-kvm-0.14.1+noroms/qapi/qapi-dealloc-visitor.c qemu-kvm-0.15.0+noroms/qapi/qapi-dealloc-visitor.c --- qemu-kvm-0.14.1+noroms/qapi/qapi-dealloc-visitor.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qapi-dealloc-visitor.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,147 @@ +/* + * Dealloc Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Michael Roth + * + * 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 "qapi-dealloc-visitor.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" + +typedef struct StackEntry +{ + void *value; + QTAILQ_ENTRY(StackEntry) node; +} StackEntry; + +struct QapiDeallocVisitor +{ + Visitor visitor; + QTAILQ_HEAD(, StackEntry) stack; +}; + +static QapiDeallocVisitor *to_qov(Visitor *v) +{ + return container_of(v, QapiDeallocVisitor, visitor); +} + +static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value) +{ + StackEntry *e = qemu_mallocz(sizeof(*e)); + + e->value = value; + QTAILQ_INSERT_HEAD(&qov->stack, e, node); +} + +static void *qapi_dealloc_pop(QapiDeallocVisitor *qov) +{ + StackEntry *e = QTAILQ_FIRST(&qov->stack); + QObject *value; + QTAILQ_REMOVE(&qov->stack, e, node); + value = e->value; + qemu_free(e); + return value; +} + +static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t unused, + Error **errp) +{ + QapiDeallocVisitor *qov = to_qov(v); + qapi_dealloc_push(qov, obj); +} + +static void qapi_dealloc_end_struct(Visitor *v, Error **errp) +{ + QapiDeallocVisitor *qov = to_qov(v); + void **obj = qapi_dealloc_pop(qov); + if (obj) { + qemu_free(*obj); + } +} + +static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp) +{ +} + +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **list, + Error **errp) +{ + GenericList *retval = *list; + qemu_free(retval->value); + *list = retval->next; + return retval; +} + +static void qapi_dealloc_end_list(Visitor *v, Error **errp) +{ +} + +static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ + if (obj) { + qemu_free(*obj); + } +} + +static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ +} + +static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ +} + +static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name, + Error **errp) +{ +} + +static void qapi_dealloc_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, + Error **errp) +{ +} + +Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v) +{ + return &v->visitor; +} + +void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v) +{ + qemu_free(v); +} + +QapiDeallocVisitor *qapi_dealloc_visitor_new(void) +{ + QapiDeallocVisitor *v; + + v = qemu_mallocz(sizeof(*v)); + + v->visitor.start_struct = qapi_dealloc_start_struct; + v->visitor.end_struct = qapi_dealloc_end_struct; + v->visitor.start_list = qapi_dealloc_start_list; + v->visitor.next_list = qapi_dealloc_next_list; + v->visitor.end_list = qapi_dealloc_end_list; + v->visitor.type_enum = qapi_dealloc_type_enum; + v->visitor.type_int = qapi_dealloc_type_int; + v->visitor.type_bool = qapi_dealloc_type_bool; + v->visitor.type_str = qapi_dealloc_type_str; + v->visitor.type_number = qapi_dealloc_type_number; + + QTAILQ_INIT(&v->stack); + + return v; +} diff -Nru qemu-kvm-0.14.1+noroms/qapi/qapi-dealloc-visitor.h qemu-kvm-0.15.0+noroms/qapi/qapi-dealloc-visitor.h --- qemu-kvm-0.14.1+noroms/qapi/qapi-dealloc-visitor.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qapi-dealloc-visitor.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,26 @@ +/* + * Dealloc Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Michael Roth + * + * 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 QAPI_DEALLOC_VISITOR_H +#define QAPI_DEALLOC_VISITOR_H + +#include "qapi-visit-core.h" + +typedef struct QapiDeallocVisitor QapiDeallocVisitor; + +QapiDeallocVisitor *qapi_dealloc_visitor_new(void); +void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d); + +Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/qapi/qapi-types-core.h qemu-kvm-0.15.0+noroms/qapi/qapi-types-core.h --- qemu-kvm-0.14.1+noroms/qapi/qapi-types-core.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qapi-types-core.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,20 @@ +/* + * Core Definitions for QAPI-generated Types + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 QAPI_TYPES_CORE_H +#define QAPI_TYPES_CORE_H + +#include "qemu-common.h" +#include "error.h" + +#endif diff -Nru qemu-kvm-0.14.1+noroms/qapi/qapi-visit-core.c qemu-kvm-0.15.0+noroms/qapi/qapi-visit-core.c --- qemu-kvm-0.14.1+noroms/qapi/qapi-visit-core.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qapi-visit-core.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,118 @@ +/* + * Core Definitions for QAPI Visitor Classes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 "qapi/qapi-visit-core.h" + +void visit_start_handle(Visitor *v, void **obj, const char *kind, + const char *name, Error **errp) +{ + if (!error_is_set(errp) && v->start_handle) { + v->start_handle(v, obj, kind, name, errp); + } +} + +void visit_end_handle(Visitor *v, Error **errp) +{ + if (!error_is_set(errp) && v->end_handle) { + v->end_handle(v, errp); + } +} + +void visit_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp) +{ + if (!error_is_set(errp)) { + v->start_struct(v, obj, kind, name, size, errp); + } +} + +void visit_end_struct(Visitor *v, Error **errp) +{ + if (!error_is_set(errp)) { + v->end_struct(v, errp); + } +} + +void visit_start_list(Visitor *v, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->start_list(v, name, errp); + } +} + +GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) +{ + if (!error_is_set(errp)) { + return v->next_list(v, list, errp); + } + + return 0; +} + +void visit_end_list(Visitor *v, Error **errp) +{ + if (!error_is_set(errp)) { + v->end_list(v, errp); + } +} + +void visit_start_optional(Visitor *v, bool *present, const char *name, + Error **errp) +{ + if (!error_is_set(errp) && v->start_optional) { + v->start_optional(v, present, name, errp); + } +} + +void visit_end_optional(Visitor *v, Error **errp) +{ + if (!error_is_set(errp) && v->end_optional) { + v->end_optional(v, errp); + } +} + +void visit_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_enum(v, obj, strings, kind, name, errp); + } +} + +void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_int(v, obj, name, errp); + } +} + +void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_bool(v, obj, name, errp); + } +} + +void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_str(v, obj, name, errp); + } +} + +void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_number(v, obj, name, errp); + } +} diff -Nru qemu-kvm-0.14.1+noroms/qapi/qapi-visit-core.h qemu-kvm-0.15.0+noroms/qapi/qapi-visit-core.h --- qemu-kvm-0.14.1+noroms/qapi/qapi-visit-core.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qapi-visit-core.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,76 @@ +/* + * Core Definitions for QAPI Visitor Classes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 QAPI_VISITOR_CORE_H +#define QAPI_VISITOR_CORE_H + +#include "qapi/qapi-types-core.h" +#include + +typedef struct GenericList +{ + void *value; + struct GenericList *next; +} GenericList; + +typedef struct Visitor Visitor; + +struct Visitor +{ + /* Must be set */ + void (*start_struct)(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp); + void (*end_struct)(Visitor *v, Error **errp); + + void (*start_list)(Visitor *v, const char *name, Error **errp); + GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp); + void (*end_list)(Visitor *v, Error **errp); + + void (*type_enum)(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, Error **errp); + + void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp); + void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp); + void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp); + void (*type_number)(Visitor *v, double *obj, const char *name, + Error **errp); + + /* May be NULL */ + void (*start_optional)(Visitor *v, bool *present, const char *name, + Error **errp); + void (*end_optional)(Visitor *v, Error **errp); + + void (*start_handle)(Visitor *v, void **obj, const char *kind, + const char *name, Error **errp); + void (*end_handle)(Visitor *v, Error **errp); +}; + +void visit_start_handle(Visitor *v, void **obj, const char *kind, + const char *name, Error **errp); +void visit_end_handle(Visitor *v, Error **errp); +void visit_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp); +void visit_end_struct(Visitor *v, Error **errp); +void visit_start_list(Visitor *v, const char *name, Error **errp); +GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp); +void visit_end_list(Visitor *v, Error **errp); +void visit_start_optional(Visitor *v, bool *present, const char *name, + Error **errp); +void visit_end_optional(Visitor *v, Error **errp); +void visit_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, Error **errp); +void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp); +void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp); +void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp); +void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/qapi/qmp-core.h qemu-kvm-0.15.0+noroms/qapi/qmp-core.h --- qemu-kvm-0.14.1+noroms/qapi/qmp-core.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qmp-core.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,41 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 QMP_CORE_H +#define QMP_CORE_H + +#include "qobject.h" +#include "qdict.h" +#include "error.h" + +typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); + +typedef enum QmpCommandType +{ + QCT_NORMAL, +} QmpCommandType; + +typedef struct QmpCommand +{ + const char *name; + QmpCommandType type; + QmpCommandFunc *fn; + QTAILQ_ENTRY(QmpCommand) node; +} QmpCommand; + +void qmp_register_command(const char *name, QmpCommandFunc *fn); +QmpCommand *qmp_find_command(const char *name); +QObject *qmp_dispatch(QObject *request); + +#endif + diff -Nru qemu-kvm-0.14.1+noroms/qapi/qmp-dispatch.c qemu-kvm-0.15.0+noroms/qapi/qmp-dispatch.c --- qemu-kvm-0.14.1+noroms/qapi/qmp-dispatch.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qmp-dispatch.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,124 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 "qemu-objects.h" +#include "qapi/qmp-core.h" +#include "json-parser.h" +#include "error.h" +#include "error_int.h" +#include "qerror.h" + +static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) +{ + const QDictEntry *ent; + const char *arg_name; + const QObject *arg_obj; + bool has_exec_key = false; + QDict *dict = NULL; + + if (qobject_type(request) != QTYPE_QDICT) { + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, + "request is not a dictionary"); + return NULL; + } + + dict = qobject_to_qdict(request); + + for (ent = qdict_first(dict); ent; + ent = qdict_next(dict, ent)) { + arg_name = qdict_entry_key(ent); + arg_obj = qdict_entry_value(ent); + + if (!strcmp(arg_name, "execute")) { + if (qobject_type(arg_obj) != QTYPE_QSTRING) { + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", + "string"); + return NULL; + } + has_exec_key = true; + } else if (strcmp(arg_name, "arguments")) { + error_set(errp, QERR_QMP_EXTRA_MEMBER, arg_name); + return NULL; + } + } + + if (!has_exec_key) { + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute"); + return NULL; + } + + return dict; +} + +static QObject *do_qmp_dispatch(QObject *request, Error **errp) +{ + const char *command; + QDict *args, *dict; + QmpCommand *cmd; + QObject *ret = NULL; + + + dict = qmp_dispatch_check_obj(request, errp); + if (!dict || error_is_set(errp)) { + return NULL; + } + + command = qdict_get_str(dict, "execute"); + cmd = qmp_find_command(command); + if (cmd == NULL) { + error_set(errp, QERR_COMMAND_NOT_FOUND, command); + return NULL; + } + + if (!qdict_haskey(dict, "arguments")) { + args = qdict_new(); + } else { + args = qdict_get_qdict(dict, "arguments"); + QINCREF(args); + } + + switch (cmd->type) { + case QCT_NORMAL: + cmd->fn(args, &ret, errp); + if (!error_is_set(errp) && ret == NULL) { + ret = QOBJECT(qdict_new()); + } + break; + } + + QDECREF(args); + + return ret; +} + +QObject *qmp_dispatch(QObject *request) +{ + Error *err = NULL; + QObject *ret; + QDict *rsp; + + ret = do_qmp_dispatch(request, &err); + + rsp = qdict_new(); + if (err) { + qdict_put_obj(rsp, "error", error_get_qobject(err)); + error_free(err); + } else if (ret) { + qdict_put_obj(rsp, "return", ret); + } else { + QDECREF(rsp); + return NULL; + } + + return QOBJECT(rsp); +} diff -Nru qemu-kvm-0.14.1+noroms/qapi/qmp-input-visitor.c qemu-kvm-0.15.0+noroms/qapi/qmp-input-visitor.c --- qemu-kvm-0.14.1+noroms/qapi/qmp-input-visitor.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qmp-input-visitor.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,301 @@ +/* + * Input Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 "qmp-input-visitor.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" +#include "qerror.h" + +#define QIV_STACK_SIZE 1024 + +typedef struct StackObject +{ + const QObject *obj; + const QListEntry *entry; +} StackObject; + +struct QmpInputVisitor +{ + Visitor visitor; + QObject *obj; + StackObject stack[QIV_STACK_SIZE]; + int nb_stack; +}; + +static QmpInputVisitor *to_qiv(Visitor *v) +{ + return container_of(v, QmpInputVisitor, visitor); +} + +static const QObject *qmp_input_get_object(QmpInputVisitor *qiv, + const char *name) +{ + const QObject *qobj; + + if (qiv->nb_stack == 0) { + qobj = qiv->obj; + } else { + qobj = qiv->stack[qiv->nb_stack - 1].obj; + } + + if (name && qobject_type(qobj) == QTYPE_QDICT) { + return qdict_get(qobject_to_qdict(qobj), name); + } else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) { + return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); + } + + return qobj; +} + +static void qmp_input_push(QmpInputVisitor *qiv, const QObject *obj, Error **errp) +{ + qiv->stack[qiv->nb_stack].obj = obj; + if (qobject_type(obj) == QTYPE_QLIST) { + qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj)); + } + qiv->nb_stack++; + + if (qiv->nb_stack >= QIV_STACK_SIZE) { + error_set(errp, QERR_BUFFER_OVERRUN); + return; + } +} + +static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) +{ + qiv->nb_stack--; + if (qiv->nb_stack < 0) { + error_set(errp, QERR_BUFFER_OVERRUN); + return; + } +} + +static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "QDict"); + return; + } + + qmp_input_push(qiv, qobj, errp); + if (error_is_set(errp)) { + return; + } + + if (obj) { + *obj = qemu_mallocz(size); + } +} + +static void qmp_input_end_struct(Visitor *v, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + + qmp_input_pop(qiv, errp); +} + +static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "list"); + return; + } + + qmp_input_push(qiv, qobj, errp); +} + +static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + GenericList *entry; + StackObject *so = &qiv->stack[qiv->nb_stack - 1]; + + if (so->entry == NULL) { + return NULL; + } + + entry = qemu_mallocz(sizeof(*entry)); + if (*list) { + so->entry = qlist_next(so->entry); + if (so->entry == NULL) { + qemu_free(entry); + return NULL; + } + (*list)->next = entry; + } + *list = entry; + + + return entry; +} + +static void qmp_input_end_list(Visitor *v, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + + qmp_input_pop(qiv, errp); +} + +static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QINT) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "integer"); + return; + } + + *obj = qint_get_int(qobject_to_qint(qobj)); +} + +static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "boolean"); + return; + } + + *obj = qbool_get_int(qobject_to_qbool(qobj)); +} + +static void qmp_input_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "string"); + return; + } + + *obj = qemu_strdup(qstring_get_str(qobject_to_qstring(qobj))); +} + +static void qmp_input_type_number(Visitor *v, double *obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QFLOAT) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "double"); + return; + } + + *obj = qfloat_get_double(qobject_to_qfloat(qobj)); +} + +static void qmp_input_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, + Error **errp) +{ + int64_t value = 0; + char *enum_str; + + assert(strings); + + qmp_input_type_str(v, &enum_str, name, errp); + if (error_is_set(errp)) { + return; + } + + while (strings[value] != NULL) { + if (strcmp(strings[value], enum_str) == 0) { + break; + } + value++; + } + + if (strings[value] == NULL) { + error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null"); + return; + } + + *obj = value; +} + +static void qmp_input_start_optional(Visitor *v, bool *present, + const char *name, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj) { + *present = false; + return; + } + + *present = true; +} + +static void qmp_input_end_optional(Visitor *v, Error **errp) +{ +} + +Visitor *qmp_input_get_visitor(QmpInputVisitor *v) +{ + return &v->visitor; +} + +void qmp_input_visitor_cleanup(QmpInputVisitor *v) +{ + qobject_decref(v->obj); + qemu_free(v); +} + +QmpInputVisitor *qmp_input_visitor_new(QObject *obj) +{ + QmpInputVisitor *v; + + v = qemu_mallocz(sizeof(*v)); + + v->visitor.start_struct = qmp_input_start_struct; + v->visitor.end_struct = qmp_input_end_struct; + v->visitor.start_list = qmp_input_start_list; + v->visitor.next_list = qmp_input_next_list; + v->visitor.end_list = qmp_input_end_list; + v->visitor.type_enum = qmp_input_type_enum; + v->visitor.type_int = qmp_input_type_int; + v->visitor.type_bool = qmp_input_type_bool; + v->visitor.type_str = qmp_input_type_str; + v->visitor.type_number = qmp_input_type_number; + v->visitor.start_optional = qmp_input_start_optional; + v->visitor.end_optional = qmp_input_end_optional; + + v->obj = obj; + qobject_incref(v->obj); + + return v; +} diff -Nru qemu-kvm-0.14.1+noroms/qapi/qmp-input-visitor.h qemu-kvm-0.15.0+noroms/qapi/qmp-input-visitor.h --- qemu-kvm-0.14.1+noroms/qapi/qmp-input-visitor.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qmp-input-visitor.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,27 @@ +/* + * Input Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 QMP_INPUT_VISITOR_H +#define QMP_INPUT_VISITOR_H + +#include "qapi-visit-core.h" +#include "qobject.h" + +typedef struct QmpInputVisitor QmpInputVisitor; + +QmpInputVisitor *qmp_input_visitor_new(QObject *obj); +void qmp_input_visitor_cleanup(QmpInputVisitor *v); + +Visitor *qmp_input_get_visitor(QmpInputVisitor *v); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/qapi/qmp-output-visitor.c qemu-kvm-0.15.0+noroms/qapi/qmp-output-visitor.c --- qemu-kvm-0.14.1+noroms/qapi/qmp-output-visitor.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qmp-output-visitor.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,239 @@ +/* + * Core Definitions for QAPI/QMP Command Registry + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 "qmp-output-visitor.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" +#include "qerror.h" + +typedef struct QStackEntry +{ + QObject *value; + QTAILQ_ENTRY(QStackEntry) node; +} QStackEntry; + +typedef QTAILQ_HEAD(QStack, QStackEntry) QStack; + +struct QmpOutputVisitor +{ + Visitor visitor; + QStack stack; +}; + +#define qmp_output_add(qov, name, value) \ + qmp_output_add_obj(qov, name, QOBJECT(value)) +#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value)) + +static QmpOutputVisitor *to_qov(Visitor *v) +{ + return container_of(v, QmpOutputVisitor, visitor); +} + +static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value) +{ + QStackEntry *e = qemu_mallocz(sizeof(*e)); + + e->value = value; + QTAILQ_INSERT_HEAD(&qov->stack, e, node); +} + +static QObject *qmp_output_pop(QmpOutputVisitor *qov) +{ + QStackEntry *e = QTAILQ_FIRST(&qov->stack); + QObject *value; + QTAILQ_REMOVE(&qov->stack, e, node); + value = e->value; + qemu_free(e); + return value; +} + +static QObject *qmp_output_first(QmpOutputVisitor *qov) +{ + QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack); + return e->value; +} + +static QObject *qmp_output_last(QmpOutputVisitor *qov) +{ + QStackEntry *e = QTAILQ_FIRST(&qov->stack); + return e->value; +} + +static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name, + QObject *value) +{ + QObject *cur; + + if (QTAILQ_EMPTY(&qov->stack)) { + qmp_output_push_obj(qov, value); + return; + } + + cur = qmp_output_last(qov); + + switch (qobject_type(cur)) { + case QTYPE_QDICT: + qdict_put_obj(qobject_to_qdict(cur), name, value); + break; + case QTYPE_QLIST: + qlist_append_obj(qobject_to_qlist(cur), value); + break; + default: + qobject_decref(qmp_output_pop(qov)); + qmp_output_push_obj(qov, value); + break; + } +} + +static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t unused, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + QDict *dict = qdict_new(); + + qmp_output_add(qov, name, dict); + qmp_output_push(qov, dict); +} + +static void qmp_output_end_struct(Visitor *v, Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_pop(qov); +} + +static void qmp_output_start_list(Visitor *v, const char *name, Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + QList *list = qlist_new(); + + qmp_output_add(qov, name, list); + qmp_output_push(qov, list); +} + +static GenericList *qmp_output_next_list(Visitor *v, GenericList **list, + Error **errp) +{ + GenericList *retval = *list; + *list = retval->next; + return retval; +} + +static void qmp_output_end_list(Visitor *v, Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_pop(qov); +} + +static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add(qov, name, qint_from_int(*obj)); +} + +static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add(qov, name, qbool_from_int(*obj)); +} + +static void qmp_output_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + if (*obj) { + qmp_output_add(qov, name, qstring_from_str(*obj)); + } else { + qmp_output_add(qov, name, qstring_from_str("")); + } +} + +static void qmp_output_type_number(Visitor *v, double *obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add(qov, name, qfloat_from_double(*obj)); +} + +static void qmp_output_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, + Error **errp) +{ + int i = 0; + int value = *obj; + char *enum_str; + + assert(strings); + while (strings[i++] != NULL); + if (value >= i - 1) { + error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null"); + return; + } + + enum_str = (char *)strings[value]; + qmp_output_type_str(v, &enum_str, name, errp); +} + +QObject *qmp_output_get_qobject(QmpOutputVisitor *qov) +{ + QObject *obj = qmp_output_first(qov); + if (obj) { + qobject_incref(obj); + } + return obj; +} + +Visitor *qmp_output_get_visitor(QmpOutputVisitor *v) +{ + return &v->visitor; +} + +void qmp_output_visitor_cleanup(QmpOutputVisitor *v) +{ + QStackEntry *e, *tmp; + + QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) { + QTAILQ_REMOVE(&v->stack, e, node); + if (e->value) { + qobject_decref(e->value); + } + qemu_free(e); + } + + qemu_free(v); +} + +QmpOutputVisitor *qmp_output_visitor_new(void) +{ + QmpOutputVisitor *v; + + v = qemu_mallocz(sizeof(*v)); + + v->visitor.start_struct = qmp_output_start_struct; + v->visitor.end_struct = qmp_output_end_struct; + v->visitor.start_list = qmp_output_start_list; + v->visitor.next_list = qmp_output_next_list; + v->visitor.end_list = qmp_output_end_list; + v->visitor.type_enum = qmp_output_type_enum; + v->visitor.type_int = qmp_output_type_int; + v->visitor.type_bool = qmp_output_type_bool; + v->visitor.type_str = qmp_output_type_str; + v->visitor.type_number = qmp_output_type_number; + + QTAILQ_INIT(&v->stack); + + return v; +} diff -Nru qemu-kvm-0.14.1+noroms/qapi/qmp-output-visitor.h qemu-kvm-0.15.0+noroms/qapi/qmp-output-visitor.h --- qemu-kvm-0.14.1+noroms/qapi/qmp-output-visitor.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qmp-output-visitor.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,28 @@ +/* + * Output Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 QMP_OUTPUT_VISITOR_H +#define QMP_OUTPUT_VISITOR_H + +#include "qapi-visit-core.h" +#include "qobject.h" + +typedef struct QmpOutputVisitor QmpOutputVisitor; + +QmpOutputVisitor *qmp_output_visitor_new(void); +void qmp_output_visitor_cleanup(QmpOutputVisitor *v); + +QObject *qmp_output_get_qobject(QmpOutputVisitor *v); +Visitor *qmp_output_get_visitor(QmpOutputVisitor *v); + +#endif diff -Nru qemu-kvm-0.14.1+noroms/qapi/qmp-registry.c qemu-kvm-0.15.0+noroms/qapi/qmp-registry.c --- qemu-kvm-0.14.1+noroms/qapi/qmp-registry.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi/qmp-registry.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,40 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * Michael Roth + * + * 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 "qapi/qmp-core.h" + +static QTAILQ_HEAD(, QmpCommand) qmp_commands = + QTAILQ_HEAD_INITIALIZER(qmp_commands); + +void qmp_register_command(const char *name, QmpCommandFunc *fn) +{ + QmpCommand *cmd = qemu_mallocz(sizeof(*cmd)); + + cmd->name = name; + cmd->type = QCT_NORMAL; + cmd->fn = fn; + QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node); +} + +QmpCommand *qmp_find_command(const char *name) +{ + QmpCommand *i; + + QTAILQ_FOREACH(i, &qmp_commands, node) { + if (strcmp(i->name, name) == 0) { + return i; + } + } + return NULL; +} diff -Nru qemu-kvm-0.14.1+noroms/qapi-schema-guest.json qemu-kvm-0.15.0+noroms/qapi-schema-guest.json --- qemu-kvm-0.14.1+noroms/qapi-schema-guest.json 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi-schema-guest.json 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,217 @@ +# *-*- Mode: Python -*-* + +## +# @guest-sync: +# +# Echo back a unique integer value +# +# This is used by clients talking to the guest agent over the +# wire to ensure the stream is in sync and doesn't contain stale +# data from previous client. All guest agent responses should be +# ignored until the provided unique integer value is returned, +# and it is up to the client to handle stale whole or +# partially-delivered JSON text in such a way that this response +# can be obtained. +# +# Such clients should also preceed this command +# with a 0xFF byte to make such the guest agent flushes any +# partially read JSON data from a previous session. +# +# @id: randomly generated 64-bit integer +# +# Returns: The unique integer id passed in by the client +# +# Since: 0.15.0 +## +{ 'command': 'guest-sync' + 'data': { 'id': 'int' }, + 'returns': 'int' } + +## +# @guest-ping: +# +# Ping the guest agent, a non-error return implies success +# +# Since: 0.15.0 +## +{ 'command': 'guest-ping' } + +## +# @guest-info: +# +# Get some information about the guest agent. +# +# Since: 0.15.0 +## +{ 'type': 'GuestAgentInfo', 'data': {'version': 'str'} } +{ 'command': 'guest-info', + 'returns': 'GuestAgentInfo' } + +## +# @guest-shutdown: +# +# Initiate guest-activated shutdown. Note: this is an asynchronous +# shutdown request, with no guaruntee of successful shutdown. Errors +# will be logged to guest's syslog. +# +# @mode: #optional "halt", "powerdown" (default), or "reboot" +# +# Returns: Nothing on success +# +# Since: 0.15.0 +## +{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } } + +## +# @guest-file-open: +# +# Open a file in the guest and retrieve a file handle for it +# +# @filepath: Full path to the file in the guest to open. +# +# @mode: #optional open mode, as per fopen(), "r" is the default. +# +# Returns: Guest file handle on success. +# +# Since: 0.15.0 +## +{ 'command': 'guest-file-open', + 'data': { 'path': 'str', '*mode': 'str' }, + 'returns': 'int' } + +## +# @guest-file-close: +# +# Close an open file in the guest +# +# @handle: filehandle returned by guest-file-open +# +# Returns: Nothing on success. +# +# Since: 0.15.0 +## +{ 'command': 'guest-file-close', + 'data': { 'handle': 'int' } } + +## +# @guest-file-read: +# +# Read from an open file in the guest. Data will be base64-encoded +# +# @handle: filehandle returned by guest-file-open +# +# @count: #optional maximum number of bytes to read (default is 4KB) +# +# Returns: GuestFileRead on success. Note: count is number of bytes read +# *before* base64 encoding bytes read. +# +# Since: 0.15.0 +## +{ 'type': 'GuestFileRead', + 'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } } + +{ 'command': 'guest-file-read', + 'data': { 'handle': 'int', '*count': 'int' }, + 'returns': 'GuestFileRead' } + +## +# @guest-file-write: +# +# Write to an open file in the guest. +# +# @handle: filehandle returned by guest-file-open +# +# @buf-b64: base64-encoded string representing data to be written +# +# @count: #optional bytes to write (actual bytes, after base64-decode), +# default is all content in buf-b64 buffer after base64 decoding +# +# Returns: GuestFileWrite on success. Note: count is the number of bytes +# base64-decoded bytes written +# +# Since: 0.15.0 +## +{ 'type': 'GuestFileWrite', + 'data': { 'count': 'int', 'eof': 'bool' } } +{ 'command': 'guest-file-write', + 'data': { 'handle': 'int', 'buf-b64': 'str', '*count': 'int' }, + 'returns': 'GuestFileWrite' } + +## +# @guest-file-seek: +# +# Seek to a position in the file, as with fseek(), and return the +# current file position afterward. Also encapsulates ftell()'s +# functionality, just Set offset=0, whence=SEEK_CUR. +# +# @handle: filehandle returned by guest-file-open +# +# @offset: bytes to skip over in the file stream +# +# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek() +# +# Returns: GuestFileSeek on success. +# +# Since: 0.15.0 +## +{ 'type': 'GuestFileSeek', + 'data': { 'position': 'int', 'eof': 'bool' } } + +{ 'command': 'guest-file-seek', + 'data': { 'handle': 'int', 'offset': 'int', 'whence': 'int' }, + 'returns': 'GuestFileSeek' } + +## +# @guest-file-flush: +# +# Write file changes bufferred in userspace to disk/kernel buffers +# +# @handle: filehandle returned by guest-file-open +# +# Returns: Nothing on success. +# +# Since: 0.15.0 +## +{ 'command': 'guest-file-flush', + 'data': { 'handle': 'int' } } + +## +# @guest-fsfreeze-status: +# +# Get guest fsfreeze state. error state indicates failure to thaw 1 or more +# previously frozen filesystems, or failure to open a previously cached +# filesytem (filesystem unmounted/directory changes, etc). +# +# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below) +# +# Since: 0.15.0 +## +{ 'enum': 'GuestFsfreezeStatus', + 'data': [ 'thawed', 'frozen', 'error' ] } +{ 'command': 'guest-fsfreeze-status', + 'returns': 'GuestFsfreezeStatus' } + +## +# @guest-fsfreeze-freeze: +# +# Sync and freeze all non-network guest filesystems +# +# Returns: Number of file systems frozen on success +# +# Since: 0.15.0 +## +{ 'command': 'guest-fsfreeze-freeze', + 'returns': 'int' } + +## +# @guest-fsfreeze-thaw: +# +# Unfreeze frozen guest fileystems +# +# Returns: Number of file systems thawed +# If error, -1 (unknown error) or -errno +# +# Since: 0.15.0 +## +{ 'command': 'guest-fsfreeze-thaw', + 'returns': 'int' } diff -Nru qemu-kvm-0.14.1+noroms/qapi-schema-test.json qemu-kvm-0.15.0+noroms/qapi-schema-test.json --- qemu-kvm-0.14.1+noroms/qapi-schema-test.json 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qapi-schema-test.json 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,22 @@ +# *-*- Mode: Python -*-* + +# for testing enums +{ 'enum': 'EnumOne', + 'data': [ 'value1', 'value2', 'value3' ] } +{ 'type': 'NestedEnumsOne', + 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } + +# for testing nested structs +{ 'type': 'UserDefOne', + 'data': { 'integer': 'int', 'string': 'str' } } + +{ 'type': 'UserDefTwo', + 'data': { 'string': 'str', + 'dict': { 'string': 'str', + 'dict': { 'userdef': 'UserDefOne', 'string': 'str' }, + '*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } } + +# testing commands +{ 'command': 'user_def_cmd', 'data': {} } +{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} } +{ 'command': 'user_def_cmd2', 'data': {'ud1a': 'UserDefOne', 'ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' } diff -Nru qemu-kvm-0.14.1+noroms/qemu-char.c qemu-kvm-0.15.0+noroms/qemu-char.c --- qemu-kvm-0.14.1+noroms/qemu-char.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-char.c 2011-08-09 07:40:29.000000000 -0500 @@ -35,7 +35,6 @@ #include #include -#include #include #include #include @@ -169,6 +168,11 @@ return s->get_msgfd ? s->get_msgfd(s) : -1; } +int qemu_chr_add_client(CharDriverState *s, int fd) +{ + return s->chr_add_client ? s->chr_add_client(s, fd) : -1; +} + void qemu_chr_accept_input(CharDriverState *s) { if (s->chr_accept_input) @@ -197,6 +201,10 @@ IOEventHandler *fd_event, void *opaque) { + if (!opaque && !fd_can_read && !fd_read && !fd_event) { + /* chr driver being released. */ + ++s->avail_connections; + } s->chr_can_read = fd_can_read; s->chr_read = fd_read; s->chr_event = fd_event; @@ -216,13 +224,15 @@ return len; } -static CharDriverState *qemu_chr_open_null(QemuOpts *opts) +static int qemu_chr_open_null(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; chr = qemu_mallocz(sizeof(CharDriverState)); chr->chr_write = null_chr_write; - return chr; + + *_chr= chr; + return 0; } /* MUX driver for serial I/O splitting */ @@ -267,7 +277,7 @@ int64_t ti; int secs; - ti = qemu_get_clock(rt_clock); + ti = qemu_get_clock_ms(rt_clock); if (d->timestamps_start == -1) d->timestamps_start = ti; ti -= d->timestamps_start; @@ -476,6 +486,9 @@ chr->chr_write = mux_chr_write; chr->chr_update_read_handler = mux_chr_update_read_handler; chr->chr_accept_input = mux_chr_accept_input; + /* Frontend guest-open / -close notification is not support with muxes */ + chr->chr_guest_open = NULL; + chr->chr_guest_close = NULL; /* Muxes are always open on creation */ qemu_chr_generic_open(chr); @@ -628,18 +641,21 @@ return chr; } -static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) +static int qemu_chr_open_file_out(QemuOpts *opts, CharDriverState **_chr) { int fd_out; TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); - if (fd_out < 0) - return NULL; - return qemu_chr_open_fd(-1, fd_out); + if (fd_out < 0) { + return -errno; + } + + *_chr = qemu_chr_open_fd(-1, fd_out); + return 0; } -static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) +static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) { int fd_in, fd_out; char filename_in[256], filename_out[256]; @@ -647,7 +663,7 @@ if (filename == NULL) { fprintf(stderr, "chardev: pipe: no filename given\n"); - return NULL; + return -EINVAL; } snprintf(filename_in, 256, "%s.in", filename); @@ -659,11 +675,14 @@ close(fd_in); if (fd_out >= 0) close(fd_out); - TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY)); - if (fd_in < 0) - return NULL; + TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); + if (fd_in < 0) { + return -errno; + } } - return qemu_chr_open_fd(fd_in, fd_out); + + *_chr = qemu_chr_open_fd(fd_in, fd_out); + return 0; } @@ -754,12 +773,14 @@ fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) +static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; - if (stdio_nb_clients >= STDIO_MAX_CLIENTS) - return NULL; + if (stdio_nb_clients >= STDIO_MAX_CLIENTS) { + return -EBUSY; + } + if (stdio_nb_clients == 0) { old_fd0_flags = fcntl(0, F_GETFL); tcgetattr (0, &oldtty); @@ -776,7 +797,8 @@ display_type != DT_NOGRAPHIC); qemu_chr_set_echo(chr, false); - return chr; + *_chr = chr; + return 0; } #ifdef __sun__ @@ -911,7 +933,7 @@ * timeout to the normal (much longer) poll interval before the * timer triggers. */ - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 10); } static void pty_chr_state(CharDriverState *chr, int connected) @@ -925,7 +947,7 @@ /* (re-)connect poll interval for idle guests: once per second. * We check more frequently in case the guests sends data to * the virtual device linked to our pty. */ - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 1000); } else { if (!s->connected) qemu_chr_generic_open(chr); @@ -963,7 +985,7 @@ qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; PtyCharDriver *s; @@ -981,7 +1003,7 @@ s = qemu_mallocz(sizeof(PtyCharDriver)); if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) { - return NULL; + return -errno; } /* Set raw attributes on the pty. */ @@ -1001,9 +1023,10 @@ chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_close = pty_chr_close; - s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr); + s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); - return chr; + *_chr = chr; + return 0; } static void tty_serial_init(int fd, int speed, @@ -1204,30 +1227,28 @@ } } -static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) +static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; - TFR(fd = open(filename, O_RDWR | O_NONBLOCK)); + TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK)); if (fd < 0) { - return NULL; + return -errno; } tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd); - if (!chr) { - close(fd); - return NULL; - } chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; - return chr; + + *_chr = chr; + return 0; } #else /* ! __linux__ && ! __sun__ */ -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) { - return NULL; + return -ENOTSUP; } #endif /* __linux__ || __sun__ */ @@ -1341,7 +1362,7 @@ qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1349,12 +1370,13 @@ int fd; TFR(fd = open(filename, O_RDWR)); - if (fd < 0) - return NULL; + if (fd < 0) { + return -errno; + } if (ioctl(fd, PPCLAIM) < 0) { close(fd); - return NULL; + return -errno; } drv = qemu_mallocz(sizeof(ParallelCharDriver)); @@ -1369,14 +1391,15 @@ qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; } #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) { - int fd = (int)(long)chr->opaque; + int fd = (int)(intptr_t)chr->opaque; uint8_t b; switch(cmd) { @@ -1411,21 +1434,24 @@ return 0; } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; - fd = open(filename, O_RDWR); - if (fd < 0) - return NULL; + fd = qemu_open(filename, O_RDWR); + if (fd < 0) { + return -errno; + } chr = qemu_mallocz(sizeof(CharDriverState)); - chr->opaque = (void *)(long)fd; + chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - return chr; + + *_chr = chr; + return 0; } #endif @@ -1631,7 +1657,7 @@ return 0; } -static CharDriverState *qemu_chr_open_win(QemuOpts *opts) +static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1646,10 +1672,12 @@ if (win_chr_init(chr, filename) < 0) { free(s); free(chr); - return NULL; + return -EIO; } qemu_chr_generic_open(chr); - return chr; + + *_chr = chr; + return 0; } static int win_chr_pipe_poll(void *opaque) @@ -1731,7 +1759,7 @@ } -static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) +static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1746,13 +1774,15 @@ if (win_chr_pipe_init(chr, filename) < 0) { free(s); free(chr); - return NULL; + return -EIO; } qemu_chr_generic_open(chr); - return chr; + + *_chr = chr; + return 0; } -static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) +static int qemu_chr_open_win_file(HANDLE fd_out, CharDriverState **pchr) { CharDriverState *chr; WinCharState *s; @@ -1763,25 +1793,27 @@ chr->opaque = s; chr->chr_write = win_chr_write; qemu_chr_generic_open(chr); - return chr; + *pchr = chr; + return 0; } -static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) +static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **chr) { - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); + return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), chr); } -static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) +static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr) { const char *file_out = qemu_opt_get(opts, "path"); HANDLE fd_out; fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd_out == INVALID_HANDLE_VALUE) - return NULL; + if (fd_out == INVALID_HANDLE_VALUE) { + return -EIO; + } - return qemu_chr_open_win_file(fd_out); + return qemu_chr_open_win_file(fd_out, _chr); } #endif /* !_WIN32 */ @@ -1828,7 +1860,7 @@ if (s->max_size == 0) return; - s->bufcnt = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); + s->bufcnt = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0); s->bufptr = s->bufcnt; if (s->bufcnt <= 0) return; @@ -1862,11 +1894,12 @@ qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) +static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; int fd = -1; + int ret; chr = qemu_mallocz(sizeof(CharDriverState)); s = qemu_mallocz(sizeof(NetCharDriver)); @@ -1874,6 +1907,7 @@ fd = inet_dgram_opts(opts); if (fd < 0) { fprintf(stderr, "inet_dgram_opts failed\n"); + ret = -errno; goto return_err; } @@ -1884,16 +1918,17 @@ chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; - return chr; + + *_chr = chr; + return 0; return_err: - if (chr) - free(chr); - if (s) - free(s); - if (fd >= 0) + qemu_free(chr); + qemu_free(s); + if (fd >= 0) { closesocket(fd); - return NULL; + } + return ret; } /***********************************************************/ @@ -2043,7 +2078,7 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) { TCPCharDriver *s = chr->opaque; - return recv(s->fd, buf, len, 0); + return qemu_recv(s->fd, buf, len, 0); } #endif @@ -2117,6 +2152,22 @@ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); } +static int tcp_chr_add_client(CharDriverState *chr, int fd) +{ + TCPCharDriver *s = chr->opaque; + if (s->fd != -1) + return -1; + + socket_set_nonblock(fd); + if (s->do_nodelay) + socket_set_nodelay(fd); + s->fd = fd; + qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + tcp_chr_connect(chr); + + return 0; +} + static void tcp_chr_accept(void *opaque) { CharDriverState *chr = opaque; @@ -2149,12 +2200,8 @@ break; } } - socket_set_nonblock(fd); - if (s->do_nodelay) - socket_set_nodelay(fd); - s->fd = fd; - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); - tcp_chr_connect(chr); + if (tcp_chr_add_client(chr, fd) < 0) + close(fd); } static void tcp_chr_close(CharDriverState *chr) @@ -2172,7 +2219,7 @@ qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) +static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; @@ -2182,6 +2229,7 @@ int do_nodelay; int is_unix; int is_telnet; + int ret; is_listen = qemu_opt_get_bool(opts, "server", 0); is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); @@ -2207,8 +2255,10 @@ fd = inet_connect_opts(opts); } } - if (fd < 0) + if (fd < 0) { + ret = -errno; goto fail; + } if (!is_waitconnect) socket_set_nonblock(fd); @@ -2224,6 +2274,7 @@ chr->chr_write = tcp_chr_write; chr->chr_close = tcp_chr_close; chr->get_msgfd = tcp_get_msgfd; + chr->chr_add_client = tcp_chr_add_client; if (is_listen) { s->listen_fd = fd; @@ -2260,14 +2311,16 @@ tcp_chr_accept(chr); socket_set_nonblock(s->listen_fd); } - return chr; + + *_chr = chr; + return 0; fail: if (fd >= 0) closesocket(fd); qemu_free(s); qemu_free(chr); - return NULL; + return ret; } /***********************************************************/ @@ -2460,7 +2513,7 @@ static const struct { const char *name; - CharDriverState *(*open)(QemuOpts *opts); + int (*open)(QemuOpts *opts, CharDriverState **chr); } backend_table[] = { { .name = "null", .open = qemu_chr_open_null }, { .name = "socket", .open = qemu_chr_open_socket }, @@ -2500,6 +2553,7 @@ { CharDriverState *chr; int i; + int ret; if (qemu_opts_id(opts) == NULL) { fprintf(stderr, "chardev: no id specified\n"); @@ -2521,10 +2575,10 @@ return NULL; } - chr = backend_table[i].open(opts); - if (!chr) { - fprintf(stderr, "chardev: opening backend \"%s\" failed\n", - qemu_opt_get(opts, "backend")); + ret = backend_table[i].open(opts, &chr); + if (ret < 0) { + fprintf(stderr, "chardev: opening backend \"%s\" failed: %s\n", + qemu_opt_get(opts, "backend"), strerror(-ret)); return NULL; } @@ -2540,7 +2594,10 @@ snprintf(base->label, len, "%s-base", qemu_opts_id(opts)); chr = qemu_chr_open_mux(base); chr->filename = base->filename; + chr->avail_connections = MAX_MUX; QTAILQ_INSERT_TAIL(&chardevs, chr, next); + } else { + chr->avail_connections = 1; } chr->label = qemu_strdup(qemu_opts_id(opts)); return chr; @@ -2575,6 +2632,20 @@ } } +void qemu_chr_guest_open(struct CharDriverState *chr) +{ + if (chr->chr_guest_open) { + chr->chr_guest_open(chr); + } +} + +void qemu_chr_guest_close(struct CharDriverState *chr) +{ + if (chr->chr_guest_close) { + chr->chr_guest_close(chr); + } +} + void qemu_chr_close(CharDriverState *chr) { QTAILQ_REMOVE(&chardevs, chr, next); diff -Nru qemu-kvm-0.14.1+noroms/qemu-char.h qemu-kvm-0.15.0+noroms/qemu-char.h --- qemu-kvm-0.14.1+noroms/qemu-char.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-char.h 2011-08-09 07:40:29.000000000 -0500 @@ -57,6 +57,7 @@ void (*chr_update_read_handler)(struct CharDriverState *s); int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfd)(struct CharDriverState *s); + int (*chr_add_client)(struct CharDriverState *chr, int fd); IOEventHandler *chr_event; IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; @@ -65,11 +66,14 @@ void (*chr_close)(struct CharDriverState *chr); void (*chr_accept_input)(struct CharDriverState *chr); void (*chr_set_echo)(struct CharDriverState *chr, bool echo); + void (*chr_guest_open)(struct CharDriverState *chr); + void (*chr_guest_close)(struct CharDriverState *chr); void *opaque; QEMUBH *bh; char *label; char *filename; int opened; + int avail_connections; QTAILQ_ENTRY(CharDriverState) next; }; @@ -78,6 +82,8 @@ 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_guest_open(struct CharDriverState *chr); +void qemu_chr_guest_close(struct CharDriverState *chr); void qemu_chr_close(CharDriverState *chr); void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); @@ -94,6 +100,7 @@ void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len); int qemu_chr_get_msgfd(CharDriverState *s); void qemu_chr_accept_input(CharDriverState *s); +int qemu_chr_add_client(CharDriverState *s, int fd); void qemu_chr_info_print(Monitor *mon, const QObject *ret_data); void qemu_chr_info(Monitor *mon, QObject **ret_data); CharDriverState *qemu_chr_find(const char *name); diff -Nru qemu-kvm-0.14.1+noroms/qemu-common.h qemu-kvm-0.15.0+noroms/qemu-common.h --- qemu-kvm-0.14.1+noroms/qemu-common.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-common.h 2011-08-09 07:40:29.000000000 -0500 @@ -2,22 +2,19 @@ #ifndef QEMU_COMMON_H #define QEMU_COMMON_H +#include "compiler.h" #include "config-host.h" -#define QEMU_NORETURN __attribute__ ((__noreturn__)) -#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT -#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define QEMU_WARN_UNUSED_RESULT -#endif - -#define QEMU_BUILD_BUG_ON(x) typedef char __build_bug_on__##__LINE__[(x)?-1:1]; +#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) typedef struct QEMUTimer QEMUTimer; typedef struct QEMUFile QEMUFile; typedef struct QEMUBH QEMUBH; typedef struct DeviceState DeviceState; +struct Monitor; +typedef struct Monitor Monitor; + /* we put basic includes here to avoid repeating them in device drivers */ #include #include @@ -33,7 +30,17 @@ #include #include #include +#include #include +#include + +#ifdef _WIN32 +#include "qemu-os-win32.h" +#endif + +#ifdef CONFIG_POSIX +#include "qemu-os-posix.h" +#endif #ifndef O_LARGEFILE #define O_LARGEFILE 0 @@ -68,22 +75,6 @@ #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); @@ -98,17 +89,11 @@ _fullpath(resolved_path, path, _MAX_PATH); return resolved_path; } - -#define PRId64 "I64d" -#define PRIx64 "I64x" -#define PRIu64 "I64u" -#define PRIo64 "I64o" #endif /* FIXME: Remove NEED_CPU_H. */ #ifndef NEED_CPU_H -#include #include "osdep.h" #include "bswap.h" @@ -118,6 +103,11 @@ #endif /* !defined(NEED_CPU_H) */ +/* main function, renamed */ +#if defined(CONFIG_COCOA) +int qemu_main(int argc, char **argv, char **envp); +#endif + /* bottom halves */ typedef void QEMUBHFunc(void *opaque); @@ -188,11 +178,6 @@ #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); @@ -210,10 +195,17 @@ void qemu_set_cloexec(int fd); #ifndef _WIN32 +int qemu_add_child_watch(pid_t pid); int qemu_eventfd(int pipefd[2]); int qemu_pipe(int pipefd[2]); #endif +#ifdef _WIN32 +#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, (void *)buf, len, flags) +#else +#define qemu_recv(sockfd, buf, len, flags) recv(sockfd, buf, len, flags) +#endif + /* Error handling. */ void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); @@ -223,6 +215,9 @@ typedef int IOCanReadHandler(void *opaque); typedef void IOHandler(void *opaque); +void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds); +void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int rc); + struct ParallelIOArg { void *buffer; int count; @@ -288,7 +283,9 @@ /* Unblock cpu */ void qemu_cpu_kick(void *env); -int qemu_cpu_self(void *env); +void qemu_cpu_kick_self(void); +int qemu_cpu_is_self(void *env); +bool all_cpu_threads_idle(void); /* work queue */ struct qemu_work_item { @@ -325,8 +322,19 @@ void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, size_t skip); -struct Monitor; -typedef struct Monitor Monitor; +void qemu_progress_init(int enabled, float min_skip); +void qemu_progress_end(void); +void qemu_progress_print(float delta, int max); + +#define QEMU_FILE_TYPE_BIOS 0 +#define QEMU_FILE_TYPE_KEYMAP 1 +char *qemu_find_file(int type, const char *name); + +/* 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); /* Convert a byte between binary and BCD. */ static inline uint8_t to_bcd(uint8_t val) diff -Nru qemu-kvm-0.14.1+noroms/qemu-config.c qemu-kvm-0.15.0+noroms/qemu-config.c --- qemu-kvm-0.14.1+noroms/qemu-config.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-config.c 2011-08-09 07:40:29.000000000 -0500 @@ -2,7 +2,6 @@ #include "qemu-error.h" #include "qemu-option.h" #include "qemu-config.h" -#include "sysemu.h" #include "hw/qdev.h" static QemuOptsList qemu_drive_opts = { @@ -24,6 +23,7 @@ },{ .name = "index", .type = QEMU_OPT_NUMBER, + .help = "index number", },{ .name = "cyls", .type = QEMU_OPT_NUMBER, @@ -47,6 +47,7 @@ },{ .name = "snapshot", .type = QEMU_OPT_BOOL, + .help = "enable/disable snapshot mode", },{ .name = "file", .type = QEMU_OPT_STRING, @@ -66,12 +67,15 @@ },{ .name = "serial", .type = QEMU_OPT_STRING, + .help = "disk serial number", },{ .name = "rerror", .type = QEMU_OPT_STRING, + .help = "read error action", },{ .name = "werror", .type = QEMU_OPT_STRING, + .help = "write error action", },{ .name = "addr", .type = QEMU_OPT_STRING, @@ -79,6 +83,7 @@ },{ .name = "readonly", .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", },{ .name = "boot", .type = QEMU_OPT_BOOL, @@ -311,7 +316,7 @@ .name = "file", .type = QEMU_OPT_STRING, }, - { /* end if list */ } + { /* end of list */ } }, }; #endif @@ -390,6 +395,12 @@ .name = "disable-ticketing", .type = QEMU_OPT_BOOL, },{ + .name = "disable-copy-paste", + .type = QEMU_OPT_BOOL, + },{ + .name = "sasl", + .type = QEMU_OPT_BOOL, + },{ .name = "x509-dir", .type = QEMU_OPT_STRING, },{ @@ -435,7 +446,7 @@ .name = "playback-compression", .type = QEMU_OPT_BOOL, }, - { /* end if list */ } + { /* end of list */ } }, }; @@ -451,7 +462,25 @@ .name = "romfile", .type = QEMU_OPT_STRING, }, - { /* end if list */ } + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_machine_opts = { + .name = "machine", + .implied_opt_name = "type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head), + .desc = { + { + .name = "type", + .type = QEMU_OPT_STRING, + .help = "emulated machine" + }, { + .name = "accel", + .type = QEMU_OPT_STRING, + .help = "accelerator list", + }, + { /* End of list */ } }, }; @@ -469,6 +498,7 @@ &qemu_trace_opts, #endif &qemu_option_rom_opts, + &qemu_machine_opts, NULL, }; diff -Nru qemu-kvm-0.14.1+noroms/qemu-doc.texi qemu-kvm-0.15.0+noroms/qemu-doc.texi --- qemu-kvm-0.14.1+noroms/qemu-doc.texi 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-doc.texi 2011-08-09 07:40:29.000000000 -0500 @@ -278,7 +278,11 @@ @c man begin OPTIONS -During the graphical emulation, you can use the following keys: +During the graphical emulation, you can use special key combinations to change +modes. The default key mappings are shown below, but if you use @code{-alt-grab} +then the modifier is Ctrl-Alt-Shift (instead of Ctrl-Alt) and if you use +@code{-ctrl-grab} then the modifier is the right Ctrl key (instead of Ctrl-Alt): + @table @key @item Ctrl-Alt-f @kindex Ctrl-Alt-f diff -Nru qemu-kvm-0.14.1+noroms/qemu-error.c qemu-kvm-0.15.0+noroms/qemu-error.c --- qemu-kvm-0.14.1+noroms/qemu-error.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-error.c 2011-08-09 07:40:29.000000000 -0500 @@ -12,7 +12,6 @@ #include #include "monitor.h" -#include "sysemu.h" /* * Print to current monitor if we have one, else to stderr. @@ -194,6 +193,8 @@ /* * Print an error message to current monitor if we have one, else to stderr. + * Format arguments like sprintf(). The result should not contain + * newlines. * Prepend the current location and append a newline. * It's wrong to call this in a QMP monitor. Use qerror_report() there. */ diff -Nru qemu-kvm-0.14.1+noroms/qemu-ga.c qemu-kvm-0.15.0+noroms/qemu-ga.c --- qemu-kvm-0.14.1+noroms/qemu-ga.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qemu-ga.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,637 @@ +/* + * QEMU Guest Agent + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Adam Litke + * Michael Roth + * + * 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 +#include +#include +#include +#include +#include +#include "qemu_socket.h" +#include "json-streamer.h" +#include "json-parser.h" +#include "qint.h" +#include "qjson.h" +#include "qga/guest-agent-core.h" +#include "module.h" +#include "signal.h" +#include "qerror.h" +#include "error_int.h" + +#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid" +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */ +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */ + +struct GAState { + JSONMessageParser parser; + GMainLoop *main_loop; + GIOChannel *conn_channel; + GIOChannel *listen_channel; + const char *path; + const char *method; + bool virtio; /* fastpath to check for virtio to deal with poll() quirks */ + GACommandState *command_state; + GLogLevelFlags log_level; + FILE *log_file; + bool logging_enabled; +}; + +static struct GAState *ga_state; + +static void quit_handler(int sig) +{ + g_debug("recieved signal num %d, quitting", sig); + + if (g_main_loop_is_running(ga_state->main_loop)) { + g_main_loop_quit(ga_state->main_loop); + } +} + +static void register_signal_handlers(void) +{ + struct sigaction sigact; + int ret; + + memset(&sigact, 0, sizeof(struct sigaction)); + sigact.sa_handler = quit_handler; + + ret = sigaction(SIGINT, &sigact, NULL); + if (ret == -1) { + g_error("error configuring signal handler: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + ret = sigaction(SIGTERM, &sigact, NULL); + if (ret == -1) { + g_error("error configuring signal handler: %s", strerror(errno)); + } +} + +static void usage(const char *cmd) +{ + printf( +"Usage: %s -c \n" +"QEMU Guest Agent %s\n" +"\n" +" -m, --method transport method: one of unix-listen, virtio-serial, or\n" +" isa-serial (virtio-serial is the default)\n" +" -p, --path device/socket path (%s is the default for virtio-serial)\n" +" -l, --logfile set logfile path, logs to stderr by default\n" +" -f, --pidfile specify pidfile (default is %s)\n" +" -v, --verbose log extra debugging information\n" +" -V, --version print version information and exit\n" +" -d, --daemonize become a daemon\n" +" -h, --help display this help and exit\n" +"\n" +"Report bugs to \n" + , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT); +} + +static void conn_channel_close(GAState *s); + +static const char *ga_log_level_str(GLogLevelFlags level) +{ + switch (level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: + return "error"; + case G_LOG_LEVEL_CRITICAL: + return "critical"; + case G_LOG_LEVEL_WARNING: + return "warning"; + case G_LOG_LEVEL_MESSAGE: + return "message"; + case G_LOG_LEVEL_INFO: + return "info"; + case G_LOG_LEVEL_DEBUG: + return "debug"; + default: + return "user"; + } +} + +bool ga_logging_enabled(GAState *s) +{ + return s->logging_enabled; +} + +void ga_disable_logging(GAState *s) +{ + s->logging_enabled = false; +} + +void ga_enable_logging(GAState *s) +{ + s->logging_enabled = true; +} + +static void ga_log(const gchar *domain, GLogLevelFlags level, + const gchar *msg, gpointer opaque) +{ + GAState *s = opaque; + GTimeVal time; + const char *level_str = ga_log_level_str(level); + + if (!ga_logging_enabled(s)) { + return; + } + + level &= G_LOG_LEVEL_MASK; + if (g_strcmp0(domain, "syslog") == 0) { + syslog(LOG_INFO, "%s: %s", level_str, msg); + } else if (level & s->log_level) { + g_get_current_time(&time); + fprintf(s->log_file, + "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg); + fflush(s->log_file); + } +} + +static void become_daemon(const char *pidfile) +{ + pid_t pid, sid; + int pidfd; + char *pidstr = NULL; + + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + if (pid > 0) { + exit(EXIT_SUCCESS); + } + + pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR); + if (pidfd == -1) { + g_critical("Cannot create pid file, %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (asprintf(&pidstr, "%d", getpid()) == -1) { + g_critical("Cannot allocate memory"); + goto fail; + } + if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { + free(pidstr); + g_critical("Failed to write pid file"); + goto fail; + } + + umask(0); + sid = setsid(); + if (sid < 0) { + goto fail; + } + if ((chdir("/")) < 0) { + goto fail; + } + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + free(pidstr); + return; + +fail: + unlink(pidfile); + g_critical("failed to daemonize"); + exit(EXIT_FAILURE); +} + +static int conn_channel_send_buf(GIOChannel *channel, const char *buf, + gsize count) +{ + GError *err = NULL; + gsize written = 0; + GIOStatus status; + + while (count) { + status = g_io_channel_write_chars(channel, buf, count, &written, &err); + g_debug("sending data, count: %d", (int)count); + if (err != NULL) { + g_warning("error sending newline: %s", err->message); + return err->code; + } + if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) { + return -EPIPE; + } + + if (status == G_IO_STATUS_NORMAL) { + count -= written; + } + } + + return 0; +} + +static int conn_channel_send_payload(GIOChannel *channel, QObject *payload) +{ + int ret = 0; + const char *buf; + QString *payload_qstr; + GError *err = NULL; + + g_assert(payload && channel); + + payload_qstr = qobject_to_json(payload); + if (!payload_qstr) { + return -EINVAL; + } + + qstring_append_chr(payload_qstr, '\n'); + buf = qstring_get_str(payload_qstr); + ret = conn_channel_send_buf(channel, buf, strlen(buf)); + if (ret) { + goto out_free; + } + + g_io_channel_flush(channel, &err); + if (err != NULL) { + g_warning("error flushing payload: %s", err->message); + ret = err->code; + goto out_free; + } + +out_free: + QDECREF(payload_qstr); + if (err) { + g_error_free(err); + } + return ret; +} + +static void process_command(GAState *s, QDict *req) +{ + QObject *rsp = NULL; + int ret; + + g_assert(req); + g_debug("processing command"); + rsp = qmp_dispatch(QOBJECT(req)); + if (rsp) { + ret = conn_channel_send_payload(s->conn_channel, rsp); + if (ret) { + g_warning("error sending payload: %s", strerror(ret)); + } + qobject_decref(rsp); + } else { + g_warning("error getting response"); + } +} + +/* handle requests/control events coming in over the channel */ +static void process_event(JSONMessageParser *parser, QList *tokens) +{ + GAState *s = container_of(parser, GAState, parser); + QObject *obj; + QDict *qdict; + Error *err = NULL; + int ret; + + g_assert(s && parser); + + g_debug("process_event: called"); + obj = json_parser_parse_err(tokens, NULL, &err); + if (err || !obj || qobject_type(obj) != QTYPE_QDICT) { + qobject_decref(obj); + qdict = qdict_new(); + if (!err) { + g_warning("failed to parse event: unknown error"); + error_set(&err, QERR_JSON_PARSING); + } else { + g_warning("failed to parse event: %s", error_get_pretty(err)); + } + qdict_put_obj(qdict, "error", error_get_qobject(err)); + error_free(err); + } else { + qdict = qobject_to_qdict(obj); + } + + g_assert(qdict); + + /* handle host->guest commands */ + if (qdict_haskey(qdict, "execute")) { + process_command(s, qdict); + } else { + if (!qdict_haskey(qdict, "error")) { + QDECREF(qdict); + qdict = qdict_new(); + g_warning("unrecognized payload format"); + error_set(&err, QERR_UNSUPPORTED); + qdict_put_obj(qdict, "error", error_get_qobject(err)); + error_free(err); + } + ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict)); + if (ret) { + g_warning("error sending payload: %s", strerror(ret)); + } + } + + QDECREF(qdict); +} + +static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition, + gpointer data) +{ + GAState *s = data; + gchar buf[1024]; + gsize count; + GError *err = NULL; + memset(buf, 0, 1024); + GIOStatus status = g_io_channel_read_chars(channel, buf, 1024, + &count, &err); + if (err != NULL) { + g_warning("error reading channel: %s", err->message); + conn_channel_close(s); + g_error_free(err); + return false; + } + switch (status) { + case G_IO_STATUS_ERROR: + g_warning("problem"); + return false; + case G_IO_STATUS_NORMAL: + g_debug("read data, count: %d, data: %s", (int)count, buf); + json_message_parser_feed(&s->parser, (char *)buf, (int)count); + case G_IO_STATUS_AGAIN: + /* virtio causes us to spin here when no process is attached to + * host-side chardev. sleep a bit to mitigate this + */ + if (s->virtio) { + usleep(100*1000); + } + return true; + case G_IO_STATUS_EOF: + g_debug("received EOF"); + conn_channel_close(s); + if (s->virtio) { + return true; + } + return false; + default: + g_warning("unknown channel read status, closing"); + conn_channel_close(s); + return false; + } + return true; +} + +static int conn_channel_add(GAState *s, int fd) +{ + GIOChannel *conn_channel; + GError *err = NULL; + + g_assert(s && !s->conn_channel); + conn_channel = g_io_channel_unix_new(fd); + g_assert(conn_channel); + g_io_channel_set_encoding(conn_channel, NULL, &err); + if (err != NULL) { + g_warning("error setting channel encoding to binary"); + g_error_free(err); + return -1; + } + g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP, + conn_channel_read, s); + s->conn_channel = conn_channel; + return 0; +} + +static gboolean listen_channel_accept(GIOChannel *channel, + GIOCondition condition, gpointer data) +{ + GAState *s = data; + g_assert(channel != NULL); + int ret, conn_fd; + bool accepted = false; + struct sockaddr_un addr; + socklen_t addrlen = sizeof(addr); + + conn_fd = qemu_accept(g_io_channel_unix_get_fd(s->listen_channel), + (struct sockaddr *)&addr, &addrlen); + if (conn_fd == -1) { + g_warning("error converting fd to gsocket: %s", strerror(errno)); + goto out; + } + fcntl(conn_fd, F_SETFL, O_NONBLOCK); + ret = conn_channel_add(s, conn_fd); + if (ret) { + g_warning("error setting up connection"); + goto out; + } + accepted = true; + +out: + /* only accept 1 connection at a time */ + return !accepted; +} + +/* start polling for readable events on listen fd, new==true + * indicates we should use the existing s->listen_channel + */ +static int listen_channel_add(GAState *s, int listen_fd, bool new) +{ + if (new) { + s->listen_channel = g_io_channel_unix_new(listen_fd); + } + g_io_add_watch(s->listen_channel, G_IO_IN, + listen_channel_accept, s); + return 0; +} + +/* cleanup state for closed connection/session, start accepting new + * connections if we're in listening mode + */ +static void conn_channel_close(GAState *s) +{ + if (strcmp(s->method, "unix-listen") == 0) { + g_io_channel_shutdown(s->conn_channel, true, NULL); + listen_channel_add(s, 0, false); + } else if (strcmp(s->method, "virtio-serial") == 0) { + /* we spin on EOF for virtio-serial, so back off a bit. also, + * dont close the connection in this case, it'll resume normal + * operation when another process connects to host chardev + */ + usleep(100*1000); + goto out_noclose; + } + g_io_channel_unref(s->conn_channel); + s->conn_channel = NULL; +out_noclose: + return; +} + +static void init_guest_agent(GAState *s) +{ + struct termios tio; + int ret, fd; + + if (s->method == NULL) { + /* try virtio-serial as our default */ + s->method = "virtio-serial"; + } + + if (s->path == NULL) { + if (strcmp(s->method, "virtio-serial") != 0) { + g_critical("must specify a path for this channel"); + exit(EXIT_FAILURE); + } + /* try the default path for the virtio-serial port */ + s->path = QGA_VIRTIO_PATH_DEFAULT; + } + + if (strcmp(s->method, "virtio-serial") == 0) { + s->virtio = true; + fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC); + if (fd == -1) { + g_critical("error opening channel: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + ret = conn_channel_add(s, fd); + if (ret) { + g_critical("error adding channel to main loop"); + exit(EXIT_FAILURE); + } + } else if (strcmp(s->method, "isa-serial") == 0) { + fd = qemu_open(s->path, O_RDWR | O_NOCTTY); + if (fd == -1) { + g_critical("error opening channel: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + tcgetattr(fd, &tio); + /* set up serial port for non-canonical, dumb byte streaming */ + tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | + INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | + IMAXBEL); + tio.c_oflag = 0; + tio.c_lflag = 0; + tio.c_cflag |= QGA_BAUDRATE_DEFAULT; + /* 1 available byte min or reads will block (we'll set non-blocking + * elsewhere, else we have to deal with read()=0 instead) + */ + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + /* flush everything waiting for read/xmit, it's garbage at this point */ + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &tio); + ret = conn_channel_add(s, fd); + if (ret) { + g_error("error adding channel to main loop"); + } + } else if (strcmp(s->method, "unix-listen") == 0) { + fd = unix_listen(s->path, NULL, strlen(s->path)); + if (fd == -1) { + g_critical("error opening path: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + ret = listen_channel_add(s, fd, true); + if (ret) { + g_critical("error binding/listening to specified socket"); + exit(EXIT_FAILURE); + } + } else { + g_critical("unsupported channel method/type: %s", s->method); + exit(EXIT_FAILURE); + } + + json_message_parser_init(&s->parser, process_event); + s->main_loop = g_main_loop_new(NULL, false); +} + +int main(int argc, char **argv) +{ + const char *sopt = "hVvdm:p:l:f:"; + const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT; + const struct option lopt[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "logfile", 0, NULL, 'l' }, + { "pidfile", 0, NULL, 'f' }, + { "verbose", 0, NULL, 'v' }, + { "method", 0, NULL, 'm' }, + { "path", 0, NULL, 'p' }, + { "daemonize", 0, NULL, 'd' }, + { NULL, 0, NULL, 0 } + }; + int opt_ind = 0, ch, daemonize = 0; + GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; + FILE *log_file = stderr; + GAState *s; + + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + switch (ch) { + case 'm': + method = optarg; + break; + case 'p': + path = optarg; + break; + case 'l': + log_file = fopen(optarg, "a"); + if (!log_file) { + g_critical("unable to open specified log file: %s", + strerror(errno)); + return EXIT_FAILURE; + } + break; + case 'f': + pidfile = optarg; + break; + case 'v': + /* enable all log levels */ + log_level = G_LOG_LEVEL_MASK; + break; + case 'V': + printf("QEMU Guest Agent %s\n", QGA_VERSION); + return 0; + case 'd': + daemonize = 1; + break; + case 'h': + usage(argv[0]); + return 0; + case '?': + g_print("Unknown option, try '%s --help' for more information.\n", + argv[0]); + return EXIT_FAILURE; + } + } + + if (daemonize) { + g_debug("starting daemon"); + become_daemon(pidfile); + } + + s = qemu_mallocz(sizeof(GAState)); + s->conn_channel = NULL; + s->path = path; + s->method = method; + s->log_file = log_file; + s->log_level = log_level; + g_log_set_default_handler(ga_log, s); + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); + s->logging_enabled = true; + s->command_state = ga_command_state_new(); + ga_command_state_init(s, s->command_state); + ga_command_state_init_all(s->command_state); + ga_state = s; + + module_call_init(MODULE_INIT_QAPI); + init_guest_agent(ga_state); + register_signal_handlers(); + + g_main_loop_run(ga_state->main_loop); + + ga_command_state_cleanup_all(ga_state->command_state); + unlink(pidfile); + + return 0; +} diff -Nru qemu-kvm-0.14.1+noroms/qemu-img.c qemu-kvm-0.15.0+noroms/qemu-img.c --- qemu-kvm-0.14.1+noroms/qemu-img.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-img.c 2011-08-09 07:40:29.000000000 -0500 @@ -40,6 +40,7 @@ /* Default to cache=writeback as data integrity is not important for qemu-tcg. */ #define BDRV_O_FLAGS BDRV_O_CACHE_WB +#define BDRV_DEFAULT_CACHE "writeback" static void format_print(void *opaque, const char *name) { @@ -64,6 +65,8 @@ "Command parameters:\n" " 'filename' is a disk image filename\n" " 'fmt' is the disk image format. It is guessed automatically in most cases\n" + " 'cache' is the cache mode used to write the output disk image, the valid\n" + " options are: 'none', 'writeback' (default), 'writethrough' and 'unsafe'\n" " 'size' is the disk image size in bytes. Optional suffixes\n" " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n" " and T (terabyte, 1024G) are supported. 'b' is ignored.\n" @@ -77,6 +80,7 @@ " 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" + " '-p' show progress of command (only certain commands)\n" "\n" "Parameters to snapshot subcommand:\n" " 'snapshot' is the name of the snapshot to create, apply or delete\n" @@ -179,6 +183,27 @@ } #endif +static int set_cache_flag(const char *mode, int *flags) +{ + *flags &= ~BDRV_O_CACHE_MASK; + + if (!strcmp(mode, "none") || !strcmp(mode, "off")) { + *flags |= BDRV_O_CACHE_WB; + *flags |= BDRV_O_NOCACHE; + } else if (!strcmp(mode, "writeback")) { + *flags |= BDRV_O_CACHE_WB; + } else if (!strcmp(mode, "unsafe")) { + *flags |= BDRV_O_CACHE_WB; + *flags |= BDRV_O_NO_FLUSH; + } else if (!strcmp(mode, "writethrough")) { + /* this is the default */ + } else { + return -1; + } + + return 0; +} + static int print_block_option_help(const char *filename, const char *fmt) { BlockDriver *drv, *proto_drv; @@ -303,11 +328,11 @@ fmt = optarg; break; case 'e': - error_report("qemu-img: option -e is deprecated, please use \'-o " + error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); return 1; case '6': - error_report("qemu-img: option -6 is deprecated, please use \'-o " + error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); return 1; case 'o': @@ -440,13 +465,14 @@ static int img_commit(int argc, char **argv) { - int c, ret; - const char *filename, *fmt; + int c, ret, flags; + const char *filename, *fmt, *cache; BlockDriverState *bs; fmt = NULL; + cache = BDRV_DEFAULT_CACHE; for(;;) { - c = getopt(argc, argv, "f:h"); + c = getopt(argc, argv, "f:ht:"); if (c == -1) { break; } @@ -458,6 +484,9 @@ case 'f': fmt = optarg; break; + case 't': + cache = optarg; + break; } } if (optind >= argc) { @@ -465,7 +494,14 @@ } filename = argv[optind++]; - bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR); + flags = BDRV_O_RDWR; + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + + bs = bdrv_new_open(filename, fmt, flags); if (!bs) { return 1; } @@ -495,14 +531,37 @@ return 0; } +/* + * Checks whether the sector is not a zero sector. + * + * Attention! The len must be a multiple of 4 * sizeof(long) due to + * restriction of optimizations in this function. + */ static int is_not_zero(const uint8_t *sector, int len) { + /* + * Use long as the biggest available internal data type that fits into the + * CPU register and unroll the loop to smooth out the effect of memory + * latency. + */ + int i; - len >>= 2; - for(i = 0;i < len; i++) { - if (((uint32_t *)sector)[i] != 0) + long d0, d1, d2, d3; + const long * const data = (const long *) sector; + + len /= sizeof(long); + + for(i = 0; i < len; i += 4) { + d0 = data[i + 0]; + d1 = data[i + 1]; + d2 = data[i + 2]; + d3 = data[i + 3]; + + if (d0 || d1 || d2 || d3) { return 1; + } } + return 0; } @@ -567,7 +626,8 @@ static int img_convert(int argc, char **argv) { int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors; - const char *fmt, *out_fmt, *out_baseimg, *out_filename; + int progress = 0, flags; + const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; BlockDriverState **bs = NULL, *out_bs = NULL; int64_t total_sectors, nb_sectors, sector_num, bs_offset; @@ -579,13 +639,15 @@ QEMUOptionParameter *out_baseimg_param; char *options = NULL; const char *snapshot_name = NULL; + float local_progress; fmt = NULL; out_fmt = "raw"; + cache = "unsafe"; out_baseimg = NULL; compress = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:"); + c = getopt(argc, argv, "f:O:B:s:hce6o:pt:"); if (c == -1) { break; } @@ -607,11 +669,11 @@ compress = 1; break; case 'e': - error_report("qemu-img: option -e is deprecated, please use \'-o " + error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); return 1; case '6': - error_report("qemu-img: option -6 is deprecated, please use \'-o " + error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); return 1; case 'o': @@ -620,6 +682,12 @@ case 's': snapshot_name = optarg; break; + case 'p': + progress = 1; + break; + case 't': + cache = optarg; + break; } } @@ -642,6 +710,9 @@ goto out; } + qemu_progress_init(progress, 2.0); + qemu_progress_print(0, 100); + bs = qemu_mallocz(bs_n * sizeof(BlockDriverState *)); total_sectors = 0; @@ -658,12 +729,12 @@ if (snapshot_name != NULL) { if (bs_n > 1) { - error_report("No support for concatenating multiple snapshot\n"); + error_report("No support for concatenating multiple snapshot"); ret = -1; goto out; } if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) { - error_report("Failed to load snapshot\n"); + error_report("Failed to load snapshot"); ret = -1; goto out; } @@ -747,8 +818,14 @@ goto out; } - out_bs = bdrv_new_open(out_filename, out_fmt, - BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH); + flags = BDRV_O_RDWR; + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + + out_bs = bdrv_new_open(out_filename, out_fmt, flags); if (!out_bs) { ret = -1; goto out; @@ -773,6 +850,11 @@ } cluster_sectors = cluster_size >> 9; sector_num = 0; + + nb_sectors = total_sectors; + local_progress = (float)100 / + (nb_sectors / MIN(nb_sectors, cluster_sectors)); + for(;;) { int64_t bs_num; int remainder; @@ -832,6 +914,7 @@ } } sector_num += n; + qemu_progress_print(local_progress, 100); } /* signal EOF to align */ bdrv_write_compressed(out_bs, 0, NULL, 0); @@ -839,6 +922,10 @@ int has_zero_init = bdrv_has_zero_init(out_bs); sector_num = 0; // total number of sectors converted so far + nb_sectors = total_sectors - sector_num; + local_progress = (float)100 / + (nb_sectors / MIN(nb_sectors, IO_BUF_SIZE / 512)); + for(;;) { nb_sectors = total_sectors - sector_num; if (nb_sectors <= 0) { @@ -912,9 +999,11 @@ n -= n1; buf1 += n1 * 512; } + qemu_progress_print(local_progress, 100); } } out: + qemu_progress_end(); free_option_parameters(create_options); free_option_parameters(param); qemu_free(buf); @@ -935,35 +1024,6 @@ return 0; } -#ifdef _WIN32 -static int64_t get_allocated_file_size(const char *filename) -{ - typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high); - get_compressed_t get_compressed; - struct _stati64 st; - - /* WinNT support GetCompressedFileSize to determine allocate size */ - get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA"); - if (get_compressed) { - DWORD high, low; - low = get_compressed(filename, &high); - if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) - return (((int64_t) high) << 32) + low; - } - - if (_stati64(filename, &st) < 0) - return -1; - return st.st_size; -} -#else -static int64_t get_allocated_file_size(const char *filename) -{ - struct stat st; - if (stat(filename, &st) < 0) - return -1; - return (int64_t)st.st_blocks * 512; -} -#endif static void dump_snapshots(BlockDriverState *bs) { @@ -1023,7 +1083,7 @@ 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); + allocated_size = bdrv_get_allocated_file_size(bs); if (allocated_size < 0) { snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); } else { @@ -1181,17 +1241,18 @@ 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; + const char *fmt, *cache, *out_basefmt, *out_baseimg; int c, flags, ret; int unsafe = 0; + int progress = 0; /* Parse commandline parameters */ fmt = NULL; + cache = BDRV_DEFAULT_CACHE; out_baseimg = NULL; out_basefmt = NULL; - for(;;) { - c = getopt(argc, argv, "uhf:F:b:"); + c = getopt(argc, argv, "uhf:F:b:pt:"); if (c == -1) { break; } @@ -1212,21 +1273,36 @@ case 'u': unsafe = 1; break; + case 'p': + progress = 1; + break; + case 't': + cache = optarg; + break; } } - if ((optind >= argc) || !out_baseimg) { + if ((optind >= argc) || (!unsafe && !out_baseimg)) { help(); } filename = argv[optind++]; + qemu_progress_init(progress, 2.0); + qemu_progress_print(0, 100); + + flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + /* * 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; @@ -1295,12 +1371,15 @@ int n; uint8_t * buf_old; uint8_t * buf_new; + float local_progress; buf_old = qemu_malloc(IO_BUF_SIZE); buf_new = qemu_malloc(IO_BUF_SIZE); bdrv_get_geometry(bs, &num_sectors); + local_progress = (float)100 / + (num_sectors / MIN(num_sectors, IO_BUF_SIZE / 512)); for (sector = 0; sector < num_sectors; sector += n) { /* How many sectors can we handle with the next read? */ @@ -1348,6 +1427,7 @@ written += pnum; } + qemu_progress_print(local_progress, 100); } qemu_free(buf_old); @@ -1368,6 +1448,7 @@ out_baseimg, strerror(-ret)); } + qemu_progress_print(100, 0); /* * 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 @@ -1375,10 +1456,15 @@ * backing file, in case of a crash this would lead to corruption. */ out: + qemu_progress_end(); /* Cleanup */ if (!unsafe) { - bdrv_delete(bs_old_backing); - bdrv_delete(bs_new_backing); + if (bs_old_backing != NULL) { + bdrv_delete(bs_old_backing); + } + if (bs_new_backing != NULL) { + bdrv_delete(bs_new_backing); + } } bdrv_delete(bs); @@ -1404,6 +1490,16 @@ { NULL } }; + /* Remove size from argv manually so that negative numbers are not treated + * as options by getopt. */ + if (argc < 3) { + help(); + return 1; + } + + size = argv[--argc]; + + /* Parse getopt arguments */ fmt = NULL; for(;;) { c = getopt(argc, argv, "f:h"); @@ -1420,11 +1516,10 @@ break; } } - if (optind + 1 >= argc) { + if (optind >= argc) { help(); } filename = argv[optind++]; - size = argv[optind++]; /* Choose grow, shrink, or absolute resize mode */ switch (size[0]) { diff -Nru qemu-kvm-0.14.1+noroms/qemu-img-cmds.hx qemu-kvm-0.15.0+noroms/qemu-img-cmds.hx --- qemu-kvm-0.14.1+noroms/qemu-img-cmds.hx 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-img-cmds.hx 2011-08-09 07:40:29.000000000 -0500 @@ -22,15 +22,15 @@ ETEXI DEF("commit", img_commit, - "commit [-f fmt] filename") + "commit [-f fmt] [-t cache] filename") STEXI @item commit [-f @var{fmt}] @var{filename} ETEXI DEF("convert", img_convert, - "convert [-c] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") + "convert [-c] [-p] [-f fmt] [-t cache] [-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}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-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, @@ -46,9 +46,9 @@ ETEXI DEF("rebase", img_rebase, - "rebase [-f fmt] [-u] -b backing_file [-F backing_fmt] filename") + "rebase [-f fmt] [-t cache] [-p] [-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} +@item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} ETEXI DEF("resize", img_resize, diff -Nru qemu-kvm-0.14.1+noroms/qemu-img.texi qemu-kvm-0.15.0+noroms/qemu-img.texi --- qemu-kvm-0.14.1+noroms/qemu-img.texi 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-img.texi 2011-08-09 07:40:29.000000000 -0500 @@ -38,6 +38,8 @@ indicates that target image must be compressed (qcow format only) @item -h with or without a command shows help and lists the supported formats +@item -p +display progress bar (convert and rebase commands only) @end table Parameters to snapshot subcommand: @@ -84,7 +86,7 @@ Commit the changes recorded in @var{filename} in its base image. -@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-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} or a snapshot @var{snapshot_name} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} @@ -114,7 +116,7 @@ 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} +@item rebase [-f @var{fmt}] [-p] [-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. @@ -173,12 +175,6 @@ space. Use @code{qemu-img info} to know the real size used by the image or @code{ls -ls} on Unix/Linux. -@item host_device - -Host device format. This format should be used instead of raw when -converting to block devices or other devices where "holes" are not -supported. - @item qcow2 QEMU image format, the most versatile format. Use it to have smaller images (useful if your filesystem does not supports holes, for example diff -Nru qemu-kvm-0.14.1+noroms/qemu-io.c qemu-kvm-0.15.0+noroms/qemu-io.c --- qemu-kvm-0.14.1+noroms/qemu-io.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-io.c 2011-08-09 07:40:29.000000000 -0500 @@ -20,7 +20,7 @@ #define VERSION "0.0.1" -#define CMD_NOFILE_OK 0x01 +#define CMD_NOFILE_OK 0x01 char *progname; static BlockDriverState *bs; @@ -35,16 +35,16 @@ */ static int parse_pattern(const char *arg) { - char *endptr = NULL; - long pattern; + char *endptr = NULL; + long pattern; - pattern = strtol(arg, &endptr, 0); - if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') { - printf("%s is not a valid pattern byte\n", arg); - return -1; - } + pattern = strtol(arg, &endptr, 0); + if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') { + printf("%s is not a valid pattern byte\n", arg); + return -1; + } - return pattern; + return pattern; } /* @@ -54,70 +54,73 @@ * that is specified on the command line. */ -#define MISALIGN_OFFSET 16 +#define MISALIGN_OFFSET 16 static void *qemu_io_alloc(size_t len, int pattern) { - void *buf; + void *buf; - if (misalign) - len += MISALIGN_OFFSET; - buf = qemu_blockalign(bs, len); - memset(buf, pattern, len); - if (misalign) - buf += MISALIGN_OFFSET; - return buf; + if (misalign) { + len += MISALIGN_OFFSET; + } + buf = qemu_blockalign(bs, len); + memset(buf, pattern, len); + if (misalign) { + buf += MISALIGN_OFFSET; + } + return buf; } static void qemu_io_free(void *p) { - if (misalign) - p -= MISALIGN_OFFSET; - qemu_vfree(p); + if (misalign) { + p -= MISALIGN_OFFSET; + } + qemu_vfree(p); } -static void -dump_buffer(const void *buffer, int64_t offset, int len) +static void dump_buffer(const void *buffer, int64_t offset, int len) { - int i, j; - const uint8_t *p; + int i, j; + const uint8_t *p; - for (i = 0, p = buffer; i < len; i += 16) { - const uint8_t *s = p; + for (i = 0, p = buffer; i < len; i += 16) { + const uint8_t *s = p; - printf("%08" PRIx64 ": ", offset + i); - for (j = 0; j < 16 && i + j < len; j++, p++) - printf("%02x ", *p); - printf(" "); - for (j = 0; j < 16 && i + j < len; j++, s++) { - if (isalnum(*s)) - printf("%c", *s); - else - printf("."); - } - printf("\n"); - } + printf("%08" PRIx64 ": ", offset + i); + for (j = 0; j < 16 && i + j < len; j++, p++) { + printf("%02x ", *p); + } + printf(" "); + for (j = 0; j < 16 && i + j < len; j++, s++) { + if (isalnum(*s)) { + printf("%c", *s); + } else { + printf("."); + } + } + printf("\n"); + } } -static void -print_report(const char *op, struct timeval *t, int64_t offset, - int count, int total, int cnt, int Cflag) +static void print_report(const char *op, struct timeval *t, int64_t offset, + int count, int total, int cnt, int Cflag) { - char s1[64], s2[64], ts[64]; + char s1[64], s2[64], ts[64]; - timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0); - if (!Cflag) { - cvtstr((double)total, s1, sizeof(s1)); - cvtstr(tdiv((double)total, *t), s2, sizeof(s2)); - 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 */ - printf("%d,%d,%s,%.3f,%.3f\n", - total, cnt, ts, - tdiv((double)total, *t), - tdiv((double)cnt, *t)); - } + timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0); + if (!Cflag) { + cvtstr((double)total, s1, sizeof(s1)); + cvtstr(tdiv((double)total, *t), s2, sizeof(s2)); + 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 */ + printf("%d,%d,%s,%.3f,%.3f\n", + total, cnt, ts, + tdiv((double)total, *t), + tdiv((double)cnt, *t)); + } } /* @@ -127,192 +130,200 @@ static void * create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern) { - size_t *sizes = calloc(nr_iov, sizeof(size_t)); - size_t count = 0; - void *buf = NULL; - void *p; - int i; - - for (i = 0; i < nr_iov; i++) { - char *arg = argv[i]; - int64_t len; - - len = cvtnum(arg); - if (len < 0) { - printf("non-numeric length argument -- %s\n", arg); - goto fail; - } - - /* should be SIZE_T_MAX, but that doesn't exist */ - if (len > INT_MAX) { - printf("too large length argument -- %s\n", arg); - goto fail; - } - - if (len & 0x1ff) { - printf("length argument %" PRId64 - " is not sector aligned\n", len); - goto fail; - } - - sizes[i] = len; - count += len; - } - - qemu_iovec_init(qiov, nr_iov); - - buf = p = qemu_io_alloc(count, pattern); - - for (i = 0; i < nr_iov; i++) { - qemu_iovec_add(qiov, p, sizes[i]); - p += sizes[i]; - } + size_t *sizes = calloc(nr_iov, sizeof(size_t)); + size_t count = 0; + void *buf = NULL; + void *p; + int i; + + for (i = 0; i < nr_iov; i++) { + char *arg = argv[i]; + int64_t len; + + len = cvtnum(arg); + if (len < 0) { + printf("non-numeric length argument -- %s\n", arg); + goto fail; + } + + /* should be SIZE_T_MAX, but that doesn't exist */ + if (len > INT_MAX) { + printf("too large length argument -- %s\n", arg); + goto fail; + } + + if (len & 0x1ff) { + printf("length argument %" PRId64 + " is not sector aligned\n", len); + goto fail; + } + + sizes[i] = len; + count += len; + } + + qemu_iovec_init(qiov, nr_iov); + + buf = p = qemu_io_alloc(count, pattern); + + for (i = 0; i < nr_iov; i++) { + qemu_iovec_add(qiov, p, sizes[i]); + p += sizes[i]; + } fail: - free(sizes); - return buf; + free(sizes); + return buf; } static int do_read(char *buf, int64_t offset, int count, int *total) { - int ret; + int ret; - ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9); - if (ret < 0) - return ret; - *total = count; - return 1; + ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9); + if (ret < 0) { + return ret; + } + *total = count; + return 1; } static int do_write(char *buf, int64_t offset, int count, int *total) { - int ret; + int ret; - ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9); - if (ret < 0) - return ret; - *total = count; - return 1; + ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9); + if (ret < 0) { + return ret; + } + *total = count; + return 1; } static int do_pread(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_pread(bs, offset, (uint8_t *)buf, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_pread(bs, offset, (uint8_t *)buf, count); + if (*total < 0) { + return *total; + } + return 1; } static int do_pwrite(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count); + if (*total < 0) { + return *total; + } + return 1; } static int do_load_vmstate(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); + if (*total < 0) { + return *total; + } + return 1; } static int do_save_vmstate(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count); + if (*total < 0) { + return *total; + } + return 1; } #define NOT_DONE 0x7fffffff static void aio_rw_done(void *opaque, int ret) { - *(int *)opaque = ret; + *(int *)opaque = ret; } static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total) { - BlockDriverAIOCB *acb; - int async_ret = NOT_DONE; + BlockDriverAIOCB *acb; + int async_ret = NOT_DONE; - acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); - if (!acb) - return -EIO; - - while (async_ret == NOT_DONE) - qemu_aio_wait(); + acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); + if (!acb) { + return -EIO; + } + while (async_ret == NOT_DONE) { + qemu_aio_wait(); + } - *total = qiov->size; - return async_ret < 0 ? async_ret : 1; + *total = qiov->size; + return async_ret < 0 ? async_ret : 1; } static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total) { - BlockDriverAIOCB *acb; - int async_ret = NOT_DONE; + BlockDriverAIOCB *acb; + int async_ret = NOT_DONE; - acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); - if (!acb) - return -EIO; + acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); + if (!acb) { + return -EIO; + } - while (async_ret == NOT_DONE) - qemu_aio_wait(); + while (async_ret == NOT_DONE) { + qemu_aio_wait(); + } - *total = qiov->size; - return async_ret < 0 ? async_ret : 1; + *total = qiov->size; + return async_ret < 0 ? async_ret : 1; } struct multiwrite_async_ret { - int num_done; - int error; + int num_done; + int error; }; static void multiwrite_cb(void *opaque, int ret) { - struct multiwrite_async_ret *async_ret = opaque; + struct multiwrite_async_ret *async_ret = opaque; - async_ret->num_done++; - if (ret < 0) { - async_ret->error = ret; - } + 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(); - } + 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; + } - return async_ret.error < 0 ? async_ret.error : 1; + 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) +static void read_help(void) { - printf( + printf( "\n" " reads a range of bytes from the given offset\n" "\n" @@ -335,94 +346,95 @@ static int read_f(int argc, char **argv); static const cmdinfo_t read_cmd = { - .name = "read", - .altname = "r", - .cfunc = read_f, - .argmin = 2, - .argmax = -1, - .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len", - .oneline = "reads a number of bytes at a specified offset", - .help = read_help, + .name = "read", + .altname = "r", + .cfunc = read_f, + .argmin = 2, + .argmax = -1, + .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len", + .oneline = "reads a number of bytes at a specified offset", + .help = read_help, }; -static int -read_f(int argc, char **argv) +static int read_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; - int Pflag = 0, sflag = 0, lflag = 0, bflag = 0; - int c, cnt; - char *buf; - int64_t offset; - int count; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int pattern = 0, pattern_offset = 0, pattern_count = 0; - - while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) { - switch (c) { - case 'b': - bflag = 1; - break; - case 'C': - Cflag = 1; - break; - case 'l': - lflag = 1; - pattern_count = cvtnum(optarg); - if (pattern_count < 0) { - printf("non-numeric length argument -- %s\n", optarg); - return 0; - } - break; - case 'p': - pflag = 1; - break; - case 'P': - Pflag = 1; - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - case 'q': - qflag = 1; - break; - case 's': - sflag = 1; - pattern_offset = cvtnum(optarg); - if (pattern_offset < 0) { - printf("non-numeric length argument -- %s\n", optarg); - return 0; - } - break; - case 'v': - vflag = 1; - break; - default: - return command_usage(&read_cmd); - } - } - - if (optind != argc - 2) - return command_usage(&read_cmd); - - if (bflag && pflag) { - printf("-b and -p cannot be specified at the same time\n"); - return 0; - } - - 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; - } + struct timeval t1, t2; + int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; + int Pflag = 0, sflag = 0, lflag = 0, bflag = 0; + int c, cnt; + char *buf; + int64_t offset; + int count; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int pattern = 0, pattern_offset = 0, pattern_count = 0; + + while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) { + switch (c) { + case 'b': + bflag = 1; + break; + case 'C': + Cflag = 1; + break; + case 'l': + lflag = 1; + pattern_count = cvtnum(optarg); + if (pattern_count < 0) { + printf("non-numeric length argument -- %s\n", optarg); + return 0; + } + break; + case 'p': + pflag = 1; + break; + case 'P': + Pflag = 1; + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + case 's': + sflag = 1; + pattern_offset = cvtnum(optarg); + if (pattern_offset < 0) { + printf("non-numeric length argument -- %s\n", optarg); + return 0; + } + break; + case 'v': + vflag = 1; + break; + default: + return command_usage(&read_cmd); + } + } + + if (optind != argc - 2) { + return command_usage(&read_cmd); + } + + if (bflag && pflag) { + printf("-b and -p cannot be specified at the same time\n"); + return 0; + } + + 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; + } if (!Pflag && (lflag || sflag)) { return command_usage(&read_cmd); @@ -437,66 +449,68 @@ return 0; } - if (!pflag) - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - - if (count & 0x1ff) { - printf("count %d is not sector aligned\n", - count); - return 0; - } - } - - buf = qemu_io_alloc(count, 0xab); - - gettimeofday(&t1, NULL); - if (pflag) - cnt = do_pread(buf, offset, count, &total); - else if (bflag) - cnt = do_load_vmstate(buf, offset, count, &total); - else - cnt = do_read(buf, offset, count, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("read failed: %s\n", strerror(-cnt)); - goto out; - } - - if (Pflag) { - 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 %" - PRId64 ", %d bytes\n", - offset + pattern_offset, pattern_count); - } - free(cmp_buf); - } - - if (qflag) - goto out; - - if (vflag) - dump_buffer(buf, offset, count); - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("read", &t2, offset, count, total, cnt, Cflag); + if (!pflag) { + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + if (count & 0x1ff) { + printf("count %d is not sector aligned\n", + count); + return 0; + } + } + + buf = qemu_io_alloc(count, 0xab); + + gettimeofday(&t1, NULL); + if (pflag) { + cnt = do_pread(buf, offset, count, &total); + } else if (bflag) { + cnt = do_load_vmstate(buf, offset, count, &total); + } else { + cnt = do_read(buf, offset, count, &total); + } + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("read failed: %s\n", strerror(-cnt)); + goto out; + } + + if (Pflag) { + 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 %" + PRId64 ", %d bytes\n", + offset + pattern_offset, pattern_count); + } + free(cmp_buf); + } + + if (qflag) { + goto out; + } + + if (vflag) { + dump_buffer(buf, offset, count); + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("read", &t2, offset, count, total, cnt, Cflag); out: - qemu_io_free(buf); + qemu_io_free(buf); - return 0; + return 0; } -static void -readv_help(void) +static void readv_help(void) { - printf( + printf( "\n" " reads a range of bytes from the given offset into multiple buffers\n" "\n" @@ -516,111 +530,112 @@ static int readv_f(int argc, char **argv); static const cmdinfo_t readv_cmd = { - .name = "readv", - .cfunc = readv_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cqv] [-P pattern ] off len [len..]", - .oneline = "reads a number of bytes at a specified offset", - .help = readv_help, + .name = "readv", + .cfunc = readv_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cqv] [-P pattern ] off len [len..]", + .oneline = "reads a number of bytes at a specified offset", + .help = readv_help, }; -static int -readv_f(int argc, char **argv) +static int readv_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, qflag = 0, vflag = 0; - int c, cnt; - char *buf; - int64_t offset; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - QEMUIOVector qiov; - int pattern = 0; - int Pflag = 0; - - while ((c = getopt(argc, argv, "CP:qv")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'P': - Pflag = 1; - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - case 'q': - qflag = 1; - break; - case 'v': - vflag = 1; - break; - default: - return command_usage(&readv_cmd); - } - } - - if (optind > argc - 2) - return command_usage(&readv_cmd); - - - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } - optind++; - - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - nr_iov = argc - optind; - buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab); - - gettimeofday(&t1, NULL); - cnt = do_aio_readv(&qiov, offset, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("readv failed: %s\n", strerror(-cnt)); - goto out; - } - - if (Pflag) { - 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 %" - PRId64 ", %zd bytes\n", - offset, qiov.size); - } - free(cmp_buf); - } - - if (qflag) - goto out; - - if (vflag) - dump_buffer(buf, offset, qiov.size); - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("read", &t2, offset, qiov.size, total, cnt, Cflag); + struct timeval t1, t2; + int Cflag = 0, qflag = 0, vflag = 0; + int c, cnt; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + QEMUIOVector qiov; + int pattern = 0; + int Pflag = 0; + + while ((c = getopt(argc, argv, "CP:qv")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'P': + Pflag = 1; + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + case 'v': + vflag = 1; + break; + default: + return command_usage(&readv_cmd); + } + } + + if (optind > argc - 2) { + return command_usage(&readv_cmd); + } + + + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + optind++; + + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + nr_iov = argc - optind; + buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab); + + gettimeofday(&t1, NULL); + cnt = do_aio_readv(&qiov, offset, &total); + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("readv failed: %s\n", strerror(-cnt)); + goto out; + } + + if (Pflag) { + 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 %" + PRId64 ", %zd bytes\n", offset, qiov.size); + } + free(cmp_buf); + } + + if (qflag) { + goto out; + } + + if (vflag) { + dump_buffer(buf, offset, qiov.size); + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("read", &t2, offset, qiov.size, total, cnt, Cflag); out: - qemu_io_free(buf); - return 0; + qemu_io_free(buf); + return 0; } -static void -write_help(void) +static void write_help(void) { - printf( + printf( "\n" " writes a range of bytes from the given offset\n" "\n" @@ -640,121 +655,124 @@ static int write_f(int argc, char **argv); static const cmdinfo_t write_cmd = { - .name = "write", - .altname = "w", - .cfunc = write_f, - .argmin = 2, - .argmax = -1, - .args = "[-abCpq] [-P pattern ] off len", - .oneline = "writes a number of bytes at a specified offset", - .help = write_help, + .name = "write", + .altname = "w", + .cfunc = write_f, + .argmin = 2, + .argmax = -1, + .args = "[-abCpq] [-P pattern ] off len", + .oneline = "writes a number of bytes at a specified offset", + .help = write_help, }; -static int -write_f(int argc, char **argv) +static int write_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, pflag = 0, qflag = 0, bflag = 0; - int c, cnt; - char *buf; - int64_t offset; - int count; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int pattern = 0xcd; - - while ((c = getopt(argc, argv, "bCpP:q")) != EOF) { - switch (c) { - case 'b': - bflag = 1; - break; - case 'C': - Cflag = 1; - break; - case 'p': - pflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - case 'q': - qflag = 1; - break; - default: - return command_usage(&write_cmd); - } - } - - if (optind != argc - 2) - return command_usage(&write_cmd); - - if (bflag && pflag) { - printf("-b and -p cannot be specified at the same time\n"); - return 0; - } - - 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; - } - - if (!pflag) { - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - if (count & 0x1ff) { - printf("count %d is not sector aligned\n", - count); - return 0; - } - } - - buf = qemu_io_alloc(count, pattern); - - gettimeofday(&t1, NULL); - if (pflag) - cnt = do_pwrite(buf, offset, count, &total); - else if (bflag) - cnt = do_save_vmstate(buf, offset, count, &total); - else - cnt = do_write(buf, offset, count, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("write 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, offset, count, total, cnt, Cflag); + struct timeval t1, t2; + int Cflag = 0, pflag = 0, qflag = 0, bflag = 0; + int c, cnt; + char *buf; + int64_t offset; + int count; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int pattern = 0xcd; + + while ((c = getopt(argc, argv, "bCpP:q")) != EOF) { + switch (c) { + case 'b': + bflag = 1; + break; + case 'C': + Cflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + default: + return command_usage(&write_cmd); + } + } + + if (optind != argc - 2) { + return command_usage(&write_cmd); + } + + if (bflag && pflag) { + printf("-b and -p cannot be specified at the same time\n"); + return 0; + } + + 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; + } + + if (!pflag) { + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + if (count & 0x1ff) { + printf("count %d is not sector aligned\n", + count); + return 0; + } + } + + buf = qemu_io_alloc(count, pattern); + + gettimeofday(&t1, NULL); + if (pflag) { + cnt = do_pwrite(buf, offset, count, &total); + } else if (bflag) { + cnt = do_save_vmstate(buf, offset, count, &total); + } else { + cnt = do_write(buf, offset, count, &total); + } + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("write 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, offset, count, total, cnt, Cflag); out: - qemu_io_free(buf); + qemu_io_free(buf); - return 0; + return 0; } static void writev_help(void) { - printf( + printf( "\n" " writes a range of bytes from the given offset source from multiple buffers\n" "\n" @@ -772,90 +790,91 @@ static int writev_f(int argc, char **argv); static const cmdinfo_t writev_cmd = { - .name = "writev", - .cfunc = writev_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..]", - .oneline = "writes a number of bytes at a specified offset", - .help = writev_help, + .name = "writev", + .cfunc = writev_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..]", + .oneline = "writes a number of bytes at a specified offset", + .help = writev_help, }; -static int -writev_f(int argc, char **argv) +static int writev_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, qflag = 0; - int c, cnt; - char *buf; - int64_t offset; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - int pattern = 0xcd; - QEMUIOVector qiov; - - 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); - - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } - optind++; - - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - nr_iov = argc - optind; - buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern); - - gettimeofday(&t1, NULL); - cnt = do_aio_writev(&qiov, offset, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("writev 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, offset, qiov.size, total, cnt, Cflag); + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, cnt; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + int pattern = 0xcd; + QEMUIOVector qiov; + + 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); + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + optind++; + + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + nr_iov = argc - optind; + buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern); + + gettimeofday(&t1, NULL); + cnt = do_aio_writev(&qiov, offset, &total); + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("writev 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, offset, qiov.size, total, cnt, Cflag); out: - qemu_io_free(buf); - return 0; + qemu_io_free(buf); + return 0; } -static void -multiwrite_help(void) +static void multiwrite_help(void) { - printf( + 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" @@ -876,217 +895,215 @@ 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, + .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) +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; - } + 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); + /* 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; + 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; - char *buf; - int qflag; - int vflag; - int Cflag; - int Pflag; - int pattern; - struct timeval t1; + QEMUIOVector qiov; + int64_t offset; + char *buf; + int qflag; + int vflag; + int Cflag; + int Pflag; + int pattern; + struct timeval t1; }; -static void -aio_write_done(void *opaque, int ret) +static void aio_write_done(void *opaque, int ret) { - struct aio_ctx *ctx = opaque; - struct timeval t2; + struct aio_ctx *ctx = opaque; + struct timeval t2; - gettimeofday(&t2, NULL); + gettimeofday(&t2, NULL); - if (ret < 0) { - printf("aio_write failed: %s\n", strerror(-ret)); - goto out; - } + if (ret < 0) { + printf("aio_write failed: %s\n", strerror(-ret)); + goto out; + } - if (ctx->qflag) { - goto out; - } + if (ctx->qflag) { + goto out; + } - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, ctx->t1); - print_report("wrote", &t2, ctx->offset, ctx->qiov.size, - ctx->qiov.size, 1, ctx->Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, ctx->t1); + print_report("wrote", &t2, ctx->offset, ctx->qiov.size, + ctx->qiov.size, 1, ctx->Cflag); out: - qemu_io_free(ctx->buf); - free(ctx); + qemu_io_free(ctx->buf); + free(ctx); } -static void -aio_read_done(void *opaque, int ret) +static void aio_read_done(void *opaque, int ret) { - struct aio_ctx *ctx = opaque; - struct timeval t2; + struct aio_ctx *ctx = opaque; + struct timeval t2; - gettimeofday(&t2, NULL); + gettimeofday(&t2, NULL); + + if (ret < 0) { + printf("readv failed: %s\n", strerror(-ret)); + goto out; + } + + if (ctx->Pflag) { + void *cmp_buf = malloc(ctx->qiov.size); + + memset(cmp_buf, ctx->pattern, ctx->qiov.size); + if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size); + } + free(cmp_buf); + } + + if (ctx->qflag) { + goto out; + } + + if (ctx->vflag) { + dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size); + } - if (ret < 0) { - printf("readv failed: %s\n", strerror(-ret)); - goto out; - } - - if (ctx->Pflag) { - void *cmp_buf = malloc(ctx->qiov.size); - - memset(cmp_buf, ctx->pattern, ctx->qiov.size); - if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { - printf("Pattern verification failed at offset %" - PRId64 ", %zd bytes\n", - ctx->offset, ctx->qiov.size); - } - free(cmp_buf); - } - - if (ctx->qflag) { - goto out; - } - - if (ctx->vflag) { - dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size); - } - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, ctx->t1); - print_report("read", &t2, ctx->offset, ctx->qiov.size, - ctx->qiov.size, 1, ctx->Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, ctx->t1); + print_report("read", &t2, ctx->offset, ctx->qiov.size, + ctx->qiov.size, 1, ctx->Cflag); out: - qemu_io_free(ctx->buf); - free(ctx); + qemu_io_free(ctx->buf); + free(ctx); } -static void -aio_read_help(void) +static void aio_read_help(void) { - printf( + printf( "\n" " asynchronously reads a range of bytes from the given offset\n" "\n" @@ -1107,88 +1124,86 @@ static int aio_read_f(int argc, char **argv); static const cmdinfo_t aio_read_cmd = { - .name = "aio_read", - .cfunc = aio_read_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cqv] [-P pattern ] off len [len..]", - .oneline = "asynchronously reads a number of bytes", - .help = aio_read_help, + .name = "aio_read", + .cfunc = aio_read_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cqv] [-P pattern ] off len [len..]", + .oneline = "asynchronously reads a number of bytes", + .help = aio_read_help, }; -static int -aio_read_f(int argc, char **argv) +static int aio_read_f(int argc, char **argv) { - int nr_iov, c; - struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); - BlockDriverAIOCB *acb; - - while ((c = getopt(argc, argv, "CP:qv")) != EOF) { - switch (c) { - case 'C': - ctx->Cflag = 1; - break; - case 'P': - ctx->Pflag = 1; - ctx->pattern = parse_pattern(optarg); - if (ctx->pattern < 0) { - free(ctx); - return 0; - } - break; - case 'q': - ctx->qflag = 1; - break; - case 'v': - ctx->vflag = 1; - break; - default: - free(ctx); - return command_usage(&aio_read_cmd); - } - } - - if (optind > argc - 2) { - free(ctx); - return command_usage(&aio_read_cmd); - } - - ctx->offset = cvtnum(argv[optind]); - if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - free(ctx); - return 0; - } - optind++; - - if (ctx->offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - ctx->offset); - free(ctx); - return 0; - } - - nr_iov = argc - optind; - ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab); - - gettimeofday(&ctx->t1, NULL); - acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_read_done, ctx); - if (!acb) { - free(ctx->buf); - free(ctx); - return -EIO; - } + int nr_iov, c; + struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); + BlockDriverAIOCB *acb; + + while ((c = getopt(argc, argv, "CP:qv")) != EOF) { + switch (c) { + case 'C': + ctx->Cflag = 1; + break; + case 'P': + ctx->Pflag = 1; + ctx->pattern = parse_pattern(optarg); + if (ctx->pattern < 0) { + free(ctx); + return 0; + } + break; + case 'q': + ctx->qflag = 1; + break; + case 'v': + ctx->vflag = 1; + break; + default: + free(ctx); + return command_usage(&aio_read_cmd); + } + } + + if (optind > argc - 2) { + free(ctx); + return command_usage(&aio_read_cmd); + } - return 0; + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + free(ctx); + return 0; + } + optind++; + + if (ctx->offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); + free(ctx); + return 0; + } + + nr_iov = argc - optind; + ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab); + + gettimeofday(&ctx->t1, NULL); + acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_read_done, ctx); + if (!acb) { + free(ctx->buf); + free(ctx); + return -EIO; + } + + return 0; } -static void -aio_write_help(void) +static void aio_write_help(void) { - printf( + printf( "\n" -" asynchronously writes a range of bytes from the given offset source \n" +" asynchronously writes a range of bytes from the given offset source\n" " from multiple buffers\n" "\n" " Example:\n" @@ -1207,199 +1222,196 @@ static int aio_write_f(int argc, char **argv); static const cmdinfo_t aio_write_cmd = { - .name = "aio_write", - .cfunc = aio_write_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..]", - .oneline = "asynchronously writes a number of bytes", - .help = aio_write_help, + .name = "aio_write", + .cfunc = aio_write_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..]", + .oneline = "asynchronously writes a number of bytes", + .help = aio_write_help, }; -static int -aio_write_f(int argc, char **argv) +static int aio_write_f(int argc, char **argv) { - int nr_iov, c; - int pattern = 0xcd; - struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); - BlockDriverAIOCB *acb; - - while ((c = getopt(argc, argv, "CqP:")) != EOF) { - switch (c) { - case 'C': - ctx->Cflag = 1; - break; - case 'q': - ctx->qflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - default: - free(ctx); - return command_usage(&aio_write_cmd); - } - } - - if (optind > argc - 2) { - free(ctx); - return command_usage(&aio_write_cmd); - } - - ctx->offset = cvtnum(argv[optind]); - if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - free(ctx); - return 0; - } - optind++; - - if (ctx->offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - ctx->offset); - free(ctx); - return 0; - } - - nr_iov = argc - optind; - ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern); - - gettimeofday(&ctx->t1, NULL); - acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_write_done, ctx); - if (!acb) { - free(ctx->buf); - free(ctx); - return -EIO; - } + int nr_iov, c; + int pattern = 0xcd; + struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); + BlockDriverAIOCB *acb; + + while ((c = getopt(argc, argv, "CqP:")) != EOF) { + switch (c) { + case 'C': + ctx->Cflag = 1; + break; + case 'q': + ctx->qflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + default: + free(ctx); + return command_usage(&aio_write_cmd); + } + } - return 0; + if (optind > argc - 2) { + free(ctx); + return command_usage(&aio_write_cmd); + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + free(ctx); + return 0; + } + optind++; + + if (ctx->offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); + free(ctx); + return 0; + } + + nr_iov = argc - optind; + ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern); + + gettimeofday(&ctx->t1, NULL); + acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_write_done, ctx); + if (!acb) { + free(ctx->buf); + free(ctx); + return -EIO; + } + + return 0; } -static int -aio_flush_f(int argc, char **argv) +static int aio_flush_f(int argc, char **argv) { - qemu_aio_flush(); - return 0; + qemu_aio_flush(); + return 0; } static const cmdinfo_t aio_flush_cmd = { - .name = "aio_flush", - .cfunc = aio_flush_f, - .oneline = "completes all outstanding aio requests" + .name = "aio_flush", + .cfunc = aio_flush_f, + .oneline = "completes all outstanding aio requests" }; -static int -flush_f(int argc, char **argv) +static int flush_f(int argc, char **argv) { - bdrv_flush(bs); - return 0; + bdrv_flush(bs); + return 0; } static const cmdinfo_t flush_cmd = { - .name = "flush", - .altname = "f", - .cfunc = flush_f, - .oneline = "flush all in-core file state to disk", + .name = "flush", + .altname = "f", + .cfunc = flush_f, + .oneline = "flush all in-core file state to disk", }; -static int -truncate_f(int argc, char **argv) +static int truncate_f(int argc, char **argv) { - int64_t offset; - int ret; + int64_t offset; + int ret; + + offset = cvtnum(argv[1]); + if (offset < 0) { + printf("non-numeric truncate argument -- %s\n", argv[1]); + return 0; + } - offset = cvtnum(argv[1]); - if (offset < 0) { - printf("non-numeric truncate argument -- %s\n", argv[1]); - return 0; - } - - ret = bdrv_truncate(bs, offset); - if (ret < 0) { - printf("truncate: %s\n", strerror(-ret)); - return 0; - } + ret = bdrv_truncate(bs, offset); + if (ret < 0) { + printf("truncate: %s\n", strerror(-ret)); + return 0; + } - return 0; + return 0; } static const cmdinfo_t truncate_cmd = { - .name = "truncate", - .altname = "t", - .cfunc = truncate_f, - .argmin = 1, - .argmax = 1, - .args = "off", - .oneline = "truncates the current file at the given offset", + .name = "truncate", + .altname = "t", + .cfunc = truncate_f, + .argmin = 1, + .argmax = 1, + .args = "off", + .oneline = "truncates the current file at the given offset", }; -static int -length_f(int argc, char **argv) +static int length_f(int argc, char **argv) { - int64_t size; - char s1[64]; + int64_t size; + char s1[64]; - size = bdrv_getlength(bs); - if (size < 0) { - printf("getlength: %s\n", strerror(-size)); - return 0; - } - - cvtstr(size, s1, sizeof(s1)); - printf("%s\n", s1); - return 0; + size = bdrv_getlength(bs); + if (size < 0) { + printf("getlength: %s\n", strerror(-size)); + return 0; + } + + cvtstr(size, s1, sizeof(s1)); + printf("%s\n", s1); + return 0; } static const cmdinfo_t length_cmd = { - .name = "length", - .altname = "l", - .cfunc = length_f, - .oneline = "gets the length of the current file", + .name = "length", + .altname = "l", + .cfunc = length_f, + .oneline = "gets the length of the current file", }; -static int -info_f(int argc, char **argv) +static int info_f(int argc, char **argv) { - BlockDriverInfo bdi; - char s1[64], s2[64]; - int ret; + BlockDriverInfo bdi; + char s1[64], s2[64]; + int ret; - if (bs->drv && bs->drv->format_name) - printf("format name: %s\n", bs->drv->format_name); - if (bs->drv && bs->drv->protocol_name) - printf("format name: %s\n", bs->drv->protocol_name); + if (bs->drv && bs->drv->format_name) { + printf("format name: %s\n", bs->drv->format_name); + } + if (bs->drv && bs->drv->protocol_name) { + printf("format name: %s\n", bs->drv->protocol_name); + } - ret = bdrv_get_info(bs, &bdi); - if (ret) - return 0; + ret = bdrv_get_info(bs, &bdi); + if (ret) { + return 0; + } - cvtstr(bdi.cluster_size, s1, sizeof(s1)); - cvtstr(bdi.vm_state_offset, s2, sizeof(s2)); + cvtstr(bdi.cluster_size, s1, sizeof(s1)); + cvtstr(bdi.vm_state_offset, s2, sizeof(s2)); - printf("cluster size: %s\n", s1); - printf("vm state offset: %s\n", s2); + printf("cluster size: %s\n", s1); + printf("vm state offset: %s\n", s2); - return 0; + return 0; } static const cmdinfo_t info_cmd = { - .name = "info", - .altname = "i", - .cfunc = info_f, - .oneline = "prints information about the current file", + .name = "info", + .altname = "i", + .cfunc = info_f, + .oneline = "prints information about the current file", }; -static void -discard_help(void) +static void discard_help(void) { - printf( + printf( "\n" " discards a range of bytes from the given offset\n" "\n" @@ -1415,148 +1427,147 @@ 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, + .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) +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); - } + 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; + return 0; } -static int -alloc_f(int argc, char **argv) +static int alloc_f(int argc, char **argv) { - int64_t offset; - int nb_sectors, remaining; - char s1[64]; - int num, sum_alloc; - int ret; - - offset = cvtnum(argv[1]); - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - if (argc == 3) - nb_sectors = cvtnum(argv[2]); - else - nb_sectors = 1; - - remaining = nb_sectors; - sum_alloc = 0; - while (remaining) { - ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num); - remaining -= num; - if (ret) { - sum_alloc += num; - } - } - - cvtstr(offset, s1, sizeof(s1)); - - printf("%d/%d sectors allocated at offset %s\n", - sum_alloc, nb_sectors, s1); - return 0; + int64_t offset; + int nb_sectors, remaining; + char s1[64]; + int num, sum_alloc; + int ret; + + offset = cvtnum(argv[1]); + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + if (argc == 3) { + nb_sectors = cvtnum(argv[2]); + } else { + nb_sectors = 1; + } + + remaining = nb_sectors; + sum_alloc = 0; + while (remaining) { + ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num); + remaining -= num; + if (ret) { + sum_alloc += num; + } + } + + cvtstr(offset, s1, sizeof(s1)); + + printf("%d/%d sectors allocated at offset %s\n", + sum_alloc, nb_sectors, s1); + return 0; } static const cmdinfo_t alloc_cmd = { - .name = "alloc", - .altname = "a", - .argmin = 1, - .argmax = 2, - .cfunc = alloc_f, - .args = "off [sectors]", - .oneline = "checks if a sector is present in the file", + .name = "alloc", + .altname = "a", + .argmin = 1, + .argmax = 2, + .cfunc = alloc_f, + .args = "off [sectors]", + .oneline = "checks if a sector is present in the file", }; -static int -map_f(int argc, char **argv) +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); + 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; + return 0; } static const cmdinfo_t map_cmd = { @@ -1569,50 +1580,48 @@ }; -static int -close_f(int argc, char **argv) +static int close_f(int argc, char **argv) { - bdrv_close(bs); - bs = NULL; - return 0; + bdrv_close(bs); + bs = NULL; + return 0; } static const cmdinfo_t close_cmd = { - .name = "close", - .altname = "c", - .cfunc = close_f, - .oneline = "close the current open file", + .name = "close", + .altname = "c", + .cfunc = close_f, + .oneline = "close the current open file", }; static int openfile(char *name, int flags, int growable) { - if (bs) { - fprintf(stderr, "file open already, try 'help close'\n"); - return 1; - } - - if (growable) { - 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, NULL) < 0) { - fprintf(stderr, "%s: can't open device %s\n", progname, name); - bs = NULL; - return 1; - } - } + if (bs) { + fprintf(stderr, "file open already, try 'help close'\n"); + return 1; + } + + if (growable) { + 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"); - return 0; + if (bdrv_open(bs, name, flags, NULL) < 0) { + fprintf(stderr, "%s: can't open device %s\n", progname, name); + bs = NULL; + return 1; + } + } + + return 0; } -static void -open_help(void) +static void open_help(void) { - printf( + printf( "\n" " opens a new file in the requested mode\n" "\n" @@ -1630,80 +1639,78 @@ static int open_f(int argc, char **argv); static const cmdinfo_t open_cmd = { - .name = "open", - .altname = "o", - .cfunc = open_f, - .argmin = 1, - .argmax = -1, - .flags = CMD_NOFILE_OK, - .args = "[-Crsn] [path]", - .oneline = "open the file specified by path", - .help = open_help, + .name = "open", + .altname = "o", + .cfunc = open_f, + .argmin = 1, + .argmax = -1, + .flags = CMD_NOFILE_OK, + .args = "[-Crsn] [path]", + .oneline = "open the file specified by path", + .help = open_help, }; -static int -open_f(int argc, char **argv) +static int open_f(int argc, char **argv) +{ + int flags = 0; + int readonly = 0; + int growable = 0; + int c; + + while ((c = getopt(argc, argv, "snrg")) != EOF) { + switch (c) { + case 's': + flags |= BDRV_O_SNAPSHOT; + break; + case 'n': + flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + break; + case 'r': + readonly = 1; + break; + case 'g': + growable = 1; + break; + default: + return command_usage(&open_cmd); + } + } + + if (!readonly) { + flags |= BDRV_O_RDWR; + } + + if (optind != argc - 1) { + return command_usage(&open_cmd); + } + + return openfile(argv[optind], flags, growable); +} + +static int init_args_command(int index) +{ + /* only one device allowed so far */ + if (index >= 1) { + return 0; + } + return ++index; +} + +static int init_check_command(const cmdinfo_t *ct) { - int flags = 0; - int readonly = 0; - int growable = 0; - int c; - - while ((c = getopt(argc, argv, "snrg")) != EOF) { - switch (c) { - case 's': - flags |= BDRV_O_SNAPSHOT; - break; - case 'n': - flags |= BDRV_O_NOCACHE; - break; - case 'r': - readonly = 1; - break; - case 'g': - growable = 1; - break; - default: - return command_usage(&open_cmd); - } - } - - if (!readonly) { - flags |= BDRV_O_RDWR; - } - - if (optind != argc - 1) - return command_usage(&open_cmd); - - return openfile(argv[optind], flags, growable); -} - -static int -init_args_command( - int index) -{ - /* only one device allowed so far */ - if (index >= 1) - return 0; - return ++index; -} - -static int -init_check_command( - const cmdinfo_t *ct) -{ - if (ct->flags & CMD_FLAG_GLOBAL) - return 1; - if (!(ct->flags & CMD_NOFILE_OK) && !bs) { - fprintf(stderr, "no file open, try 'help open'\n"); - return 0; - } - return 1; + if (ct->flags & CMD_FLAG_GLOBAL) { + return 1; + } + if (!(ct->flags & CMD_NOFILE_OK) && !bs) { + fprintf(stderr, "no file open, try 'help open'\n"); + return 0; + } + return 1; } static void usage(const char *name) { - printf( + printf( "Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n" "QEMU Disk exerciser\n" "\n" @@ -1717,115 +1724,117 @@ " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" "\n", - name); + name); } int main(int argc, char **argv) { - int readonly = 0; - int growable = 0; - 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' }, - { "read-only", 0, NULL, 'r' }, - { "snapshot", 0, NULL, 's' }, - { "nocache", 0, NULL, 'n' }, - { "misalign", 0, NULL, 'm' }, - { "growable", 0, NULL, 'g' }, - { "native-aio", 0, NULL, 'k' }, - { NULL, 0, NULL, 0 } - }; - int c; - int opt_index = 0; - int flags = 0; - - progname = basename(argv[0]); - - while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { - switch (c) { - case 's': - flags |= BDRV_O_SNAPSHOT; - break; - case 'n': - flags |= BDRV_O_NOCACHE; - break; - case 'c': - add_user_command(optarg); - break; - case 'r': - readonly = 1; - break; - case 'm': - misalign = 1; - break; - case 'g': - growable = 1; - break; - case 'k': - flags |= BDRV_O_NATIVE_AIO; - break; - case 'V': - printf("%s version %s\n", progname, VERSION); - exit(0); - case 'h': - usage(progname); - exit(0); - default: - usage(progname); - exit(1); - } - } - - if ((argc - optind) > 1) { - usage(progname); - exit(1); - } - - bdrv_init(); - - /* initialize commands */ - quit_init(); - help_init(); - add_command(&open_cmd); - add_command(&close_cmd); - add_command(&read_cmd); - 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); - add_command(&flush_cmd); - 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_RDWR; - } - - if ((argc - optind) == 1) - openfile(argv[optind], flags, growable); - command_loop(); - - /* - * Make sure all outstanding requests get flushed the program exits. - */ - qemu_aio_flush(); - - if (bs) - bdrv_close(bs); - return 0; + int readonly = 0; + int growable = 0; + 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' }, + { "read-only", 0, NULL, 'r' }, + { "snapshot", 0, NULL, 's' }, + { "nocache", 0, NULL, 'n' }, + { "misalign", 0, NULL, 'm' }, + { "growable", 0, NULL, 'g' }, + { "native-aio", 0, NULL, 'k' }, + { NULL, 0, NULL, 0 } + }; + int c; + int opt_index = 0; + int flags = 0; + + progname = basename(argv[0]); + + while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { + switch (c) { + case 's': + flags |= BDRV_O_SNAPSHOT; + break; + case 'n': + flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + break; + case 'c': + add_user_command(optarg); + break; + case 'r': + readonly = 1; + break; + case 'm': + misalign = 1; + break; + case 'g': + growable = 1; + break; + case 'k': + flags |= BDRV_O_NATIVE_AIO; + break; + case 'V': + printf("%s version %s\n", progname, VERSION); + exit(0); + case 'h': + usage(progname); + exit(0); + default: + usage(progname); + exit(1); + } + } + + if ((argc - optind) > 1) { + usage(progname); + exit(1); + } + + bdrv_init(); + + /* initialize commands */ + quit_init(); + help_init(); + add_command(&open_cmd); + add_command(&close_cmd); + add_command(&read_cmd); + 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); + add_command(&flush_cmd); + 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_RDWR; + } + + if ((argc - optind) == 1) { + openfile(argv[optind], flags, growable); + } + command_loop(); + + /* + * Make sure all outstanding requests get flushed the program exits. + */ + qemu_aio_flush(); + + if (bs) { + bdrv_close(bs); + } + return 0; } diff -Nru qemu-kvm-0.14.1+noroms/qemu-kvm.c qemu-kvm-0.15.0+noroms/qemu-kvm.c --- qemu-kvm-0.14.1+noroms/qemu-kvm.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-kvm.c 2011-08-09 07:40:29.000000000 -0500 @@ -18,31 +18,9 @@ #include "compatfd.h" #include "gdbstub.h" #include "monitor.h" +#include "cpus.h" #include "qemu-kvm.h" -#include "libkvm.h" - -#include -#include -#include -#include -#include -#include "compatfd.h" -#include - -#define false 0 -#define true 1 - -#ifndef PR_MCE_KILL -#define PR_MCE_KILL 33 -#endif - -#ifndef BUS_MCEERR_AR -#define BUS_MCEERR_AR 4 -#endif -#ifndef BUS_MCEERR_AO -#define BUS_MCEERR_AO 5 -#endif #define EXPECTED_KVM_API_VERSION 12 @@ -55,364 +33,116 @@ int kvm_pit_reinject = 1; int kvm_nested = 0; - -KVMState *kvm_state; -kvm_context_t kvm_context; - -pthread_mutex_t qemu_mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t qemu_vcpu_cond = PTHREAD_COND_INITIALIZER; -pthread_cond_t qemu_system_cond = PTHREAD_COND_INITIALIZER; -pthread_cond_t qemu_pause_cond = PTHREAD_COND_INITIALIZER; -pthread_cond_t qemu_work_cond = PTHREAD_COND_INITIALIZER; -__thread CPUState *current_env; - -static int qemu_system_ready; - -#define SIG_IPI (SIGRTMIN+4) - -pthread_t io_thread; -static int io_thread_sigfd = -1; - -static CPUState *kvm_debug_cpu_requested; - -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -/* The list of ioperm_data */ -static QLIST_HEAD(, ioperm_data) ioperm_head; -#endif - #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) -int kvm_abi = EXPECTED_KVM_API_VERSION; -int kvm_page_size; - -#ifdef KVM_CAP_SET_GUEST_DEBUG -static int kvm_debug(CPUState *env, - struct kvm_debug_exit_arch *arch_info) -{ - int handle = kvm_arch_debug(arch_info); - - if (handle) { - kvm_debug_cpu_requested = env; - env->stopped = 1; - } - return handle; -} -#endif - -static int handle_unhandled(uint64_t reason) -{ - fprintf(stderr, "kvm: unhandled exit %" PRIx64 "\n", reason); - 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) +static inline void set_gsi(KVMState *s, unsigned int gsi) { - uint32_t *bitmap = kvm->used_gsi_bitmap; + uint32_t *bitmap = s->used_gsi_bitmap; - if (gsi < kvm->max_gsi) + if (gsi < s->max_gsi) { bitmap[gsi / 32] |= 1U << (gsi % 32); - else + } else { DPRINTF("Invalid GSI %u\n", gsi); + } } -static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi) +static inline void clear_gsi(KVMState *s, unsigned int gsi) { - uint32_t *bitmap = kvm->used_gsi_bitmap; + uint32_t *bitmap = s->used_gsi_bitmap; - if (gsi < kvm->max_gsi) + if (gsi < s->max_gsi) { bitmap[gsi / 32] &= ~(1U << (gsi % 32)); - else + } else { DPRINTF("Invalid GSI %u\n", gsi); + } } -static int kvm_create_context(void); - -int kvm_init(void) +static int kvm_init_irq_routing(KVMState *s) { - int fd; +#ifdef KVM_CAP_IRQ_ROUTING int r, gsi_count; - - fd = open("/dev/kvm", O_RDWR); - if (fd == -1) { - perror("open /dev/kvm"); - return -1; - } - r = ioctl(fd, KVM_GET_API_VERSION, 0); - if (r == -1) { - fprintf(stderr, - "kvm kernel version too old: " - "KVM_GET_API_VERSION ioctl not supported\n"); - goto out_close; - } - if (r < EXPECTED_KVM_API_VERSION) { - fprintf(stderr, "kvm kernel version too old: " - "We expect API version %d or newer, but got " - "version %d\n", EXPECTED_KVM_API_VERSION, r); - goto out_close; - } - if (r > EXPECTED_KVM_API_VERSION) { - fprintf(stderr, "kvm userspace version too old\n"); - goto out_close; - } - kvm_abi = r; - kvm_page_size = getpagesize(); - kvm_state = qemu_mallocz(sizeof(*kvm_state)); - kvm_context = &kvm_state->kvm_context; - - kvm_state->fd = fd; - kvm_state->vmfd = -1; - kvm_context->opaque = cpu_single_env; - kvm_context->dirty_pages_log_all = 0; - kvm_context->no_irqchip_creation = 0; - kvm_context->no_pit_creation = 0; - -#ifdef KVM_CAP_SET_GUEST_DEBUG - QTAILQ_INIT(&kvm_state->kvm_sw_breakpoints); -#endif - - gsi_count = kvm_get_gsi_count(kvm_context); + gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING); if (gsi_count > 0) { int gsi_bits, i; /* Round up so we can search ints using ffs */ gsi_bits = ALIGN(gsi_count, 32); - kvm_context->used_gsi_bitmap = qemu_mallocz(gsi_bits / 8); - kvm_context->max_gsi = gsi_bits; + s->used_gsi_bitmap = qemu_mallocz(gsi_bits / 8); + s->max_gsi = gsi_bits; /* Mark any over-allocated bits as already in use */ for (i = gsi_count; i < gsi_bits; i++) { - set_gsi(kvm_context, i); + set_gsi(s, i); } } - kvm_cpu_register_phys_memory_client(); - - pthread_mutex_lock(&qemu_mutex); - return kvm_create_context(); - - out_close: - close(fd); - return -1; -} - -static void kvm_finalize(KVMState *s) -{ - /* FIXME - if (kvm->vcpu_fd[0] != -1) - close(kvm->vcpu_fd[0]); - if (kvm->vm_fd != -1) - close(kvm->vm_fd); - */ - close(s->fd); - free(s); -} - -void kvm_disable_irqchip_creation(kvm_context_t kvm) -{ - kvm->no_irqchip_creation = 1; -} - -void kvm_disable_pit_creation(kvm_context_t kvm) -{ - 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; + s->irq_routes = qemu_mallocz(sizeof(*s->irq_routes)); + s->nr_allocated_irq_routes = 0; - r = kvm_vm_ioctl(kvm_state, KVM_CREATE_VCPU, id); + r = kvm_arch_init_irq_routing(); if (r < 0) { - fprintf(stderr, "kvm_create_vcpu: %m\n"); - fprintf(stderr, "Failed to create vCPU. Check the -smp parameter.\n"); - goto err; - } - - env->kvm_fd = r; - env->kvm_state = kvm_state; - - mmap_size = kvm_ioctl(kvm_state, KVM_GET_VCPU_MMAP_SIZE, 0); - if (mmap_size < 0) { - fprintf(stderr, "get vcpu mmap size: %m\n"); - goto err_fd; - } - env->kvm_run = - mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, env->kvm_fd, - 0); - if (env->kvm_run == MAP_FAILED) { - fprintf(stderr, "mmap vcpu area: %m\n"); - 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); - err: - /* We're no good with semi-broken states. */ - abort(); -} - -static int kvm_set_boot_vcpu_id(kvm_context_t kvm, uint32_t id) -{ -#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) { - return kvm_vm_ioctl(kvm_state, KVM_SET_BOOT_CPU_ID, id); + return r; } - return -ENOSYS; -#else - return -ENOSYS; -#endif -} - -int kvm_create_vm(kvm_context_t kvm) -{ - int fd; -#ifdef KVM_CAP_IRQ_ROUTING - kvm->irq_routes = qemu_mallocz(sizeof(*kvm->irq_routes)); - kvm->nr_allocated_irq_routes = 0; #endif - fd = kvm_ioctl(kvm_state, KVM_CREATE_VM, 0); - if (fd < 0) { - fprintf(stderr, "kvm_create_vm: %m\n"); - return -1; - } - kvm_state->vmfd = fd; return 0; } -static int kvm_create_default_phys_mem(kvm_context_t kvm, - unsigned long phys_mem_bytes, - void **vm_mem) -{ -#ifdef KVM_CAP_USER_MEMORY - int r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY); - if (r > 0) - return 0; - fprintf(stderr, - "Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported\n"); -#else -#error Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported -#endif - return -1; -} - -void kvm_create_irqchip(kvm_context_t kvm) +int kvm_create_irqchip(KVMState *s) { +#ifdef KVM_CAP_IRQCHIP int r; - kvm->irqchip_in_kernel = 0; -#ifdef KVM_CAP_IRQCHIP - if (!kvm->no_irqchip_creation) { - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_IRQCHIP); - if (r > 0) { /* kernel irqchip supported */ - r = kvm_vm_ioctl(kvm_state, KVM_CREATE_IRQCHIP); - if (r >= 0) { - kvm->irqchip_inject_ioctl = KVM_IRQ_LINE; -#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) { - kvm->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS; - } -#endif - kvm->irqchip_in_kernel = 1; - } else - fprintf(stderr, "Create kernel PIC irqchip failed\n"); - } + if (!kvm_irqchip || !kvm_check_extension(s, KVM_CAP_IRQCHIP)) { + return 0; } -#endif - kvm_state->irqchip_in_kernel = kvm->irqchip_in_kernel; -} - -int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, void **vm_mem) -{ - int r, i; - r = kvm_create_vm(kvm); + r = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); if (r < 0) { + fprintf(stderr, "Create kernel PIC irqchip failed\n"); return r; } - r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem); - if (r < 0) { - return r; - } - for (i = 0; i < ARRAY_SIZE(kvm_state->slots); i++) { - kvm_state->slots[i].slot = i; + + s->irqchip_inject_ioctl = KVM_IRQ_LINE; +#if defined(KVM_CAP_IRQ_INJECT_STATUS) && defined(KVM_IRQ_LINE_STATUS) + if (kvm_check_extension(s, KVM_CAP_IRQ_INJECT_STATUS)) { + s->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS; } +#endif + s->irqchip_in_kernel = 1; - r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem); + r = kvm_init_irq_routing(s); if (r < 0) { return r; } - - kvm_create_irqchip(kvm); +#endif return 0; } #ifdef KVM_CAP_IRQCHIP -int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status) +int kvm_set_irq(int irq, int level, int *status) { struct kvm_irq_level event; int r; - if (!kvm->irqchip_in_kernel) { + if (!kvm_state->irqchip_in_kernel) { return 0; } event.level = level; event.irq = irq; - r = kvm_vm_ioctl(kvm_state, kvm->irqchip_inject_ioctl, &event); + r = kvm_vm_ioctl(kvm_state, kvm_state->irqchip_inject_ioctl, + &event); if (r < 0) { - perror("kvm_set_irq_level"); + perror("kvm_set_irq"); } if (status) { #ifdef KVM_CAP_IRQ_INJECT_STATUS - *status = - (kvm->irqchip_inject_ioctl == KVM_IRQ_LINE) ? 1 : event.status; + *status = (kvm_state->irqchip_inject_ioctl == KVM_IRQ_LINE) ? + 1 : event.status; #else *status = 1; #endif @@ -421,28 +151,28 @@ return 1; } -int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip) +int kvm_get_irqchip(KVMState *s, struct kvm_irqchip *chip) { int r; - if (!kvm->irqchip_in_kernel) { + if (!s->irqchip_in_kernel) { return 0; } - r = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, chip); + r = kvm_vm_ioctl(s, KVM_GET_IRQCHIP, chip); if (r < 0) { perror("kvm_get_irqchip\n"); } return r; } -int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip) +int kvm_set_irqchip(KVMState *s, struct kvm_irqchip *chip) { int r; - if (!kvm->irqchip_in_kernel) { + if (!s->irqchip_in_kernel) { return 0; } - r = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip); + r = kvm_vm_ioctl(s, KVM_SET_IRQCHIP, chip); if (r < 0) { perror("kvm_set_irqchip\n"); } @@ -451,296 +181,53 @@ #endif -static int handle_debug(CPUState *env) -{ -#ifdef KVM_CAP_SET_GUEST_DEBUG - struct kvm_run *run = env->kvm_run; - - return kvm_debug(env, &run->debug.arch); -#else - return 0; -#endif -} - -int kvm_get_regs(CPUState *env, struct kvm_regs *regs) -{ - return kvm_vcpu_ioctl(env, KVM_GET_REGS, regs); -} - -int kvm_set_regs(CPUState *env, struct kvm_regs *regs) -{ - return kvm_vcpu_ioctl(env, KVM_SET_REGS, regs); -} - -#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) { - return kvm_vcpu_ioctl(env, KVM_GET_MP_STATE, mp_state); - } - return -ENOSYS; -} - -int kvm_set_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) { - return kvm_vcpu_ioctl(env, KVM_SET_MP_STATE, mp_state); - } - return -ENOSYS; -} -#endif - -static int handle_mmio(CPUState *env) -{ - unsigned long addr = env->kvm_run->mmio.phys_addr; - struct kvm_run *kvm_run = env->kvm_run; - 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) { - return 0; - } - - cpu_physical_memory_rw(addr, data, kvm_run->mmio.len, kvm_run->mmio.is_write); - return 0; -} - -int handle_io_window(kvm_context_t kvm) -{ - return 1; -} - -int handle_shutdown(kvm_context_t kvm, CPUState *env) -{ - /* stop the current vcpu from going back to guest mode */ - env->stopped = 1; - - qemu_system_reset_request(); - return 1; -} - -static inline void push_nmi(kvm_context_t kvm) -{ -#ifdef KVM_CAP_USER_NMI - kvm_arch_push_nmi(kvm->opaque); -#endif /* KVM_CAP_USER_NMI */ -} - -void post_kvm_run(kvm_context_t kvm, CPUState *env) -{ - pthread_mutex_lock(&qemu_mutex); - kvm_arch_post_run(env, env->kvm_run); - cpu_single_env = env; -} - -int pre_kvm_run(kvm_context_t kvm, CPUState *env) -{ - kvm_arch_pre_run(env, env->kvm_run); - - pthread_mutex_unlock(&qemu_mutex); - return 0; -} - -int kvm_is_ready_for_interrupt_injection(CPUState *env) -{ - return env->kvm_run->ready_for_interrupt_injection; -} - -int kvm_run(CPUState *env) -{ - int r; - kvm_context_t kvm = &env->kvm_state->kvm_context; - struct kvm_run *run = env->kvm_run; - 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) { - run->request_interrupt_window = kvm_arch_try_push_interrupts(env); - } -#endif - - r = pre_kvm_run(kvm, env); - 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) { - r = -errno; - post_kvm_run(kvm, env); - fprintf(stderr, "kvm_run: %s\n", strerror(-r)); - return r; - } - - post_kvm_run(kvm, env); - - kvm_flush_coalesced_mmio_buffer(); - -#if !defined(__s390__) - if (r == -1) { - r = handle_io_window(kvm); - goto more; - } -#endif - if (1) { - switch (run->exit_reason) { - case KVM_EXIT_UNKNOWN: - r = handle_unhandled(run->hw.hardware_exit_reason); - break; - case KVM_EXIT_FAIL_ENTRY: - 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, - run->ex.error_code); - kvm_show_regs(env); - kvm_show_code(env); - abort(); - break; - case KVM_EXIT_IO: - r = kvm_handle_io(run->io.port, - (uint8_t *)run + run->io.data_offset, - run->io.direction, - run->io.size, - run->io.count); - r = 0; - break; - case KVM_EXIT_DEBUG: - r = handle_debug(env); - break; - case KVM_EXIT_MMIO: - r = handle_mmio(env); - break; - case KVM_EXIT_HLT: - r = kvm_arch_halt(env); - break; - case KVM_EXIT_IRQ_WINDOW_OPEN: - break; - case KVM_EXIT_SHUTDOWN: - r = handle_shutdown(kvm, env); - break; -#if defined(__s390__) - case KVM_EXIT_S390_SIEIC: - r = kvm_s390_handle_intercept(kvm, env, run); - break; - case KVM_EXIT_S390_RESET: - r = kvm_s390_handle_reset(kvm, env, run); - break; -#endif - case KVM_EXIT_INTERNAL_ERROR: - kvm_handle_internal_error(env, run); - r = 1; - break; - default: - if (kvm_arch_run(env)) { - fprintf(stderr, "unhandled vm exit: 0x%x\n", run->exit_reason); - kvm_show_regs(env); - abort(); - } - break; - } - } -more: - if (!r) { - goto again; - } - return r; -} - -int kvm_inject_irq(CPUState *env, unsigned irq) -{ - struct kvm_interrupt intr; - - intr.irq = irq; - return kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr); -} - -int kvm_inject_nmi(CPUState *env) -{ -#ifdef KVM_CAP_USER_NMI - return kvm_vcpu_ioctl(env, KVM_NMI); -#else - return -ENOSYS; -#endif -} - -int kvm_init_coalesced_mmio(kvm_context_t kvm) -{ - int r = 0; - kvm_state->coalesced_mmio = 0; -#ifdef KVM_CAP_COALESCED_MMIO - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO); - if (r > 0) { - kvm_state->coalesced_mmio = r; - return 0; - } -#endif - return r; -} - #ifdef KVM_CAP_DEVICE_ASSIGNMENT -int kvm_assign_pci_device(kvm_context_t kvm, +int kvm_assign_pci_device(KVMState *s, struct kvm_assigned_pci_dev *assigned_dev) { - return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_PCI_DEVICE, assigned_dev); + return kvm_vm_ioctl(s, KVM_ASSIGN_PCI_DEVICE, assigned_dev); } -static int kvm_old_assign_irq(kvm_context_t kvm, +static int kvm_old_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { - return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_IRQ, assigned_irq); + return kvm_vm_ioctl(s, KVM_ASSIGN_IRQ, assigned_irq); } #ifdef KVM_CAP_ASSIGN_DEV_IRQ -int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq) +int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { int ret; - ret = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ); + ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ); if (ret > 0) { - return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_DEV_IRQ, assigned_irq); + return kvm_vm_ioctl(s, KVM_ASSIGN_DEV_IRQ, assigned_irq); } - return kvm_old_assign_irq(kvm, assigned_irq); + return kvm_old_assign_irq(s, assigned_irq); } -int kvm_deassign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq) +int kvm_deassign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { - return kvm_vm_ioctl(kvm_state, KVM_DEASSIGN_DEV_IRQ, assigned_irq); + return kvm_vm_ioctl(s, KVM_DEASSIGN_DEV_IRQ, assigned_irq); } #else -int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq) +int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq) { - return kvm_old_assign_irq(kvm, assigned_irq); + return kvm_old_assign_irq(s, assigned_irq); } #endif #endif #ifdef KVM_CAP_DEVICE_DEASSIGNMENT -int kvm_deassign_pci_device(kvm_context_t kvm, +int kvm_deassign_pci_device(KVMState *s, struct kvm_assigned_pci_dev *assigned_dev) { - return kvm_vm_ioctl(kvm_state, KVM_DEASSIGN_PCI_DEVICE, assigned_dev); + return kvm_vm_ioctl(s, KVM_DEASSIGN_PCI_DEVICE, assigned_dev); } #endif -int kvm_reinject_control(kvm_context_t kvm, int pit_reinject) +int kvm_reinject_control(KVMState *s, int pit_reinject) { #ifdef KVM_CAP_REINJECT_CONTROL int r; @@ -748,9 +235,9 @@ control.pit_reinject = pit_reinject; - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_REINJECT_CONTROL); + r = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_REINJECT_CONTROL); if (r > 0) { - return kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); + return kvm_vm_ioctl(s, KVM_REINJECT_CONTROL, &control); } #endif return -ENOSYS; @@ -766,21 +253,10 @@ return r; } -int kvm_get_gsi_count(kvm_context_t kvm) -{ -#ifdef KVM_CAP_IRQ_ROUTING - return kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING); -#else - return -EINVAL; -#endif -} - int kvm_clear_gsi_routes(void) { #ifdef KVM_CAP_IRQ_ROUTING - kvm_context_t kvm = kvm_context; - - kvm->irq_routes->nr = 0; + kvm_state->irq_routes->nr = 0; return 0; #else return -EINVAL; @@ -790,34 +266,34 @@ int kvm_add_routing_entry(struct kvm_irq_routing_entry *entry) { #ifdef KVM_CAP_IRQ_ROUTING - kvm_context_t kvm = kvm_context; + KVMState *s = kvm_state; 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 (s->irq_routes->nr == s->nr_allocated_irq_routes) { + n = s->nr_allocated_irq_routes * 2; if (n < 64) { n = 64; } size = sizeof(struct kvm_irq_routing); size += n * sizeof(*new); - z = realloc(kvm->irq_routes, size); + z = realloc(s->irq_routes, size); if (!z) { return -ENOMEM; } - kvm->nr_allocated_irq_routes = n; - kvm->irq_routes = z; + s->nr_allocated_irq_routes = n; + s->irq_routes = z; } - n = kvm->irq_routes->nr++; - new = &kvm->irq_routes->entries[n]; + n = s->irq_routes->nr++; + new = &s->irq_routes->entries[n]; memset(new, 0, sizeof(*new)); new->gsi = entry->gsi; new->type = entry->type; new->flags = entry->flags; new->u = entry->u; - set_gsi(kvm, entry->gsi); + set_gsi(s, entry->gsi); return 0; #else @@ -844,21 +320,21 @@ int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry) { #ifdef KVM_CAP_IRQ_ROUTING - kvm_context_t kvm = kvm_context; + KVMState *s = kvm_state; struct kvm_irq_routing_entry *e, *p; int i, gsi, found = 0; gsi = entry->gsi; - for (i = 0; i < kvm->irq_routes->nr; ++i) { - e = &kvm->irq_routes->entries[i]; + for (i = 0; i < s->irq_routes->nr; ++i) { + e = &s->irq_routes->entries[i]; if (e->type == entry->type && e->gsi == gsi) { switch (e->type) { case KVM_IRQ_ROUTING_IRQCHIP:{ if (e->u.irqchip.irqchip == entry->u.irqchip.irqchip && e->u.irqchip.pin == entry->u.irqchip.pin) { - p = &kvm->irq_routes->entries[--kvm->irq_routes->nr]; + p = &s->irq_routes->entries[--s->irq_routes->nr]; *e = *p; found = 1; } @@ -870,7 +346,7 @@ && e->u.msi.address_hi == entry->u.msi.address_hi && e->u.msi.data == entry->u.msi.data) { - p = &kvm->irq_routes->entries[--kvm->irq_routes->nr]; + p = &s->irq_routes->entries[--s->irq_routes->nr]; *e = *p; found = 1; } @@ -882,13 +358,13 @@ if (found) { /* If there are no other users of this GSI * mark it available in the bitmap */ - for (i = 0; i < kvm->irq_routes->nr; i++) { - e = &kvm->irq_routes->entries[i]; + for (i = 0; i < s->irq_routes->nr; i++) { + e = &s->irq_routes->entries[i]; if (e->gsi == gsi) break; } - if (i == kvm->irq_routes->nr) { - clear_gsi(kvm, gsi); + if (i == s->irq_routes->nr) { + clear_gsi(s, gsi); } return 0; @@ -905,7 +381,7 @@ struct kvm_irq_routing_entry *newentry) { #ifdef KVM_CAP_IRQ_ROUTING - kvm_context_t kvm = kvm_context; + KVMState *s = kvm_state; struct kvm_irq_routing_entry *e; int i; @@ -913,8 +389,8 @@ return -EINVAL; } - for (i = 0; i < kvm->irq_routes->nr; ++i) { - e = &kvm->irq_routes->entries[i]; + for (i = 0; i < s->irq_routes->nr; ++i) { + e = &s->irq_routes->entries[i]; if (e->type != entry->type || e->gsi != entry->gsi) { continue; } @@ -964,10 +440,10 @@ int kvm_commit_irq_routes(void) { #ifdef KVM_CAP_IRQ_ROUTING - kvm_context_t kvm = kvm_context; + KVMState *s = kvm_state; - kvm->irq_routes->flags = 0; - return kvm_vm_ioctl(kvm_state, KVM_SET_GSI_ROUTING, kvm->irq_routes); + s->irq_routes->flags = 0; + return kvm_vm_ioctl(s, KVM_SET_GSI_ROUTING, s->irq_routes); #else return -ENOSYS; #endif @@ -975,12 +451,12 @@ int kvm_get_irq_route_gsi(void) { - kvm_context_t kvm = kvm_context; + KVMState *s = kvm_state; int i, bit; - uint32_t *buf = kvm->used_gsi_bitmap; + uint32_t *buf = s->used_gsi_bitmap; /* Return the lowest unused GSI in the bitmap */ - for (i = 0; i < kvm->max_gsi / 32; i++) { + for (i = 0; i < s->max_gsi / 32; i++) { bit = ffs(~buf[i]); if (!bit) { continue; @@ -992,627 +468,96 @@ return -ENOSPC; } -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) +static void kvm_msi_routing_entry(struct kvm_irq_routing_entry *e, + KVMMsiMessage *msg) { - e->gsi = gsi; + e->gsi = msg->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; + e->u.msi.address_lo = msg->addr_lo; + e->u.msi.address_hi = msg->addr_hi; + e->u.msi.data = msg->data; } -int kvm_add_msix(uint32_t gsi, uint32_t addr_lo, - uint32_t addr_hi, uint32_t data) +int kvm_msi_message_add(KVMMsiMessage *msg) { struct kvm_irq_routing_entry e; + int ret; - kvm_msix_routing_entry(&e, gsi, addr_lo, addr_hi, data); + ret = kvm_get_irq_route_gsi(); + if (ret < 0) { + return ret; + } + msg->gsi = ret; + + kvm_msi_routing_entry(&e, msg); return kvm_add_routing_entry(&e); } -int kvm_del_msix(uint32_t gsi, uint32_t addr_lo, - uint32_t addr_hi, uint32_t data) +int kvm_msi_message_del(KVMMsiMessage *msg) { struct kvm_irq_routing_entry e; - kvm_msix_routing_entry(&e, gsi, addr_lo, addr_hi, data); + kvm_msi_routing_entry(&e, msg); 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) +int kvm_msi_message_update(KVMMsiMessage *old, KVMMsiMessage *new) { struct kvm_irq_routing_entry e1, e2; + int ret; - 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); -} + new->gsi = old->gsi; + if (memcmp(old, new, sizeof(KVMMsiMessage)) == 0) { + return 0; + } + kvm_msi_routing_entry(&e1, old); + kvm_msi_routing_entry(&e2, new); -#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 - -static int _kvm_irqfd(kvm_context_t kvm, int fd, int gsi, int flags) -{ - struct kvm_irqfd data = { - .fd = fd, - .gsi = gsi, - .flags = flags, - }; - - return kvm_vm_ioctl(kvm_state, KVM_IRQFD, &data); -} - -int kvm_irqfd(kvm_context_t kvm, int gsi, int flags) -{ - int r; - int fd; - - if (!kvm_check_extension(kvm_state, KVM_CAP_IRQFD)) - return -ENOENT; - - fd = eventfd(0, 0); - if (fd < 0) { - return -errno; - } - - r = _kvm_irqfd(kvm, fd, gsi, 0); - if (r < 0) { - close(fd); - return -errno; - } - - return fd; -} - -#else /* KVM_CAP_IRQFD */ - -int kvm_irqfd(kvm_context_t kvm, int gsi, int flags) -{ - return -ENOSYS; -} - -#endif /* KVM_CAP_IRQFD */ -unsigned long kvm_get_thread_id(void) -{ - return syscall(SYS_gettid); -} - -static void qemu_cond_wait(pthread_cond_t *cond) -{ - CPUState *env = cpu_single_env; - - pthread_cond_wait(cond, &qemu_mutex); - cpu_single_env = env; -} - -static void sig_ipi_handler(int n) -{ -} - -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 (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr)) - sigbus_reraise(); -} - -void on_vcpu(CPUState *env, void (*func)(void *data), void *data) -{ - struct qemu_work_item wi; - - if (env == current_env) { - func(data); - return; - } - - wi.func = func; - wi.data = data; - if (!env->kvm_cpu_state.queued_work_first) { - env->kvm_cpu_state.queued_work_first = &wi; - } 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) { - qemu_cond_wait(&qemu_work_cond); - } -} - -static void do_kvm_cpu_synchronize_state(void *_env) -{ - CPUState *env = _env; - - 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_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) -{ - cpu_interrupt(current_env, (long) data); -} - -void kvm_inject_interrupt(CPUState *env, int mask) -{ - on_vcpu(env, inject_interrupt, (void *) (long) mask); -} - -void kvm_update_interrupt_request(CPUState *env) -{ - int signal = 0; - - if (env) { - 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) { - signal = 1; - } - - if (signal) { - env->kvm_cpu_state.signalled = 1; - if (env->kvm_cpu_state.thread) { - pthread_kill(env->kvm_cpu_state.thread, SIG_IPI); - } - } - } -} - -int kvm_cpu_exec(CPUState *env) -{ - int r; - - r = kvm_run(env); - if (r < 0) { - printf("kvm_run returned %d\n", r); - vm_stop(0); - } - - return 0; -} - -int kvm_cpu_is_stopped(CPUState *env) -{ - return !vm_running || env->stopped; -} - -static void flush_queued_work(CPUState *env) -{ - struct qemu_work_item *wi; - - 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; - wi->func(wi->data); - wi->done = true; - } - env->kvm_cpu_state.queued_work_last = NULL; - pthread_cond_broadcast(&qemu_work_cond); -} - -static void kvm_main_loop_wait(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 { - pthread_mutex_unlock(&qemu_mutex); - - r = sigtimedwait(&waitset, &siginfo, &ts); - e = errno; - - pthread_mutex_lock(&qemu_mutex); - - if (r == -1 && !(e == EAGAIN || e == EINTR)) { - printf("sigtimedwait: %s\n", strerror(e)); - exit(1); - } - - switch (r) { - case SIGBUS: - if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) - sigbus_reraise(); - break; - default: - break; - } - - r = sigpending(&chkset); - if (r == -1) { - printf("sigpending: %s\n", strerror(e)); - exit(1); - } - } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS)); - - cpu_single_env = env; - flush_queued_work(env); - - if (env->stop) { - env->stop = 0; - env->stopped = 1; - pthread_cond_signal(&qemu_pause_cond); - } - - env->kvm_cpu_state.signalled = 0; -} - -static int all_threads_paused(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - if (penv->stop) { - return 0; - } - penv = (CPUState *) penv->next_cpu; + ret = kvm_update_routing_entry(&e1, &e2); + if (ret < 0) { + return ret; } return 1; } -static void pause_all_threads(void) -{ - CPUState *penv = first_cpu; - - while (penv) { - if (penv != cpu_single_env) { - penv->stop = 1; - pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI); - } else { - penv->stop = 0; - penv->stopped = 1; - cpu_exit(penv); - } - penv = (CPUState *) penv->next_cpu; - } - - while (!all_threads_paused()) { - qemu_cond_wait(&qemu_pause_cond); - } -} - -static void resume_all_threads(void) -{ - CPUState *penv = first_cpu; - - assert(!cpu_single_env); - - while (penv) { - penv->stop = 0; - penv->stopped = 0; - pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI); - penv = (CPUState *) penv->next_cpu; - } -} - -static void kvm_vm_state_change_handler(void *context, int running, int reason) -{ - if (running) { - resume_all_threads(); - } else { - pause_all_threads(); - } -} - -static void setup_kernel_sigmask(CPUState *env) -{ - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - sigaddset(&set, SIGIO); - sigaddset(&set, SIGALRM); - sigprocmask(SIG_BLOCK, &set, NULL); - - sigprocmask(SIG_BLOCK, NULL, &set); - sigdelset(&set, SIG_IPI); - sigdelset(&set, SIGBUS); - - kvm_set_signal_mask(env, &set); -} - -static void qemu_kvm_system_reset(void) -{ - pause_all_threads(); - - qemu_system_reset(); - - resume_all_threads(); -} - -static void process_irqchip_events(CPUState *env) +#ifdef KVM_CAP_DEVICE_MSIX +int kvm_assign_set_msix_nr(KVMState *s, struct kvm_assigned_msix_nr *msix_nr) { - kvm_arch_process_irqchip_events(env); - if (kvm_arch_has_work(env)) - env->halted = 0; + return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_NR, msix_nr); } -static int kvm_main_loop_cpu(CPUState *env) +int kvm_assign_set_msix_entry(KVMState *s, + struct kvm_assigned_msix_entry *entry) { - while (1) { - int run_cpu = !kvm_cpu_is_stopped(env); - if (run_cpu && !kvm_irqchip_in_kernel()) { - process_irqchip_events(env); - run_cpu = !env->halted; - } - if (run_cpu) { - kvm_cpu_exec(env); - kvm_main_loop_wait(env, 0); - } else { - kvm_main_loop_wait(env, 1000); - } - } - pthread_mutex_unlock(&qemu_mutex); - return 0; + return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_ENTRY, entry); } - -static void *ap_main_loop(void *_env) -{ - CPUState *env = _env; - sigset_t signals; -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT - struct ioperm_data *data = NULL; -#endif - - current_env = env; - env->thread_id = kvm_get_thread_id(); - sigfillset(&signals); - sigprocmask(SIG_BLOCK, &signals, NULL); - -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT - /* do ioperm for io ports of assigned devices */ - QLIST_FOREACH(data, &ioperm_head, entries) - on_vcpu(env, kvm_arch_do_ioperm, data); #endif - pthread_mutex_lock(&qemu_mutex); - cpu_single_env = 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) { - qemu_cond_wait(&qemu_system_cond); - } - - /* re-initialize cpu_single_env after re-acquiring qemu_mutex */ - cpu_single_env = env; - - kvm_main_loop_cpu(env); - return NULL; -} - -int kvm_init_vcpu(CPUState *env) -{ - pthread_create(&env->kvm_cpu_state.thread, NULL, ap_main_loop, env); - - while (env->created == 0) { - qemu_cond_wait(&qemu_vcpu_cond); - } - - return 0; -} - -int kvm_vcpu_inited(CPUState *env) -{ - return env->created; -} - #ifdef TARGET_I386 void kvm_hpet_disable_kpit(void) { struct kvm_pit_state2 ps2; - kvm_get_pit2(kvm_context, &ps2); + kvm_get_pit2(kvm_state, &ps2); ps2.flags |= KVM_PIT_FLAGS_HPET_LEGACY; - kvm_set_pit2(kvm_context, &ps2); + kvm_set_pit2(kvm_state, &ps2); } void kvm_hpet_enable_kpit(void) { struct kvm_pit_state2 ps2; - kvm_get_pit2(kvm_context, &ps2); + kvm_get_pit2(kvm_state, &ps2); ps2.flags &= ~KVM_PIT_FLAGS_HPET_LEGACY; - kvm_set_pit2(kvm_context, &ps2); + kvm_set_pit2(kvm_state, &ps2); } #endif -int kvm_init_ap(void) -{ - struct sigaction action; - - qemu_add_vm_change_state_handler(kvm_vm_state_change_handler, NULL); - - signal(SIG_IPI, sig_ipi_handler); - - 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 0; -} - -/* 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); - } - } -} - -int kvm_main_loop(void) -{ - sigset_t mask; - int sigfd; - - io_thread = pthread_self(); - qemu_system_ready = 1; - - sigemptyset(&mask); - sigaddset(&mask, SIGIO); - sigaddset(&mask, SIGALRM); - sigaddset(&mask, SIGBUS); - sigprocmask(SIG_BLOCK, &mask, NULL); - - sigfd = qemu_signalfd(&mask); - if (sigfd == -1) { - fprintf(stderr, "failed to create signalfd\n"); - return -errno; - } - - fcntl(sigfd, F_SETFL, O_NONBLOCK); - - qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL, - (void *)(unsigned long) sigfd); - - pthread_cond_broadcast(&qemu_system_cond); - - io_thread_sigfd = sigfd; - cpu_single_env = NULL; - - while (1) { - main_loop_wait(0); - if (qemu_shutdown_requested()) { - monitor_protocol_event(QEVENT_SHUTDOWN, NULL); - if (qemu_no_shutdown()) { - vm_stop(0); - } else { - break; - } - } else if (qemu_powerdown_requested()) { - monitor_protocol_event(QEVENT_POWERDOWN, NULL); - qemu_irq_raise(qemu_system_powerdown); - } else if (qemu_reset_requested()) { - qemu_kvm_system_reset(); - } 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; -} - #if !defined(TARGET_I386) int kvm_arch_init_irq_routing(void) { @@ -1620,165 +565,93 @@ } #endif -extern int no_hpet; +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT +typedef struct KVMIOPortRegion { + unsigned long start; + unsigned long size; + int status; + QLIST_ENTRY(KVMIOPortRegion) entry; +} KVMIOPortRegion; -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"; +static QLIST_HEAD(, KVMIOPortRegion) ioport_regions; +static void do_set_ioport_access(void *data) +{ + KVMIOPortRegion *region = data; + bool enable = region->status > 0; int r; - if (!kvm_irqchip) { - kvm_disable_irqchip_creation(kvm_context); - } - if (!kvm_pit) { - kvm_disable_pit_creation(kvm_context); - } - if (kvm_create(kvm_context, 0, NULL) < 0) { - kvm_finalize(kvm_state); - return -1; - } - r = kvm_arch_qemu_create_context(); + r = kvm_arch_set_ioport_access(region->start, region->size, enable); 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; - } - } - - /* 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) { - return r; - } - - kvm_state->vcpu_events = 0; -#ifdef KVM_CAP_VCPU_EVENTS - 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()) { - irq0override = 0; -#ifdef TARGET_I386 - /* if kernel can't do irq routing, interrupt source - * override 0->2 can not be set up as required by hpet, - * so disable hpet. - */ - no_hpet = 1; - } else if (!qemu_kvm_has_pit_state2()) { - no_hpet = 1; - } -#else - } -#endif + region->status = r; + } else { + region->status = 1; } - - return 0; -} - -#ifdef KVM_CAP_IRQCHIP - -int kvm_set_irq(int irq, int level, int *status) -{ - return kvm_set_irq_level(kvm_context, irq, level, status); -} - -#endif - -static void kvm_mutex_unlock(void) -{ - assert(!cpu_single_env); - pthread_mutex_unlock(&qemu_mutex); } -static void kvm_mutex_lock(void) +int kvm_add_ioport_region(unsigned long start, unsigned long size) { - pthread_mutex_lock(&qemu_mutex); - cpu_single_env = NULL; -} + KVMIOPortRegion *region = qemu_mallocz(sizeof(KVMIOPortRegion)); + CPUState *env; + int r = 0; -void qemu_mutex_unlock_iothread(void) -{ - if (kvm_enabled()) { - kvm_mutex_unlock(); + region->start = start; + region->size = size; + region->status = 1; + QLIST_INSERT_HEAD(&ioport_regions, region, entry); + + if (qemu_system_is_ready()) { + for (env = first_cpu; env != NULL; env = env->next_cpu) { + run_on_cpu(env, do_set_ioport_access, region); + if (region->status < 0) { + r = region->status; + kvm_remove_ioport_region(start, size); + break; + } + } } + return r; } -void qemu_mutex_lock_iothread(void) +int kvm_remove_ioport_region(unsigned long start, unsigned long size) { - if (kvm_enabled()) { - kvm_mutex_lock(); + KVMIOPortRegion *region, *tmp; + CPUState *env; + int r = -ENOENT; + + QLIST_FOREACH_SAFE(region, &ioport_regions, entry, tmp) { + if (region->start == start && region->size == size) { + region->status = 0; + } + if (qemu_system_is_ready()) { + for (env = first_cpu; env != NULL; env = env->next_cpu) { + run_on_cpu(env, do_set_ioport_access, region); + } + } + QLIST_REMOVE(region, entry); + qemu_free(region); + r = 0; } + return r; } +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -void kvm_add_ioperm_data(struct ioperm_data *data) +int kvm_update_ioport_access(CPUState *env) { - QLIST_INSERT_HEAD(&ioperm_head, data, entries); -} +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT + KVMIOPortRegion *region; + int r; -void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num) -{ - struct ioperm_data *data; + assert(qemu_cpu_is_self(env)); - data = QLIST_FIRST(&ioperm_head); - while (data) { - struct ioperm_data *next = QLIST_NEXT(data, entries); + QLIST_FOREACH(region, &ioport_regions, entry) { + bool enable = region->status > 0; - if (data->start_port == start_port && data->num == num) { - QLIST_REMOVE(data, entries); - qemu_free(data); + r = kvm_arch_set_ioport_access(region->start, region->size, enable); + if (r < 0) { + return r; } - - data = next; - } -} - -void kvm_ioperm(CPUState *env, void *data) -{ - if (kvm_enabled() && qemu_system_ready) { - on_vcpu(env, kvm_arch_do_ioperm, data); } +#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ + return 0; } - -#endif - -int kvm_set_boot_cpu_id(uint32_t id) -{ - return kvm_set_boot_vcpu_id(kvm_context, id); -} - diff -Nru qemu-kvm-0.14.1+noroms/qemu-kvm.h qemu-kvm-0.15.0+noroms/qemu-kvm.h --- qemu-kvm-0.14.1+noroms/qemu-kvm.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-kvm.h 2011-08-09 07:40:29.000000000 -0500 @@ -15,10 +15,6 @@ #ifdef CONFIG_KVM -#if defined(__s390__) -#include -#endif - #include #ifndef __user @@ -31,11 +27,7 @@ /* FIXME: share this number with kvm */ /* FIXME: or dynamically alloc/realloc regions */ -#ifdef __s390__ -#define KVM_MAX_NUM_MEM_REGIONS 1u -#define MAX_VCPUS 64 -#define LIBKVM_S390_ORIGIN (0UL) -#elif defined(__ia64__) +#if defined(__ia64__) #define KVM_MAX_NUM_MEM_REGIONS 32u #define MAX_VCPUS 256 #else @@ -43,285 +35,10 @@ #define MAX_VCPUS 16 #endif -/* kvm abi verison variable */ -extern int kvm_abi; - -/** - * \brief The KVM context - * - * The verbose KVM context - */ - -struct kvm_context { - void *opaque; - /// is dirty pages logging enabled for all regions or not - int dirty_pages_log_all; - /// do not create in-kernel irqchip if set - int no_irqchip_creation; - /// in-kernel irqchip status - int irqchip_in_kernel; - /// ioctl to use to inject interrupts - int irqchip_inject_ioctl; - /// do not create in-kernel pit if set - int no_pit_creation; -#ifdef KVM_CAP_IRQ_ROUTING - struct kvm_irq_routing *irq_routes; - int nr_allocated_irq_routes; -#endif - void *used_gsi_bitmap; - int max_gsi; -}; - -typedef struct kvm_context *kvm_context_t; - #include "kvm.h" -int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory, - void **vm_mem); -int kvm_alloc_userspace_memory(kvm_context_t kvm, unsigned long memory, - void **vm_mem); - -int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, - void **vm_mem); - -int kvm_arch_run(CPUState *env); - - -void kvm_show_code(CPUState *env); - -int handle_halt(CPUState *env); - -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); -int handle_io_window(kvm_context_t kvm); -int try_push_interrupts(kvm_context_t kvm); - -#if defined(__x86_64__) || defined(__i386__) -struct kvm_x86_mce; -#endif - -/*! - * \brief Disable the in-kernel IRQCHIP creation - * - * In-kernel irqchip is enabled by default. If userspace irqchip is to be used, - * this should be called prior to kvm_create(). - * - * \param kvm Pointer to the kvm_context - */ -void kvm_disable_irqchip_creation(kvm_context_t kvm); - -/*! - * \brief Disable the in-kernel PIT creation - * - * In-kernel pit is enabled by default. If userspace pit is to be used, - * this should be called prior to kvm_create(). - * - * \param kvm Pointer to the kvm_context - */ -void kvm_disable_pit_creation(kvm_context_t kvm); - -/*! - * \brief Create new virtual machine - * - * This creates a new virtual machine, maps physical RAM to it, and creates a - * virtual CPU for it.\n - * \n - * Memory gets mapped for addresses 0->0xA0000, 0xC0000->phys_mem_bytes - * - * \param kvm Pointer to the current kvm_context - * \param phys_mem_bytes The amount of physical ram you want the VM to have - * \param phys_mem This pointer will be set to point to the memory that - * kvm_create allocates for physical RAM - * \return 0 on success - */ -int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, - void **phys_mem); -int kvm_create_vm(kvm_context_t kvm); -void kvm_create_irqchip(kvm_context_t kvm); - -/*! - * \brief Start the VCPU - * - * This starts the VCPU and virtualization is started.\n - * \n - * This function will not return until any of these conditions are met: - * - An IO/MMIO handler does not return "0" - * - An exception that neither the guest OS, nor KVM can handle occurs - * - * \note This function will call the callbacks registered in kvm_init() - * to emulate those functions - * \note If you at any point want to interrupt the VCPU, kvm_run() will - * listen to the EINTR signal. This allows you to simulate external interrupts - * and asyncronous IO. - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should be started - * \return 0 on success, but you really shouldn't expect this function to - * return except for when an error has occured, or when you have sent it - * an EINTR signal. - */ -int kvm_run(CPUState *env); - -/*! - * \brief Check if a vcpu is ready for interrupt injection - * - * This checks if vcpu interrupts are not masked by mov ss or sti. - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \return boolean indicating interrupt injection readiness - */ -int kvm_is_ready_for_interrupt_injection(CPUState *env); - -/*! - * \brief Read VCPU registers - * - * This gets the GP registers from the VCPU and outputs them - * into a kvm_regs structure - * - * \note This function returns a \b copy of the VCPUs registers.\n - * If you wish to modify the VCPUs GP registers, you should call kvm_set_regs() - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \param regs Pointer to a kvm_regs which will be populated with the VCPUs - * registers values - * \return 0 on success - */ -int kvm_get_regs(CPUState *env, struct kvm_regs *regs); - -/*! - * \brief Write VCPU registers - * - * This sets the GP registers on the VCPU from a kvm_regs 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_regs which will be populated with the VCPUs - * registers values - * \return 0 on success - */ -int kvm_set_regs(CPUState *env, struct kvm_regs *regs); - -#ifdef KVM_CAP_MP_STATE -/*! - * * \brief Read VCPU MP state - * - */ -int kvm_get_mpstate(CPUState *env, struct kvm_mp_state *mp_state); - -/*! - * * \brief Write VCPU MP state - * - */ -int kvm_set_mpstate(CPUState *env, struct kvm_mp_state *mp_state); -#endif - -#if defined(__i386__) || defined(__x86_64__) -/*! - * \brief Simulate an external vectored interrupt - * - * This allows you to simulate an external vectored interrupt. - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \param irq Vector number - * \return 0 on success - */ -int kvm_inject_irq(CPUState *env, unsigned irq); - - -/*! - * \brief Setting the number of shadow pages to be allocated to the vm - * - * \param kvm pointer to kvm_context - * \param nrshadow_pages number of pages to be allocated - */ -int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages); - -/*! - * \brief Getting the number of shadow pages that are allocated to the vm - * - * \param kvm pointer to kvm_context - * \param nrshadow_pages number of pages to be allocated - */ -int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages); - -#endif - -/*! - * \brief Dump VCPU registers - * - * This dumps some of the information that KVM has about a virtual CPU, namely: - * - GP Registers - * - * A much more verbose version of this is available as kvm_dump_vcpu() - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \return 0 on success - */ -void kvm_show_regs(CPUState *env); +int kvm_create_irqchip(KVMState *s); -void *kvm_create_phys_mem(kvm_context_t, unsigned long phys_start, - unsigned long len, int log, int writable); -void kvm_destroy_phys_mem(kvm_context_t, unsigned long 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_range(kvm_context_t kvm, unsigned long phys_addr, - unsigned long end_addr, void *opaque, - int (*cb)(unsigned long start, - unsigned long len, void *bitmap, - void *opaque)); -int kvm_register_coalesced_mmio(kvm_context_t kvm, uint64_t addr, - uint32_t size); -int kvm_unregister_coalesced_mmio(kvm_context_t kvm, uint64_t addr, - uint32_t size); - -/*! - * \brief Get a bitmap of guest ram pages which are allocated to the guest. - * - * \param kvm Pointer to the current kvm_context - * \param phys_addr Memory slot phys addr - * \param bitmap Long aligned address of a big enough bitmap (one bit per page) - */ -int kvm_get_mem_map(kvm_context_t kvm, unsigned long phys_addr, void *bitmap); -int kvm_get_mem_map_range(kvm_context_t kvm, unsigned long phys_addr, - unsigned long len, void *buf, void *opaque, - int (*cb)(unsigned long start, - unsigned long len, void *bitmap, - void *opaque)); -int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status); - -int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, uint64_t phys_start, - uint64_t len); -int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, uint64_t phys_start, - uint64_t len); -/*! - * \brief Enable dirty-pages-logging for all memory regions - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_dirty_pages_log_enable_all(kvm_context_t kvm); - -/*! - * \brief Disable dirty-page-logging for some memory regions - * - * Disable dirty-pages-logging for those memory regions that were - * created with dirty-page-logging disabled. - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_dirty_pages_log_reset(kvm_context_t kvm); - -#ifdef KVM_CAP_IRQCHIP /*! * \brief Dump in kernel IRQCHIP contents * @@ -331,7 +48,7 @@ * \param kvm Pointer to the current kvm_context * \param chip The irq chip device to be dumped */ -int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip); +int kvm_get_irqchip(KVMState *s, struct kvm_irqchip *chip); /*! * \brief Set in kernel IRQCHIP contents @@ -343,7 +60,7 @@ * \param kvm Pointer to the current kvm_context * \param chip THe irq chip device to be written */ -int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip); +int kvm_set_irqchip(KVMState *s, struct kvm_irqchip *chip); #if defined(__i386__) || defined(__x86_64__) /*! @@ -368,33 +85,6 @@ */ int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s); -#endif - -/*! - * \brief Simulate an NMI - * - * This allows you to simulate a non-maskable interrupt. - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \return 0 on success - */ -int kvm_inject_nmi(CPUState *env); - -#endif - -/*! - * \brief Initialize coalesced MMIO - * - * Check for coalesced MMIO capability and store in context - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_init_coalesced_mmio(kvm_context_t kvm); - -#ifdef KVM_CAP_PIT - -#if defined(__i386__) || defined(__x86_64__) /*! * \brief Get in kernel PIT of the virtual domain * @@ -403,7 +93,7 @@ * \param kvm Pointer to the current kvm_context * \param s PIT state of the virtual domain */ -int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s); +int kvm_get_pit(KVMState *s, struct kvm_pit_state *pit_state); /*! * \brief Set in kernel PIT of the virtual domain @@ -414,18 +104,9 @@ * \param kvm Pointer to the current kvm_context * \param s PIT state of the virtual domain */ -int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s); +int kvm_set_pit(KVMState *s, struct kvm_pit_state *pit_state); -int kvm_reinject_control(kvm_context_t kvm, int pit_reinject); - -#ifdef KVM_CAP_PIT_STATE2 -/*! - * \brief Check for kvm support of kvm_pit_state2 - * - * \param kvm Pointer to the current kvm_context - * \return 0 on success - */ -int kvm_has_pit_state2(kvm_context_t kvm); +int kvm_reinject_control(KVMState *s, int pit_reinject); /*! * \brief Set in kernel PIT state2 of the virtual domain @@ -435,7 +116,7 @@ * \param ps2 PIT state2 of the virtual domain * \return 0 on success */ -int kvm_set_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2); +int kvm_set_pit2(KVMState *s, struct kvm_pit_state2 *ps2); /*! * \brief Get in kernel PIT state2 of the virtual domain @@ -445,27 +126,12 @@ * \param ps2 PIT state2 of the virtual domain * \return 0 on success */ -int kvm_get_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2); +int kvm_get_pit2(KVMState *s, struct kvm_pit_state2 *ps2); #endif -#endif -#endif - -#ifdef KVM_CAP_VAPIC int kvm_enable_vapic(CPUState *env, uint64_t vapic); -#endif - -#if defined(__s390__) -int kvm_s390_initial_reset(kvm_context_t kvm, int slot); -int kvm_s390_interrupt(kvm_context_t kvm, int slot, - struct kvm_s390_interrupt *kvmint); -int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw); -int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr); -#endif - -#ifdef KVM_CAP_DEVICE_ASSIGNMENT /*! * \brief Notifies host kernel about a PCI device to be assigned to a guest * @@ -475,7 +141,7 @@ * \param kvm Pointer to the current kvm_context * \param assigned_dev Parameters, like bus, devfn number, etc */ -int kvm_assign_pci_device(kvm_context_t kvm, +int kvm_assign_pci_device(KVMState *s, struct kvm_assigned_pci_dev *assigned_dev); /*! @@ -487,9 +153,8 @@ * \param kvm Pointer to the current kvm_context * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc */ -int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq); +int kvm_assign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq); -#ifdef KVM_CAP_ASSIGN_DEV_IRQ /*! * \brief Deassign IRQ for an assigned device * @@ -499,11 +164,8 @@ * \param kvm Pointer to the current kvm_context * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc */ -int kvm_deassign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq); -#endif -#endif +int kvm_deassign_irq(KVMState *s, struct kvm_assigned_irq *assigned_irq); -#ifdef KVM_CAP_DEVICE_DEASSIGNMENT /*! * \brief Notifies host kernel about a PCI device to be deassigned from a guest * @@ -513,20 +175,8 @@ * \param kvm Pointer to the current kvm_context * \param assigned_dev Parameters, like bus, devfn number, etc */ -int kvm_deassign_pci_device(kvm_context_t kvm, +int kvm_deassign_pci_device(KVMState *s, struct kvm_assigned_pci_dev *assigned_dev); -#endif - -/*! - * \brief Determines the number of gsis that can be routed - * - * Returns the number of distinct gsis that can be routed by kvm. This is - * also the number of distinct routes (if a gsi has two routes, than another - * gsi cannot be used...) - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_get_gsi_count(kvm_context_t kvm); /*! * \brief Clears the temporary irq routing table @@ -581,191 +231,40 @@ struct kvm_irq_routing_entry *newentry); -/*! - * \brief Create a file descriptor for injecting interrupts - * - * Creates an eventfd based file-descriptor that maps to a specific GSI - * in the guest. eventfd compliant signaling (write() from userspace, or - * eventfd_signal() from kernelspace) will cause the GSI to inject - * itself into the guest at the next available window. - * - * \param kvm Pointer to the current kvm_context - * \param gsi GSI to assign to this fd - * \param flags reserved, must be zero - */ -int kvm_irqfd(kvm_context_t kvm, int gsi, int flags); - -#ifdef KVM_CAP_DEVICE_MSIX -int kvm_assign_set_msix_nr(kvm_context_t kvm, - struct kvm_assigned_msix_nr *msix_nr); -int kvm_assign_set_msix_entry(kvm_context_t kvm, +int kvm_assign_set_msix_nr(KVMState *s, struct kvm_assigned_msix_nr *msix_nr); +int kvm_assign_set_msix_entry(KVMState *s, struct kvm_assigned_msix_entry *entry); -#endif #else /* !CONFIG_KVM */ -typedef struct kvm_context *kvm_context_t; -typedef struct kvm_vcpu_context *kvm_vcpu_context_t; - struct kvm_pit_state { }; #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); -int kvm_vcpu_inited(CPUState *env); 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_physical_memory_set_dirty_tracking(int enable); - -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); -#ifndef CONFIG_USER_ONLY -void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr, unsigned long size, - int log, int writable); - -void kvm_cpu_destroy_phys_mem(target_phys_addr_t start_addr, - unsigned long size); -void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, - int log); -#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); - -void kvm_arch_save_regs(CPUState *env); -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); -void kvm_arch_push_nmi(void *opaque); -void kvm_arch_cpu_reset(CPUState *env); -int kvm_set_boot_cpu_id(uint32_t id); - -void qemu_kvm_aio_wait_start(void); -void qemu_kvm_aio_wait(void); -void qemu_kvm_aio_wait_end(void); - void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write); int kvm_arch_init_irq_routing(void); -int kvm_mmio_read(void *opaque, uint64_t addr, uint8_t * data, int len); -int kvm_mmio_write(void *opaque, uint64_t addr, uint8_t * data, int len); - -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -struct ioperm_data; - -void kvm_ioperm(CPUState *env, void *data); -void kvm_add_ioperm_data(struct ioperm_data *data); -void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num); -void kvm_arch_do_ioperm(void *_data); -#endif - -#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) -#define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8) +int kvm_add_ioport_region(unsigned long start, unsigned long size); +int kvm_remove_ioport_region(unsigned long start, unsigned long size); -#ifdef CONFIG_KVM -#include "qemu-queue.h" +int kvm_update_ioport_access(CPUState *env); +int kvm_arch_set_ioport_access(unsigned long start, unsigned long size, + bool enable); extern int kvm_irqchip; extern int kvm_pit; extern int kvm_pit_reinject; -extern int kvm_nested; -extern kvm_context_t kvm_context; - -struct ioperm_data { - unsigned long start_port; - unsigned long num; - int turn_on; - QLIST_ENTRY(ioperm_data) entries; -}; - -void qemu_kvm_cpu_stop(CPUState *env); -int kvm_arch_halt(CPUState *env); -int handle_tpr_access(void *opaque, CPUState *env, uint64_t rip, - int is_write); - -#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 -#else -#define kvm_nested 0 -#define qemu_kvm_has_gsi_routing() (0) -#ifdef TARGET_I386 -#define qemu_kvm_has_pit_state2() (0) -#endif -#define qemu_kvm_cpu_stop(env) do {} while(0) -#endif - -#ifdef CONFIG_KVM - -typedef struct KVMSlot { - target_phys_addr_t start_addr; - ram_addr_t memory_size; - ram_addr_t phys_offset; - int slot; - int flags; -} KVMSlot; - -typedef struct kvm_dirty_log KVMDirtyLog; - -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; -}; - -extern struct KVMState *kvm_state; +extern unsigned int kvm_shadow_memory; +int kvm_handle_tpr_access(CPUState *env); 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.14.1+noroms/qemu-kvm-ia64.c qemu-kvm-0.15.0+noroms/qemu-kvm-ia64.c --- qemu-kvm-0.14.1+noroms/qemu-kvm-ia64.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-kvm-ia64.c 2011-08-09 07:40:29.000000000 -0500 @@ -16,12 +16,12 @@ return 0; } -void kvm_arch_load_regs(CPUState *env, int level) +void kvm_arch_put_registers(CPUState *env, int level) { } -void kvm_arch_save_regs(CPUState *env) +void kvm_arch_get_registers(CPUState *env) { } @@ -120,7 +120,7 @@ #endif } -void kvm_arch_cpu_reset(CPUState *env) +void kvm_arch_reset_vcpu(CPUState *env) { if (kvm_irqchip_in_kernel(kvm_context)) { #ifdef KVM_CAP_MP_STATE diff -Nru qemu-kvm-0.14.1+noroms/qemu-kvm-x86.c qemu-kvm-0.15.0+noroms/qemu-kvm-x86.c --- qemu-kvm-0.14.1+noroms/qemu-kvm-x86.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-kvm-x86.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,7 +15,6 @@ #include #include "qemu-kvm.h" -#include "libkvm.h" #include #include #include @@ -24,151 +23,35 @@ #include "kvm.h" #include "hw/apic.h" -#define MSR_IA32_TSC 0x10 - -static struct kvm_msr_list *kvm_msr_list; -extern unsigned int kvm_shadow_memory; - -int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr) -{ - int r; - - 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) -{ - 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; - } - } 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; - - 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; -} - -static int kvm_init_identity_map_page(kvm_context_t kvm) +static int kvm_create_pit(KVMState *s) { -#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 (kvm_pit) { + r = kvm_vm_ioctl(s, KVM_CREATE_PIT); if (r < 0) { - fprintf(stderr, "kvm_init_identity_map_page: " - "unable to set identity mapping addr\n"); + fprintf(stderr, "Create kernel PIC irqchip failed\n"); return r; } - } -#endif - return 0; -} - -static int kvm_create_pit(kvm_context_t kvm) -{ -#ifdef KVM_CAP_PIT - int 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"); + if (!kvm_pit_reinject) { + r = kvm_reinject_control(s, 0); + if (r < 0) { + fprintf(stderr, + "failure to disable in-kernel PIT reinjection\n"); return r; } } } -#endif - return 0; -} - -int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, - void **vm_mem) -{ - int r = 0; - - r = kvm_init_tss(kvm); - if (r < 0) { - return r; - } - - r = kvm_init_identity_map_page(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_create_pit(kvm); - if (r < 0) { - return r; - } - - r = kvm_init_coalesced_mmio(kvm); - if (r < 0) { - return r; - } - return 0; } -#ifdef KVM_EXIT_TPR_ACCESS - -static int kvm_handle_tpr_access(CPUState *env) +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); - return 0; + return 1; } @@ -181,33 +64,6 @@ 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; - - switch (run->exit_reason) { -#ifdef KVM_EXIT_SET_TPR - case KVM_EXIT_SET_TPR: - break; -#endif -#ifdef KVM_EXIT_TPR_ACCESS - case KVM_EXIT_TPR_ACCESS: - r = kvm_handle_tpr_access(env); - break; -#endif - default: - r = 1; - break; - } - - return r; -} - -#ifdef KVM_CAP_IRQCHIP - int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s) { int r = 0; @@ -239,220 +95,38 @@ return r; } -#endif - -#ifdef KVM_CAP_PIT - -int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s) +int kvm_get_pit(KVMState *s, struct kvm_pit_state *pit_state) { if (!kvm_pit_in_kernel()) { return 0; } - return kvm_vm_ioctl(kvm_state, KVM_GET_PIT, s); + return kvm_vm_ioctl(s, KVM_GET_PIT, pit_state); } -int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s) +int kvm_set_pit(KVMState *s, struct kvm_pit_state *pit_state) { if (!kvm_pit_in_kernel()) { return 0; } - return kvm_vm_ioctl(kvm_state, KVM_SET_PIT, s); + return kvm_vm_ioctl(s, KVM_SET_PIT, pit_state); } -#ifdef KVM_CAP_PIT_STATE2 -int kvm_get_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2) +int kvm_get_pit2(KVMState *s, struct kvm_pit_state2 *ps2) { if (!kvm_pit_in_kernel()) { return 0; } - return kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, ps2); + return kvm_vm_ioctl(s, KVM_GET_PIT2, ps2); } -int kvm_set_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2) +int kvm_set_pit2(KVMState *s, struct kvm_pit_state2 *ps2) { if (!kvm_pit_in_kernel()) { return 0; } - return kvm_vm_ioctl(kvm_state, KVM_SET_PIT2, ps2); -} - -#endif -#endif - -int kvm_has_pit_state2(kvm_context_t kvm) -{ - int r = 0; - -#ifdef KVM_CAP_PIT_STATE2 - r = kvm_check_extension(kvm_state, KVM_CAP_PIT_STATE2); -#endif - 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); -} - - -/* - * Returns available msr list. User must free. - */ -static struct kvm_msr_list *kvm_get_msr_list(void) -{ - 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; -} - -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); -} - -static void print_dt(FILE *file, const char *name, struct kvm_dtable *dt) -{ - 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_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages) -{ -#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL - 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; - } -#endif - return -1; -} - -int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages) -{ -#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL - 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; - } -#endif - return -1; + return kvm_vm_ioctl(s, KVM_SET_PIT2, ps2); } -#ifdef KVM_CAP_VAPIC static int kvm_enable_tpr_access_reporting(CPUState *env) { int r; @@ -464,302 +138,25 @@ } return kvm_vcpu_ioctl(env, KVM_TPR_ACCESS_REPORTING, &tac); } -#endif - -#ifdef KVM_CAP_ADJUST_CLOCK -static struct kvm_clock_data kvmclock_data; - -static void kvmclock_pre_save(void *opaque) -{ - struct kvm_clock_data *cl = opaque; - - kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, cl); -} - -static int kvmclock_post_load(void *opaque, int version_id) -{ - struct kvm_clock_data *cl = opaque; - - return kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, cl); -} - -static const VMStateDescription vmstate_kvmclock= { - .name = "kvmclock", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .pre_save = kvmclock_pre_save, - .post_load = kvmclock_post_load, - .fields = (VMStateField []) { - VMSTATE_U64(clock, struct kvm_clock_data), - VMSTATE_END_OF_LIST() - } -}; -#endif - -int kvm_arch_qemu_create_context(void) -{ - int r; - struct utsname utsname; - - uname(&utsname); - lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; - - if (kvm_shadow_memory) { - kvm_set_shadow_pages(kvm_context, kvm_shadow_memory); - } - - /* initialize has_msr_star/has_msr_hsave_pa */ - r = kvm_get_supported_msrs(kvm_state); - if (r < 0) { - return r; - } - - kvm_msr_list = kvm_get_msr_list(); - if (!kvm_msr_list) { - return -1; - } - -#ifdef KVM_CAP_ADJUST_CLOCK - if (kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK)) { - vmstate_register(NULL, 0, &vmstate_kvmclock, &kvmclock_data); - } -#endif - - r = kvm_set_boot_cpu_id(0); - if (r < 0 && r != -ENOSYS) { - return r; - } - - return 0; -} - -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) { - env->mp_state = -1; - } 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 -} - -static void kvm_arch_load_mpstate(CPUState *env) -{ -#ifdef KVM_CAP_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) { - mp_state.mp_state = env->mp_state; - kvm_set_mpstate(env, &mp_state); - } -#endif -} - -#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()); - - kvm_getput_regs(env, 1); - - kvm_put_xsave(env); - kvm_put_xcrs(env); - - kvm_put_sregs(env); - - rc = kvm_put_msrs(env, level); - if (rc < 0) { - perror("kvm__msrs FAILED"); - } - - if (level >= KVM_PUT_RESET_STATE) { - kvm_arch_load_mpstate(env); - kvm_load_lapic(env); - } - if (level == KVM_PUT_FULL_STATE) { - if (env->kvm_vcpu_update_vapic) { - kvm_tpr_enable_vapic(env); - } - } - - kvm_put_vcpu_events(env, level); - kvm_put_debugregs(env); - - /* must be last */ - kvm_guest_debug_workarounds(env); -} - -void kvm_arch_save_regs(CPUState *env) -{ - int rc; - - assert(kvm_cpu_is_stopped(env) || env->thread_id == kvm_get_thread_id()); - - kvm_getput_regs(env, 0); - - kvm_get_xsave(env); - kvm_get_xcrs(env); - - kvm_get_sregs(env); - - 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); -} static int _kvm_arch_init_vcpu(CPUState *env) { kvm_arch_reset_vcpu(env); -#ifdef KVM_EXIT_TPR_ACCESS kvm_enable_tpr_access_reporting(env); -#endif - return 0; -} - -int kvm_arch_halt(CPUState *env) -{ - - if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && - (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 (!kvm_irqchip_in_kernel()) { - kvm_set_cr8(env, cpu_get_apic_tpr(env->apic_state)); - } - return 0; + return kvm_update_ioport_access(env); } -int kvm_arch_has_work(CPUState *env) +#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT +int kvm_arch_set_ioport_access(unsigned long start, unsigned long size, + bool enable) { - if (((env->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK)) || - (env->interrupt_request & CPU_INTERRUPT_NMI)) { - return 1; + if (ioperm(start, size, enable) < 0) { + return -errno; } return 0; } - -int kvm_arch_try_push_interrupts(void *opaque) -{ - CPUState *env = cpu_single_env; - int r, irq; - - 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); - } - } - } - - return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0; -} - -#ifdef KVM_CAP_USER_NMI -void kvm_arch_push_nmi(void *opaque) -{ - CPUState *env = cpu_single_env; - int r; - - if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI))) { - return; - } - - env->interrupt_request &= ~CPU_INTERRUPT_NMI; - r = kvm_inject_nmi(env); - if (r < 0) { - printf("cpu %d fail inject NMI\n", env->cpu_index); - } -} -#endif /* KVM_CAP_USER_NMI */ - -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; - - if (!kvm_msr_list) { - return -1; - } - - 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); - } - - msr_data.info.nmsrs = n; - - return kvm_vcpu_ioctl(env, KVM_SET_MSRS, &msr_data); -} - - -void kvm_arch_cpu_reset(CPUState *env) -{ - kvm_reset_msrs(env); - kvm_arch_reset_vcpu(env); -} - -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -void kvm_arch_do_ioperm(void *_data) -{ - struct ioperm_data *data = _data; - ioperm(data->start_port, data->num, data->turn_on); -} #endif /* @@ -769,7 +166,7 @@ { int i, r; - if (kvm_irqchip && kvm_has_gsi_routing()) { + if (kvm_has_gsi_routing()) { kvm_clear_gsi_routes(); for (i = 0; i < 8; ++i) { if (i == 2) { @@ -787,9 +184,9 @@ } } for (i = 0; i < 24; ++i) { - if (i == 0 && irq0override) { + if (i == 0) { r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, 2); - } else if (i != 2 || !irq0override) { + } else if (i != 2) { r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, i); } if (r < 0) { @@ -797,18 +194,17 @@ } } kvm_commit_irq_routes(); - } - return 0; -} -void kvm_arch_process_irqchip_events(CPUState *env) -{ - if (env->interrupt_request & CPU_INTERRUPT_INIT) { - kvm_cpu_synchronize_state(env); - do_cpu_init(env); - } - if (env->interrupt_request & CPU_INTERRUPT_SIPI) { - kvm_cpu_synchronize_state(env); - do_cpu_sipi(env); + if (!kvm_has_pit_state2()) { + no_hpet = 1; + } + } else { + /* If kernel can't do irq routing, interrupt source + * override 0->2 can not be set up as required by HPET. + * so we have to disable it. + */ + no_hpet = 1; } + + return 0; } diff -Nru qemu-kvm-0.14.1+noroms/qemu-lock.h qemu-kvm-0.15.0+noroms/qemu-lock.h --- qemu-kvm-0.14.1+noroms/qemu-lock.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-lock.h 2011-08-09 07:40:29.000000000 -0500 @@ -15,15 +15,11 @@ * License along with this library; if not, see */ -/* Locking primitives. Most of this code should be redundant - - system emulation doesn't need/use locking, NPTL userspace uses - pthread mutexes, and non-NPTL userspace isn't threadsafe anyway. - In either case a spinlock is probably the wrong kind of lock. - Spinlocks are only good if you know annother CPU has the lock and is - likely to release it soon. In environments where you have more threads - than physical CPUs (the extreme case being a single CPU host) a spinlock - simply wastes CPU until the OS decides to preempt it. */ -#if defined(CONFIG_USE_NPTL) +/* configure guarantees us that we have pthreads on any host except + * mingw32, which doesn't support any of the user-only targets. + * So we can simply assume we have pthread mutexes here. + */ +#if defined(CONFIG_USER_ONLY) #include #define spin_lock pthread_mutex_lock @@ -33,198 +29,15 @@ #else -#if defined(__hppa__) - -typedef int spinlock_t[4]; - -#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 } - -static inline void resetlock (spinlock_t *p) -{ - (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1; -} - -#else - +/* Empty implementations, on the theory that system mode emulation + * is single-threaded. This means that these functions should only + * be used from code run in the TCG cpu thread, and cannot protect + * data structures which might also be accessed from the IO thread + * or from signal handlers. + */ typedef int spinlock_t; - #define SPIN_LOCK_UNLOCKED 0 -static inline void resetlock (spinlock_t *p) -{ - *p = SPIN_LOCK_UNLOCKED; -} - -#endif - -#if defined(_ARCH_PPC) -static inline int testandset (int *p) -{ - int ret; - __asm__ __volatile__ ( - " lwarx %0,0,%1\n" - " xor. %0,%3,%0\n" - " bne $+12\n" - " stwcx. %2,0,%1\n" - " bne- $-16\n" - : "=&r" (ret) - : "r" (p), "r" (1), "r" (0) - : "cr0", "memory"); - return ret; -} -#elif defined(__i386__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__x86_64__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__s390__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" - " jl 0b" - : "=&d" (ret) - : "r" (1), "a" (p), "0" (*p) - : "cc", "memory" ); - return ret; -} -#elif defined(__alpha__) -static inline int testandset (int *p) -{ - int ret; - unsigned long one; - - __asm__ __volatile__ ("0: mov 1,%2\n" - " ldl_l %0,%1\n" - " stl_c %2,%1\n" - " beq %2,1f\n" - ".subsection 2\n" - "1: br 0b\n" - ".previous" - : "=r" (ret), "=m" (*p), "=r" (one) - : "m" (*p)); - return ret; -} -#elif defined(__sparc__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__("ldstub [%1], %0" - : "=r" (ret) - : "r" (p) - : "memory"); - - return (ret ? 1 : 0); -} -#elif defined(__arm__) -static inline int testandset (int *spinlock) -{ - register unsigned int ret; - __asm__ __volatile__("swp %0, %1, [%2]" - : "=r"(ret) - : "0"(1), "r"(spinlock)); - - return ret; -} -#elif defined(__mc68000) -static inline int testandset (int *p) -{ - char ret; - __asm__ __volatile__("tas %1; sne %0" - : "=r" (ret) - : "m" (p) - : "cc","memory"); - return ret; -} -#elif defined(__hppa__) - -/* Because malloc only guarantees 8-byte alignment for malloc'd data, - and GCC only guarantees 8-byte alignment for stack locals, we can't - be assured of 16-byte alignment for atomic lock data even if we - specify "__attribute ((aligned(16)))" in the type declaration. So, - we use a struct containing an array of four ints for the atomic lock - type and dynamically select the 16-byte aligned int from the array - for the semaphore. */ -#define __PA_LDCW_ALIGNMENT 16 -static inline void *ldcw_align (void *p) { - unsigned long a = (unsigned long)p; - a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1); - return (void *)a; -} - -static inline int testandset (spinlock_t *p) -{ - unsigned int ret; - p = ldcw_align(p); - __asm__ __volatile__("ldcw 0(%1),%0" - : "=r" (ret) - : "r" (p) - : "memory" ); - return !ret; -} - -#elif defined(__ia64) - -#include "ia64intrin.h" - -static inline int testandset (int *p) -{ - return (int)cmpxchg_acq(p,0,1); -} -#elif defined(__mips__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ( - " .set push \n" - " .set noat \n" - " .set mips2 \n" - "1: li $1, 1 \n" - " ll %0, %1 \n" - " sc $1, %1 \n" - " beqz $1, 1b \n" - " .set pop " - : "=r" (ret), "+R" (*p) - : - : "memory"); - - return ret; -} -#else -#error unimplemented CPU support -#endif - -#if defined(CONFIG_USER_ONLY) -static inline void spin_lock(spinlock_t *lock) -{ - while (testandset(lock)); -} - -static inline void spin_unlock(spinlock_t *lock) -{ - resetlock(lock); -} -#else static inline void spin_lock(spinlock_t *lock) { } @@ -232,6 +45,5 @@ static inline void spin_unlock(spinlock_t *lock) { } -#endif #endif diff -Nru qemu-kvm-0.14.1+noroms/qemu-nbd.c qemu-kvm-0.15.0+noroms/qemu-nbd.c --- qemu-kvm-0.14.1+noroms/qemu-nbd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-nbd.c 2011-08-09 07:40:29.000000000 -0500 @@ -238,7 +238,7 @@ flags |= BDRV_O_SNAPSHOT; break; case 'n': - flags |= BDRV_O_NOCACHE; + flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; break; case 'b': bindto = optarg; @@ -359,7 +359,7 @@ if (!verbose) { /* detach client and server */ - if (daemon(0, 0) == -1) { + if (qemu_daemon(0, 0) == -1) { err(EXIT_FAILURE, "Failed to daemonize"); } } diff -Nru qemu-kvm-0.14.1+noroms/qemu-options.hx qemu-kvm-0.15.0+noroms/qemu-options.hx --- qemu-kvm-0.14.1+noroms/qemu-options.hx 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-options.hx 2011-08-09 07:40:29.000000000 -0500 @@ -27,14 +27,29 @@ Display version information and exit ETEXI -DEF("M", HAS_ARG, QEMU_OPTION_M, - "-M machine select emulated machine (-M ? for list)\n", QEMU_ARCH_ALL) +DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ + "-machine [type=]name[,prop[=value][,...]]\n" + " selects emulated machine (-machine ? for list)\n" + " property accel=accel1[:accel2[:...]] selects accelerator\n" + " supported accelerators are kvm, xen, tcg (default: tcg)\n", + QEMU_ARCH_ALL) STEXI -@item -M @var{machine} -@findex -M -Select the emulated @var{machine} (@code{-M ?} for list) +@item -machine [type=]@var{name}[,prop=@var{value}[,...]] +@findex -machine +Select the emulated machine by @var{name}. Use @code{-machine ?} to list +available machines. Supported machine properties are: +@table @option +@item accel=@var{accels1}[:@var{accels2}[:...]] +This is used to enable an accelerator. Depending on the target architecture, +kvm, xen, or tcg can be available. By default, tcg is used. If there is more +than one accelerator specified, the next one is used if the previous one fails +to initialize. +@end table ETEXI +HXCOMM Deprecated by -machine +DEF("M", HAS_ARG, QEMU_OPTION_M, "", QEMU_ARCH_ALL) + DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, "-cpu cpu select CPU (-cpu ? for list)\n", QEMU_ARCH_ALL) STEXI @@ -160,6 +175,14 @@ 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 werror=@var{action},rerror=@var{action} +Specify which @var{action} to take on write and read errors. Valid actions are: +"ignore" (ignore the error and try to continue), "stop" (pause QEMU), +"report" (report the error to the guest), "enospc" (pause QEMU only if the +host disk is full; report the error to the guest otherwise). +The default setting is @option{werror=enospc} and @option{rerror=report}. +@item readonly +Open drive @option{file} as read-only. Guest write attempts will fail. @item boot=@var{boot} @var{boot} is "on" or "off" and allows for booting from non-traditional interfaces, such as virtio. @end table @@ -592,6 +615,37 @@ @table @option ETEXI +DEF("display", HAS_ARG, QEMU_OPTION_display, + "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n" + " [,window_close=on|off]|curses|none|\n" + " vnc=[,]\n" + " select display type\n", QEMU_ARCH_ALL) +STEXI +@item -display @var{type} +@findex -display +Select type of display to use. This option is a replacement for the +old style -sdl/-curses/... options. Valid values for @var{type} are +@table @option +@item sdl +Display video output via SDL (usually in a separate graphics +window; see the SDL documentation for other possibilities). +@item curses +Display video output via curses. For graphics device models which +support a text mode, QEMU can display this output using a +curses/ncurses interface. Nothing is displayed when the graphics +device is in graphical mode or if the graphics device does not support +a text mode. Generally only the VGA device models support text mode. +@item none +Do not display video output. The guest will still see an emulated +graphics card, but its output will not be displayed to the QEMU +user. This option differs from the -nographic option in that it +only affects what is done with video output; -nographic also changes +the destination of the serial and parallel port data. +@item vnc +Start a VNC server on display +@end table +ETEXI + DEF("nographic", 0, QEMU_OPTION_nographic, "-nographic disable graphical output and redirect serial I/Os to console\n", QEMU_ARCH_ALL) @@ -605,11 +659,9 @@ with a serial console. ETEXI -#ifdef CONFIG_CURSES DEF("curses", 0, QEMU_OPTION_curses, "-curses use a curses/ncurses interface instead of SDL\n", QEMU_ARCH_ALL) -#endif STEXI @item -curses @findex curses @@ -618,11 +670,9 @@ curses/ncurses interface. Nothing is displayed in graphical mode. ETEXI -#ifdef CONFIG_SDL DEF("no-frame", 0, QEMU_OPTION_no_frame, "-no-frame open SDL window without a frame and window decorations\n", QEMU_ARCH_ALL) -#endif STEXI @item -no-frame @findex -no-frame @@ -631,42 +681,36 @@ workspace more convenient. ETEXI -#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", QEMU_ARCH_ALL) -#endif STEXI @item -alt-grab @findex -alt-grab -Use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt). +Use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt). Note that this also +affects the special keys (for fullscreen, monitor-mode switching, etc). 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", QEMU_ARCH_ALL) -#endif STEXI @item -ctrl-grab @findex -ctrl-grab -Use Right-Ctrl to grab mouse (instead of Ctrl-Alt). +Use Right-Ctrl to grab mouse (instead of Ctrl-Alt). Note that this also +affects the special keys (for fullscreen, monitor-mode switching, etc). ETEXI -#ifdef CONFIG_SDL DEF("no-quit", 0, QEMU_OPTION_no_quit, "-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", QEMU_ARCH_ALL) -#endif STEXI @item -sdl @findex -sdl @@ -695,9 +739,25 @@ @item password= Set the password you need to authenticate. +@item sasl +Require that the client use SASL to authenticate with the spice. +The exact choice of authentication method used is controlled from the +system / user's SASL configuration file for the 'qemu' service. This +is typically found in /etc/sasl2/qemu.conf. If running QEMU as an +unprivileged user, an environment variable SASL_CONF_PATH can be used +to make it search alternate locations for the service config. +While some SASL auth methods can also provide data encryption (eg GSSAPI), +it is recommended that SASL always be combined with the 'tls' and +'x509' settings to enable use of SSL and server certificates. This +ensures a data encryption preventing compromise of authentication +credentials. + @item disable-ticketing Allow client connects without authentication. +@item disable-copy-paste +Disable copy paste between the client and the guest. + @item tls-port= Set the TCP port spice is listening on for encrypted channels. @@ -752,6 +812,15 @@ Rotate graphical output 90 deg left (only PXA LCD). ETEXI +DEF("rotate", HAS_ARG, QEMU_OPTION_rotate, + "-rotate rotate graphical output some deg left (only PXA LCD)\n", + QEMU_ARCH_ALL) +STEXI +@item -rotate +@findex -rotate +Rotate graphical output some deg left (only PXA LCD). +ETEXI + DEF("vga", HAS_ARG, QEMU_OPTION_vga, "-vga [std|cirrus|vmware|qxl|xenfb|none]\n" " select video card type\n", QEMU_ARCH_ALL) @@ -915,6 +984,15 @@ depending on its encoding settings. Enabling this option can save a lot of bandwidth at the expense of quality. +@item non-adaptive + +Disable adaptive encodings. Adaptive encodings are enabled by default. +An adaptive encoding will try to detect frequently updated screen regions, +and send updates in these regions using a lossy encoding (like JPEG). +This can be really helpful to save bandwidth when playing videos. Disabling +adaptive encodings allows to restore the original static behavior of encodings +like Tight. + @end table ETEXI @@ -1039,7 +1117,7 @@ "-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" " create a new Network Interface Card and connect it to VLAN 'n'\n" #ifdef CONFIG_SLIRP - "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n]\n" + "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=on|off]\n" " [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n" " [,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 @@ -1126,23 +1204,23 @@ @item net=@var{addr}[/@var{mask}] Set IP network address the guest will see. Optionally specify the netmask, either in the form a.b.c.d or as number of valid top-most bits. Default is -10.0.2.0/8. +10.0.2.0/24. @item host=@var{addr} Specify the guest-visible address of the host. Default is the 2nd IP in the guest network, i.e. x.x.x.2. -@item restrict=y|yes|n|no -If this options is enabled, the guest will be isolated, i.e. it will not be +@item restrict=on|off +If this option is enabled, the guest will be isolated, i.e. it will not be able to contact the host and no guest IP packets will be routed over the host -to the outside. This option does not affect explicitly set forwarding rule. +to the outside. This option does not affect any explicitly set forwarding rules. @item hostname=@var{name} Specifies the client hostname reported by the builtin DHCP server. @item dhcpstart=@var{addr} Specify the first of the 16 IPs the built-in DHCP server can assign. Default -is the 16th to 31st IP in the guest network, i.e. x.x.x.16 to x.x.x.31. +is the 15th to 31st IP in the guest network, i.e. x.x.x.15 to x.x.x.31. @item dns=@var{addr} Specify the guest-visible address of the virtual nameserver. The address must @@ -1963,6 +2041,15 @@ Output log in /tmp/qemu.log ETEXI +DEF("D", HAS_ARG, QEMU_OPTION_D, \ + "-D logfile output log to logfile (instead of the default /tmp/qemu.log)\n", + QEMU_ARCH_ALL) +STEXI +@item -D +@findex -D +Output log in logfile instead of /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" \ @@ -2355,8 +2442,6 @@ "-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", QEMU_ARCH_I386) DEF("nvram", HAS_ARG, QEMU_OPTION_nvram, "-nvram FILE provide ia64 nvram contents\n", QEMU_ARCH_ALL) DEF("tdf", 0, QEMU_OPTION_tdf, diff -Nru qemu-kvm-0.14.1+noroms/qemu-os-win32.h qemu-kvm-0.15.0+noroms/qemu-os-win32.h --- qemu-kvm-0.14.1+noroms/qemu-os-win32.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-os-win32.h 2011-08-09 07:40:29.000000000 -0500 @@ -26,6 +26,12 @@ #ifndef QEMU_OS_WIN32_H #define QEMU_OS_WIN32_H +#include +#include + +/* Declaration of ffs() is missing in MinGW's strings.h. */ +int ffs(int i); + /* Polling handling */ /* return TRUE if no sleep should be done afterwards */ diff -Nru qemu-kvm-0.14.1+noroms/qemu-progress.c qemu-kvm-0.15.0+noroms/qemu-progress.c --- qemu-kvm-0.14.1+noroms/qemu-progress.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qemu-progress.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,150 @@ +/* + * QEMU progress printing utility functions + * + * Copyright (C) 2011 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. + */ + +#include "qemu-common.h" +#include "osdep.h" +#include "sysemu.h" +#include + +struct progress_state { + float current; + float last_print; + float min_skip; + void (*print)(void); + void (*end)(void); +}; + +static struct progress_state state; +static volatile sig_atomic_t print_pending; + +/* + * Simple progress print function. + * @percent relative percent of current operation + * @max percent of total operation + */ +static void progress_simple_print(void) +{ + printf(" (%3.2f/100%%)\r", state.current); + fflush(stdout); +} + +static void progress_simple_end(void) +{ + printf("\n"); +} + +static void progress_simple_init(void) +{ + state.print = progress_simple_print; + state.end = progress_simple_end; +} + +#ifdef CONFIG_POSIX +static void sigusr_print(int signal) +{ + print_pending = 1; +} +#endif + +static void progress_dummy_print(void) +{ + if (print_pending) { + fprintf(stderr, " (%3.2f/100%%)\n", state.current); + print_pending = 0; + } +} + +static void progress_dummy_end(void) +{ +} + +static void progress_dummy_init(void) +{ +#ifdef CONFIG_POSIX + struct sigaction action; + + memset(&action, 0, sizeof(action)); + sigfillset(&action.sa_mask); + action.sa_handler = sigusr_print; + action.sa_flags = 0; + sigaction(SIGUSR1, &action, NULL); +#endif + + state.print = progress_dummy_print; + state.end = progress_dummy_end; +} + +/* + * Initialize progress reporting. + * If @enabled is false, actual reporting is suppressed. The user can + * still trigger a report by sending a SIGUSR1. + * Reports are also suppressed unless we've had at least @min_skip + * percent progress since the last report. + */ +void qemu_progress_init(int enabled, float min_skip) +{ + state.min_skip = min_skip; + if (enabled) { + progress_simple_init(); + } else { + progress_dummy_init(); + } +} + +void qemu_progress_end(void) +{ + state.end(); +} + +/* + * Report progress. + * @delta is how much progress we made. + * If @max is zero, @delta is an absolut value of the total job done. + * Else, @delta is a progress delta since the last call, as a fraction + * of @max. I.e. the delta is @delta * @max / 100. This allows + * relative accounting of functions which may be a different fraction of + * the full job, depending on the context they are called in. I.e. + * a function might be considered 40% of the full job if used from + * bdrv_img_create() but only 20% if called from img_convert(). + */ +void qemu_progress_print(float delta, int max) +{ + float current; + + if (max == 0) { + current = delta; + } else { + current = state.current + delta / 100 * max; + } + if (current > 100) { + current = 100; + } + state.current = current; + + if (current > (state.last_print + state.min_skip) || + (current == 100) || (current == 0)) { + state.last_print = state.current; + state.print(); + } +} diff -Nru qemu-kvm-0.14.1+noroms/qemu_socket.h qemu-kvm-0.15.0+noroms/qemu_socket.h --- qemu-kvm-0.14.1+noroms/qemu_socket.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu_socket.h 2011-08-09 07:40:29.000000000 -0500 @@ -54,9 +54,6 @@ /* Old, ipv4 only bits. Don't use for new code. */ int parse_host_port(struct sockaddr_in *saddr, const char *str); -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.14.1+noroms/qemu-sockets.c qemu-kvm-0.15.0+noroms/qemu-sockets.c --- qemu-kvm-0.14.1+noroms/qemu-sockets.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-sockets.c 2011-08-09 07:40:29.000000000 -0500 @@ -627,24 +627,28 @@ int unix_listen_opts(QemuOpts *opts) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_connect_opts(QemuOpts *opts) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_listen(const char *path, char *ostr, int olen) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } int unix_connect(const char *path) { fprintf(stderr, "unix sockets are not available on windows\n"); + errno = ENOTSUP; return -1; } diff -Nru qemu-kvm-0.14.1+noroms/qemu-thread.c qemu-kvm-0.15.0+noroms/qemu-thread.c --- qemu-kvm-0.14.1+noroms/qemu-thread.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-thread.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,192 +0,0 @@ -/* - * Wrappers around mutex/cond/thread functions - * - * Copyright Red Hat, Inc. 2009 - * - * Author: - * Marcelo Tosatti - * - * 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 -#include -#include -#include -#include -#include -#include "qemu-thread.h" - -static void error_exit(int err, const char *msg) -{ - fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err)); - exit(1); -} - -void qemu_mutex_init(QemuMutex *mutex) -{ - int err; - - err = pthread_mutex_init(&mutex->lock, NULL); - if (err) - 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; - - err = pthread_mutex_lock(&mutex->lock); - if (err) - error_exit(err, __func__); -} - -int qemu_mutex_trylock(QemuMutex *mutex) -{ - return pthread_mutex_trylock(&mutex->lock); -} - -static void timespec_add_ms(struct timespec *ts, uint64_t msecs) -{ - ts->tv_sec = ts->tv_sec + (long)(msecs / 1000); - ts->tv_nsec = (ts->tv_nsec + ((long)msecs % 1000) * 1000000); - if (ts->tv_nsec >= 1000000000) { - ts->tv_nsec -= 1000000000; - ts->tv_sec++; - } -} - -int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs) -{ - int err; - struct timespec ts; - - clock_gettime(CLOCK_REALTIME, &ts); - timespec_add_ms(&ts, msecs); - - err = pthread_mutex_timedlock(&mutex->lock, &ts); - if (err && err != ETIMEDOUT) - error_exit(err, __func__); - return err; -} - -void qemu_mutex_unlock(QemuMutex *mutex) -{ - int err; - - err = pthread_mutex_unlock(&mutex->lock); - if (err) - error_exit(err, __func__); -} - -void qemu_cond_init(QemuCond *cond) -{ - int err; - - err = pthread_cond_init(&cond->cond, NULL); - if (err) - 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; - - err = pthread_cond_signal(&cond->cond); - if (err) - error_exit(err, __func__); -} - -void qemu_cond_broadcast(QemuCond *cond) -{ - int err; - - err = pthread_cond_broadcast(&cond->cond); - if (err) - error_exit(err, __func__); -} - -void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex) -{ - int err; - - err = pthread_cond_wait(&cond->cond, &mutex->lock); - if (err) - error_exit(err, __func__); -} - -int qemu_cond_timedwait(QemuCond *cond, QemuMutex *mutex, uint64_t msecs) -{ - struct timespec ts; - int err; - - clock_gettime(CLOCK_REALTIME, &ts); - timespec_add_ms(&ts, msecs); - - err = pthread_cond_timedwait(&cond->cond, &mutex->lock, &ts); - if (err && err != ETIMEDOUT) - error_exit(err, __func__); - return err; -} - -void qemu_thread_create(QemuThread *thread, - void *(*start_routine)(void*), - void *arg) -{ - 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) -{ - int err; - - err = pthread_kill(thread->thread, sig); - if (err) - error_exit(err, __func__); -} - -void qemu_thread_self(QemuThread *thread) -{ - thread->thread = pthread_self(); -} - -int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2) -{ - return pthread_equal(thread1->thread, thread2->thread); -} - -void qemu_thread_exit(void *retval) -{ - pthread_exit(retval); -} diff -Nru qemu-kvm-0.14.1+noroms/qemu-thread.h qemu-kvm-0.15.0+noroms/qemu-thread.h --- qemu-kvm-0.14.1+noroms/qemu-thread.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-thread.h 2011-08-09 07:40:29.000000000 -0500 @@ -1,44 +1,41 @@ #ifndef __QEMU_THREAD_H #define __QEMU_THREAD_H 1 -#include "semaphore.h" -#include "pthread.h" -struct QemuMutex { - pthread_mutex_t lock; -}; - -struct QemuCond { - pthread_cond_t cond; -}; - -struct QemuThread { - pthread_t thread; -}; +#include typedef struct QemuMutex QemuMutex; typedef struct QemuCond QemuCond; typedef struct QemuThread QemuThread; +#ifdef _WIN32 +#include "qemu-thread-win32.h" +#else +#include "qemu-thread-posix.h" +#endif + 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); + +/* + * IMPORTANT: The implementation does not guarantee that pthread_cond_signal + * and pthread_cond_broadcast can be called except while the same mutex is + * held as in the corresponding pthread_cond_wait calls! + */ void qemu_cond_signal(QemuCond *cond); void qemu_cond_broadcast(QemuCond *cond); void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex); -int qemu_cond_timedwait(QemuCond *cond, QemuMutex *mutex, uint64_t msecs); void qemu_thread_create(QemuThread *thread, void *(*start_routine)(void*), void *arg); -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_get_self(QemuThread *thread); +int qemu_thread_is_self(QemuThread *thread); void qemu_thread_exit(void *retval); #endif diff -Nru qemu-kvm-0.14.1+noroms/qemu-thread-posix.c qemu-kvm-0.15.0+noroms/qemu-thread-posix.c --- qemu-kvm-0.14.1+noroms/qemu-thread-posix.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qemu-thread-posix.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,149 @@ +/* + * Wrappers around mutex/cond/thread functions + * + * Copyright Red Hat, Inc. 2009 + * + * Author: + * Marcelo Tosatti + * + * 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 +#include +#include +#include +#include +#include +#include "qemu-thread.h" + +static void error_exit(int err, const char *msg) +{ + fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err)); + exit(1); +} + +void qemu_mutex_init(QemuMutex *mutex) +{ + int err; + pthread_mutexattr_t mutexattr; + + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK); + err = pthread_mutex_init(&mutex->lock, &mutexattr); + pthread_mutexattr_destroy(&mutexattr); + if (err) + 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; + + err = pthread_mutex_lock(&mutex->lock); + if (err) + error_exit(err, __func__); +} + +int qemu_mutex_trylock(QemuMutex *mutex) +{ + return pthread_mutex_trylock(&mutex->lock); +} + +void qemu_mutex_unlock(QemuMutex *mutex) +{ + int err; + + err = pthread_mutex_unlock(&mutex->lock); + if (err) + error_exit(err, __func__); +} + +void qemu_cond_init(QemuCond *cond) +{ + int err; + + err = pthread_cond_init(&cond->cond, NULL); + if (err) + 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; + + err = pthread_cond_signal(&cond->cond); + if (err) + error_exit(err, __func__); +} + +void qemu_cond_broadcast(QemuCond *cond) +{ + int err; + + err = pthread_cond_broadcast(&cond->cond); + if (err) + error_exit(err, __func__); +} + +void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex) +{ + int err; + + err = pthread_cond_wait(&cond->cond, &mutex->lock); + if (err) + error_exit(err, __func__); +} + +void qemu_thread_create(QemuThread *thread, + void *(*start_routine)(void*), + void *arg) +{ + 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_get_self(QemuThread *thread) +{ + thread->thread = pthread_self(); +} + +int qemu_thread_is_self(QemuThread *thread) +{ + return pthread_equal(pthread_self(), thread->thread); +} + +void qemu_thread_exit(void *retval) +{ + pthread_exit(retval); +} diff -Nru qemu-kvm-0.14.1+noroms/qemu-thread-posix.h qemu-kvm-0.15.0+noroms/qemu-thread-posix.h --- qemu-kvm-0.14.1+noroms/qemu-thread-posix.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qemu-thread-posix.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,17 @@ +#ifndef __QEMU_THREAD_POSIX_H +#define __QEMU_THREAD_POSIX_H 1 +#include "pthread.h" + +struct QemuMutex { + pthread_mutex_t lock; +}; + +struct QemuCond { + pthread_cond_t cond; +}; + +struct QemuThread { + pthread_t thread; +}; + +#endif diff -Nru qemu-kvm-0.14.1+noroms/qemu-thread-win32.c qemu-kvm-0.15.0+noroms/qemu-thread-win32.c --- qemu-kvm-0.14.1+noroms/qemu-thread-win32.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qemu-thread-win32.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,281 @@ +/* + * Win32 implementation for mutex/cond/thread functions + * + * 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 "qemu-thread.h" +#include +#include +#include + +static void error_exit(int err, const char *msg) +{ + char *pstr; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, err, 0, (LPTSTR)&pstr, 2, NULL); + fprintf(stderr, "qemu: %s: %s\n", msg, pstr); + LocalFree(pstr); + exit(1); +} + +void qemu_mutex_init(QemuMutex *mutex) +{ + mutex->owner = 0; + InitializeCriticalSection(&mutex->lock); +} + +void qemu_mutex_destroy(QemuMutex *mutex) +{ + assert(mutex->owner == 0); + DeleteCriticalSection(&mutex->lock); +} + +void qemu_mutex_lock(QemuMutex *mutex) +{ + EnterCriticalSection(&mutex->lock); + + /* Win32 CRITICAL_SECTIONs are recursive. Assert that we're not + * using them as such. + */ + assert(mutex->owner == 0); + mutex->owner = GetCurrentThreadId(); +} + +int qemu_mutex_trylock(QemuMutex *mutex) +{ + int owned; + + owned = TryEnterCriticalSection(&mutex->lock); + if (owned) { + assert(mutex->owner == 0); + mutex->owner = GetCurrentThreadId(); + } + return !owned; +} + +void qemu_mutex_unlock(QemuMutex *mutex) +{ + assert(mutex->owner == GetCurrentThreadId()); + mutex->owner = 0; + LeaveCriticalSection(&mutex->lock); +} + +void qemu_cond_init(QemuCond *cond) +{ + memset(cond, 0, sizeof(*cond)); + + cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL); + if (!cond->sema) { + error_exit(GetLastError(), __func__); + } + cond->continue_event = CreateEvent(NULL, /* security */ + FALSE, /* auto-reset */ + FALSE, /* not signaled */ + NULL); /* name */ + if (!cond->continue_event) { + error_exit(GetLastError(), __func__); + } +} + +void qemu_cond_destroy(QemuCond *cond) +{ + BOOL result; + result = CloseHandle(cond->continue_event); + if (!result) { + error_exit(GetLastError(), __func__); + } + cond->continue_event = 0; + result = CloseHandle(cond->sema); + if (!result) { + error_exit(GetLastError(), __func__); + } + cond->sema = 0; +} + +void qemu_cond_signal(QemuCond *cond) +{ + DWORD result; + + /* + * Signal only when there are waiters. cond->waiters is + * incremented by pthread_cond_wait under the external lock, + * so we are safe about that. + */ + if (cond->waiters == 0) { + return; + } + + /* + * Waiting threads decrement it outside the external lock, but + * only if another thread is executing pthread_cond_broadcast and + * has the mutex. So, it also cannot be decremented concurrently + * with this particular access. + */ + cond->target = cond->waiters - 1; + result = SignalObjectAndWait(cond->sema, cond->continue_event, + INFINITE, FALSE); + if (result == WAIT_ABANDONED || result == WAIT_FAILED) { + error_exit(GetLastError(), __func__); + } +} + +void qemu_cond_broadcast(QemuCond *cond) +{ + BOOLEAN result; + /* + * As in pthread_cond_signal, access to cond->waiters and + * cond->target is locked via the external mutex. + */ + if (cond->waiters == 0) { + return; + } + + cond->target = 0; + result = ReleaseSemaphore(cond->sema, cond->waiters, NULL); + if (!result) { + error_exit(GetLastError(), __func__); + } + + /* + * At this point all waiters continue. Each one takes its + * slice of the semaphore. Now it's our turn to wait: Since + * the external mutex is held, no thread can leave cond_wait, + * yet. For this reason, we can be sure that no thread gets + * a chance to eat *more* than one slice. OTOH, it means + * that the last waiter must send us a wake-up. + */ + WaitForSingleObject(cond->continue_event, INFINITE); +} + +void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex) +{ + /* + * This access is protected under the mutex. + */ + cond->waiters++; + + /* + * Unlock external mutex and wait for signal. + * NOTE: we've held mutex locked long enough to increment + * waiters count above, so there's no problem with + * leaving mutex unlocked before we wait on semaphore. + */ + qemu_mutex_unlock(mutex); + WaitForSingleObject(cond->sema, INFINITE); + + /* Now waiters must rendez-vous with the signaling thread and + * let it continue. For cond_broadcast this has heavy contention + * and triggers thundering herd. So goes life. + * + * Decrease waiters count. The mutex is not taken, so we have + * to do this atomically. + * + * All waiters contend for the mutex at the end of this function + * until the signaling thread relinquishes it. To ensure + * each waiter consumes exactly one slice of the semaphore, + * the signaling thread stops until it is told by the last + * waiter that it can go on. + */ + if (InterlockedDecrement(&cond->waiters) == cond->target) { + SetEvent(cond->continue_event); + } + + qemu_mutex_lock(mutex); +} + +struct QemuThreadData { + QemuThread *thread; + void *(*start_routine)(void *); + void *arg; +}; + +static int qemu_thread_tls_index = TLS_OUT_OF_INDEXES; + +static unsigned __stdcall win32_start_routine(void *arg) +{ + struct QemuThreadData data = *(struct QemuThreadData *) arg; + QemuThread *thread = data.thread; + + free(arg); + TlsSetValue(qemu_thread_tls_index, thread); + + /* + * Use DuplicateHandle instead of assigning thread->thread in the + * creating thread to avoid races. It's simpler this way than with + * synchronization. + */ + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &thread->thread, + 0, FALSE, DUPLICATE_SAME_ACCESS); + + qemu_thread_exit(data.start_routine(data.arg)); + abort(); +} + +void qemu_thread_exit(void *arg) +{ + QemuThread *thread = TlsGetValue(qemu_thread_tls_index); + thread->ret = arg; + CloseHandle(thread->thread); + thread->thread = NULL; + ExitThread(0); +} + +static inline void qemu_thread_init(void) +{ + if (qemu_thread_tls_index == TLS_OUT_OF_INDEXES) { + qemu_thread_tls_index = TlsAlloc(); + if (qemu_thread_tls_index == TLS_OUT_OF_INDEXES) { + error_exit(ERROR_NO_SYSTEM_RESOURCES, __func__); + } + } +} + + +void qemu_thread_create(QemuThread *thread, + void *(*start_routine)(void *), + void *arg) +{ + HANDLE hThread; + + struct QemuThreadData *data; + qemu_thread_init(); + data = qemu_malloc(sizeof *data); + data->thread = thread; + data->start_routine = start_routine; + data->arg = arg; + + hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine, + data, 0, NULL); + if (!hThread) { + error_exit(GetLastError(), __func__); + } + CloseHandle(hThread); +} + +void qemu_thread_get_self(QemuThread *thread) +{ + if (!thread->thread) { + /* In the main thread of the process. Initialize the QemuThread + pointer in TLS, and use the dummy GetCurrentThread handle as + the identifier for qemu_thread_is_self. */ + qemu_thread_init(); + TlsSetValue(qemu_thread_tls_index, thread); + thread->thread = GetCurrentThread(); + } +} + +int qemu_thread_is_self(QemuThread *thread) +{ + QemuThread *this_thread = TlsGetValue(qemu_thread_tls_index); + return this_thread->thread == thread->thread; +} diff -Nru qemu-kvm-0.14.1+noroms/qemu-thread-win32.h qemu-kvm-0.15.0+noroms/qemu-thread-win32.h --- qemu-kvm-0.14.1+noroms/qemu-thread-win32.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qemu-thread-win32.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,21 @@ +#ifndef __QEMU_THREAD_WIN32_H +#define __QEMU_THREAD_WIN32_H 1 +#include "windows.h" + +struct QemuMutex { + CRITICAL_SECTION lock; + LONG owner; +}; + +struct QemuCond { + LONG waiters, target; + HANDLE sema; + HANDLE continue_event; +}; + +struct QemuThread { + HANDLE thread; + void *ret; +}; + +#endif diff -Nru qemu-kvm-0.14.1+noroms/qemu-timer.c qemu-kvm-0.15.0+noroms/qemu-timer.c --- qemu-kvm-0.14.1+noroms/qemu-timer.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-timer.c 2011-08-09 07:40:29.000000000 -0500 @@ -39,15 +39,6 @@ #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 @@ -110,6 +101,7 @@ } } +#ifndef CONFIG_IOTHREAD static int64_t qemu_icount_delta(void) { if (!use_icount) { @@ -123,6 +115,7 @@ return cpu_get_icount() - cpu_get_clock(); } } +#endif /* enable cpu_get_ticks() */ void cpu_enable_ticks(void) @@ -155,12 +148,17 @@ struct QEMUClock { int type; int enabled; - /* XXX: add frequency */ + + QEMUTimer *warp_timer; + + NotifierList reset_notifiers; + int64_t last; }; struct QEMUTimer { QEMUClock *clock; - int64_t expire_time; + int64_t expire_time; /* in nanoseconds */ + int scale; QEMUTimerCB *cb; void *opaque; struct QEMUTimer *next; @@ -171,14 +169,23 @@ int (*start)(struct qemu_alarm_timer *t); void (*stop)(struct qemu_alarm_timer *t); void (*rearm)(struct qemu_alarm_timer *t); - void *priv; - +#if defined(__linux__) + int fd; + timer_t timer; +#elif defined(_WIN32) + HANDLE timer; +#endif char expired; char pending; }; static struct qemu_alarm_timer *alarm_timer; +static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) +{ + return timer_head && (timer_head->expire_time <= current_time); +} + int qemu_alarm_pending(void) { return alarm_timer->pending; @@ -202,10 +209,9 @@ #ifdef _WIN32 -struct qemu_alarm_win32 { - MMRESULT timerId; - unsigned int period; -} alarm_win32_data = {0, 0}; +static int mm_start_timer(struct qemu_alarm_timer *t); +static void mm_stop_timer(struct qemu_alarm_timer *t); +static void mm_rearm_timer(struct qemu_alarm_timer *t); static int win32_start_timer(struct qemu_alarm_timer *t); static void win32_stop_timer(struct qemu_alarm_timer *t); @@ -215,6 +221,7 @@ static int unix_start_timer(struct qemu_alarm_timer *t); static void unix_stop_timer(struct qemu_alarm_timer *t); +static void unix_rearm_timer(struct qemu_alarm_timer *t); #ifdef __linux__ @@ -222,12 +229,6 @@ 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 */ @@ -249,7 +250,7 @@ return; cur_time = cpu_get_clock(); - cur_icount = qemu_get_clock(vm_clock); + cur_icount = qemu_get_clock_ns(vm_clock); delta = cur_icount - cur_time; /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ if (delta > 0 @@ -271,14 +272,14 @@ static void icount_adjust_rt(void * opaque) { qemu_mod_timer(icount_rt_timer, - qemu_get_clock(rt_clock) + 1000); + qemu_get_clock_ms(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); + qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10); icount_adjust(); } @@ -291,18 +292,14 @@ #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}, + dynticks_stop_timer, dynticks_rearm_timer}, #endif - {"unix", unix_start_timer, unix_stop_timer, NULL, NULL}, + {"unix", unix_start_timer, unix_stop_timer, unix_rearm_timer}, #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}, + {"mmtimer", mm_start_timer, mm_stop_timer, NULL}, + {"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer}, + {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer}, + {"win32", win32_start_timer, win32_stop_timer, NULL}, #endif {NULL, } }; @@ -382,9 +379,15 @@ static QEMUClock *qemu_new_clock(int type) { QEMUClock *clock; + clock = qemu_mallocz(sizeof(QEMUClock)); clock->type = type; clock->enabled = 1; + notifier_list_init(&clock->reset_notifiers); + /* required to detect & report backward jumps */ + if (type == QEMU_CLOCK_HOST) { + clock->last = get_clock_realtime(); + } return clock; } @@ -393,7 +396,92 @@ clock->enabled = enabled; } -QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) +static int64_t vm_clock_warp_start; + +static void icount_warp_rt(void *opaque) +{ + if (vm_clock_warp_start == -1) { + return; + } + + if (vm_running) { + int64_t clock = qemu_get_clock_ns(rt_clock); + int64_t warp_delta = clock - vm_clock_warp_start; + if (use_icount == 1) { + qemu_icount_bias += warp_delta; + } else { + /* + * In adaptive mode, do not let the vm_clock run too + * far ahead of real time. + */ + int64_t cur_time = cpu_get_clock(); + int64_t cur_icount = qemu_get_clock_ns(vm_clock); + int64_t delta = cur_time - cur_icount; + qemu_icount_bias += MIN(warp_delta, delta); + } + if (qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL], + qemu_get_clock_ns(vm_clock))) { + qemu_notify_event(); + } + } + vm_clock_warp_start = -1; +} + +void qemu_clock_warp(QEMUClock *clock) +{ + int64_t deadline; + + if (!clock->warp_timer) { + return; + } + + /* + * There are too many global variables to make the "warp" behavior + * applicable to other clocks. But a clock argument removes the + * need for if statements all over the place. + */ + assert(clock == vm_clock); + + /* + * If the CPUs have been sleeping, advance the vm_clock timer now. This + * ensures that the deadline for the timer is computed correctly below. + * This also makes sure that the insn counter is synchronized before the + * CPU starts running, in case the CPU is woken by an event other than + * the earliest vm_clock timer. + */ + icount_warp_rt(NULL); + if (!all_cpu_threads_idle() || !active_timers[clock->type]) { + qemu_del_timer(clock->warp_timer); + return; + } + + vm_clock_warp_start = qemu_get_clock_ns(rt_clock); + deadline = qemu_next_icount_deadline(); + if (deadline > 0) { + /* + * Ensure the vm_clock proceeds even when the virtual CPU goes to + * sleep. Otherwise, the CPU might be waiting for a future timer + * interrupt to wake it up, but the interrupt never comes because + * the vCPU isn't running any insns and thus doesn't advance the + * vm_clock. + * + * An extreme solution for this problem would be to never let VCPUs + * sleep in icount mode if there is a pending vm_clock timer; rather + * time could just advance to the next vm_clock event. Instead, we + * do stop VCPUs and only advance vm_clock after some "real" time, + * (related to the time left until the next event) has passed. This + * rt_clock timer will do this. This avoids that the warps are too + * visible externally---for example, you will not be sending network + * packets continously instead of every 100ms. + */ + qemu_mod_timer(clock->warp_timer, vm_clock_warp_start + deadline); + } else { + qemu_notify_event(); + } +} + +QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, + QEMUTimerCB *cb, void *opaque) { QEMUTimer *ts; @@ -401,6 +489,7 @@ ts->clock = clock; ts->cb = cb; ts->opaque = opaque; + ts->scale = scale; return ts; } @@ -431,7 +520,7 @@ /* 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) +static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) { QEMUTimer **pt, *t; @@ -443,10 +532,9 @@ pt = &active_timers[ts->clock->type]; for(;;) { t = *pt; - if (!t) - break; - if (t->expire_time > expire_time) + if (!qemu_timer_expired_ns(t, expire_time)) { break; + } pt = &t->next; } ts->expire_time = expire_time; @@ -459,11 +547,20 @@ qemu_rearm_alarm_timer(alarm_timer); } /* Interrupt execution to force deadline recalculation. */ - if (use_icount) + qemu_clock_warp(ts->clock); + if (use_icount) { qemu_notify_event(); + } } } +/* 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) +{ + qemu_mod_timer_ns(ts, expire_time * ts->scale); +} + int qemu_timer_pending(QEMUTimer *ts) { QEMUTimer *t; @@ -476,9 +573,7 @@ int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) { - if (!timer_head) - return 0; - return (timer_head->expire_time <= current_time); + return qemu_timer_expired_ns(timer_head, current_time * timer_head->scale); } static void qemu_run_timers(QEMUClock *clock) @@ -489,12 +584,13 @@ if (!clock->enabled) return; - current_time = qemu_get_clock (clock); + current_time = qemu_get_clock_ns(clock); ptimer_head = &active_timers[clock->type]; for(;;) { ts = *ptimer_head; - if (!ts || ts->expire_time > current_time) + if (!qemu_timer_expired_ns(ts, current_time)) { break; + } /* remove timer from the list before calling the callback */ *ptimer_head = ts->next; ts->next = NULL; @@ -504,11 +600,13 @@ } } -int64_t qemu_get_clock(QEMUClock *clock) +int64_t qemu_get_clock_ns(QEMUClock *clock) { + int64_t now, last; + switch(clock->type) { case QEMU_CLOCK_REALTIME: - return get_clock() / 1000000; + return get_clock(); default: case QEMU_CLOCK_VIRTUAL: if (use_icount) { @@ -517,25 +615,24 @@ return cpu_get_clock(); } case QEMU_CLOCK_HOST: - return get_clock_realtime(); + now = get_clock_realtime(); + last = clock->last; + clock->last = now; + if (now < last) { + notifier_list_notify(&clock->reset_notifiers, &now); + } + return now; } } -int64_t qemu_get_clock_ns(QEMUClock *clock) +void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) { - 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(); - } + notifier_list_add(&clock->reset_notifiers, notifier); +} + +void qemu_unregister_clock_reset_notifier(QEMUClock *clock, Notifier *notifier) +{ + notifier_list_remove(&clock->reset_notifiers, notifier); } void init_clocks(void) @@ -566,7 +663,7 @@ expire_time = qemu_get_be64(f); if (expire_time != -1) { - qemu_mod_timer(ts, expire_time); + qemu_mod_timer_ns(ts, expire_time); } else { qemu_del_timer(ts); } @@ -591,6 +688,10 @@ if (!option) return; +#ifdef CONFIG_IOTHREAD + vm_clock->warp_timer = qemu_new_timer_ns(rt_clock, icount_warp_rt, NULL); +#endif + if (strcmp(option, "auto") != 0) { icount_time_shift = strtol(option, NULL, 0); use_icount = 1; @@ -608,12 +709,12 @@ 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); + icount_rt_timer = qemu_new_timer_ms(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_get_clock_ms(rt_clock) + 1000); + icount_vm_timer = qemu_new_timer_ns(vm_clock, icount_adjust_vm, NULL); qemu_mod_timer(icount_vm_timer, - qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); + qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10); } void qemu_run_all_timers(void) @@ -638,9 +739,7 @@ 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) +static void CALLBACK host_alarm_handler(PVOID lpParam, BOOLEAN unused) #else static void host_alarm_handler(int host_signum) #endif @@ -655,7 +754,7 @@ 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); + ti = qemu_get_clock_ns(vm_clock); if (last_clock != 0) { delta = ti - last_clock; if (delta < delta_min) @@ -686,21 +785,16 @@ } } -int64_t qemu_next_deadline(void) +int64_t qemu_next_icount_deadline(void) { /* To avoid problems with overflow limit this to 2^32. */ int64_t delta = INT32_MAX; + assert(use_icount); 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; @@ -715,7 +809,7 @@ if (!use_icount && active_timers[QEMU_CLOCK_VIRTUAL]) { delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - - qemu_get_clock(vm_clock); + qemu_get_clock_ns(vm_clock); } else { delta = INT32_MAX; } @@ -726,7 +820,7 @@ delta = hdelta; } if (active_timers[QEMU_CLOCK_REALTIME]) { - rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time * 1000000 - + rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time - qemu_get_clock_ns(rt_clock)); if (rtdelta < delta) delta = rtdelta; @@ -737,106 +831,7 @@ #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); -} +#include "compatfd.h" static int dynticks_start_timer(struct qemu_alarm_timer *t) { @@ -857,6 +852,12 @@ memset(&ev, 0, sizeof(ev)); ev.sigev_value.sival_int = 0; ev.sigev_notify = SIGEV_SIGNAL; +#ifdef SIGEV_THREAD_ID + if (qemu_signalfd_available()) { + ev.sigev_notify = SIGEV_THREAD_ID; + ev._sigev_un._tid = qemu_get_thread_id(); + } +#endif /* SIGEV_THREAD_ID */ ev.sigev_signo = SIGALRM; if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { @@ -868,21 +869,21 @@ return -1; } - t->priv = (void *)(long)host_timer; + t->timer = host_timer; return 0; } static void dynticks_stop_timer(struct qemu_alarm_timer *t) { - timer_t host_timer = (timer_t)(long)t->priv; + timer_t host_timer = t->timer; timer_delete(host_timer); } static void dynticks_rearm_timer(struct qemu_alarm_timer *t) { - timer_t host_timer = (timer_t)(long)t->priv; + timer_t host_timer = t->timer; struct itimerspec timeout; int64_t nearest_delta_ns = INT64_MAX; int64_t current_ns; @@ -925,8 +926,6 @@ static int unix_start_timer(struct qemu_alarm_timer *t) { struct sigaction act; - struct itimerval itv; - int err; /* timer signal */ sigfillset(&act.sa_mask); @@ -934,18 +933,35 @@ act.sa_handler = host_alarm_handler; sigaction(SIGALRM, &act, NULL); + return 0; +} - 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; +static void unix_rearm_timer(struct qemu_alarm_timer *t) +{ + struct itimerval itv; + int64_t nearest_delta_ns = INT64_MAX; + int err; - err = setitimer(ITIMER_REAL, &itv, NULL); - if (err) - return -1; + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; - return 0; + nearest_delta_ns = qemu_next_alarm_deadline(); + if (nearest_delta_ns < MIN_TIMER_REARM_NS) + nearest_delta_ns = MIN_TIMER_REARM_NS; + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; /* 0 for one-shot timer */ + itv.it_value.tv_sec = nearest_delta_ns / 1000000000; + itv.it_value.tv_usec = (nearest_delta_ns % 1000000000) / 1000; + err = setitimer(ITIMER_REAL, &itv, NULL); + if (err) { + perror("setitimer"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } } static void unix_stop_timer(struct qemu_alarm_timer *t) @@ -961,75 +977,161 @@ #ifdef _WIN32 -static int win32_start_timer(struct qemu_alarm_timer *t) +static MMRESULT mm_timer; +static unsigned mm_period; + +static void CALLBACK mm_alarm_handler(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, + DWORD_PTR dw2) +{ + struct qemu_alarm_timer *t = alarm_timer; + if (!t) { + return; + } + if (alarm_has_dynticks(t) || qemu_next_alarm_deadline() <= 0) { + t->expired = alarm_has_dynticks(t); + t->pending = 1; + qemu_notify_event(); + } +} + +static int mm_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); + mm_period = tc.wPeriodMin; + timeBeginPeriod(mm_period); flags = TIME_CALLBACK_FUNCTION; - if (alarm_has_dynticks(t)) + if (alarm_has_dynticks(t)) { flags |= TIME_ONESHOT; - else + } else { flags |= TIME_PERIODIC; + } - data->timerId = timeSetEvent(1, // interval (ms) - data->period, // resolution - host_alarm_handler, // function - (DWORD)t, // parameter - flags); + mm_timer = timeSetEvent(1, /* interval (ms) */ + mm_period, /* resolution */ + mm_alarm_handler, /* function */ + (DWORD_PTR)t, /* parameter */ + flags); - if (!data->timerId) { + if (!mm_timer) { fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", GetLastError()); - timeEndPeriod(data->period); + timeEndPeriod(mm_period); return -1; } return 0; } -static void win32_stop_timer(struct qemu_alarm_timer *t) +static void mm_stop_timer(struct qemu_alarm_timer *t) { - struct qemu_alarm_win32 *data = t->priv; - - timeKillEvent(data->timerId); - timeEndPeriod(data->period); + timeKillEvent(mm_timer); + timeEndPeriod(mm_period); } -static void win32_rearm_timer(struct qemu_alarm_timer *t) +static void mm_rearm_timer(struct qemu_alarm_timer *t) { - struct qemu_alarm_win32 *data = t->priv; + int nearest_delta_ms; assert(alarm_has_dynticks(t)); if (!active_timers[QEMU_CLOCK_REALTIME] && !active_timers[QEMU_CLOCK_VIRTUAL] && - !active_timers[QEMU_CLOCK_HOST]) + !active_timers[QEMU_CLOCK_HOST]) { return; + } - timeKillEvent(data->timerId); + timeKillEvent(mm_timer); - data->timerId = timeSetEvent(1, - data->period, - host_alarm_handler, - (DWORD)t, - TIME_ONESHOT | TIME_CALLBACK_FUNCTION); + nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000; + if (nearest_delta_ms < 1) { + nearest_delta_ms = 1; + } + mm_timer = timeSetEvent(nearest_delta_ms, + mm_period, + mm_alarm_handler, + (DWORD_PTR)t, + TIME_ONESHOT | TIME_CALLBACK_FUNCTION); - if (!data->timerId) { + if (!mm_timer) { fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n", GetLastError()); - timeEndPeriod(data->period); + timeEndPeriod(mm_period); exit(1); } } +static int win32_start_timer(struct qemu_alarm_timer *t) +{ + HANDLE hTimer; + BOOLEAN success; + + /* If you call ChangeTimerQueueTimer on a one-shot timer (its period + is zero) that has already expired, the timer is not updated. Since + creating a new timer is relatively expensive, set a bogus one-hour + interval in the dynticks case. */ + success = CreateTimerQueueTimer(&hTimer, + NULL, + host_alarm_handler, + t, + 1, + alarm_has_dynticks(t) ? 3600000 : 1, + WT_EXECUTEINTIMERTHREAD); + + if (!success) { + fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", + GetLastError()); + return -1; + } + + t->timer = hTimer; + return 0; +} + +static void win32_stop_timer(struct qemu_alarm_timer *t) +{ + HANDLE hTimer = t->timer; + + if (hTimer) { + DeleteTimerQueueTimer(NULL, hTimer, NULL); + } +} + +static void win32_rearm_timer(struct qemu_alarm_timer *t) +{ + HANDLE hTimer = t->timer; + int nearest_delta_ms; + BOOLEAN success; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; + + nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000; + if (nearest_delta_ms < 1) { + nearest_delta_ms = 1; + } + success = ChangeTimerQueueTimer(NULL, + hTimer, + nearest_delta_ms, + 3600000); + + if (!success) { + fprintf(stderr, "Failed to rearm win32 alarm timer: %ld\n", + GetLastError()); + exit(-1); + } + +} + #endif /* _WIN32 */ static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason) @@ -1076,18 +1178,9 @@ int qemu_calculate_timeout(void) { +#ifndef CONFIG_IOTHREAD 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 { @@ -1103,7 +1196,7 @@ } else { /* Wait for either IO to occur or the next timer event. */ - add = qemu_next_deadline(); + add = qemu_next_icount_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. */ @@ -1118,5 +1211,8 @@ } return timeout; +#else /* CONFIG_IOTHREAD */ + return 1000; +#endif } diff -Nru qemu-kvm-0.14.1+noroms/qemu-timer.h qemu-kvm-0.15.0+noroms/qemu-timer.h --- qemu-kvm-0.14.1+noroms/qemu-timer.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-timer.h 2011-08-09 07:40:29.000000000 -0500 @@ -2,16 +2,20 @@ #define QEMU_TIMER_H #include "qemu-common.h" +#include "notify.h" #include #include #ifdef _WIN32 #include -#include #endif /* timers */ +#define SCALE_MS 1000000 +#define SCALE_US 1000 +#define SCALE_NS 1 + typedef struct QEMUClock QEMUClock; typedef void QEMUTimerCB(void *opaque); @@ -33,11 +37,16 @@ the virtual clock. */ 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); +void qemu_clock_warp(QEMUClock *clock); + +void qemu_register_clock_reset_notifier(QEMUClock *clock, Notifier *notifier); +void qemu_unregister_clock_reset_notifier(QEMUClock *clock, + Notifier *notifier); -QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque); +QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, + QEMUTimerCB *cb, void *opaque); void qemu_free_timer(QEMUTimer *ts); void qemu_del_timer(QEMUTimer *ts); void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time); @@ -46,7 +55,7 @@ void qemu_run_all_timers(void); int qemu_alarm_pending(void); -int64_t qemu_next_deadline(void); +int64_t qemu_next_icount_deadline(void); void configure_alarms(char const *opt); void configure_icount(const char *option); int qemu_calculate_timeout(void); @@ -54,6 +63,27 @@ int init_timer_alarm(void); void quit_timers(void); +int64_t cpu_get_ticks(void); +void cpu_enable_ticks(void); +void cpu_disable_ticks(void); + +static inline QEMUTimer *qemu_new_timer_ns(QEMUClock *clock, QEMUTimerCB *cb, + void *opaque) +{ + return qemu_new_timer(clock, SCALE_NS, cb, opaque); +} + +static inline QEMUTimer *qemu_new_timer_ms(QEMUClock *clock, QEMUTimerCB *cb, + void *opaque) +{ + return qemu_new_timer(clock, SCALE_MS, cb, opaque); +} + +static inline int64_t qemu_get_clock_ms(QEMUClock *clock) +{ + return qemu_get_clock_ns(clock) / SCALE_MS; +} + static inline int64_t get_ticks_per_sec(void) { return 1000000000LL; @@ -118,8 +148,6 @@ void ptimer_set_count(ptimer_state *s, uint64_t count); void ptimer_run(ptimer_state *s, int oneshot); void ptimer_stop(ptimer_state *s); -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); diff -Nru qemu-kvm-0.14.1+noroms/qemu-tool.c qemu-kvm-0.15.0+noroms/qemu-tool.c --- qemu-kvm-0.14.1+noroms/qemu-tool.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qemu-tool.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,11 +15,11 @@ #include "monitor.h" #include "qemu-timer.h" #include "qemu-log.h" -#include "sysemu.h" #include QEMUClock *rt_clock; +QEMUClock *vm_clock; FILE *logfile; @@ -56,58 +56,43 @@ { } -void async_context_push(void) -{ -} - -void async_context_pop(void) +void monitor_protocol_event(MonitorEvent event, QObject *data) { } -int get_async_context_id(void) +int qemu_set_fd_handler2(int fd, + IOCanReadHandler *fd_read_poll, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque) { return 0; } -void monitor_protocol_event(MonitorEvent event, QObject *data) +void qemu_notify_event(void) { } -QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) +QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale, + QEMUTimerCB *cb, void *opaque) { - QEMUBH *bh; - - bh = qemu_malloc(sizeof(*bh)); - bh->cb = cb; - bh->opaque = opaque; - - return bh; -} - -int qemu_bh_poll(void) -{ - return 0; + return qemu_malloc(1); } -void qemu_bh_schedule(QEMUBH *bh) +void qemu_free_timer(QEMUTimer *ts) { - bh->cb(bh->opaque); + qemu_free(ts); } -void qemu_bh_cancel(QEMUBH *bh) +void qemu_del_timer(QEMUTimer *ts) { } -void qemu_bh_delete(QEMUBH *bh) +void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) { - qemu_free(bh); } -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) +int64_t qemu_get_clock_ns(QEMUClock *clock) { return 0; } diff -Nru qemu-kvm-0.14.1+noroms/qerror.c qemu-kvm-0.15.0+noroms/qerror.c --- qemu-kvm-0.14.1+noroms/qerror.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qerror.c 2011-08-09 07:40:29.000000000 -0500 @@ -141,6 +141,11 @@ .desc = "Invalid JSON syntax", }, { + .error_fmt = QERR_JSON_PARSE_ERROR, + .desc = "JSON parse error, %(message)", + + }, + { .error_fmt = QERR_KVM_MISSING_CAP, .desc = "Using KVM without %(capability), %(feature) unavailable", }, @@ -193,6 +198,10 @@ .desc = "Could not set password", }, { + .error_fmt = QERR_ADD_CLIENT_FAILED, + .desc = "Could not add client", + }, + { .error_fmt = QERR_TOO_MANY_FILES, .desc = "Too many open files", }, @@ -201,6 +210,10 @@ .desc = "An undefined error has ocurred", }, { + .error_fmt = QERR_UNSUPPORTED, + .desc = "this feature or command is not currently supported", + }, + { .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, .desc = "'%(device)' uses a %(format) feature which is not " "supported by this qemu version: %(feature)", @@ -209,6 +222,14 @@ .error_fmt = QERR_VNC_SERVER_FAILED, .desc = "Could not start VNC server on %(target)", }, + { + .error_fmt = QERR_QGA_LOGGING_FAILED, + .desc = "Guest agent failed to log non-optional log statement", + }, + { + .error_fmt = QERR_QGA_COMMAND_FAILED, + .desc = "Guest agent command failed, error was '%(message)'", + }, {} }; @@ -326,12 +347,14 @@ return qerr; } -static void parse_error(const QError *qerror, int c) +static void parse_error(const QErrorStringTable *entry, int c) { - qerror_abort(qerror, "expected '%c' in '%s'", c, qerror->entry->desc); + fprintf(stderr, "expected '%c' in '%s'", c, entry->desc); + abort(); } -static const char *append_field(QString *outstr, const QError *qerror, +static const char *append_field(QDict *error, QString *outstr, + const QErrorStringTable *entry, const char *start) { QObject *obj; @@ -340,23 +363,23 @@ const char *end, *key; if (*start != '%') - parse_error(qerror, '%'); + parse_error(entry, '%'); start++; if (*start != '(') - parse_error(qerror, '('); + parse_error(entry, '('); start++; end = strchr(start, ')'); if (!end) - parse_error(qerror, ')'); + parse_error(entry, ')'); key_qs = qstring_from_substr(start, 0, end - start - 1); key = qstring_get_str(key_qs); - qdict = qobject_to_qdict(qdict_get(qerror->error, "data")); + qdict = qobject_to_qdict(qdict_get(error, "data")); obj = qdict_get(qdict, key); if (!obj) { - qerror_abort(qerror, "key '%s' not found in QDict", key); + abort(); } switch (qobject_type(obj)) { @@ -367,41 +390,60 @@ qstring_append_int(outstr, qdict_get_int(qdict, key)); break; default: - qerror_abort(qerror, "invalid type '%c'", qobject_type(obj)); + abort(); } QDECREF(key_qs); return ++end; } -/** - * qerror_human(): Format QError data into human-readable string. - * - * Formats according to member 'desc' of the specified QError object. - */ -QString *qerror_human(const QError *qerror) +static QString *qerror_format_desc(QDict *error, + const QErrorStringTable *entry) { - const char *p; QString *qstring; + const char *p; - assert(qerror->entry != NULL); + assert(entry != NULL); qstring = qstring_new(); - for (p = qerror->entry->desc; *p != '\0';) { + for (p = entry->desc; *p != '\0';) { if (*p != '%') { qstring_append_chr(qstring, *p++); } else if (*(p + 1) == '%') { qstring_append_chr(qstring, '%'); p += 2; } else { - p = append_field(qstring, qerror, p); + p = append_field(error, qstring, entry, p); } } return qstring; } +QString *qerror_format(const char *fmt, QDict *error) +{ + const QErrorStringTable *entry = NULL; + int i; + + for (i = 0; qerror_table[i].error_fmt; i++) { + if (strcmp(qerror_table[i].error_fmt, fmt) == 0) { + entry = &qerror_table[i]; + break; + } + } + + return qerror_format_desc(error, entry); +} + +/** + * qerror_human(): Format QError data into human-readable string. + */ +QString *qerror_human(const QError *qerror) +{ + return qerror_format_desc(qerror->error, qerror->entry); +} + /** * qerror_print(): Print QError data * diff -Nru qemu-kvm-0.14.1+noroms/qerror.h qemu-kvm-0.15.0+noroms/qerror.h --- qemu-kvm-0.14.1+noroms/qerror.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qerror.h 2011-08-09 07:40:29.000000000 -0500 @@ -39,6 +39,7 @@ 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); +QString *qerror_format(const char *fmt, QDict *error); #define qerror_report(fmt, ...) \ qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) QError *qobject_to_qerror(const QObject *obj); @@ -120,6 +121,12 @@ #define QERR_JSON_PARSING \ "{ 'class': 'JSONParsing', 'data': {} }" +#define QERR_JSON_PARSE_ERROR \ + "{ 'class': 'JSONParseError', 'data': { 'message': %s } }" + +#define QERR_BUFFER_OVERRUN \ + "{ 'class': 'BufferOverrun', 'data': {} }" + #define QERR_KVM_MISSING_CAP \ "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }" @@ -159,16 +166,31 @@ #define QERR_SET_PASSWD_FAILED \ "{ 'class': 'SetPasswdFailed', 'data': {} }" +#define QERR_ADD_CLIENT_FAILED \ + "{ 'class': 'AddClientFailed', 'data': {} }" + #define QERR_TOO_MANY_FILES \ "{ 'class': 'TooManyFiles', 'data': {} }" #define QERR_UNDEFINED_ERROR \ "{ 'class': 'UndefinedError', 'data': {} }" +#define QERR_UNSUPPORTED \ + "{ 'class': 'Unsupported', '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 } }" +#define QERR_FEATURE_DISABLED \ + "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }" + +#define QERR_QGA_LOGGING_FAILED \ + "{ 'class': 'QgaLoggingFailed', 'data': {} }" + +#define QERR_QGA_COMMAND_FAILED \ + "{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }" + #endif /* QERROR_H */ diff -Nru qemu-kvm-0.14.1+noroms/qga/guest-agent-commands.c qemu-kvm-0.15.0+noroms/qga/guest-agent-commands.c --- qemu-kvm-0.14.1+noroms/qga/guest-agent-commands.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qga/guest-agent-commands.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,561 @@ +/* + * QEMU Guest Agent commands + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Michael Roth + * + * 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 + +#if defined(__linux__) +#include +#include + +#if defined(__linux__) && defined(FIFREEZE) +#define CONFIG_FSFREEZE +#endif +#endif + +#include +#include +#include "qga/guest-agent-core.h" +#include "qga-qmp-commands.h" +#include "qerror.h" +#include "qemu-queue.h" + +static GAState *ga_state; + +/* Note: in some situations, like with the fsfreeze, logging may be + * temporarilly disabled. if it is necessary that a command be able + * to log for accounting purposes, check ga_logging_enabled() beforehand, + * and use the QERR_QGA_LOGGING_DISABLED to generate an error + */ +static void slog(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap); + va_end(ap); +} + +int64_t qmp_guest_sync(int64_t id, Error **errp) +{ + return id; +} + +void qmp_guest_ping(Error **err) +{ + slog("guest-ping called"); +} + +struct GuestAgentInfo *qmp_guest_info(Error **err) +{ + GuestAgentInfo *info = qemu_mallocz(sizeof(GuestAgentInfo)); + + info->version = g_strdup(QGA_VERSION); + + return info; +} + +void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) +{ + int ret; + const char *shutdown_flag; + + slog("guest-shutdown called, mode: %s", mode); + if (!has_mode || strcmp(mode, "powerdown") == 0) { + shutdown_flag = "-P"; + } else if (strcmp(mode, "halt") == 0) { + shutdown_flag = "-H"; + } else if (strcmp(mode, "reboot") == 0) { + shutdown_flag = "-r"; + } else { + error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", + "halt|powerdown|reboot"); + return; + } + + ret = fork(); + if (ret == 0) { + /* child, start the shutdown */ + setsid(); + fclose(stdin); + fclose(stdout); + fclose(stderr); + + ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", + "hypervisor initiated shutdown", (char*)NULL); + if (ret) { + slog("guest-shutdown failed: %s", strerror(errno)); + } + exit(!!ret); + } else if (ret < 0) { + error_set(err, QERR_UNDEFINED_ERROR); + } +} + +typedef struct GuestFileHandle { + uint64_t id; + FILE *fh; + QTAILQ_ENTRY(GuestFileHandle) next; +} GuestFileHandle; + +static struct { + QTAILQ_HEAD(, GuestFileHandle) filehandles; +} guest_file_state; + +static void guest_file_handle_add(FILE *fh) +{ + GuestFileHandle *gfh; + + gfh = qemu_mallocz(sizeof(GuestFileHandle)); + gfh->id = fileno(fh); + gfh->fh = fh; + QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); +} + +static GuestFileHandle *guest_file_handle_find(int64_t id) +{ + GuestFileHandle *gfh; + + QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) + { + if (gfh->id == id) { + return gfh; + } + } + + return NULL; +} + +int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) +{ + FILE *fh; + int fd; + int64_t ret = -1; + + if (!has_mode) { + mode = "r"; + } + slog("guest-file-open called, filepath: %s, mode: %s", path, mode); + fh = fopen(path, mode); + if (!fh) { + error_set(err, QERR_OPEN_FILE_FAILED, path); + return -1; + } + + /* set fd non-blocking to avoid common use cases (like reading from a + * named pipe) from hanging the agent + */ + fd = fileno(fh); + ret = fcntl(fd, F_GETFL); + ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); + if (ret == -1) { + error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed"); + fclose(fh); + return -1; + } + + guest_file_handle_add(fh); + slog("guest-file-open, handle: %d", fd); + return fd; +} + +void qmp_guest_file_close(int64_t handle, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + int ret; + + slog("guest-file-close called, handle: %ld", handle); + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return; + } + + ret = fclose(gfh->fh); + if (ret == -1) { + error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed"); + return; + } + + QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); + qemu_free(gfh); +} + +struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, + int64_t count, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileRead *read_data = NULL; + guchar *buf; + FILE *fh; + size_t read_count; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return NULL; + } + + if (!has_count) { + count = QGA_READ_COUNT_DEFAULT; + } else if (count < 0) { + error_set(err, QERR_INVALID_PARAMETER, "count"); + return NULL; + } + + fh = gfh->fh; + buf = qemu_mallocz(count+1); + read_count = fread(buf, 1, count, fh); + if (ferror(fh)) { + slog("guest-file-read failed, handle: %ld", handle); + error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed"); + } else { + buf[read_count] = 0; + read_data = qemu_mallocz(sizeof(GuestFileRead)); + read_data->count = read_count; + read_data->eof = feof(fh); + if (read_count) { + read_data->buf_b64 = g_base64_encode(buf, read_count); + } + } + qemu_free(buf); + clearerr(fh); + + return read_data; +} + +GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, + bool has_count, int64_t count, Error **err) +{ + GuestFileWrite *write_data = NULL; + guchar *buf; + gsize buf_len; + int write_count; + GuestFileHandle *gfh = guest_file_handle_find(handle); + FILE *fh; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return NULL; + } + + fh = gfh->fh; + buf = g_base64_decode(buf_b64, &buf_len); + + if (!has_count) { + count = buf_len; + } else if (count < 0 || count > buf_len) { + qemu_free(buf); + error_set(err, QERR_INVALID_PARAMETER, "count"); + return NULL; + } + + write_count = fwrite(buf, 1, count, fh); + if (ferror(fh)) { + slog("guest-file-write failed, handle: %ld", handle); + error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error"); + } else { + write_data = qemu_mallocz(sizeof(GuestFileWrite)); + write_data->count = write_count; + write_data->eof = feof(fh); + } + qemu_free(buf); + clearerr(fh); + + return write_data; +} + +struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, + int64_t whence, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileSeek *seek_data = NULL; + FILE *fh; + int ret; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return NULL; + } + + fh = gfh->fh; + ret = fseek(fh, offset, whence); + if (ret == -1) { + error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); + } else { + seek_data = qemu_mallocz(sizeof(GuestFileRead)); + seek_data->position = ftell(fh); + seek_data->eof = feof(fh); + } + clearerr(fh); + + return seek_data; +} + +void qmp_guest_file_flush(int64_t handle, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + FILE *fh; + int ret; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return; + } + + fh = gfh->fh; + ret = fflush(fh); + if (ret == EOF) { + error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); + } +} + +static void guest_file_init(void) +{ + QTAILQ_INIT(&guest_file_state.filehandles); +} + +#if defined(CONFIG_FSFREEZE) +static void disable_logging(void) +{ + ga_disable_logging(ga_state); +} + +static void enable_logging(void) +{ + ga_enable_logging(ga_state); +} + +typedef struct GuestFsfreezeMount { + char *dirname; + char *devtype; + QTAILQ_ENTRY(GuestFsfreezeMount) next; +} GuestFsfreezeMount; + +struct { + GuestFsfreezeStatus status; + QTAILQ_HEAD(, GuestFsfreezeMount) mount_list; +} guest_fsfreeze_state; + +/* + * Walk the mount table and build a list of local file systems + */ +static int guest_fsfreeze_build_mount_list(void) +{ + struct mntent *ment; + GuestFsfreezeMount *mount, *temp; + char const *mtab = MOUNTED; + FILE *fp; + + QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { + QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next); + qemu_free(mount->dirname); + qemu_free(mount->devtype); + qemu_free(mount); + } + + fp = setmntent(mtab, "r"); + if (!fp) { + g_warning("fsfreeze: unable to read mtab"); + return -1; + } + + while ((ment = getmntent(fp))) { + /* + * An entry which device name doesn't start with a '/' is + * either a dummy file system or a network file system. + * Add special handling for smbfs and cifs as is done by + * coreutils as well. + */ + if ((ment->mnt_fsname[0] != '/') || + (strcmp(ment->mnt_type, "smbfs") == 0) || + (strcmp(ment->mnt_type, "cifs") == 0)) { + continue; + } + + mount = qemu_mallocz(sizeof(GuestFsfreezeMount)); + mount->dirname = qemu_strdup(ment->mnt_dir); + mount->devtype = qemu_strdup(ment->mnt_type); + + QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next); + } + + endmntent(fp); + + return 0; +} + +/* + * Return status of freeze/thaw + */ +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) +{ + return guest_fsfreeze_state.status; +} + +/* + * Walk list of mounted file systems in the guest, and freeze the ones which + * are real local file systems. + */ +int64_t qmp_guest_fsfreeze_freeze(Error **err) +{ + int ret = 0, i = 0; + struct GuestFsfreezeMount *mount, *temp; + int fd; + char err_msg[512]; + + slog("guest-fsfreeze called"); + + if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { + return 0; + } + + ret = guest_fsfreeze_build_mount_list(); + if (ret < 0) { + return ret; + } + + /* cannot risk guest agent blocking itself on a write in this state */ + disable_logging(); + + QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { + fd = qemu_open(mount->dirname, O_RDONLY); + if (fd == -1) { + sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + /* we try to cull filesytems we know won't work in advance, but other + * filesytems may not implement fsfreeze for less obvious reasons. + * these will report EOPNOTSUPP, so we simply ignore them. when + * thawing, these filesystems will return an EINVAL instead, due to + * not being in a frozen state. Other filesystem-specific + * errors may result in EINVAL, however, so the user should check the + * number * of filesystems returned here against those returned by the + * thaw operation to determine whether everything completed + * successfully + */ + ret = ioctl(fd, FIFREEZE); + if (ret < 0 && errno != EOPNOTSUPP) { + sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + close(fd); + goto error; + } + close(fd); + + i++; + } + + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN; + return i; + +error: + if (i > 0) { + qmp_guest_fsfreeze_thaw(NULL); + } + return 0; +} + +/* + * Walk list of frozen file systems in the guest, and thaw them. + */ +int64_t qmp_guest_fsfreeze_thaw(Error **err) +{ + int ret; + GuestFsfreezeMount *mount, *temp; + int fd, i = 0; + bool has_error = false; + + QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { + fd = qemu_open(mount->dirname, O_RDONLY); + if (fd == -1) { + has_error = true; + continue; + } + ret = ioctl(fd, FITHAW); + if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) { + has_error = true; + close(fd); + continue; + } + close(fd); + i++; + } + + if (has_error) { + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR; + } else { + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; + } + enable_logging(); + return i; +} + +static void guest_fsfreeze_init(void) +{ + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; + QTAILQ_INIT(&guest_fsfreeze_state.mount_list); +} + +static void guest_fsfreeze_cleanup(void) +{ + int64_t ret; + Error *err = NULL; + + if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { + ret = qmp_guest_fsfreeze_thaw(&err); + if (ret < 0 || err) { + slog("failed to clean up frozen filesystems"); + } + } +} +#else +/* + * Return status of freeze/thaw + */ +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} + +/* + * Walk list of mounted file systems in the guest, and freeze the ones which + * are real local file systems. + */ +int64_t qmp_guest_fsfreeze_freeze(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} + +/* + * Walk list of frozen file systems in the guest, and thaw them. + */ +int64_t qmp_guest_fsfreeze_thaw(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + + return 0; +} +#endif + +/* register init/cleanup routines for stateful command groups */ +void ga_command_state_init(GAState *s, GACommandState *cs) +{ + ga_state = s; +#if defined(CONFIG_FSFREEZE) + ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup); +#endif + ga_command_state_add(cs, guest_file_init, NULL); +} diff -Nru qemu-kvm-0.14.1+noroms/qga/guest-agent-command-state.c qemu-kvm-0.15.0+noroms/qga/guest-agent-command-state.c --- qemu-kvm-0.14.1+noroms/qga/guest-agent-command-state.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qga/guest-agent-command-state.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,73 @@ +/* + * QEMU Guest Agent command state interfaces + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Michael Roth + * + * 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 "qga/guest-agent-core.h" + +struct GACommandState { + GSList *groups; +}; + +typedef struct GACommandGroup { + void (*init)(void); + void (*cleanup)(void); +} GACommandGroup; + +/* handle init/cleanup for stateful guest commands */ + +void ga_command_state_add(GACommandState *cs, + void (*init)(void), + void (*cleanup)(void)) +{ + GACommandGroup *cg = qemu_mallocz(sizeof(GACommandGroup)); + cg->init = init; + cg->cleanup = cleanup; + cs->groups = g_slist_append(cs->groups, cg); +} + +static void ga_command_group_init(gpointer opaque, gpointer unused) +{ + GACommandGroup *cg = opaque; + + g_assert(cg); + if (cg->init) { + cg->init(); + } +} + +void ga_command_state_init_all(GACommandState *cs) +{ + g_assert(cs); + g_slist_foreach(cs->groups, ga_command_group_init, NULL); +} + +static void ga_command_group_cleanup(gpointer opaque, gpointer unused) +{ + GACommandGroup *cg = opaque; + + g_assert(cg); + if (cg->cleanup) { + cg->cleanup(); + } +} + +void ga_command_state_cleanup_all(GACommandState *cs) +{ + g_assert(cs); + g_slist_foreach(cs->groups, ga_command_group_cleanup, NULL); +} + +GACommandState *ga_command_state_new(void) +{ + GACommandState *cs = qemu_mallocz(sizeof(GACommandState)); + cs->groups = NULL; + return cs; +} diff -Nru qemu-kvm-0.14.1+noroms/qga/guest-agent-core.h qemu-kvm-0.15.0+noroms/qga/guest-agent-core.h --- qemu-kvm-0.14.1+noroms/qga/guest-agent-core.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/qga/guest-agent-core.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,31 @@ +/* + * QEMU Guest Agent core declarations + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Adam Litke + * Michael Roth + * + * 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 "qapi/qmp-core.h" +#include "qemu-common.h" + +#define QGA_VERSION "1.0" +#define QGA_READ_COUNT_DEFAULT 4 << 10 + +typedef struct GAState GAState; +typedef struct GACommandState GACommandState; + +void ga_command_state_init(GAState *s, GACommandState *cs); +void ga_command_state_add(GACommandState *cs, + void (*init)(void), + void (*cleanup)(void)); +void ga_command_state_init_all(GACommandState *cs); +void ga_command_state_cleanup_all(GACommandState *cs); +GACommandState *ga_command_state_new(void); +bool ga_logging_enabled(GAState *s); +void ga_disable_logging(GAState *s); +void ga_enable_logging(GAState *s); diff -Nru qemu-kvm-0.14.1+noroms/qlist.h qemu-kvm-0.15.0+noroms/qlist.h --- qemu-kvm-0.14.1+noroms/qlist.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qlist.h 2011-08-09 07:40:29.000000000 -0500 @@ -16,6 +16,7 @@ #include "qobject.h" #include "qemu-queue.h" #include "qemu-common.h" +#include "qemu-queue.h" typedef struct QListEntry { QObject *value; @@ -50,4 +51,14 @@ int qlist_empty(const QList *qlist); QList *qobject_to_qlist(const QObject *obj); +static inline const QListEntry *qlist_first(const QList *qlist) +{ + return QTAILQ_FIRST(&qlist->head); +} + +static inline const QListEntry *qlist_next(const QListEntry *entry) +{ + return QTAILQ_NEXT(entry, next); +} + #endif /* QLIST_H */ diff -Nru qemu-kvm-0.14.1+noroms/QMP/qmp.py qemu-kvm-0.15.0+noroms/QMP/qmp.py --- qemu-kvm-0.14.1+noroms/QMP/qmp.py 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/QMP/qmp.py 2011-08-09 07:40:29.000000000 -0500 @@ -22,19 +22,24 @@ pass class QEMUMonitorProtocol: - def __init__(self, address): + def __init__(self, address, server=False): """ 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 + @param server: server mode listens on the socket (bool) + @raise socket.error on socket connection errors + @note No connection is established, this is done by the connect() or + accept() methods """ self.__events = [] self.__address = address self.__sock = self.__get_sock() - self.__sockfile = self.__sock.makefile() + if server: + self.__sock.bind(self.__address) + self.__sock.listen(1) def __get_sock(self): if isinstance(self.__address, tuple): @@ -43,7 +48,18 @@ family = socket.AF_UNIX return socket.socket(family, socket.SOCK_STREAM) - def __json_read(self): + def __negotiate_capabilities(self): + self.__sockfile = self.__sock.makefile() + 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 __json_read(self, only_event=False): while True: data = self.__sockfile.readline() if not data: @@ -51,7 +67,8 @@ resp = json.loads(data) if 'event' in resp: self.__events.append(resp) - continue + if not only_event: + continue return resp error = socket.error @@ -66,14 +83,19 @@ @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 + return self.__negotiate_capabilities() + + def accept(self): + """ + Await connection from QMP Monitor and perform capabilities negotiation. + + @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, _ = self.__sock.accept() + return self.__negotiate_capabilities() def cmd_obj(self, qmp_cmd): """ @@ -106,9 +128,11 @@ qmp_cmd['id'] = id return self.cmd_obj(qmp_cmd) - def get_events(self): + def get_events(self, wait=False): """ Get a list of available QMP events. + + @param wait: block until an event is available (bool) """ self.__sock.setblocking(0) try: @@ -118,6 +142,8 @@ # No data available pass self.__sock.setblocking(1) + if not self.__events and wait: + self.__json_read(only_event=True) return self.__events def clear_events(self): diff -Nru qemu-kvm-0.14.1+noroms/qmp-commands.hx qemu-kvm-0.15.0+noroms/qmp-commands.hx --- qemu-kvm-0.14.1+noroms/qmp-commands.hx 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/qmp-commands.hx 2011-08-09 07:40:29.000000000 -0500 @@ -430,6 +430,33 @@ EQMP { + .name = "inject-nmi", + .args_type = "", + .params = "", + .help = "", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_inject_nmi, + }, + +SQMP +inject-nmi +---------- + +Inject an NMI on guest's CPUs. + +Arguments: None. + +Example: + +-> { "execute": "inject-nmi" } +<- { "return": {} } + +Note: inject-nmi is only supported for x86 guest currently, it will + returns "Unsupported" error for non-x86 guest. + +EQMP + + { .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", @@ -503,79 +530,79 @@ }, SQMP -client_migrate_info ------------------- +migrate_set_speed +----------------- -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. +Set maximum speed for migrations. 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) +- "value": maximum speed, in bytes per second (json-int) Example: --> { "execute": "client_migrate_info", - "arguments": { "protocol": "spice", - "hostname": "virt42.lab.kraxel.org", - "port": 1234 } } +-> { "execute": "migrate_set_speed", "arguments": { "value": 1024 } } <- { "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", + .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 = client_migrate_info, + .mhandler.cmd_new = do_migrate_set_downtime, }, SQMP -migrate_set_speed ------------------ +migrate_set_downtime +-------------------- -Set maximum speed for migrations. +Set maximum tolerated downtime (in seconds) for migrations. Arguments: -- "value": maximum speed, in bytes per second (json-int) +- "value": maximum downtime (json-number) Example: --> { "execute": "migrate_set_speed", "arguments": { "value": 1024 } } +-> { "execute": "migrate_set_downtime", "arguments": { "value": 0.1 } } <- { "return": {} } EQMP { - .name = "migrate_set_downtime", - .args_type = "value:T", - .params = "value", - .help = "set maximum tolerated downtime (in seconds) for migrations", + .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 = do_migrate_set_downtime, + .mhandler.cmd_new = client_migrate_info, }, SQMP -migrate_set_downtime --------------------- +client_migrate_info +------------------ -Set maximum tolerated downtime (in seconds) for migrations. +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: -- "value": maximum downtime (json-number) +- "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": "migrate_set_downtime", "arguments": { "value": 0.1 } } +-> { "execute": "client_migrate_info", + "arguments": { "protocol": "spice", + "hostname": "virt42.lab.kraxel.org", + "port": 1234 } } <- { "return": {} } EQMP @@ -667,6 +694,40 @@ EQMP { + .name = "blockdev-snapshot-sync", + .args_type = "device:B,snapshot-file:s?,format:s?", + .params = "device [new-image-file] [format]", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_snapshot_blkdev, + }, + +SQMP +blockdev-snapshot-sync +---------------------- + +Synchronous snapshot of a block device. snapshot-file specifies the +target of the new image. If the file exists, or if it is a device, the +snapshot will be created in the existing file/device. If does not +exist, a new file will be created. format specifies the format of the +snapshot image, default is qcow2. + +Arguments: + +- "device": device name to snapshot (json-string) +- "snapshot-file": name of new image file (json-string) +- "format": format of new image (json-string, optional) + +Example: + +-> { "execute": "blockdev-snapshot", "arguments": { "device": "ide-hd0", + "snapshot-file": + "/some/place/my-image", + "format": "qcow2" } } +<- { "return": {} } + +EQMP + + { .name = "balloon", .args_type = "value:M", .params = "target", @@ -858,6 +919,33 @@ EQMP { + .name = "add_client", + .args_type = "protocol:s,fdname:s,skipauth:b?", + .params = "protocol fdname skipauth", + .help = "add a graphics client", + .user_print = monitor_user_noop, + .mhandler.cmd_new = add_graphics_client, + }, + +SQMP +add_client +---------- + +Add a graphics client + +Arguments: + +- "protocol": protocol name (json-string) +- "fdname": file descriptor name (json-string) + +Example: + +-> { "execute": "add_client", "arguments": { "protocol": "vnc", + "fdname": "myclient" } } +<- { "return": {} } + +EQMP + { .name = "qmp_capabilities", .args_type = "", .params = "", @@ -1039,7 +1127,8 @@ - "device": device name (json-string) - "type": device type (json-string) - - Possible values: "hd", "cdrom", "floppy", "unknown" + - deprecated, retained for backward compatibility + - Possible values: "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 @@ -1070,25 +1159,25 @@ "encrypted":false, "file":"disks/test.img" }, - "type":"hd" + "type":"unknown" }, { "device":"ide1-cd0", "locked":false, "removable":true, - "type":"cdrom" + "type":"unknown" }, { "device":"floppy0", "locked":false, "removable":true, - "type": "floppy" + "type":"unknown" }, { "device":"sd0", "locked":false, "removable":true, - "type":"floppy" + "type":"unknown" } ] } @@ -1194,6 +1283,7 @@ "nip": PPC (json-int) "pc" and "npc": sparc (json-int) "PC": mips (json-int) +- "thread_id": ID of the underlying host thread (json-int) Example: @@ -1205,12 +1295,14 @@ "current":true, "halted":false, "pc":3227107138 + "thread_id":3134 }, { "CPU":1, "current":false, "halted":true, "pc":7108165 + "thread_id":3135 } ] } diff -Nru qemu-kvm-0.14.1+noroms/rules.mak qemu-kvm-0.15.0+noroms/rules.mak --- qemu-kvm-0.14.1+noroms/rules.mak 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/rules.mak 2011-08-09 07:40:29.000000000 -0500 @@ -17,6 +17,14 @@ %.o: %.c $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") +ifeq ($(LIBTOOL),) +%.lo: %.c + @echo "missing libtool. please install and rerun configure"; exit 1 +else +%.lo: %.c + $(call quiet-command,libtool --mode=compile --quiet --tag=CC $(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," lt CC $@") +endif + %.o: %.S $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") diff -Nru qemu-kvm-0.14.1+noroms/savevm.c qemu-kvm-0.15.0+noroms/savevm.c --- qemu-kvm-0.14.1+noroms/savevm.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/savevm.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,7 +23,6 @@ */ #include #include -#include #include #include #include @@ -82,6 +81,7 @@ #include "migration.h" #include "qemu_socket.h" #include "qemu-queue.h" +#include "cpus.h" #define SELF_ANNOUNCE_ROUNDS 5 @@ -137,7 +137,7 @@ if (--count) { /* delay 50ms, 150ms, 250ms, ... */ - qemu_mod_timer(timer, qemu_get_clock(rt_clock) + + qemu_mod_timer(timer, qemu_get_clock_ms(rt_clock) + 50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100); } else { qemu_del_timer(timer); @@ -148,7 +148,7 @@ void qemu_announce_self(void) { static QEMUTimer *timer; - timer = qemu_new_timer(rt_clock, qemu_announce_self_once, &timer); + timer = qemu_new_timer_ms(rt_clock, qemu_announce_self_once, &timer); qemu_announce_self_once(&timer); } @@ -194,7 +194,7 @@ ssize_t len; do { - len = recv(s->fd, (void *)buf, size, 0); + len = qemu_recv(s->fd, buf, size, 0); } while (len == -1 && socket_error() == EINTR); if (len == -1) @@ -882,49 +882,47 @@ .put = put_uint32, }; -/* 64 bit unsigned int */ +/* 32 bit uint. See that the received value is the same than the one + in the field */ -static int get_uint64(QEMUFile *f, void *pv, size_t size) +static int get_uint32_equal(QEMUFile *f, void *pv, size_t size) { - uint64_t *v = pv; - qemu_get_be64s(f, v); - return 0; -} + uint32_t *v = pv; + uint32_t v2; + qemu_get_be32s(f, &v2); -static void put_uint64(QEMUFile *f, void *pv, size_t size) -{ - uint64_t *v = pv; - qemu_put_be64s(f, v); + if (*v == v2) { + return 0; + } + return -EINVAL; } -const VMStateInfo vmstate_info_uint64 = { - .name = "uint64", - .get = get_uint64, - .put = put_uint64, +const VMStateInfo vmstate_info_uint32_equal = { + .name = "uint32 equal", + .get = get_uint32_equal, + .put = put_uint32, }; -/* 64 bit linux kernel unsigned int */ +/* 64 bit unsigned int */ -#ifdef __linux__ -static int get_u64(QEMUFile *f, void *pv, size_t size) +static int get_uint64(QEMUFile *f, void *pv, size_t size) { uint64_t *v = pv; qemu_get_be64s(f, v); return 0; } -static void put_u64(QEMUFile *f, void *pv, size_t size) +static void put_uint64(QEMUFile *f, void *pv, size_t size) { uint64_t *v = pv; qemu_put_be64s(f, v); } -const VMStateInfo vmstate_info_u64 = { - .name = "__u64", - .get = get_u64, - .put = put_u64, +const VMStateInfo vmstate_info_uint64 = { + .name = "uint64", + .get = get_uint64, + .put = put_uint64, }; -#endif /* __linux__ */ /* 8 bit int. See that the received value is the same than the one in the field */ @@ -1009,7 +1007,7 @@ }; /* unused buffers: space that was used for some fields that are - not usefull anymore */ + not useful anymore */ static int get_unused_buffer(QEMUFile *f, void *pv, size_t size) { @@ -1331,8 +1329,12 @@ n_elems = field->num; } else if (field->flags & VMS_VARRAY_INT32) { n_elems = *(int32_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT32) { + n_elems = *(uint32_t *)(opaque+field->num_offset); } else if (field->flags & VMS_VARRAY_UINT16) { n_elems = *(uint16_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque+field->num_offset); } if (field->flags & VMS_POINTER) { base_addr = *(void **)base_addr + field->start; @@ -1393,6 +1395,8 @@ n_elems = *(int32_t *)(opaque+field->num_offset); } else if (field->flags & VMS_VARRAY_UINT16) { n_elems = *(uint16_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque+field->num_offset); } if (field->flags & VMS_POINTER) { base_addr = *(void **)base_addr + field->start; @@ -1598,7 +1602,7 @@ int ret; saved_vm_running = vm_running; - vm_stop(0); + vm_stop(VMSTOP_SAVEVM); if (qemu_savevm_state_blocked(mon)) { ret = -EINVAL; @@ -1927,7 +1931,7 @@ } saved_vm_running = vm_running; - vm_stop(0); + vm_stop(VMSTOP_SAVEVM); memset(sn, 0, sizeof(*sn)); @@ -1941,7 +1945,7 @@ sn->date_sec = tv.tv_sec; sn->date_nsec = tv.tv_usec * 1000; #endif - sn->vm_clock_nsec = qemu_get_clock(vm_clock); + sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); if (name) { ret = bdrv_snapshot_find(bs, old_sn, name); @@ -2019,6 +2023,8 @@ if (ret < 0) { return ret; } else if (sn.vm_state_size == 0) { + error_report("This is a disk-only snapshot. Revert to it offline " + "using qemu-img."); return -EINVAL; } @@ -2067,6 +2073,7 @@ return -EINVAL; } + qemu_system_reset(VMRESET_SILENT); ret = qemu_loadvm_state(f); qemu_fclose(f); diff -Nru qemu-kvm-0.14.1+noroms/scripts/checkpatch.pl qemu-kvm-0.15.0+noroms/scripts/checkpatch.pl --- qemu-kvm-0.14.1+noroms/scripts/checkpatch.pl 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/scripts/checkpatch.pl 2011-08-09 07:40:29.000000000 -0500 @@ -1495,7 +1495,7 @@ next if ($realfile !~ /\.(h|c|pl)$/); # in QEMU, no tabs are allowed - if ($rawline =~ /\t/) { + if ($rawline =~ /^\+.*\t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; ERROR("code indent should never use tabs\n" . $herevet); $rpt_cleaners = 1; @@ -2068,8 +2068,10 @@ } # , must have a space on the right. + # not required when having a single },{ on one line } elsif ($op eq ',') { - if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/ && + ($elements[$n] . $elements[$n + 2]) !~ " *}{") { ERROR("space required after that '$op' $at\n" . $hereptr); } @@ -2537,6 +2539,7 @@ } if (!defined $suppress_ifbraces{$linenr - 1} && $line =~ /\b(if|while|for|else)\b/ && + $line !~ /\#\s*if/ && $line !~ /\#\s*else/) { my $allowed = 0; diff -Nru qemu-kvm-0.14.1+noroms/scripts/get_maintainer.pl qemu-kvm-0.15.0+noroms/scripts/get_maintainer.pl --- qemu-kvm-0.14.1+noroms/scripts/get_maintainer.pl 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/scripts/get_maintainer.pl 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,2149 @@ +#!/usr/bin/perl -w +# (c) 2007, Joe Perches +# created from checkpatch.pl +# +# Print selected MAINTAINERS information for +# the files modified in a patch or for a file +# +# usage: perl scripts/get_maintainer.pl [OPTIONS] +# perl scripts/get_maintainer.pl [OPTIONS] -f +# +# Licensed under the terms of the GNU GPL License version 2 + +use strict; + +my $P = $0; +my $V = '0.26'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $lk_path = "./"; +my $email = 1; +my $email_usename = 1; +my $email_maintainer = 1; +my $email_list = 1; +my $email_subscriber_list = 0; +my $email_git_penguin_chiefs = 0; +my $email_git = 0; +my $email_git_all_signature_types = 0; +my $email_git_blame = 0; +my $email_git_blame_signatures = 1; +my $email_git_fallback = 1; +my $email_git_min_signatures = 1; +my $email_git_max_maintainers = 5; +my $email_git_min_percent = 5; +my $email_git_since = "1-year-ago"; +my $email_hg_since = "-365"; +my $interactive = 0; +my $email_remove_duplicates = 1; +my $email_use_mailmap = 1; +my $output_multiline = 1; +my $output_separator = ", "; +my $output_roles = 0; +my $output_rolestats = 1; +my $scm = 0; +my $web = 0; +my $subsystem = 0; +my $status = 0; +my $keywords = 1; +my $sections = 0; +my $file_emails = 0; +my $from_filename = 0; +my $pattern_depth = 0; +my $version = 0; +my $help = 0; + +my $vcs_used = 0; + +my $exit = 0; + +my %commit_author_hash; +my %commit_signer_hash; + +my @penguin_chief = (); +push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org"); +#Andrew wants in on most everything - 2009/01/14 +#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org"); + +my @penguin_chief_names = (); +foreach my $chief (@penguin_chief) { + if ($chief =~ m/^(.*):(.*)/) { + my $chief_name = $1; + my $chief_addr = $2; + push(@penguin_chief_names, $chief_name); + } +} +my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)"; + +# Signature types of people who are either +# a) responsible for the code in question, or +# b) familiar enough with it to give relevant feedback +my @signature_tags = (); +push(@signature_tags, "Signed-off-by:"); +push(@signature_tags, "Reviewed-by:"); +push(@signature_tags, "Acked-by:"); + +# rfc822 email address - preloaded methods go here. +my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])"; +my $rfc822_char = '[\\000-\\377]'; + +# VCS command support: class-like functions and strings + +my %VCS_cmds; + +my %VCS_cmds_git = ( + "execute_cmd" => \&git_execute_cmd, + "available" => '(which("git") ne "") && (-d ".git")', + "find_signers_cmd" => + "git log --no-color --since=\$email_git_since " . + '--format="GitCommit: %H%n' . + 'GitAuthor: %an <%ae>%n' . + 'GitDate: %aD%n' . + 'GitSubject: %s%n' . + '%b%n"' . + " -- \$file", + "find_commit_signers_cmd" => + "git log --no-color " . + '--format="GitCommit: %H%n' . + 'GitAuthor: %an <%ae>%n' . + 'GitDate: %aD%n' . + 'GitSubject: %s%n' . + '%b%n"' . + " -1 \$commit", + "find_commit_author_cmd" => + "git log --no-color " . + '--format="GitCommit: %H%n' . + 'GitAuthor: %an <%ae>%n' . + 'GitDate: %aD%n' . + 'GitSubject: %s%n"' . + " -1 \$commit", + "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file", + "blame_file_cmd" => "git blame -l \$file", + "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})", + "blame_commit_pattern" => "^([0-9a-f]+) ", + "author_pattern" => "^GitAuthor: (.*)", + "subject_pattern" => "^GitSubject: (.*)", +); + +my %VCS_cmds_hg = ( + "execute_cmd" => \&hg_execute_cmd, + "available" => '(which("hg") ne "") && (-d ".hg")', + "find_signers_cmd" => + "hg log --date=\$email_hg_since " . + "--template='HgCommit: {node}\\n" . + "HgAuthor: {author}\\n" . + "HgSubject: {desc}\\n'" . + " -- \$file", + "find_commit_signers_cmd" => + "hg log " . + "--template='HgSubject: {desc}\\n'" . + " -r \$commit", + "find_commit_author_cmd" => + "hg log " . + "--template='HgCommit: {node}\\n" . + "HgAuthor: {author}\\n" . + "HgSubject: {desc|firstline}\\n'" . + " -r \$commit", + "blame_range_cmd" => "", # not supported + "blame_file_cmd" => "hg blame -n \$file", + "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})", + "blame_commit_pattern" => "^([ 0-9a-f]+):", + "author_pattern" => "^HgAuthor: (.*)", + "subject_pattern" => "^HgSubject: (.*)", +); + +my $conf = which_conf(".get_maintainer.conf"); +if (-f $conf) { + my @conf_args; + open(my $conffile, '<', "$conf") + or warn "$P: Can't find a readable .get_maintainer.conf file $!\n"; + + while (<$conffile>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + $line =~ s/\s+/ /g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my @words = split(" ", $line); + foreach my $word (@words) { + last if ($word =~ m/^#/); + push (@conf_args, $word); + } + } + close($conffile); + unshift(@ARGV, @conf_args) if @conf_args; +} + +if (!GetOptions( + 'email!' => \$email, + 'git!' => \$email_git, + 'git-all-signature-types!' => \$email_git_all_signature_types, + 'git-blame!' => \$email_git_blame, + 'git-blame-signatures!' => \$email_git_blame_signatures, + 'git-fallback!' => \$email_git_fallback, + 'git-chief-penguins!' => \$email_git_penguin_chiefs, + 'git-min-signatures=i' => \$email_git_min_signatures, + 'git-max-maintainers=i' => \$email_git_max_maintainers, + 'git-min-percent=i' => \$email_git_min_percent, + 'git-since=s' => \$email_git_since, + 'hg-since=s' => \$email_hg_since, + 'i|interactive!' => \$interactive, + 'remove-duplicates!' => \$email_remove_duplicates, + 'mailmap!' => \$email_use_mailmap, + 'm!' => \$email_maintainer, + 'n!' => \$email_usename, + 'l!' => \$email_list, + 's!' => \$email_subscriber_list, + 'multiline!' => \$output_multiline, + 'roles!' => \$output_roles, + 'rolestats!' => \$output_rolestats, + 'separator=s' => \$output_separator, + 'subsystem!' => \$subsystem, + 'status!' => \$status, + 'scm!' => \$scm, + 'web!' => \$web, + 'pattern-depth=i' => \$pattern_depth, + 'k|keywords!' => \$keywords, + 'sections!' => \$sections, + 'fe|file-emails!' => \$file_emails, + 'f|file' => \$from_filename, + 'v|version' => \$version, + 'h|help|usage' => \$help, + )) { + die "$P: invalid argument - use --help if necessary\n"; +} + +if ($help != 0) { + usage(); + exit 0; +} + +if ($version != 0) { + print("${P} ${V}\n"); + exit 0; +} + +if (-t STDIN && !@ARGV) { + # We're talking to a terminal, but have no command line arguments. + die "$P: missing patchfile or -f file - use --help if necessary\n"; +} + +$output_multiline = 0 if ($output_separator ne ", "); +$output_rolestats = 1 if ($interactive); +$output_roles = 1 if ($output_rolestats); + +if ($sections) { + $email = 0; + $email_list = 0; + $scm = 0; + $status = 0; + $subsystem = 0; + $web = 0; + $keywords = 0; + $interactive = 0; +} else { + my $selections = $email + $scm + $status + $subsystem + $web; + if ($selections == 0) { + die "$P: Missing required option: email, scm, status, subsystem or web\n"; + } +} + +if ($email && + ($email_maintainer + $email_list + $email_subscriber_list + + $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) { + die "$P: Please select at least 1 email option\n"; +} + +if (!top_of_tree($lk_path)) { + die "$P: The current directory does not appear to be " + . "a QEMU source tree.\n"; +} + +## Read MAINTAINERS for type/value pairs + +my @typevalue = (); +my %keyword_hash; + +open (my $maint, '<', "${lk_path}MAINTAINERS") + or die "$P: Can't open MAINTAINERS: $!\n"; +while (<$maint>) { + my $line = $_; + + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + + ##Filename pattern matching + if ($type eq "F" || $type eq "X") { + $value =~ s@\.@\\\.@g; ##Convert . to \. + $value =~ s/\*/\.\*/g; ##Convert * to .* + $value =~ s/\?/\./g; ##Convert ? to . + ##if pattern is a directory and it lacks a trailing slash, add one + if ((-d $value)) { + $value =~ s@([^/])$@$1/@; + } + } elsif ($type eq "K") { + $keyword_hash{@typevalue} = $value; + } + push(@typevalue, "$type:$value"); + } elsif (!/^(\s)*$/) { + $line =~ s/\n$//g; + push(@typevalue, $line); + } +} +close($maint); + + +# +# Read mail address map +# + +my $mailmap; + +read_mailmap(); + +sub read_mailmap { + $mailmap = { + names => {}, + addresses => {} + }; + + return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap")); + + open(my $mailmap_file, '<', "${lk_path}.mailmap") + or warn "$P: Can't open .mailmap: $!\n"; + + while (<$mailmap_file>) { + s/#.*$//; #strip comments + s/^\s+|\s+$//g; #trim + + next if (/^\s*$/); #skip empty lines + #entries have one of the following formats: + # name1 + # + # name1 + # name1 name2 + # (see man git-shortlog) + if (/^(.+)<(.+)>$/) { + my $real_name = $1; + my $address = $2; + + $real_name =~ s/\s+$//; + ($real_name, $address) = parse_email("$real_name <$address>"); + $mailmap->{names}->{$address} = $real_name; + + } elsif (/^<([^\s]+)>\s*<([^\s]+)>$/) { + my $real_address = $1; + my $wrong_address = $2; + + $mailmap->{addresses}->{$wrong_address} = $real_address; + + } elsif (/^(.+)<([^\s]+)>\s*<([^\s]+)>$/) { + my $real_name = $1; + my $real_address = $2; + my $wrong_address = $3; + + $real_name =~ s/\s+$//; + ($real_name, $real_address) = + parse_email("$real_name <$real_address>"); + $mailmap->{names}->{$wrong_address} = $real_name; + $mailmap->{addresses}->{$wrong_address} = $real_address; + + } elsif (/^(.+)<([^\s]+)>\s*([^\s].*)<([^\s]+)>$/) { + my $real_name = $1; + my $real_address = $2; + my $wrong_name = $3; + my $wrong_address = $4; + + $real_name =~ s/\s+$//; + ($real_name, $real_address) = + parse_email("$real_name <$real_address>"); + + $wrong_name =~ s/\s+$//; + ($wrong_name, $wrong_address) = + parse_email("$wrong_name <$wrong_address>"); + + my $wrong_email = format_email($wrong_name, $wrong_address, 1); + $mailmap->{names}->{$wrong_email} = $real_name; + $mailmap->{addresses}->{$wrong_email} = $real_address; + } + } + close($mailmap_file); +} + +## use the filenames on the command line or find the filenames in the patchfiles + +my @files = (); +my @range = (); +my @keyword_tvi = (); +my @file_emails = (); + +if (!@ARGV) { + push(@ARGV, "&STDIN"); +} + +foreach my $file (@ARGV) { + if ($file ne "&STDIN") { + ##if $file is a directory and it lacks a trailing slash, add one + if ((-d $file)) { + $file =~ s@([^/])$@$1/@; + } elsif (!(-f $file)) { + die "$P: file '${file}' not found\n"; + } + } + if ($from_filename) { + push(@files, $file); + if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) { + open(my $f, '<', $file) + or die "$P: Can't open $file: $!\n"; + my $text = do { local($/) ; <$f> }; + close($f); + if ($keywords) { + foreach my $line (keys %keyword_hash) { + if ($text =~ m/$keyword_hash{$line}/x) { + push(@keyword_tvi, $line); + } + } + } + if ($file_emails) { + my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g; + push(@file_emails, clean_file_emails(@poss_addr)); + } + } + } else { + my $file_cnt = @files; + my $lastfile; + + open(my $patch, "< $file") + or die "$P: Can't open $file: $!\n"; + + # We can check arbitrary information before the patch + # like the commit message, mail headers, etc... + # This allows us to match arbitrary keywords against any part + # of a git format-patch generated file (subject tags, etc...) + + my $patch_prefix = ""; #Parsing the intro + + while (<$patch>) { + my $patch_line = $_; + if (m/^\+\+\+\s+(\S+)/) { + my $filename = $1; + $filename =~ s@^[^/]*/@@; + $filename =~ s@\n@@; + $lastfile = $filename; + push(@files, $filename); + $patch_prefix = "^[+-].*"; #Now parsing the actual patch + } elsif (m/^\@\@ -(\d+),(\d+)/) { + if ($email_git_blame) { + push(@range, "$lastfile:$1:$2"); + } + } elsif ($keywords) { + foreach my $line (keys %keyword_hash) { + if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) { + push(@keyword_tvi, $line); + } + } + } + } + close($patch); + + if ($file_cnt == @files) { + warn "$P: file '${file}' doesn't appear to be a patch. " + . "Add -f to options?\n"; + } + @files = sort_and_uniq(@files); + } +} + +@file_emails = uniq(@file_emails); + +my %email_hash_name; +my %email_hash_address; +my @email_to = (); +my %hash_list_to; +my @list_to = (); +my @scm = (); +my @web = (); +my @subsystem = (); +my @status = (); +my %deduplicate_name_hash = (); +my %deduplicate_address_hash = (); +my $signature_pattern; + +my @maintainers = get_maintainers(); + +if (@maintainers) { + @maintainers = merge_email(@maintainers); + output(@maintainers); +} + +if ($scm) { + @scm = uniq(@scm); + output(@scm); +} + +if ($status) { + @status = uniq(@status); + output(@status); +} + +if ($subsystem) { + @subsystem = uniq(@subsystem); + output(@subsystem); +} + +if ($web) { + @web = uniq(@web); + output(@web); +} + +exit($exit); + +sub range_is_maintained { + my ($start, $end) = @_; + + for (my $i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'S') { + if ($value =~ /(maintain|support)/i) { + return 1; + } + } + } + } + return 0; +} + +sub range_has_maintainer { + my ($start, $end) = @_; + + for (my $i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'M') { + return 1; + } + } + } + return 0; +} + +sub get_maintainers { + %email_hash_name = (); + %email_hash_address = (); + %commit_author_hash = (); + %commit_signer_hash = (); + @email_to = (); + %hash_list_to = (); + @list_to = (); + @scm = (); + @web = (); + @subsystem = (); + @status = (); + %deduplicate_name_hash = (); + %deduplicate_address_hash = (); + if ($email_git_all_signature_types) { + $signature_pattern = "(.+?)[Bb][Yy]:"; + } else { + $signature_pattern = "\(" . join("|", @signature_tags) . "\)"; + } + + # Find responsible parties + + my %exact_pattern_match_hash = (); + + foreach my $file (@files) { + + my %hash; + my $tvi = find_first_section(); + while ($tvi < @typevalue) { + my $start = find_starting_index($tvi); + my $end = find_ending_index($tvi); + my $exclude = 0; + my $i; + + #Do not match excluded file patterns + + for ($i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'X') { + if (file_match_pattern($file, $value)) { + $exclude = 1; + last; + } + } + } + } + + if (!$exclude) { + for ($i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'F') { + if (file_match_pattern($file, $value)) { + my $value_pd = ($value =~ tr@/@@); + my $file_pd = ($file =~ tr@/@@); + $value_pd++ if (substr($value,-1,1) ne "/"); + $value_pd = -1 if ($value =~ /^\.\*/); + if ($value_pd >= $file_pd && + range_is_maintained($start, $end) && + range_has_maintainer($start, $end)) { + $exact_pattern_match_hash{$file} = 1; + } + if ($pattern_depth == 0 || + (($file_pd - $value_pd) < $pattern_depth)) { + $hash{$tvi} = $value_pd; + } + } + } + } + } + } + $tvi = $end + 1; + } + + foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { + add_categories($line); + if ($sections) { + my $i; + my $start = find_starting_index($line); + my $end = find_ending_index($line); + for ($i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ /^[FX]:/) { ##Restore file patterns + $line =~ s/([^\\])\.([^\*])/$1\?$2/g; + $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ? + $line =~ s/\\\./\./g; ##Convert \. to . + $line =~ s/\.\*/\*/g; ##Convert .* to * + } + $line =~ s/^([A-Z]):/$1:\t/g; + print("$line\n"); + } + print("\n"); + } + } + } + + if ($keywords) { + @keyword_tvi = sort_and_uniq(@keyword_tvi); + foreach my $line (@keyword_tvi) { + add_categories($line); + } + } + + foreach my $email (@email_to, @list_to) { + $email->[0] = deduplicate_email($email->[0]); + } + + foreach my $file (@files) { + if ($email && + ($email_git || ($email_git_fallback && + !$exact_pattern_match_hash{$file}))) { + vcs_file_signoffs($file); + } + if ($email && $email_git_blame) { + vcs_file_blame($file); + } + } + + if ($email) { + foreach my $chief (@penguin_chief) { + if ($chief =~ m/^(.*):(.*)/) { + my $email_address; + + $email_address = format_email($1, $2, $email_usename); + if ($email_git_penguin_chiefs) { + push(@email_to, [$email_address, 'chief penguin']); + } else { + @email_to = grep($_->[0] !~ /${email_address}/, @email_to); + } + } + } + + foreach my $email (@file_emails) { + my ($name, $address) = parse_email($email); + + my $tmp_email = format_email($name, $address, $email_usename); + push_email_address($tmp_email, ''); + add_role($tmp_email, 'in file'); + } + } + + my @to = (); + if ($email || $email_list) { + if ($email) { + @to = (@to, @email_to); + } + if ($email_list) { + @to = (@to, @list_to); + } + } + + if ($interactive) { + @to = interactive_get_maintainers(\@to); + } + + return @to; +} + +sub file_match_pattern { + my ($file, $pattern) = @_; + if (substr($pattern, -1) eq "/") { + if ($file =~ m@^$pattern@) { + return 1; + } + } else { + if ($file =~ m@^$pattern@) { + my $s1 = ($file =~ tr@/@@); + my $s2 = ($pattern =~ tr@/@@); + if ($s1 == $s2) { + return 1; + } + } + } + return 0; +} + +sub usage { + print < print email address(es) if any + --git => include recent git \*-by: signers + --git-all-signature-types => include signers regardless of signature type + or use only ${signature_pattern} signers (default: $email_git_all_signature_types) + --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback) + --git-chief-penguins => include ${penguin_chiefs} + --git-min-signatures => number of signatures required (default: $email_git_min_signatures) + --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers) + --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent) + --git-blame => use git blame to find modified commits for patch or file + --git-since => git history to use (default: $email_git_since) + --hg-since => hg history to use (default: $email_hg_since) + --interactive => display a menu (mostly useful if used with the --git option) + --m => include maintainer(s) if any + --n => include name 'Full Name ' + --l => include list(s) if any + --s => include subscriber only list(s) if any + --remove-duplicates => minimize duplicate email names/addresses + --roles => show roles (status:subsystem, git-signer, list, etc...) + --rolestats => show roles and statistics (commits/total_commits, %) + --file-emails => add email addresses found in -f file (default: 0 (off)) + --scm => print SCM tree(s) if any + --status => print status if any + --subsystem => print subsystem name if any + --web => print website(s) if any + +Output type options: + --separator [, ] => separator for multiple entries on 1 line + using --separator also sets --nomultiline if --separator is not [, ] + --multiline => print 1 entry per line + +Other options: + --pattern-depth => Number of pattern directory traversals (default: 0 (all)) + --keywords => scan patch for keywords (default: $keywords) + --sections => print all of the subsystem sections with pattern matches + --mailmap => use .mailmap file (default: $email_use_mailmap) + --version => show version + --help => show this help information + +Default options: + [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0 + --remove-duplicates --rolestats] + +Notes: + Using "-f directory" may give unexpected results: + Used with "--git", git signators for _all_ files in and below + directory are examined as git recurses directories. + Any specified X: (exclude) pattern matches are _not_ ignored. + Used with "--nogit", directory is used as a pattern match, + no individual file within the directory or subdirectory + is matched. + Used with "--git-blame", does not iterate all files in directory + Using "--git-blame" is slow and may add old committers and authors + that are no longer active maintainers to the output. + Using "--roles" or "--rolestats" with git send-email --cc-cmd or any + other automated tools that expect only ["name"] + may not work because of additional output after . + Using "--rolestats" and "--git-blame" shows the #/total=% commits, + not the percentage of the entire file authored. # of commits is + not a good measure of amount of code authored. 1 major commit may + contain a thousand lines, 5 trivial commits may modify a single line. + If git is not installed, but mercurial (hg) is installed and an .hg + repository exists, the following options apply to mercurial: + --git, + --git-min-signatures, --git-max-maintainers, --git-min-percent, and + --git-blame + Use --hg-since not --git-since to control date selection + File ".get_maintainer.conf", if it exists in the QEMU source root + directory, can change whatever get_maintainer defaults are desired. + Entries in this file can be any command line argument. + This file is prepended to any additional command line arguments. + Multiple lines and # comments are allowed. +EOT +} + +sub top_of_tree { + my ($lk_path) = @_; + + if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") { + $lk_path .= "/"; + } + if ( (-f "${lk_path}COPYING") + && (-f "${lk_path}MAINTAINERS") + && (-f "${lk_path}Makefile") + && (-d "${lk_path}docs") + && (-f "${lk_path}VERSION") + && (-f "${lk_path}vl.c")) { + return 1; + } + return 0; +} + +sub parse_email { + my ($formatted_email) = @_; + + my $name = ""; + my $address = ""; + + if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) { + $name = $1; + $address = $2; + } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) { + $address = $1; + } elsif ($formatted_email =~ /^(.+\@\S*).*$/) { + $address = $1; + } + + $name =~ s/^\s+|\s+$//g; + $name =~ s/^\"|\"$//g; + $address =~ s/^\s+|\s+$//g; + + if ($name =~ /[^\w \-]/i) { ##has "must quote" chars + $name =~ s/(?"; + } + } else { + $formatted_email = $address; + } + + return $formatted_email; +} + +sub find_first_section { + my $index = 0; + + while ($index < @typevalue) { + my $tv = $typevalue[$index]; + if (($tv =~ m/^(\C):\s*(.*)/)) { + last; + } + $index++; + } + + return $index; +} + +sub find_starting_index { + my ($index) = @_; + + while ($index > 0) { + my $tv = $typevalue[$index]; + if (!($tv =~ m/^(\C):\s*(.*)/)) { + last; + } + $index--; + } + + return $index; +} + +sub find_ending_index { + my ($index) = @_; + + while ($index < @typevalue) { + my $tv = $typevalue[$index]; + if (!($tv =~ m/^(\C):\s*(.*)/)) { + last; + } + $index++; + } + + return $index; +} + +sub get_maintainer_role { + my ($index) = @_; + + my $i; + my $start = find_starting_index($index); + my $end = find_ending_index($index); + + my $role; + my $subsystem = $typevalue[$start]; + if (length($subsystem) > 20) { + $subsystem = substr($subsystem, 0, 17); + $subsystem =~ s/\s*$//; + $subsystem = $subsystem . "..."; + } + + for ($i = $start + 1; $i < $end; $i++) { + my $tv = $typevalue[$i]; + if ($tv =~ m/^(\C):\s*(.*)/) { + my $ptype = $1; + my $pvalue = $2; + if ($ptype eq "S") { + $role = $pvalue; + } + } + } + + $role = lc($role); + if ($role eq "supported") { + $role = "supporter"; + } elsif ($role eq "maintained") { + $role = "maintainer"; + } elsif ($role eq "odd fixes") { + $role = "odd fixer"; + } elsif ($role eq "orphan") { + $role = "orphan minder"; + } elsif ($role eq "obsolete") { + $role = "obsolete minder"; + } elsif ($role eq "buried alive in reporters") { + $role = "chief penguin"; + } + + return $role . ":" . $subsystem; +} + +sub get_list_role { + my ($index) = @_; + + my $i; + my $start = find_starting_index($index); + my $end = find_ending_index($index); + + my $subsystem = $typevalue[$start]; + if (length($subsystem) > 20) { + $subsystem = substr($subsystem, 0, 17); + $subsystem =~ s/\s*$//; + $subsystem = $subsystem . "..."; + } + + if ($subsystem eq "THE REST") { + $subsystem = ""; + } + + return $subsystem; +} + +sub add_categories { + my ($index) = @_; + + my $i; + my $start = find_starting_index($index); + my $end = find_ending_index($index); + + push(@subsystem, $typevalue[$start]); + + for ($i = $start + 1; $i < $end; $i++) { + my $tv = $typevalue[$i]; + if ($tv =~ m/^(\C):\s*(.*)/) { + my $ptype = $1; + my $pvalue = $2; + if ($ptype eq "L") { + my $list_address = $pvalue; + my $list_additional = ""; + my $list_role = get_list_role($i); + + if ($list_role ne "") { + $list_role = ":" . $list_role; + } + if ($list_address =~ m/([^\s]+)\s+(.*)$/) { + $list_address = $1; + $list_additional = $2; + } + if ($list_additional =~ m/subscribers-only/) { + if ($email_subscriber_list) { + if (!$hash_list_to{lc($list_address)}) { + $hash_list_to{lc($list_address)} = 1; + push(@list_to, [$list_address, + "subscriber list${list_role}"]); + } + } + } else { + if ($email_list) { + if (!$hash_list_to{lc($list_address)}) { + $hash_list_to{lc($list_address)} = 1; + push(@list_to, [$list_address, + "open list${list_role}"]); + } + } + } + } elsif ($ptype eq "M") { + my ($name, $address) = parse_email($pvalue); + if ($name eq "") { + if ($i > 0) { + my $tv = $typevalue[$i - 1]; + if ($tv =~ m/^(\C):\s*(.*)/) { + if ($1 eq "P") { + $name = $2; + $pvalue = format_email($name, $address, $email_usename); + } + } + } + } + if ($email_maintainer) { + my $role = get_maintainer_role($i); + push_email_addresses($pvalue, $role); + } + } elsif ($ptype eq "T") { + push(@scm, $pvalue); + } elsif ($ptype eq "W") { + push(@web, $pvalue); + } elsif ($ptype eq "S") { + push(@status, $pvalue); + } + } + } +} + +sub email_inuse { + my ($name, $address) = @_; + + return 1 if (($name eq "") && ($address eq "")); + return 1 if (($name ne "") && exists($email_hash_name{lc($name)})); + return 1 if (($address ne "") && exists($email_hash_address{lc($address)})); + + return 0; +} + +sub push_email_address { + my ($line, $role) = @_; + + my ($name, $address) = parse_email($line); + + if ($address eq "") { + return 0; + } + + if (!$email_remove_duplicates) { + push(@email_to, [format_email($name, $address, $email_usename), $role]); + } elsif (!email_inuse($name, $address)) { + push(@email_to, [format_email($name, $address, $email_usename), $role]); + $email_hash_name{lc($name)}++ if ($name ne ""); + $email_hash_address{lc($address)}++; + } + + return 1; +} + +sub push_email_addresses { + my ($address, $role) = @_; + + my @address_list = (); + + if (rfc822_valid($address)) { + push_email_address($address, $role); + } elsif (@address_list = rfc822_validlist($address)) { + my $array_count = shift(@address_list); + while (my $entry = shift(@address_list)) { + push_email_address($entry, $role); + } + } else { + if (!push_email_address($address, $role)) { + warn("Invalid MAINTAINERS address: '" . $address . "'\n"); + } + } +} + +sub add_role { + my ($line, $role) = @_; + + my ($name, $address) = parse_email($line); + my $email = format_email($name, $address, $email_usename); + + foreach my $entry (@email_to) { + if ($email_remove_duplicates) { + my ($entry_name, $entry_address) = parse_email($entry->[0]); + if (($name eq $entry_name || $address eq $entry_address) + && ($role eq "" || !($entry->[1] =~ m/$role/)) + ) { + if ($entry->[1] eq "") { + $entry->[1] = "$role"; + } else { + $entry->[1] = "$entry->[1],$role"; + } + } + } else { + if ($email eq $entry->[0] + && ($role eq "" || !($entry->[1] =~ m/$role/)) + ) { + if ($entry->[1] eq "") { + $entry->[1] = "$role"; + } else { + $entry->[1] = "$entry->[1],$role"; + } + } + } + } +} + +sub which { + my ($bin) = @_; + + foreach my $path (split(/:/, $ENV{PATH})) { + if (-e "$path/$bin") { + return "$path/$bin"; + } + } + + return ""; +} + +sub which_conf { + my ($conf) = @_; + + foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { + if (-e "$path/$conf") { + return "$path/$conf"; + } + } + + return ""; +} + +sub mailmap_email { + my ($line) = @_; + + my ($name, $address) = parse_email($line); + my $email = format_email($name, $address, 1); + my $real_name = $name; + my $real_address = $address; + + if (exists $mailmap->{names}->{$email} || + exists $mailmap->{addresses}->{$email}) { + if (exists $mailmap->{names}->{$email}) { + $real_name = $mailmap->{names}->{$email}; + } + if (exists $mailmap->{addresses}->{$email}) { + $real_address = $mailmap->{addresses}->{$email}; + } + } else { + if (exists $mailmap->{names}->{$address}) { + $real_name = $mailmap->{names}->{$address}; + } + if (exists $mailmap->{addresses}->{$address}) { + $real_address = $mailmap->{addresses}->{$address}; + } + } + return format_email($real_name, $real_address, 1); +} + +sub mailmap { + my (@addresses) = @_; + + my @mapped_emails = (); + foreach my $line (@addresses) { + push(@mapped_emails, mailmap_email($line)); + } + merge_by_realname(@mapped_emails) if ($email_use_mailmap); + return @mapped_emails; +} + +sub merge_by_realname { + my %address_map; + my (@emails) = @_; + + foreach my $email (@emails) { + my ($name, $address) = parse_email($email); + if (exists $address_map{$name}) { + $address = $address_map{$name}; + $email = format_email($name, $address, 1); + } else { + $address_map{$name} = $address; + } + } +} + +sub git_execute_cmd { + my ($cmd) = @_; + my @lines = (); + + my $output = `$cmd`; + $output =~ s/^\s*//gm; + @lines = split("\n", $output); + + return @lines; +} + +sub hg_execute_cmd { + my ($cmd) = @_; + my @lines = (); + + my $output = `$cmd`; + @lines = split("\n", $output); + + return @lines; +} + +sub extract_formatted_signatures { + my (@signature_lines) = @_; + + my @type = @signature_lines; + + s/\s*(.*):.*/$1/ for (@type); + + # cut -f2- -d":" + s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines); + +## Reformat email addresses (with names) to avoid badly written signatures + + foreach my $signer (@signature_lines) { + $signer = deduplicate_email($signer); + } + + return (\@type, \@signature_lines); +} + +sub vcs_find_signers { + my ($cmd) = @_; + my $commits; + my @lines = (); + my @signatures = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + my $pattern = $VCS_cmds{"commit_pattern"}; + + $commits = grep(/$pattern/, @lines); # of commits + + @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines); + + return (0, @signatures) if !@signatures; + + save_commits_by_author(@lines) if ($interactive); + save_commits_by_signer(@lines) if ($interactive); + + if (!$email_git_penguin_chiefs) { + @signatures = grep(!/${penguin_chiefs}/i, @signatures); + } + + my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); + + return ($commits, @$signers_ref); +} + +sub vcs_find_author { + my ($cmd) = @_; + my @lines = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + if (!$email_git_penguin_chiefs) { + @lines = grep(!/${penguin_chiefs}/i, @lines); + } + + return @lines if !@lines; + + my @authors = (); + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"author_pattern"}/) { + my $author = $1; + my ($name, $address) = parse_email($author); + $author = format_email($name, $address, 1); + push(@authors, $author); + } + } + + save_commits_by_author(@lines) if ($interactive); + save_commits_by_signer(@lines) if ($interactive); + + return @authors; +} + +sub vcs_save_commits { + my ($cmd) = @_; + my @lines = (); + my @commits = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) { + push(@commits, $1); + } + } + + return @commits; +} + +sub vcs_blame { + my ($file) = @_; + my $cmd; + my @commits = (); + + return @commits if (!(-f $file)); + + if (@range && $VCS_cmds{"blame_range_cmd"} eq "") { + my @all_commits = (); + + $cmd = $VCS_cmds{"blame_file_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + @all_commits = vcs_save_commits($cmd); + + foreach my $file_range_diff (@range) { + next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); + my $diff_file = $1; + my $diff_start = $2; + my $diff_length = $3; + next if ("$file" ne "$diff_file"); + for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) { + push(@commits, $all_commits[$i]); + } + } + } elsif (@range) { + foreach my $file_range_diff (@range) { + next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); + my $diff_file = $1; + my $diff_start = $2; + my $diff_length = $3; + next if ("$file" ne "$diff_file"); + $cmd = $VCS_cmds{"blame_range_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + push(@commits, vcs_save_commits($cmd)); + } + } else { + $cmd = $VCS_cmds{"blame_file_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + @commits = vcs_save_commits($cmd); + } + + foreach my $commit (@commits) { + $commit =~ s/^\^//g; + } + + return @commits; +} + +my $printed_novcs = 0; +sub vcs_exists { + %VCS_cmds = %VCS_cmds_git; + return 1 if eval $VCS_cmds{"available"}; + %VCS_cmds = %VCS_cmds_hg; + return 2 if eval $VCS_cmds{"available"}; + %VCS_cmds = (); + if (!$printed_novcs) { + warn("$P: No supported VCS found. Add --nogit to options?\n"); + warn("Using a git repository produces better results.\n"); + warn("Try latest git repository using:\n"); + warn("git clone git://git.qemu.org/qemu.git\n"); + $printed_novcs = 1; + } + return 0; +} + +sub vcs_is_git { + vcs_exists(); + return $vcs_used == 1; +} + +sub vcs_is_hg { + return $vcs_used == 2; +} + +sub interactive_get_maintainers { + my ($list_ref) = @_; + my @list = @$list_ref; + + vcs_exists(); + + my %selected; + my %authored; + my %signed; + my $count = 0; + my $maintained = 0; + foreach my $entry (@list) { + $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i); + $selected{$count} = 1; + $authored{$count} = 0; + $signed{$count} = 0; + $count++; + } + + #menu loop + my $done = 0; + my $print_options = 0; + my $redraw = 1; + while (!$done) { + $count = 0; + if ($redraw) { + printf STDERR "\n%1s %2s %-65s", + "*", "#", "email/list and role:stats"; + if ($email_git || + ($email_git_fallback && !$maintained) || + $email_git_blame) { + print STDERR "auth sign"; + } + print STDERR "\n"; + foreach my $entry (@list) { + my $email = $entry->[0]; + my $role = $entry->[1]; + my $sel = ""; + $sel = "*" if ($selected{$count}); + my $commit_author = $commit_author_hash{$email}; + my $commit_signer = $commit_signer_hash{$email}; + my $authored = 0; + my $signed = 0; + $authored++ for (@{$commit_author}); + $signed++ for (@{$commit_signer}); + printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email; + printf STDERR "%4d %4d", $authored, $signed + if ($authored > 0 || $signed > 0); + printf STDERR "\n %s\n", $role; + if ($authored{$count}) { + my $commit_author = $commit_author_hash{$email}; + foreach my $ref (@{$commit_author}) { + print STDERR " Author: @{$ref}[1]\n"; + } + } + if ($signed{$count}) { + my $commit_signer = $commit_signer_hash{$email}; + foreach my $ref (@{$commit_signer}) { + print STDERR " @{$ref}[2]: @{$ref}[1]\n"; + } + } + + $count++; + } + } + my $date_ref = \$email_git_since; + $date_ref = \$email_hg_since if (vcs_is_hg()); + if ($print_options) { + $print_options = 0; + if (vcs_exists()) { + print STDERR <; + chomp($input); + + $redraw = 1; + my $rerun = 0; + my @wish = split(/[, ]+/, $input); + foreach my $nr (@wish) { + $nr = lc($nr); + my $sel = substr($nr, 0, 1); + my $str = substr($nr, 1); + my $val = 0; + $val = $1 if $str =~ /^(\d+)$/; + + if ($sel eq "y") { + $interactive = 0; + $done = 1; + $output_rolestats = 0; + $output_roles = 0; + last; + } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) { + $selected{$nr - 1} = !$selected{$nr - 1}; + } elsif ($sel eq "*" || $sel eq '^') { + my $toggle = 0; + $toggle = 1 if ($sel eq '*'); + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = $toggle; + } + } elsif ($sel eq "0") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i}; + } + } elsif ($sel eq "t") { + if (lc($str) eq "m") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(maintainer|supporter)/i); + } + } elsif (lc($str) eq "g") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(author|commit|signer)/i); + } + } elsif (lc($str) eq "l") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(open list)/i); + } + } elsif (lc($str) eq "s") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(subscriber list)/i); + } + } + } elsif ($sel eq "a") { + if ($val > 0 && $val <= $count) { + $authored{$val - 1} = !$authored{$val - 1}; + } elsif ($str eq '*' || $str eq '^') { + my $toggle = 0; + $toggle = 1 if ($str eq '*'); + for (my $i = 0; $i < $count; $i++) { + $authored{$i} = $toggle; + } + } + } elsif ($sel eq "s") { + if ($val > 0 && $val <= $count) { + $signed{$val - 1} = !$signed{$val - 1}; + } elsif ($str eq '*' || $str eq '^') { + my $toggle = 0; + $toggle = 1 if ($str eq '*'); + for (my $i = 0; $i < $count; $i++) { + $signed{$i} = $toggle; + } + } + } elsif ($sel eq "o") { + $print_options = 1; + $redraw = 1; + } elsif ($sel eq "g") { + if ($str eq "f") { + bool_invert(\$email_git_fallback); + } else { + bool_invert(\$email_git); + } + $rerun = 1; + } elsif ($sel eq "b") { + if ($str eq "s") { + bool_invert(\$email_git_blame_signatures); + } else { + bool_invert(\$email_git_blame); + } + $rerun = 1; + } elsif ($sel eq "c") { + if ($val > 0) { + $email_git_min_signatures = $val; + $rerun = 1; + } + } elsif ($sel eq "x") { + if ($val > 0) { + $email_git_max_maintainers = $val; + $rerun = 1; + } + } elsif ($sel eq "%") { + if ($str ne "" && $val >= 0) { + $email_git_min_percent = $val; + $rerun = 1; + } + } elsif ($sel eq "d") { + if (vcs_is_git()) { + $email_git_since = $str; + } elsif (vcs_is_hg()) { + $email_hg_since = $str; + } + $rerun = 1; + } elsif ($sel eq "t") { + bool_invert(\$email_git_all_signature_types); + $rerun = 1; + } elsif ($sel eq "f") { + bool_invert(\$file_emails); + $rerun = 1; + } elsif ($sel eq "r") { + bool_invert(\$email_remove_duplicates); + $rerun = 1; + } elsif ($sel eq "m") { + bool_invert(\$email_use_mailmap); + read_mailmap(); + $rerun = 1; + } elsif ($sel eq "k") { + bool_invert(\$keywords); + $rerun = 1; + } elsif ($sel eq "p") { + if ($str ne "" && $val >= 0) { + $pattern_depth = $val; + $rerun = 1; + } + } elsif ($sel eq "h" || $sel eq "?") { + print STDERR <[0]; + $address = $deduplicate_name_hash{lc($name)}->[1]; + $matched = 1; + } elsif ($deduplicate_address_hash{lc($address)}) { + $name = $deduplicate_address_hash{lc($address)}->[0]; + $address = $deduplicate_address_hash{lc($address)}->[1]; + $matched = 1; + } + if (!$matched) { + $deduplicate_name_hash{lc($name)} = [ $name, $address ]; + $deduplicate_address_hash{lc($address)} = [ $name, $address ]; + } + $email = format_email($name, $address, 1); + $email = mailmap_email($email); + return $email; +} + +sub save_commits_by_author { + my (@lines) = @_; + + my @authors = (); + my @commits = (); + my @subjects = (); + + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"author_pattern"}/) { + my $author = $1; + $author = deduplicate_email($author); + push(@authors, $author); + } + push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/); + push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/); + } + + for (my $i = 0; $i < @authors; $i++) { + my $exists = 0; + foreach my $ref(@{$commit_author_hash{$authors[$i]}}) { + if (@{$ref}[0] eq $commits[$i] && + @{$ref}[1] eq $subjects[$i]) { + $exists = 1; + last; + } + } + if (!$exists) { + push(@{$commit_author_hash{$authors[$i]}}, + [ ($commits[$i], $subjects[$i]) ]); + } + } +} + +sub save_commits_by_signer { + my (@lines) = @_; + + my $commit = ""; + my $subject = ""; + + foreach my $line (@lines) { + $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/); + $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/); + if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) { + my @signatures = ($line); + my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); + my @types = @$types_ref; + my @signers = @$signers_ref; + + my $type = $types[0]; + my $signer = $signers[0]; + + $signer = deduplicate_email($signer); + + my $exists = 0; + foreach my $ref(@{$commit_signer_hash{$signer}}) { + if (@{$ref}[0] eq $commit && + @{$ref}[1] eq $subject && + @{$ref}[2] eq $type) { + $exists = 1; + last; + } + } + if (!$exists) { + push(@{$commit_signer_hash{$signer}}, + [ ($commit, $subject, $type) ]); + } + } + } +} + +sub vcs_assign { + my ($role, $divisor, @lines) = @_; + + my %hash; + my $count = 0; + + return if (@lines <= 0); + + if ($divisor <= 0) { + warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n"); + $divisor = 1; + } + + @lines = mailmap(@lines); + + return if (@lines <= 0); + + @lines = sort(@lines); + + # uniq -c + $hash{$_}++ for @lines; + + # sort -rn + foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { + my $sign_offs = $hash{$line}; + my $percent = $sign_offs * 100 / $divisor; + + $percent = 100 if ($percent > 100); + $count++; + last if ($sign_offs < $email_git_min_signatures || + $count > $email_git_max_maintainers || + $percent < $email_git_min_percent); + push_email_address($line, ''); + if ($output_rolestats) { + my $fmt_percent = sprintf("%.0f", $percent); + add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%"); + } else { + add_role($line, $role); + } + } +} + +sub vcs_file_signoffs { + my ($file) = @_; + + my @signers = (); + my $commits; + + $vcs_used = vcs_exists(); + return if (!$vcs_used); + + my $cmd = $VCS_cmds{"find_signers_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd + + ($commits, @signers) = vcs_find_signers($cmd); + + foreach my $signer (@signers) { + $signer = deduplicate_email($signer); + } + + vcs_assign("commit_signer", $commits, @signers); +} + +sub vcs_file_blame { + my ($file) = @_; + + my @signers = (); + my @all_commits = (); + my @commits = (); + my $total_commits; + my $total_lines; + + $vcs_used = vcs_exists(); + return if (!$vcs_used); + + @all_commits = vcs_blame($file); + @commits = uniq(@all_commits); + $total_commits = @commits; + $total_lines = @all_commits; + + if ($email_git_blame_signatures) { + if (vcs_is_hg()) { + my $commit_count; + my @commit_signers = (); + my $commit = join(" -r ", @commits); + my $cmd; + + $cmd = $VCS_cmds{"find_commit_signers_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd + + ($commit_count, @commit_signers) = vcs_find_signers($cmd); + + push(@signers, @commit_signers); + } else { + foreach my $commit (@commits) { + my $commit_count; + my @commit_signers = (); + my $cmd; + + $cmd = $VCS_cmds{"find_commit_signers_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd + + ($commit_count, @commit_signers) = vcs_find_signers($cmd); + + push(@signers, @commit_signers); + } + } + } + + if ($from_filename) { + if ($output_rolestats) { + my @blame_signers; + if (vcs_is_hg()) {{ # Double brace for last exit + my $commit_count; + my @commit_signers = (); + @commits = uniq(@commits); + @commits = sort(@commits); + my $commit = join(" -r ", @commits); + my $cmd; + + $cmd = $VCS_cmds{"find_commit_author_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd + + my @lines = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + if (!$email_git_penguin_chiefs) { + @lines = grep(!/${penguin_chiefs}/i, @lines); + } + + last if !@lines; + + my @authors = (); + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"author_pattern"}/) { + my $author = $1; + $author = deduplicate_email($author); + push(@authors, $author); + } + } + + save_commits_by_author(@lines) if ($interactive); + save_commits_by_signer(@lines) if ($interactive); + + push(@signers, @authors); + }} + else { + foreach my $commit (@commits) { + my $i; + my $cmd = $VCS_cmds{"find_commit_author_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + my @author = vcs_find_author($cmd); + next if !@author; + + my $formatted_author = deduplicate_email($author[0]); + + my $count = grep(/$commit/, @all_commits); + for ($i = 0; $i < $count ; $i++) { + push(@blame_signers, $formatted_author); + } + } + } + if (@blame_signers) { + vcs_assign("authored lines", $total_lines, @blame_signers); + } + } + foreach my $signer (@signers) { + $signer = deduplicate_email($signer); + } + vcs_assign("commits", $total_commits, @signers); + } else { + foreach my $signer (@signers) { + $signer = deduplicate_email($signer); + } + vcs_assign("modified commits", $total_commits, @signers); + } +} + +sub uniq { + my (@parms) = @_; + + my %saw; + @parms = grep(!$saw{$_}++, @parms); + return @parms; +} + +sub sort_and_uniq { + my (@parms) = @_; + + my %saw; + @parms = sort @parms; + @parms = grep(!$saw{$_}++, @parms); + return @parms; +} + +sub clean_file_emails { + my (@file_emails) = @_; + my @fmt_emails = (); + + foreach my $email (@file_emails) { + $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g; + my ($name, $address) = parse_email($email); + if ($name eq '"[,\.]"') { + $name = ""; + } + + my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name); + if (@nw > 2) { + my $first = $nw[@nw - 3]; + my $middle = $nw[@nw - 2]; + my $last = $nw[@nw - 1]; + + if (((length($first) == 1 && $first =~ m/[A-Za-z]/) || + (length($first) == 2 && substr($first, -1) eq ".")) || + (length($middle) == 1 || + (length($middle) == 2 && substr($middle, -1) eq "."))) { + $name = "$first $middle $last"; + } else { + $name = "$middle $last"; + } + } + + if (substr($name, -1) =~ /[,\.]/) { + $name = substr($name, 0, length($name) - 1); + } elsif (substr($name, -2) =~ /[,\.]"/) { + $name = substr($name, 0, length($name) - 2) . '"'; + } + + if (substr($name, 0, 1) =~ /[,\.]/) { + $name = substr($name, 1, length($name) - 1); + } elsif (substr($name, 0, 2) =~ /"[,\.]/) { + $name = '"' . substr($name, 2, length($name) - 2); + } + + my $fmt_email = format_email($name, $address, $email_usename); + push(@fmt_emails, $fmt_email); + } + return @fmt_emails; +} + +sub merge_email { + my @lines; + my %saw; + + for (@_) { + my ($address, $role) = @$_; + if (!$saw{$address}) { + if ($output_roles) { + push(@lines, "$address ($role)"); + } else { + push(@lines, $address); + } + $saw{$address} = 1; + } + } + + return @lines; +} + +sub output { + my (@parms) = @_; + + if ($output_multiline) { + foreach my $line (@parms) { + print("${line}\n"); + } + } else { + print(join($output_separator, @parms)); + print("\n"); + } +} + +my $rfc822re; + +sub make_rfc822re { +# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and +# comment. We must allow for rfc822_lwsp (or comments) after each of these. +# This regexp will only work on addresses which have had comments stripped +# and replaced with rfc822_lwsp. + + my $specials = '()<>@,;:\\\\".\\[\\]'; + my $controls = '\\000-\\037\\177'; + + my $dtext = "[^\\[\\]\\r\\\\]"; + my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*"; + + my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*"; + +# Use zero-width assertion to spot the limit of an atom. A simple +# $rfc822_lwsp* causes the regexp engine to hang occasionally. + my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))"; + my $word = "(?:$atom|$quoted_string)"; + my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*"; + + my $sub_domain = "(?:$atom|$domain_literal)"; + my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*"; + + my $addr_spec = "$localpart\@$rfc822_lwsp*$domain"; + + my $phrase = "$word*"; + my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)"; + my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*"; + my $mailbox = "(?:$addr_spec|$phrase$route_addr)"; + + my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*"; + my $address = "(?:$mailbox|$group)"; + + return "$rfc822_lwsp*$address"; +} + +sub rfc822_strip_comments { + my $s = shift; +# Recursively remove comments, and replace with a single space. The simpler +# regexps in the Email Addressing FAQ are imperfect - they will miss escaped +# chars in atoms, for example. + + while ($s =~ s/^((?:[^"\\]|\\.)* + (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*) + \((?:[^()\\]|\\.)*\)/$1 /osx) {} + return $s; +} + +# valid: returns true if the parameter is an RFC822 valid address +# +sub rfc822_valid { + my $s = rfc822_strip_comments(shift); + + if (!$rfc822re) { + $rfc822re = make_rfc822re(); + } + + return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/; +} + +# validlist: In scalar context, returns true if the parameter is an RFC822 +# valid list of addresses. +# +# In list context, returns an empty list on failure (an invalid +# address was found); otherwise a list whose first element is the +# number of addresses found and whose remaining elements are the +# addresses. This is needed to disambiguate failure (invalid) +# from success with no addresses found, because an empty string is +# a valid list. + +sub rfc822_validlist { + my $s = rfc822_strip_comments(shift); + + if (!$rfc822re) { + $rfc822re = make_rfc822re(); + } + # * null list items are valid according to the RFC + # * the '1' business is to aid in distinguishing failure from no results + + my @r; + if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so && + $s =~ m/^$rfc822_char*$/) { + while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) { + push(@r, $1); + } + return wantarray ? (scalar(@r), @r) : 1; + } + return wantarray ? () : 0; +} diff -Nru qemu-kvm-0.14.1+noroms/scripts/ordereddict.py qemu-kvm-0.15.0+noroms/scripts/ordereddict.py --- qemu-kvm-0.14.1+noroms/scripts/ordereddict.py 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/scripts/ordereddict.py 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,127 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# 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. + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other diff -Nru qemu-kvm-0.14.1+noroms/scripts/qapi-commands.py qemu-kvm-0.15.0+noroms/scripts/qapi-commands.py --- qemu-kvm-0.14.1+noroms/scripts/qapi-commands.py 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/scripts/qapi-commands.py 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,385 @@ +# +# QAPI command marshaller generator +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# Michael Roth +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def generate_decl_enum(name, members, genlist=True): + return mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); +''', + name=name) + +def generate_command_decl(name, args, ret_type): + arglist="" + for argname, argtype, optional, structured in parse_args(args): + argtype = c_type(argtype) + if argtype == "char *": + argtype = "const char *" + if optional: + arglist += "bool has_%s, " % c_var(argname) + arglist += "%s %s, " % (argtype, c_var(argname)) + return mcgen(''' +%(ret_type)s qmp_%(name)s(%(args)sError **errp); +''', + ret_type=c_type(ret_type), name=c_var(name), args=arglist).strip() + +def gen_sync_call(name, args, ret_type, indent=0): + ret = "" + arglist="" + retval="" + if ret_type: + retval = "retval = " + for argname, argtype, optional, structured in parse_args(args): + if optional: + arglist += "has_%s, " % c_var(argname) + arglist += "%s, " % (c_var(argname)) + push_indent(indent) + ret = mcgen(''' +%(retval)sqmp_%(name)s(%(args)serrp); + +''', + name=c_var(name), args=arglist, retval=retval).rstrip() + if ret_type: + ret += "\n" + mcgen('''' +%(marshal_output_call)s +''', + marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() + pop_indent(indent) + return ret.rstrip() + + +def gen_marshal_output_call(name, ret_type): + if not ret_type: + return "" + return "qmp_marshal_output_%s(retval, ret, errp);" % c_var(name) + +def gen_visitor_output_containers_decl(ret_type): + ret = "" + push_indent() + if ret_type: + ret += mcgen(''' +QmpOutputVisitor *mo; +QapiDeallocVisitor *md; +Visitor *v; +''') + pop_indent() + + return ret + +def gen_visitor_input_containers_decl(args): + ret = "" + + push_indent() + if len(args) > 0: + ret += mcgen(''' +QmpInputVisitor *mi; +QapiDeallocVisitor *md; +Visitor *v; +''') + pop_indent() + + return ret.rstrip() + +def gen_visitor_input_vars_decl(args): + ret = "" + push_indent() + for argname, argtype, optional, structured in parse_args(args): + if optional: + ret += mcgen(''' +bool has_%(argname)s = false; +''', + argname=c_var(argname)) + if c_type(argtype).endswith("*"): + ret += mcgen(''' +%(argtype)s %(argname)s = NULL; +''', + argname=c_var(argname), argtype=c_type(argtype)) + else: + ret += mcgen(''' +%(argtype)s %(argname)s; +''', + argname=c_var(argname), argtype=c_type(argtype)) + + pop_indent() + return ret.rstrip() + +def gen_visitor_input_block(args, obj, dealloc=False): + ret = "" + if len(args) == 0: + return ret + + push_indent() + + if dealloc: + ret += mcgen(''' +md = qapi_dealloc_visitor_new(); +v = qapi_dealloc_get_visitor(md); +''') + else: + ret += mcgen(''' +mi = qmp_input_visitor_new(%(obj)s); +v = qmp_input_get_visitor(mi); +''', + obj=obj) + + for argname, argtype, optional, structured in parse_args(args): + if optional: + ret += mcgen(''' +visit_start_optional(v, &has_%(c_name)s, "%(name)s", errp); +if (has_%(c_name)s) { +''', + c_name=c_var(argname), name=argname) + push_indent() + ret += mcgen(''' +visit_type_%(argtype)s(v, &%(c_name)s, "%(name)s", errp); +''', + c_name=c_var(argname), name=argname, argtype=argtype) + if optional: + pop_indent() + ret += mcgen(''' +} +visit_end_optional(v, errp); +''') + + if dealloc: + ret += mcgen(''' +qapi_dealloc_visitor_cleanup(md); +''') + else: + ret += mcgen(''' +qmp_input_visitor_cleanup(mi); +''') + pop_indent() + return ret.rstrip() + +def gen_marshal_output(name, args, ret_type): + if not ret_type: + return "" + ret = mcgen(''' +static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) +{ + QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); + QmpOutputVisitor *mo = qmp_output_visitor_new(); + Visitor *v; + + v = qmp_output_get_visitor(mo); + visit_type_%(ret_type)s(v, &ret_in, "unused", errp); + if (!error_is_set(errp)) { + *ret_out = qmp_output_get_qobject(mo); + } + qmp_output_visitor_cleanup(mo); + v = qapi_dealloc_get_visitor(md); + visit_type_%(ret_type)s(v, &ret_in, "unused", errp); + qapi_dealloc_visitor_cleanup(md); +} +''', + c_ret_type=c_type(ret_type), c_name=c_var(name), ret_type=ret_type) + + return ret + +def gen_marshal_input(name, args, ret_type): + ret = mcgen(''' +static void qmp_marshal_input_%(c_name)s(QDict *args, QObject **ret, Error **errp) +{ +''', + c_name=c_var(name)) + + if ret_type: + if c_type(ret_type).endswith("*"): + retval = " %s retval = NULL;" % c_type(ret_type) + else: + retval = " %s retval;" % c_type(ret_type) + ret += mcgen(''' +%(retval)s +''', + retval=retval) + + if len(args) > 0: + ret += mcgen(''' +%(visitor_input_containers_decl)s +%(visitor_input_vars_decl)s + +%(visitor_input_block)s + +''', + visitor_input_containers_decl=gen_visitor_input_containers_decl(args), + visitor_input_vars_decl=gen_visitor_input_vars_decl(args), + visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) + + ret += mcgen(''' + if (error_is_set(errp)) { + goto out; + } +%(sync_call)s +''', + sync_call=gen_sync_call(name, args, ret_type, indent=4)) + ret += mcgen(''' + +out: +''') + ret += mcgen(''' +%(visitor_input_block_cleanup)s + return; +} +''', + visitor_input_block_cleanup=gen_visitor_input_block(args, None, dealloc=True)) + return ret + +def gen_registry(commands): + registry="" + push_indent() + for cmd in commands: + registry += mcgen(''' +qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s); +''', + name=cmd['command'], c_name=c_var(cmd['command'])) + pop_indent() + ret = mcgen(''' +static void qmp_init_marshal(void) +{ +%(registry)s +} + +qapi_init(qmp_init_marshal); +''', + registry=registry.rstrip()) + return ret + +def gen_command_decl_prologue(header, guard, prefix=""): + ret = mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI function prototypes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 %(guard)s +#define %(guard)s + +#include "%(prefix)sqapi-types.h" +#include "error.h" + +''', + header=basename(h_file), guard=guardname(h_file), prefix=prefix) + return ret + +def gen_command_def_prologue(prefix="", proxy=False): + ret = mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QMP->QAPI command dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 "qemu-objects.h" +#include "qapi/qmp-core.h" +#include "qapi/qapi-visit-core.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qapi-dealloc-visitor.h" +#include "%(prefix)sqapi-types.h" +#include "%(prefix)sqapi-visit.h" + +''', + prefix=prefix) + if not proxy: + ret += '#include "%sqmp-commands.h"' % prefix + return ret + "\n" + + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir=", "type="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +dispatch_type = "sync" +c_file = 'qmp-marshal.c' +h_file = 'qmp-commands.h' + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + elif o in ("-t", "--type"): + dispatch_type = a + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +exprs = parse_schema(sys.stdin) +commands = filter(lambda expr: expr.has_key('command'), exprs) + +if dispatch_type == "sync": + fdecl = open(h_file, 'w') + fdef = open(c_file, 'w') + ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix) + fdecl.write(ret) + ret = gen_command_def_prologue(prefix=prefix) + fdef.write(ret) + + for cmd in commands: + arglist = [] + ret_type = None + if cmd.has_key('data'): + arglist = cmd['data'] + if cmd.has_key('returns'): + ret_type = cmd['returns'] + ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" + fdecl.write(ret) + if ret_type: + ret = gen_marshal_output(cmd['command'], arglist, ret_type) + "\n" + fdef.write(ret) + ret = gen_marshal_input(cmd['command'], arglist, ret_type) + "\n" + fdef.write(ret) + + fdecl.write("\n#endif"); + ret = gen_registry(commands) + fdef.write(ret) + + fdef.flush() + fdef.close() + fdecl.flush() + fdecl.close() diff -Nru qemu-kvm-0.14.1+noroms/scripts/qapi.py qemu-kvm-0.15.0+noroms/scripts/qapi.py --- qemu-kvm-0.14.1+noroms/scripts/qapi.py 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/scripts/qapi.py 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,203 @@ +# +# QAPI helper library +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict + +def tokenize(data): + while len(data): + if data[0] in ['{', '}', ':', ',', '[', ']']: + yield data[0] + data = data[1:] + elif data[0] in ' \n': + data = data[1:] + elif data[0] == "'": + data = data[1:] + string = '' + while data[0] != "'": + string += data[0] + data = data[1:] + data = data[1:] + yield string + +def parse(tokens): + if tokens[0] == '{': + ret = OrderedDict() + tokens = tokens[1:] + while tokens[0] != '}': + key = tokens[0] + tokens = tokens[1:] + + tokens = tokens[1:] # : + + value, tokens = parse(tokens) + + if tokens[0] == ',': + tokens = tokens[1:] + + ret[key] = value + tokens = tokens[1:] + return ret, tokens + elif tokens[0] == '[': + ret = [] + tokens = tokens[1:] + while tokens[0] != ']': + value, tokens = parse(tokens) + if tokens[0] == ',': + tokens = tokens[1:] + ret.append(value) + tokens = tokens[1:] + return ret, tokens + else: + return tokens[0], tokens[1:] + +def evaluate(string): + return parse(map(lambda x: x, tokenize(string)))[0] + +def parse_schema(fp): + exprs = [] + expr = '' + expr_eval = None + + for line in fp: + if line.startswith('#') or line == '\n': + continue + + if line.startswith(' '): + expr += line + elif expr: + expr_eval = evaluate(expr) + if expr_eval.has_key('enum'): + add_enum(expr_eval['enum']) + elif expr_eval.has_key('union'): + add_enum('%sKind' % expr_eval['union']) + exprs.append(expr_eval) + expr = line + else: + expr += line + + if expr: + expr_eval = evaluate(expr) + if expr_eval.has_key('enum'): + add_enum(expr_eval['enum']) + elif expr_eval.has_key('union'): + add_enum('%sKind' % expr_eval['union']) + exprs.append(expr_eval) + + return exprs + +def parse_args(typeinfo): + for member in typeinfo: + argname = member + argentry = typeinfo[member] + optional = False + structured = False + if member.startswith('*'): + argname = member[1:] + optional = True + if isinstance(argentry, OrderedDict): + structured = True + yield (argname, argentry, optional, structured) + +def de_camel_case(name): + new_name = '' + for ch in name: + if ch.isupper() and new_name: + new_name += '_' + if ch == '-': + new_name += '_' + else: + new_name += ch.lower() + return new_name + +def camel_case(name): + new_name = '' + first = True + for ch in name: + if ch in ['_', '-']: + first = True + elif first: + new_name += ch.upper() + first = False + else: + new_name += ch.lower() + return new_name + +def c_var(name): + return '_'.join(name.split('-')).lstrip("*") + +def c_list_type(name): + return '%sList' % name + +def type_name(name): + if type(name) == list: + return c_list_type(name[0]) + return name + +enum_types = [] + +def add_enum(name): + global enum_types + enum_types.append(name) + +def is_enum(name): + global enum_types + return (name in enum_types) + +def c_type(name): + if name == 'str': + return 'char *' + elif name == 'int': + return 'int64_t' + elif name == 'bool': + return 'bool' + elif name == 'number': + return 'double' + elif type(name) == list: + return '%s *' % c_list_type(name[0]) + elif is_enum(name): + return name + elif name == None or len(name) == 0: + return 'void' + elif name == name.upper(): + return '%sEvent *' % camel_case(name) + else: + return '%s *' % name + +def genindent(count): + ret = "" + for i in range(count): + ret += " " + return ret + +indent_level = 0 + +def push_indent(indent_amount=4): + global indent_level + indent_level += indent_amount + +def pop_indent(indent_amount=4): + global indent_level + indent_level -= indent_amount + +def cgen(code, **kwds): + indent = genindent(indent_level) + lines = code.split('\n') + lines = map(lambda x: indent + x, lines) + return '\n'.join(lines) % kwds + '\n' + +def mcgen(code, **kwds): + return cgen('\n'.join(code.split('\n')[1:-1]), **kwds) + +def basename(filename): + return filename.split("/")[-1] + +def guardname(filename): + return filename.replace("/", "_").replace("-", "_").split(".")[0].upper() diff -Nru qemu-kvm-0.14.1+noroms/scripts/qapi-types.py qemu-kvm-0.15.0+noroms/scripts/qapi-types.py --- qemu-kvm-0.14.1+noroms/scripts/qapi-types.py 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/scripts/qapi-types.py 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,270 @@ +# +# QAPI types generator +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def generate_fwd_struct(name, members): + return mcgen(''' +typedef struct %(name)s %(name)s; + +typedef struct %(name)sList +{ + %(name)s *value; + struct %(name)sList *next; +} %(name)sList; +''', + name=name) + +def generate_struct(structname, fieldname, members): + ret = mcgen(''' +struct %(name)s +{ +''', + name=structname) + + for argname, argentry, optional, structured in parse_args(members): + if optional: + ret += mcgen(''' + bool has_%(c_name)s; +''', + c_name=c_var(argname)) + if structured: + push_indent() + ret += generate_struct("", argname, argentry) + pop_indent() + else: + ret += mcgen(''' + %(c_type)s %(c_name)s; +''', + c_type=c_type(argentry), c_name=c_var(argname)) + + if len(fieldname): + fieldname = " " + fieldname + ret += mcgen(''' +}%(field)s; +''', + field=fieldname) + + return ret + +def generate_enum_lookup(name, values): + ret = mcgen(''' +const char *%(name)s_lookup[] = { +''', + name=name) + i = 0 + for value in values: + ret += mcgen(''' + "%(value)s", +''', + value=c_var(value).lower()) + + ret += mcgen(''' + NULL, +}; + +''') + return ret + +def generate_enum(name, values): + lookup_decl = mcgen(''' +extern const char *%(name)s_lookup[]; +''', + name=name) + + enum_decl = mcgen(''' +typedef enum %(name)s +{ +''', + name=name) + + i = 0 + for value in values: + enum_decl += mcgen(''' + %(abbrev)s_%(value)s = %(i)d, +''', + abbrev=de_camel_case(name).upper(), + value=c_var(value).upper(), + i=i) + i += 1 + + enum_decl += mcgen(''' +} %(name)s; +''', + name=name) + + return lookup_decl + enum_decl + +def generate_union(name, typeinfo): + ret = mcgen(''' +struct %(name)s +{ + %(name)sKind kind; + union { +''', + name=name) + + for key in typeinfo: + ret += mcgen(''' + %(c_type)s %(c_name)s; +''', + c_type=c_type(typeinfo[key]), + c_name=c_var(key)) + + ret += mcgen(''' + }; +}; +''') + + return ret + +def generate_type_cleanup_decl(name): + ret = mcgen(''' +void qapi_free_%(type)s(%(c_type)s obj); +''', + c_type=c_type(name),type=name) + return ret + +def generate_type_cleanup(name): + ret = mcgen(''' +void qapi_free_%(type)s(%(c_type)s obj) +{ + QapiDeallocVisitor *md; + Visitor *v; + + if (!obj) { + return; + } + + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_%(type)s(v, &obj, NULL, NULL); + qapi_dealloc_visitor_cleanup(md); +} +''', + c_type=c_type(name),type=name) + return ret + + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +c_file = 'qapi-types.c' +h_file = 'qapi-types.h' + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +fdef = open(c_file, 'w') +fdecl = open(h_file, 'w') + +fdef.write(mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * deallocation functions for schema-defined QAPI types + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * Michael Roth + * + * 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 "qapi/qapi-dealloc-visitor.h" +#include "%(prefix)sqapi-types.h" +#include "%(prefix)sqapi-visit.h" + +''', prefix=prefix)) + +fdecl.write(mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI types + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 %(guard)s +#define %(guard)s + +#include "qapi/qapi-types-core.h" +''', + guard=guardname(h_file))) + +exprs = parse_schema(sys.stdin) + +for expr in exprs: + ret = "\n" + if expr.has_key('type'): + ret += generate_fwd_struct(expr['type'], expr['data']) + elif expr.has_key('enum'): + ret += generate_enum(expr['enum'], expr['data']) + fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) + elif expr.has_key('union'): + ret += generate_fwd_struct(expr['union'], expr['data']) + "\n" + ret += generate_enum('%sKind' % expr['union'], expr['data'].keys()) + else: + continue + fdecl.write(ret) + +for expr in exprs: + ret = "\n" + if expr.has_key('type'): + ret += generate_struct(expr['type'], "", expr['data']) + "\n" + ret += generate_type_cleanup_decl(expr['type']) + fdef.write(generate_type_cleanup(expr['type']) + "\n") + elif expr.has_key('union'): + ret += generate_union(expr['union'], expr['data']) + else: + continue + fdecl.write(ret) + +fdecl.write(''' +#endif +''') + +fdecl.flush() +fdecl.close() diff -Nru qemu-kvm-0.14.1+noroms/scripts/qapi-visit.py qemu-kvm-0.15.0+noroms/scripts/qapi-visit.py --- qemu-kvm-0.14.1+noroms/scripts/qapi-visit.py 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/scripts/qapi-visit.py 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,246 @@ +# +# QAPI visitor generator +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# Michael Roth +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def generate_visit_struct_body(field_prefix, members): + ret = "" + if len(field_prefix): + field_prefix = field_prefix + "." + for argname, argentry, optional, structured in parse_args(members): + if optional: + ret += mcgen(''' +visit_start_optional(m, (obj && *obj) ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", errp); +if ((*obj)->%(prefix)shas_%(c_name)s) { +''', + c_prefix=c_var(field_prefix), prefix=field_prefix, + c_name=c_var(argname), name=argname) + push_indent() + + if structured: + ret += mcgen(''' +visit_start_struct(m, NULL, "", "%(name)s", 0, errp); +''', + name=argname) + ret += generate_visit_struct_body(field_prefix + argname, argentry) + ret += mcgen(''' +visit_end_struct(m, errp); +''') + else: + ret += mcgen(''' +visit_type_%(type)s(m, (obj && *obj) ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", errp); +''', + c_prefix=c_var(field_prefix), prefix=field_prefix, + type=type_name(argentry), c_name=c_var(argname), + name=argname) + + if optional: + pop_indent() + ret += mcgen(''' +} +visit_end_optional(m, errp); +''') + return ret + +def generate_visit_struct(name, members): + ret = mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) +{ + visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), errp); +''', + name=name) + push_indent() + ret += generate_visit_struct_body("", members) + pop_indent() + + ret += mcgen(''' + visit_end_struct(m, errp); +} +''') + return ret + +def generate_visit_list(name, members): + return mcgen(''' + +void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) +{ + GenericList *i; + + visit_start_list(m, name, errp); + + for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) { + %(name)sList *native_i = (%(name)sList *)i; + visit_type_%(name)s(m, &native_i->value, NULL, errp); + } + + visit_end_list(m, errp); +} +''', + name=name) + +def generate_visit_enum(name, members): + return mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp) +{ + visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); +} +''', + name=name) + +def generate_visit_union(name, members): + ret = generate_visit_enum('%sKind' % name, members.keys()) + + ret += mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) +{ +} +''', + name=name) + + return ret + +def generate_declaration(name, members, genlist=True): + ret = mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp); +''', + name=name) + + if genlist: + ret += mcgen(''' +void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp); +''', + name=name) + + return ret + +def generate_decl_enum(name, members, genlist=True): + return mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); +''', + name=name) + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +c_file = 'qapi-visit.c' +h_file = 'qapi-visit.h' + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +fdef = open(c_file, 'w') +fdecl = open(h_file, 'w') + +fdef.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI visitor functions + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 "%(header)s" +''', + header=basename(h_file))) + +fdecl.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI visitor function + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 %(guard)s +#define %(guard)s + +#include "qapi/qapi-visit-core.h" +#include "%(prefix)sqapi-types.h" +''', + prefix=prefix, guard=guardname(h_file))) + +exprs = parse_schema(sys.stdin) + +for expr in exprs: + if expr.has_key('type'): + ret = generate_visit_struct(expr['type'], expr['data']) + ret += generate_visit_list(expr['type'], expr['data']) + fdef.write(ret) + + ret = generate_declaration(expr['type'], expr['data']) + fdecl.write(ret) + elif expr.has_key('union'): + ret = generate_visit_union(expr['union'], expr['data']) + fdef.write(ret) + + ret = generate_decl_enum('%sKind' % expr['union'], expr['data'].keys()) + ret += generate_declaration(expr['union'], expr['data']) + fdecl.write(ret) + elif expr.has_key('enum'): + ret = generate_visit_enum(expr['enum'], expr['data']) + fdef.write(ret) + + ret = generate_decl_enum(expr['enum'], expr['data']) + fdecl.write(ret) + +fdecl.write(''' +#endif +''') + +fdecl.flush() +fdecl.close() + +fdef.flush() +fdef.close() diff -Nru qemu-kvm-0.14.1+noroms/scripts/qemu-binfmt-conf.sh qemu-kvm-0.15.0+noroms/scripts/qemu-binfmt-conf.sh --- qemu-kvm-0.14.1+noroms/scripts/qemu-binfmt-conf.sh 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/scripts/qemu-binfmt-conf.sh 2011-08-09 07:40:29.000000000 -0500 @@ -1,5 +1,5 @@ #!/bin/sh -# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC program execution by the kernel +# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390 program execution by the kernel # load the binfmt_misc module if [ ! -d /proc/sys/fs/binfmt_misc ]; then @@ -63,4 +63,6 @@ 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 +if [ $cpu != "s390x" ] ; then + echo ':s390x:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-s390x:' > /proc/sys/fs/binfmt_misc/register fi diff -Nru qemu-kvm-0.14.1+noroms/scripts/refresh-pxe-roms.sh qemu-kvm-0.15.0+noroms/scripts/refresh-pxe-roms.sh --- qemu-kvm-0.14.1+noroms/scripts/refresh-pxe-roms.sh 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/scripts/refresh-pxe-roms.sh 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,99 @@ +#!/bin/bash + +# PXE ROM build script +# +# 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 (C) 2011 Red Hat, Inc. +# Authors: Alex Williamson +# +# Usage: Run from root of qemu tree +# ./scripts/refresh-pxe-roms.sh + +QEMU_DIR=$PWD +ROM_DIR="pc-bios" +BUILD_DIR="roms/ipxe" +LOCAL_CONFIG="src/config/local/general.h" + +function cleanup () +{ + if [ -n "$SAVED_CONFIG" ]; then + cp "$SAVED_CONFIG" "$BUILD_DIR"/"$LOCAL_CONFIG" + rm "$SAVED_CONFIG" + fi + cd "$QEMU_DIR" +} + +function make_rom () +{ + cd "$BUILD_DIR"/src + + BUILD_LOG=$(mktemp) + + echo Building "$2"... + make bin/"$1".rom > "$BUILD_LOG" 2>&1 + if [ $? -ne 0 ]; then + echo Build failed + tail --lines=100 "$BUILD_LOG" + rm "$BUILD_LOG" + cleanup + exit 1 + fi + rm "$BUILD_LOG" + + cp bin/"$1".rom "$QEMU_DIR"/"$ROM_DIR"/"$2" + + cd "$QEMU_DIR" +} + +if [ ! -d "$QEMU_DIR"/"$ROM_DIR" ]; then + echo "error: can't find $ROM_DIR directory," \ + "run me from the root of the qemu tree" + exit 1 +fi + +if [ ! -d "$BUILD_DIR"/src ]; then + echo "error: $BUILD_DIR not populated, try:" + echo " git submodule init $BUILD_DIR" + echo " git submodule update $BUILD_DIR" + exit 1 +fi + +if [ -e "$BUILD_DIR"/"$LOCAL_CONFIG" ]; then + SAVED_CONFIG=$(mktemp) + cp "$BUILD_DIR"/"$LOCAL_CONFIG" "$SAVED_CONFIG" +fi + +echo "#undef BANNER_TIMEOUT" > "$BUILD_DIR"/"$LOCAL_CONFIG" +echo "#define BANNER_TIMEOUT 0" >> "$BUILD_DIR"/"$LOCAL_CONFIG" + +IPXE_VERSION=$(cd "$BUILD_DIR" && git describe --tags) +if [ -z "$IPXE_VERSION" ]; then + echo "error: unable to retrieve git version" + cleanup + exit 1 +fi + +echo "#undef PRODUCT_NAME" >> "$BUILD_DIR"/"$LOCAL_CONFIG" +echo "#define PRODUCT_NAME \"iPXE $IPXE_VERSION\"" >> "$BUILD_DIR"/"$LOCAL_CONFIG" + +make_rom 8086100e pxe-e1000.rom +make_rom 80861209 pxe-eepro100.rom +make_rom 10500940 pxe-ne2k_pci.rom +make_rom 10222000 pxe-pcnet.rom +make_rom 10ec8139 pxe-rtl8139.rom +make_rom 1af41000 pxe-virtio.rom + +echo done +cleanup diff -Nru qemu-kvm-0.14.1+noroms/scripts/simpletrace.py qemu-kvm-0.15.0+noroms/scripts/simpletrace.py --- qemu-kvm-0.14.1+noroms/scripts/simpletrace.py 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/scripts/simpletrace.py 2011-08-09 07:40:29.000000000 -0500 @@ -9,30 +9,27 @@ # # For help see docs/tracing.txt -import sys import struct import re +import inspect header_event_id = 0xffffffffffffffff header_magic = 0xf2b177cb0aa429b4 header_version = 0 +dropped_event_id = 0xfffffffffffffffe 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.""" + """Parse a trace-events file into {event_num: (name, arg1, ...)}.""" def get_argnames(args): """Extract argument names from a parameter list.""" return tuple(arg.split()[-1].lstrip('*') for arg in args.split(',')) - events = {} + events = {dropped_event_id: ('dropped', 'count')} event_num = 0 for line in fobj: m = event_re.match(line.strip()) @@ -45,20 +42,20 @@ return events def read_record(fobj): - """Deserialize a trace record from a file.""" + """Deserialize a trace record from a file into a tuple (event_num, timestamp, arg1, ..., arg6).""" 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.""" + """Deserialize trace records from a file, yielding record tuples (event_num, timestamp, arg1, ..., arg6).""" 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') + raise ValueError('not a trace file or incompatible version') while True: rec = read_record(fobj) @@ -67,27 +64,88 @@ 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) +class Analyzer(object): + """A trace file analyzer which processes trace records. + + An analyzer can be passed to run() or process(). The begin() method is + invoked, then each trace record is processed, and finally the end() method + is invoked. + + If a method matching a trace event name exists, it is invoked to process + that trace record. Otherwise the catchall() method is invoked.""" + + def begin(self): + """Called at the start of the trace.""" + pass + + def catchall(self, event, rec): + """Called if no specific method for processing a trace event has been found.""" + pass + + def end(self): + """Called at the end of the trace.""" + pass + +def process(events, log, analyzer): + """Invoke an analyzer on each event in a log.""" + if isinstance(events, str): + events = parse_events(open(events, 'r')) + if isinstance(log, str): + log = open(log, 'rb') + + def build_fn(analyzer, event): + fn = getattr(analyzer, event[0], None) + if fn is None: + return analyzer.catchall + + event_argcount = len(event) - 1 + fn_argcount = len(inspect.getargspec(fn)[0]) - 1 + if fn_argcount == event_argcount + 1: + # Include timestamp as first argument + return lambda _, rec: fn(*rec[1:2 + fn_argcount]) + else: + # Just arguments, no timestamp + return lambda _, rec: fn(*rec[2:2 + fn_argcount]) + + analyzer.begin() + fn_cache = {} + for rec in read_trace_file(log): + event_num = rec[0] + event = events[event_num] + if event_num not in fn_cache: + fn_cache[event_num] = build_fn(analyzer, event) + fn_cache[event_num](event, rec) + analyzer.end() + +def run(analyzer): + """Execute an analyzer on a trace file given on the command-line. + + This function is useful as a driver for simple analysis scripts. More + advanced scripts will want to call process() instead.""" + import sys + + if len(sys.argv) != 3: + sys.stderr.write('usage: %s \n' % sys.argv[0]) + sys.exit(1) + + events = parse_events(open(sys.argv[1], 'r')) + process(events, sys.argv[2], analyzer) + +if __name__ == '__main__': + class Formatter(Analyzer): + def __init__(self): + self.last_timestamp = None + + def catchall(self, event, rec): + timestamp = rec[1] + if self.last_timestamp is None: + self.last_timestamp = timestamp + delta_ns = timestamp - self.last_timestamp + self.last_timestamp = timestamp + + 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])) + print ' '.join(fields) + + run(Formatter()) diff -Nru qemu-kvm-0.14.1+noroms/scripts/tracetool qemu-kvm-0.15.0+noroms/scripts/tracetool --- qemu-kvm-0.14.1+noroms/scripts/tracetool 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/scripts/tracetool 2011-08-09 07:40:29.000000000 -0500 @@ -30,9 +30,11 @@ --stap Generate .stp file (DTrace with SystemTAP only) Options: - --binary [path] Full path to QEMU binary - --target-arch [arch] QEMU emulator target arch - --target-type [type] QEMU emulator target type ('system' or 'user') + --binary [path] Full path to QEMU binary + --target-arch [arch] QEMU emulator target arch + --target-type [type] QEMU emulator target type ('system' or 'user') + --probe-prefix [prefix] Prefix for dtrace probe names + (default: qemu-\$targettype-\$targetarch) EOF exit 1 @@ -49,7 +51,7 @@ { local args args=${1#*\(} - args=${args%\)*} + args=${args%%\)*} echo "$args" } @@ -336,6 +338,7 @@ name=$(get_name "$1") args=$(get_args "$1") argnames=$(get_argnames "$1", ",") + [ -z "$argnames" ] || argnames=", $argnames" fmt=$(get_fmt "$1") cat < +# +# This work is licensed under the terms of the GNU GPL version 2. +# See the COPYING file in the top-level directory. + +tmpdir=`mktemp -d` +linux="$1" +output="$2" + +if [ -z "$linux" ] || ! [ -d "$linux" ]; then + cat << EOF +usage: update-kernel-headers.sh LINUX_PATH [OUTPUT_PATH] + +LINUX_PATH Linux kernel directory to obtain the headers from +OUTPUT_PATH output directory, usually the qemu source tree (default: $PWD) +EOF + exit 1 +fi + +if [ -z "$output" ]; then + output="$PWD" +fi + +for arch in x86 powerpc s390; do + make -C "$linux" INSTALL_HDR_PATH="$tmpdir" SRCARCH=$arch headers_install + + rm -rf "$output/linux-headers/asm-$arch" + mkdir -p "$output/linux-headers/asm-$arch" + for header in kvm.h kvm_para.h; do + cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" + done + if [ $arch = x86 ]; then + cp "$tmpdir/include/asm/hyperv.h" "$output/linux-headers/asm-x86" + fi +done + +rm -rf "$output/linux-headers/linux" +mkdir -p "$output/linux-headers/linux" +for header in kvm.h kvm_para.h vhost.h virtio_config.h virtio_ring.h; do + cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" +done +if [ -L "$linux/source" ]; then + cp "$linux/source/COPYING" "$output/linux-headers" +else + cp "$linux/COPYING" "$output/linux-headers" +fi + +rm -rf "$tmpdir" diff -Nru qemu-kvm-0.14.1+noroms/simpletrace.c qemu-kvm-0.15.0+noroms/simpletrace.c --- qemu-kvm-0.14.1+noroms/simpletrace.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/simpletrace.c 2011-08-09 07:40:29.000000000 -0500 @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "qemu-timer.h" #include "trace.h" @@ -24,6 +26,12 @@ /** Trace file version number, bump if format changes */ #define HEADER_VERSION 0 +/** Records were dropped event ID */ +#define DROPPED_EVENT_ID (~(uint64_t)0 - 1) + +/** Trace record is valid */ +#define TRACE_RECORD_VALID ((uint64_t)1 << 63) + /** Trace buffer entry */ typedef struct { uint64_t event; @@ -37,126 +45,135 @@ } TraceRecord; enum { - TRACE_BUF_LEN = 64 * 1024 / sizeof(TraceRecord), + TRACE_BUF_LEN = 4096, + TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4, }; +/* + * Trace records are written out by a dedicated thread. The thread waits for + * records to become available, writes them out, and then waits again. + */ +static pthread_mutex_t trace_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t trace_available_cond = PTHREAD_COND_INITIALIZER; +static pthread_cond_t trace_empty_cond = PTHREAD_COND_INITIALIZER; +static bool trace_available; +static bool trace_writeout_enabled; + 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, ...)) +/** + * Read a trace record from the trace buffer + * + * @idx Trace buffer index + * @record Trace record to fill + * + * Returns false if the record is not valid. + */ +static bool get_trace_record(unsigned int idx, TraceRecord *record) { - stream_printf(stream, "Trace file \"%s\" %s.\n", - trace_file_name, trace_file_enabled ? "on" : "off"); -} + if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) { + return false; + } -static bool write_header(FILE *fp) -{ - static const TraceRecord header = { - .event = HEADER_EVENT_ID, - .timestamp_ns = HEADER_MAGIC, - .x1 = HEADER_VERSION, - }; + __sync_synchronize(); /* read memory barrier before accessing record */ - return fwrite(&header, sizeof header, 1, fp) == 1; + *record = trace_buf[idx]; + record->event &= ~TRACE_RECORD_VALID; + return true; } /** - * 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. + * Kick writeout thread + * + * @wait Whether to wait for writeout thread to complete */ -bool st_set_trace_file(const char *file) +static void flush_trace_file(bool wait) { - st_set_trace_file_enabled(false); - - free(trace_file_name); + pthread_mutex_lock(&trace_lock); + trace_available = true; + pthread_cond_signal(&trace_available_cond); - 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; - } + if (wait) { + pthread_cond_wait(&trace_empty_cond, &trace_lock); } - st_set_trace_file_enabled(true); - return true; + pthread_mutex_unlock(&trace_lock); } -static void flush_trace_file(void) +static void wait_for_trace_records_available(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); + pthread_mutex_lock(&trace_lock); + while (!(trace_available && trace_writeout_enabled)) { + pthread_cond_signal(&trace_empty_cond); + pthread_cond_wait(&trace_available_cond, &trace_lock); } + trace_available = false; + pthread_mutex_unlock(&trace_lock); } -void st_flush_trace_buffer(void) +static void *writeout_thread(void *opaque) { - if (trace_file_enabled) { - flush_trace_file(); - } + TraceRecord record; + unsigned int writeout_idx = 0; + unsigned int num_available, idx; + size_t unused __attribute__ ((unused)); - /* Discard written trace records */ - trace_idx = 0; -} + for (;;) { + wait_for_trace_records_available(); -void st_set_trace_file_enabled(bool enable) -{ - if (enable == trace_file_enabled) { - return; /* no change */ - } + num_available = trace_idx - writeout_idx; + if (num_available > TRACE_BUF_LEN) { + record = (TraceRecord){ + .event = DROPPED_EVENT_ID, + .x1 = num_available, + }; + unused = fwrite(&record, sizeof(record), 1, trace_fp); + writeout_idx += num_available; + } - /* Flush/discard trace buffer */ - st_flush_trace_buffer(); + idx = writeout_idx % TRACE_BUF_LEN; + while (get_trace_record(idx, &record)) { + trace_buf[idx].event = 0; /* clear valid bit */ + unused = fwrite(&record, sizeof(record), 1, trace_fp); + idx = ++writeout_idx % TRACE_BUF_LEN; + } - /* To disable, close trace file */ - if (!enable) { - fclose(trace_fp); - trace_fp = NULL; + fflush(trace_fp); } - - trace_file_enabled = enable; + return NULL; } 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]; + unsigned int idx; + uint64_t timestamp; 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; + timestamp = get_clock(); + + idx = __sync_fetch_and_add(&trace_idx, 1) % TRACE_BUF_LEN; + trace_buf[idx] = (TraceRecord){ + .event = event, + .timestamp_ns = timestamp, + .x1 = x1, + .x2 = x2, + .x3 = x3, + .x4 = x4, + .x5 = x5, + .x6 = x6, + }; + __sync_synchronize(); /* write barrier before marking as valid */ + trace_buf[idx].event |= TRACE_RECORD_VALID; - if (++trace_idx == TRACE_BUF_LEN) { - st_flush_trace_buffer(); + if ((idx + 1) % TRACE_BUF_FLUSH_THRESHOLD == 0) { + flush_trace_file(false); } } @@ -195,24 +212,93 @@ trace(event, x1, x2, x3, x4, x5, x6); } +void st_set_trace_file_enabled(bool enable) +{ + if (enable == !!trace_fp) { + return; /* no change */ + } + + /* Halt trace writeout */ + flush_trace_file(true); + trace_writeout_enabled = false; + flush_trace_file(true); + + if (enable) { + static const TraceRecord header = { + .event = HEADER_EVENT_ID, + .timestamp_ns = HEADER_MAGIC, + .x1 = HEADER_VERSION, + }; + + trace_fp = fopen(trace_file_name, "w"); + if (!trace_fp) { + return; + } + + if (fwrite(&header, sizeof header, 1, trace_fp) != 1) { + fclose(trace_fp); + trace_fp = NULL; + return; + } + + /* Resume trace writeout */ + trace_writeout_enabled = true; + flush_trace_file(false); + } else { + fclose(trace_fp); + trace_fp = NULL; + } +} + /** - * Flush the trace buffer on exit + * Set the name of a trace file + * + * @file The trace file name or NULL for the default name- set at + * config time */ -static void __attribute__((constructor)) st_init(void) +bool st_set_trace_file(const char *file) { - atexit(st_flush_trace_buffer); + 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; +} + +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_fp ? "on" : "off"); } void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) { unsigned int i; - for (i = 0; i < trace_idx; i++) { + for (i = 0; i < TRACE_BUF_LEN; i++) { + TraceRecord record; + + if (!get_trace_record(i, &record)) { + continue; + } 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); + record.event, record.x1, record.x2, + record.x3, record.x4, record.x5, + record.x6); } } @@ -226,30 +312,44 @@ } } -static TraceEvent* find_trace_event_by_name(const char *tname) +bool st_change_trace_event_state(const char *name, bool enabled) { 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]; + if (!strcmp(trace_list[i].tp_name, name)) { + trace_list[i].state = enabled; + return true; } } - return NULL; /* indicates end of list reached without a match */ + return false; +} + +void st_flush_trace_buffer(void) +{ + flush_trace_file(true); } -bool st_change_trace_event_state(const char *tname, bool tstate) +bool st_init(const char *file) { - TraceEvent *tp; + pthread_t thread; + pthread_attr_t attr; + sigset_t set, oldset; + int ret; - tp = find_trace_event_by_name(tname); - if (tp) { - tp->state = tstate; - return true; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + sigfillset(&set); + pthread_sigmask(SIG_SETMASK, &set, &oldset); + ret = pthread_create(&thread, &attr, writeout_thread, NULL); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + + if (ret != 0) { + return false; } - return false; + + atexit(st_flush_trace_buffer); + st_set_trace_file(file); + return true; } diff -Nru qemu-kvm-0.14.1+noroms/simpletrace.h qemu-kvm-0.15.0+noroms/simpletrace.h --- qemu-kvm-0.14.1+noroms/simpletrace.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/simpletrace.h 2011-08-09 07:40:29.000000000 -0500 @@ -15,6 +15,7 @@ #include #include +#ifdef CONFIG_SIMPLE_TRACE typedef uint64_t TraceEventID; typedef struct { @@ -36,5 +37,12 @@ void st_set_trace_file_enabled(bool enable); bool st_set_trace_file(const char *file); void st_flush_trace_buffer(void); +bool st_init(const char *file); +#else +static inline bool st_init(const char *file) +{ + return true; +} +#endif /* !CONFIG_SIMPLE_TRACE */ #endif /* SIMPLETRACE_H */ diff -Nru qemu-kvm-0.14.1+noroms/slirp/bootp.c qemu-kvm-0.15.0+noroms/slirp/bootp.c --- qemu-kvm-0.14.1+noroms/slirp/bootp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/bootp.c 2011-08-09 07:40:29.000000000 -0500 @@ -284,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; diff -Nru qemu-kvm-0.14.1+noroms/slirp/ip_icmp.c qemu-kvm-0.15.0+noroms/slirp/ip_icmp.c --- qemu-kvm-0.14.1+noroms/slirp/ip_icmp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/ip_icmp.c 2011-08-09 07:40:29.000000000 -0500 @@ -60,6 +60,52 @@ /* ADDR MASK REPLY (18) */ 0 }; +void icmp_init(Slirp *slirp) +{ + slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp; + slirp->icmp_last_so = &slirp->icmp; +} + +static int icmp_send(struct socket *so, struct mbuf *m, int hlen) +{ + struct ip *ip = mtod(m, struct ip *); + struct sockaddr_in addr; + + so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (so->s == -1) { + return -1; + } + + so->so_m = m; + so->so_faddr = ip->ip_dst; + so->so_laddr = ip->ip_src; + so->so_iptos = ip->ip_tos; + so->so_type = IPPROTO_ICMP; + so->so_state = SS_ISFCONNECTED; + so->so_expire = curtime + SO_EXPIRE; + + addr.sin_family = AF_INET; + addr.sin_addr = so->so_faddr; + + insque(so, &so->slirp->icmp); + + if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, + (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n", + errno, strerror(errno))); + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); + icmp_detach(so); + } + + return 0; +} + +void icmp_detach(struct socket *so) +{ + closesocket(so->s); + sofree(so); +} + /* * Process a received ICMP message. */ @@ -81,7 +127,7 @@ */ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ freeit: - m_freem(m); + m_free(m); goto end_error; } @@ -97,14 +143,18 @@ DEBUG_ARG("icmp_type = %d", icp->icmp_type); switch (icp->icmp_type) { case ICMP_ECHO: - icp->icmp_type = ICMP_ECHOREPLY; ip->ip_len += hlen; /* since ip_input subtracts this */ if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { icmp_reflect(m); + } else if (slirp->restricted) { + goto freeit; } else { struct socket *so; struct sockaddr_in addr; if ((so = socreate(slirp)) == NULL) goto freeit; + if (icmp_send(so, m, hlen) == 0) { + return; + } if(udp_attach(so) == -1) { DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", errno,strerror(errno))); @@ -153,11 +203,11 @@ case ICMP_TSTAMP: case ICMP_MASKREQ: case ICMP_REDIRECT: - m_freem(m); + m_free(m); break; default: - m_freem(m); + m_free(m); } /* swith */ end_error: @@ -319,6 +369,7 @@ m->m_len -= hlen; icp = mtod(m, struct icmp *); + icp->icmp_type = ICMP_ECHOREPLY; icp->icmp_cksum = 0; icp->icmp_cksum = cksum(m, ip->ip_len - hlen); @@ -349,3 +400,39 @@ (void ) ip_output((struct socket *)NULL, m); } + +void icmp_receive(struct socket *so) +{ + struct mbuf *m = so->so_m; + struct ip *ip = mtod(m, struct ip *); + int hlen = ip->ip_hl << 2; + u_char error_code; + struct icmp *icp; + int id, len; + + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp *); + + id = icp->icmp_id; + len = qemu_recv(so->s, icp, m->m_len, 0); + icp->icmp_id = id; + + m->m_data -= hlen; + m->m_len += hlen; + + if (len == -1 || len == 0) { + if (errno == ENETUNREACH) { + error_code = ICMP_UNREACH_NET; + } else { + error_code = ICMP_UNREACH_HOST; + } + DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno, + strerror(errno))); + icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); + } else { + icmp_reflect(so->so_m); + so->so_m = NULL; /* Don't m_free() it again! */ + } + icmp_detach(so); +} diff -Nru qemu-kvm-0.14.1+noroms/slirp/ip_icmp.h qemu-kvm-0.15.0+noroms/slirp/ip_icmp.h --- qemu-kvm-0.14.1+noroms/slirp/ip_icmp.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/ip_icmp.h 2011-08-09 07:40:29.000000000 -0500 @@ -153,9 +153,12 @@ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) +void icmp_init(Slirp *slirp); void icmp_input(struct mbuf *, int); void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message); void icmp_reflect(struct mbuf *); +void icmp_receive(struct socket *so); +void icmp_detach(struct socket *so); #endif diff -Nru qemu-kvm-0.14.1+noroms/slirp/ip_input.c qemu-kvm-0.15.0+noroms/slirp/ip_input.c --- qemu-kvm-0.14.1+noroms/slirp/ip_input.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/ip_input.c 2011-08-09 07:40:29.000000000 -0500 @@ -58,6 +58,7 @@ slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link; udp_init(slirp); tcp_init(slirp); + icmp_init(slirp); } /* @@ -118,27 +119,6 @@ goto bad; } - if (slirp->restricted) { - if ((ip->ip_dst.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP) - goto bad; - } else { - uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; - struct ex_list *ex_ptr; - - if ((ip->ip_dst.s_addr & inv_mask) == inv_mask) { - goto bad; - } - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) - if (ex_ptr->ex_addr.s_addr == ip->ip_dst.s_addr) - break; - - if (!ex_ptr) - goto bad; - } - } - /* Should drop packet if mbuf too long? hmmm... */ if (m->m_len > ip->ip_len) m_adj(m, ip->ip_len - m->m_len); @@ -225,7 +205,7 @@ } return; bad: - m_freem(m); + m_free(m); return; } @@ -318,7 +298,7 @@ break; } q = q->ipf_next; - m_freem(dtom(slirp, q->ipf_prev)); + m_free(dtom(slirp, q->ipf_prev)); ip_deq(q->ipf_prev); } @@ -384,7 +364,7 @@ return ip; dropfrag: - m_freem(m); + m_free(m); return NULL; } @@ -400,7 +380,7 @@ for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) { p = q->ipf_next; ip_deq(q); - m_freem(dtom(slirp, q)); + m_free(dtom(slirp, q)); } remque(&fp->ip_link); (void) m_free(dtom(slirp, fp)); diff -Nru qemu-kvm-0.14.1+noroms/slirp/ip_output.c qemu-kvm-0.15.0+noroms/slirp/ip_output.c --- qemu-kvm-0.14.1+noroms/slirp/ip_output.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/ip_output.c 2011-08-09 07:40:29.000000000 -0500 @@ -159,7 +159,7 @@ if (error == 0) if_output(so, m); else - m_freem(m); + m_free(m); } } @@ -167,6 +167,6 @@ return (error); bad: - m_freem(m0); + m_free(m0); goto done; } diff -Nru qemu-kvm-0.14.1+noroms/slirp/mbuf.h qemu-kvm-0.15.0+noroms/slirp/mbuf.h --- qemu-kvm-0.14.1+noroms/slirp/mbuf.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/mbuf.h 2011-08-09 07:40:29.000000000 -0500 @@ -33,9 +33,6 @@ #ifndef _MBUF_H_ #define _MBUF_H_ -#define m_freem m_free - - #define MINCSIZE 4096 /* Amount to increase mbuf if too small */ /* diff -Nru qemu-kvm-0.14.1+noroms/slirp/misc.c qemu-kvm-0.15.0+noroms/slirp/misc.c --- qemu-kvm-0.14.1+noroms/slirp/misc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/misc.c 2011-08-09 07:40:29.000000000 -0500 @@ -119,6 +119,7 @@ char *bptr; const char *curarg; int c, i, ret; + pid_t pid; DEBUG_CALL("fork_exec"); DEBUG_ARG("so = %lx", (long)so); @@ -142,7 +143,8 @@ } } - switch(fork()) { + pid = fork(); + switch(pid) { case -1: lprint("Error: fork failed: %s\n", strerror(errno)); close(s); @@ -151,11 +153,12 @@ return 0; case 0: + setsid(); + /* Set the DISPLAY */ if (do_pty == 2) { (void) close(master); #ifdef TIOCSCTTY /* XXXXX */ - (void) setsid(); ioctl(s, TIOCSCTTY, (char *)NULL); #endif } else { @@ -206,6 +209,7 @@ exit(1); default: + qemu_add_child_watch(pid); if (do_pty == 2) { close(s); so->s = master; @@ -403,4 +407,17 @@ inet_ntoa(dst_addr), ntohs(dst_port), so->so_rcv.sb_cc, so->so_snd.sb_cc); } + + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { + n = snprintf(buf, sizeof(buf), " ICMP[%d sec]", + (so->so_expire - curtime) / 1000); + src.sin_addr = so->so_laddr; + dst_addr = so->so_faddr; + memset(&buf[n], ' ', 19 - n); + buf[19] = 0; + monitor_printf(mon, "%s %3d %15s - ", buf, so->s, + src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*"); + monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } } diff -Nru qemu-kvm-0.14.1+noroms/slirp/slirp.c qemu-kvm-0.15.0+noroms/slirp/slirp.c --- qemu-kvm-0.14.1+noroms/slirp/slirp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/slirp.c 2011-08-09 07:40:29.000000000 -0500 @@ -373,6 +373,31 @@ UPD_NFDS(so->s); } } + + /* + * ICMP sockets + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + so_next = so->so_next; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + icmp_detach(so); + continue; + } else { + do_slowtimo = 1; /* Let socket expire */ + } + } + + if (so->so_state & SS_ISFCONNECTED) { + FD_SET(so->s, readfds); + UPD_NFDS(so->s); + } + } } *pnfds = nfds; @@ -393,7 +418,7 @@ global_writefds = writefds; global_xfds = xfds; - curtime = qemu_get_clock(rt_clock); + curtime = qemu_get_clock_ms(rt_clock); QTAILQ_FOREACH(slirp, &slirp_instances, entry) { /* @@ -497,7 +522,7 @@ */ #ifdef PROBE_CONN if (so->so_state & SS_ISFCONNECTING) { - ret = recv(so->s, (char *)&ret, 0,0); + ret = qemu_recv(so->s, &ret, 0,0); if (ret < 0) { /* XXX */ @@ -542,6 +567,18 @@ sorecvfrom(so); } } + + /* + * Check incoming ICMP relies. + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + so_next = so->so_next; + + if (so->s != -1 && FD_ISSET(so->s, readfds)) { + icmp_receive(so); + } + } } /* diff -Nru qemu-kvm-0.14.1+noroms/slirp/slirp.h qemu-kvm-0.15.0+noroms/slirp/slirp.h --- qemu-kvm-0.14.1+noroms/slirp/slirp.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/slirp.h 2011-08-09 07:40:29.000000000 -0500 @@ -152,6 +152,7 @@ #include "tcp_var.h" #include "tcpip.h" #include "udp.h" +#include "ip_icmp.h" #include "mbuf.h" #include "sbuf.h" #include "socket.h" @@ -218,6 +219,10 @@ struct socket udb; struct socket *udp_last_so; + /* icmp states */ + struct socket icmp; + struct socket *icmp_last_so; + /* tftp states */ char *tftp_prefix; struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; diff -Nru qemu-kvm-0.14.1+noroms/slirp/socket.c qemu-kvm-0.15.0+noroms/slirp/socket.c --- qemu-kvm-0.14.1+noroms/slirp/socket.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/socket.c 2011-08-09 07:40:29.000000000 -0500 @@ -71,6 +71,8 @@ slirp->tcp_last_so = &slirp->tcb; } else if (so == slirp->udp_last_so) { slirp->udp_last_so = &slirp->udb; + } else if (so == slirp->icmp_last_so) { + slirp->icmp_last_so = &slirp->icmp; } m_free(so->so_m); @@ -164,7 +166,7 @@ nn = readv(so->s, (struct iovec *)iov, n); DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); #else - nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0); + nn = qemu_recv(so->s, iov[0].iov_base, iov[0].iov_len,0); #endif if (nn <= 0) { if (nn < 0 && (errno == EINTR || errno == EAGAIN)) @@ -189,7 +191,7 @@ */ if (n == 2 && nn == iov[0].iov_len) { int ret; - ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0); + ret = qemu_recv(so->s, iov[1].iov_base, iov[1].iov_len,0); if (ret > 0) nn += ret; } diff -Nru qemu-kvm-0.14.1+noroms/slirp/tcp_input.c qemu-kvm-0.15.0+noroms/slirp/tcp_input.c --- qemu-kvm-0.14.1+noroms/slirp/tcp_input.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/tcp_input.c 2011-08-09 07:40:29.000000000 -0500 @@ -136,7 +136,7 @@ i = q->ti_seq + q->ti_len - ti->ti_seq; if (i > 0) { if (i >= ti->ti_len) { - m_freem(m); + m_free(m); /* * Try to present any queued data * at the left window edge to the user. @@ -170,7 +170,7 @@ q = tcpiphdr_next(q); m = tcpiphdr_prev(q)->ti_mbuf; remque(tcpiphdr2qlink(tcpiphdr_prev(q))); - m_freem(m); + m_free(m); } /* @@ -197,7 +197,7 @@ m = ti->ti_mbuf; ti = tcpiphdr_next(ti); if (so->so_state & SS_FCANTSENDMORE) - m_freem(m); + m_free(m); else { if (so->so_emu) { if (tcp_emu(so,m)) sbappend(so, m); @@ -451,7 +451,7 @@ acked = ti->ti_ack - tp->snd_una; sbdrop(&so->so_snd, acked); tp->snd_una = ti->ti_ack; - m_freem(m); + m_free(m); /* * If all outstanding data are acked, stop @@ -1260,7 +1260,7 @@ */ if (tiflags & TH_RST) goto drop; - m_freem(m); + m_free(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); return; diff -Nru qemu-kvm-0.14.1+noroms/slirp/tcp_subr.c qemu-kvm-0.15.0+noroms/slirp/tcp_subr.c --- qemu-kvm-0.14.1+noroms/slirp/tcp_subr.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/tcp_subr.c 2011-08-09 07:40:29.000000000 -0500 @@ -250,7 +250,7 @@ t = tcpiphdr_next(t); m = tcpiphdr_prev(t)->ti_mbuf; remque(tcpiphdr2qlink(tcpiphdr_prev(t))); - m_freem(m); + m_free(m); } free(tp); so->so_tcpcb = NULL; diff -Nru qemu-kvm-0.14.1+noroms/slirp/tftp.c qemu-kvm-0.15.0+noroms/slirp/tftp.c --- qemu-kvm-0.14.1+noroms/slirp/tftp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/tftp.c 2011-08-09 07:40:29.000000000 -0500 @@ -136,9 +136,9 @@ m->m_data += sizeof(struct udpiphdr); tp->tp_op = htons(TFTP_OACK); - n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", + n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", key) + 1; - n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", + n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", value) + 1; saddr.sin_addr = recv_tp->ip.ip_dst; @@ -283,7 +283,7 @@ /* skip header fields */ k = 0; - pktlen -= ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp); + pktlen -= offsetof(struct tftp_t, x.tp_buf); /* prepend tftp_prefix */ prefix_len = strlen(slirp->tftp_prefix); @@ -299,7 +299,7 @@ tftp_send_error(spt, 2, "Access violation", tp); return; } - req_fname[k] = (char)tp->x.tp_buf[k]; + req_fname[k] = tp->x.tp_buf[k]; if (req_fname[k++] == '\0') { break; } @@ -311,7 +311,7 @@ return; } - if (strcasecmp((const char *)&tp->x.tp_buf[k], "octet") != 0) { + if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) { tftp_send_error(spt, 4, "Unsupported transfer mode", tp); return; } @@ -340,7 +340,7 @@ while (k < pktlen) { const char *key, *value; - key = (const char *)&tp->x.tp_buf[k]; + key = &tp->x.tp_buf[k]; k += strlen(key) + 1; if (k >= pktlen) { @@ -348,7 +348,7 @@ return; } - value = (const char *)&tp->x.tp_buf[k]; + value = &tp->x.tp_buf[k]; k += strlen(value) + 1; if (strcasecmp(key, "tsize") == 0) { diff -Nru qemu-kvm-0.14.1+noroms/slirp/tftp.h qemu-kvm-0.15.0+noroms/slirp/tftp.h --- qemu-kvm-0.14.1+noroms/slirp/tftp.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/tftp.h 2011-08-09 07:40:29.000000000 -0500 @@ -26,7 +26,7 @@ uint16_t tp_error_code; uint8_t tp_msg[512]; } tp_error; - uint8_t tp_buf[512 + 2]; + char tp_buf[512 + 2]; } x; }; diff -Nru qemu-kvm-0.14.1+noroms/slirp/udp.c qemu-kvm-0.15.0+noroms/slirp/udp.c --- qemu-kvm-0.14.1+noroms/slirp/udp.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/slirp/udp.c 2011-08-09 07:40:29.000000000 -0500 @@ -120,23 +120,26 @@ /* * handle DHCP/BOOTP */ - if (ntohs(uh->uh_dport) == BOOTP_SERVER) { - bootp_input(m); - goto bad; - } - - if (slirp->restricted) { - goto bad; - } + if (ntohs(uh->uh_dport) == BOOTP_SERVER && + (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || + ip->ip_dst.s_addr == 0xffffffff)) { + bootp_input(m); + goto bad; + } /* * handle TFTP */ - if (ntohs(uh->uh_dport) == TFTP_SERVER) { + if (ntohs(uh->uh_dport) == TFTP_SERVER && + ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { tftp_input(m); goto bad; } + if (slirp->restricted) { + goto bad; + } + /* * Locate pcb for datagram. */ @@ -219,7 +222,7 @@ return; bad: - m_freem(m); + m_free(m); return; } diff -Nru qemu-kvm-0.14.1+noroms/softmmu-semi.h qemu-kvm-0.15.0+noroms/softmmu-semi.h --- qemu-kvm-0.14.1+noroms/softmmu-semi.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/softmmu-semi.h 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ static inline uint32_t softmmu_tget32(CPUState *env, uint32_t addr) diff -Nru qemu-kvm-0.14.1+noroms/spice-qemu-char.c qemu-kvm-0.15.0+noroms/spice-qemu-char.c --- qemu-kvm-0.14.1+noroms/spice-qemu-char.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/spice-qemu-char.c 2011-08-09 07:40:29.000000000 -0500 @@ -36,14 +36,13 @@ 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 { + if (qemu_chr_can_read(scd->chr) < last_out) { break; } + qemu_chr_read(scd->chr, p, last_out); + out += last_out; + len -= last_out; + p += last_out; } dprintf(scd, 3, "%s: %lu/%zd\n", __func__, out, len + out); @@ -131,6 +130,18 @@ qemu_free(s); } +static void spice_chr_guest_open(struct CharDriverState *chr) +{ + SpiceCharDriver *s = chr->opaque; + vmc_register_interface(s); +} + +static void spice_chr_guest_close(struct CharDriverState *chr) +{ + SpiceCharDriver *s = chr->opaque; + vmc_unregister_interface(s); +} + static void print_allowed_subtypes(void) { const char** psubtype; @@ -148,7 +159,7 @@ fprintf(stderr, "\n"); } -CharDriverState *qemu_chr_open_spice(QemuOpts *opts) +int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; SpiceCharDriver *s; @@ -160,7 +171,7 @@ if (name == NULL) { fprintf(stderr, "spice-qemu-char: missing name parameter\n"); print_allowed_subtypes(); - return NULL; + return -EINVAL; } for(;*psubtype != NULL; ++psubtype) { if (strcmp(name, *psubtype) == 0) { @@ -171,7 +182,7 @@ if (subtype == NULL) { fprintf(stderr, "spice-qemu-char: unsupported name\n"); print_allowed_subtypes(); - return NULL; + return -EINVAL; } chr = qemu_mallocz(sizeof(CharDriverState)); @@ -183,8 +194,11 @@ chr->opaque = s; chr->chr_write = spice_chr_write; chr->chr_close = spice_chr_close; + chr->chr_guest_open = spice_chr_guest_open; + chr->chr_guest_close = spice_chr_guest_close; qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; } diff -Nru qemu-kvm-0.14.1+noroms/sysemu.h qemu-kvm-0.15.0+noroms/sysemu.h --- qemu-kvm-0.14.1+noroms/sysemu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/sysemu.h 2011-08-09 07:40:29.000000000 -0500 @@ -8,22 +8,9 @@ #include "qemu-timer.h" #include "notify.h" -#ifdef _WIN32 -#include -#include "qemu-os-win32.h" -#endif - -#ifdef CONFIG_POSIX -#include "qemu-os-posix.h" -#endif - /* vl.c */ extern const char *bios_name; -#define QEMU_FILE_TYPE_BIOS 0 -#define QEMU_FILE_TYPE_KEYMAP 1 -char *qemu_find_file(int type, const char *name); - extern int vm_running; extern const char *qemu_name; extern uint8_t qemu_uuid[]; @@ -37,26 +24,36 @@ void *opaque); void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); +#define VMSTOP_USER 0 +#define VMSTOP_DEBUG 1 +#define VMSTOP_SHUTDOWN 2 +#define VMSTOP_DISKFULL 3 +#define VMSTOP_WATCHDOG 4 +#define VMSTOP_PANIC 5 +#define VMSTOP_SAVEVM 6 +#define VMSTOP_LOADVM 7 +#define VMSTOP_MIGRATE 8 + +#define VMRESET_SILENT false +#define VMRESET_REPORT true + void vm_start(void); void vm_stop(int reason); -uint64_t ram_bytes_remaining(void); -uint64_t ram_bytes_transferred(void); -uint64_t ram_bytes_total(void); - -int64_t cpu_get_ticks(void); -void cpu_enable_ticks(void); -void cpu_disable_ticks(void); - void qemu_system_reset_request(void); void qemu_system_shutdown_request(void); void qemu_system_powerdown_request(void); -int qemu_no_shutdown(void); +void qemu_system_debug_request(void); +void qemu_system_vmstop_request(int reason); +int qemu_shutdown_requested_get(void); +int qemu_reset_requested_get(void); int qemu_shutdown_requested(void); int qemu_reset_requested(void); int qemu_powerdown_requested(void); +void qemu_system_killed(int signal, pid_t pid); +void qemu_kill_report(void); extern qemu_irq qemu_system_powerdown; -void qemu_system_reset(void); +void qemu_system_reset(bool report); void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); @@ -68,10 +65,6 @@ 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 nonblocking); @@ -87,22 +80,16 @@ /* 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_NOGRAPHIC, + DT_NONE, } DisplayType; extern int autostart; -extern int incoming_expected; extern int bios_size; typedef enum { @@ -119,7 +106,6 @@ extern int graphic_width; extern int graphic_height; extern int graphic_depth; -extern uint8_t irq0override; extern DisplayType display_type; extern const char *keyboard_layout; extern int win2k_install_hack; @@ -181,8 +167,6 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; -#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) - void do_usb_add(Monitor *mon, const QDict *qdict); void do_usb_del(Monitor *mon, const QDict *qdict); void usb_info(Monitor *mon); diff -Nru qemu-kvm-0.14.1+noroms/target-alpha/cpu.h qemu-kvm-0.15.0+noroms/target-alpha/cpu.h --- qemu-kvm-0.14.1+noroms/target-alpha/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-alpha/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -28,8 +28,6 @@ #include "cpu-defs.h" -#include - #include "softfloat.h" #define TARGET_HAS_ICE 1 @@ -192,171 +190,39 @@ #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 */ -#define IPR_CC_CTL_ENA_SHIFT 32 -#define IPR_CC_CTL_COUNTER_MASK 0xfffffff0UL - IPR_VA = 0xC2, /* 21264 */ - IPR_VA_CTL = 0xC4, /* 21264 */ -#define IPR_VA_CTL_VA_48_SHIFT 1 -#define IPR_VA_CTL_VPTB_SHIFT 30 - IPR_VA_FORM = 0xC3, /* 21264 */ - /* Ibox IPRs */ - IPR_ITB_TAG = 0x00, /* 21264 */ - IPR_ITB_PTE = 0x01, /* 21264 */ - IPR_ITB_IAP = 0x02, - IPR_ITB_IA = 0x03, /* 21264 */ - IPR_ITB_IS = 0x04, /* 21264 */ - IPR_PMPC = 0x05, - IPR_EXC_ADDR = 0x06, /* 21264 */ - IPR_IVA_FORM = 0x07, /* 21264 */ - IPR_CM = 0x09, /* 21264 */ -#define IPR_CM_SHIFT 3 -#define IPR_CM_MASK (3ULL << IPR_CM_SHIFT) /* 21264 */ - IPR_IER = 0x0A, /* 21264 */ -#define IPR_IER_MASK 0x0000007fffffe000ULL - IPR_IER_CM = 0x0B, /* 21264: = CM | IER */ - IPR_SIRR = 0x0C, /* 21264 */ -#define IPR_SIRR_SHIFT 14 -#define IPR_SIRR_MASK 0x7fff - IPR_ISUM = 0x0D, /* 21264 */ - IPR_HW_INT_CLR = 0x0E, /* 21264 */ - IPR_EXC_SUM = 0x0F, - IPR_PAL_BASE = 0x10, - IPR_I_CTL = 0x11, -#define IPR_I_CTL_CHIP_ID_SHIFT 24 /* 21264 */ -#define IPR_I_CTL_BIST_FAIL (1 << 23) /* 21264 */ -#define IPR_I_CTL_IC_EN_SHIFT 2 /* 21264 */ -#define IPR_I_CTL_SDE1_SHIFT 7 /* 21264 */ -#define IPR_I_CTL_HWE_SHIFT 12 /* 21264 */ -#define IPR_I_CTL_VA_48_SHIFT 15 /* 21264 */ -#define IPR_I_CTL_SPE_SHIFT 3 /* 21264 */ -#define IPR_I_CTL_CALL_PAL_R23_SHIFT 20 /* 21264 */ - IPR_I_STAT = 0x16, /* 21264 */ - IPR_IC_FLUSH = 0x13, /* 21264 */ - IPR_IC_FLUSH_ASM = 0x12, /* 21264 */ - IPR_CLR_MAP = 0x15, - IPR_SLEEP = 0x17, - IPR_PCTX = 0x40, - IPR_PCTX_ASN = 0x01, /* field */ -#define IPR_PCTX_ASN_SHIFT 39 - IPR_PCTX_ASTER = 0x02, /* field */ -#define IPR_PCTX_ASTER_SHIFT 5 - IPR_PCTX_ASTRR = 0x04, /* field */ -#define IPR_PCTX_ASTRR_SHIFT 9 - IPR_PCTX_PPCE = 0x08, /* field */ -#define IPR_PCTX_PPCE_SHIFT 1 - IPR_PCTX_FPE = 0x10, /* field */ -#define IPR_PCTX_FPE_SHIFT 2 - IPR_PCTX_ALL = 0x5f, /* all fields */ - IPR_PCTR_CTL = 0x14, /* 21264 */ - /* Mbox IPRs */ - IPR_DTB_TAG0 = 0x20, /* 21264 */ - IPR_DTB_TAG1 = 0xA0, /* 21264 */ - IPR_DTB_PTE0 = 0x21, /* 21264 */ - IPR_DTB_PTE1 = 0xA1, /* 21264 */ - IPR_DTB_ALTMODE = 0xA6, - IPR_DTB_ALTMODE0 = 0x26, /* 21264 */ -#define IPR_DTB_ALTMODE_MASK 3 - IPR_DTB_IAP = 0xA2, - IPR_DTB_IA = 0xA3, /* 21264 */ - IPR_DTB_IS0 = 0x24, - IPR_DTB_IS1 = 0xA4, - IPR_DTB_ASN0 = 0x25, /* 21264 */ - IPR_DTB_ASN1 = 0xA5, /* 21264 */ -#define IPR_DTB_ASN_SHIFT 56 - IPR_MM_STAT = 0x27, /* 21264 */ - IPR_M_CTL = 0x28, /* 21264 */ -#define IPR_M_CTL_SPE_SHIFT 1 -#define IPR_M_CTL_SPE_MASK 7 - IPR_DC_CTL = 0x29, /* 21264 */ - IPR_DC_STAT = 0x2A, /* 21264 */ - /* Cbox IPRs */ - IPR_C_DATA = 0x2B, - IPR_C_SHIFT = 0x2C, - - IPR_ASN, - IPR_ASTEN, - IPR_ASTSR, - IPR_DATFX, - IPR_ESP, - IPR_FEN, - IPR_IPIR, - IPR_IPL, - IPR_KSP, - IPR_MCES, - IPR_PERFMON, - IPR_PCBB, - IPR_PRBR, - IPR_PTBR, - IPR_SCBB, - IPR_SISR, - IPR_SSP, - IPR_SYSPTBR, - IPR_TBCHK, - IPR_TBIA, - IPR_TBIAP, - IPR_TBIS, - IPR_TBISD, - IPR_TBISI, - IPR_USP, - IPR_VIRBND, - IPR_VPTB, - IPR_WHAMI, - IPR_ALT_MODE, -#endif - IPR_LAST, -}; +/* MMU modes definitions */ -typedef struct CPUAlphaState CPUAlphaState; +/* Alpha has 5 MMU modes: PALcode, kernel, executive, supervisor, and user. + The Unix PALcode only exposes the kernel and user modes; presumably + executive and supervisor are used by VMS. -typedef struct pal_handler_t pal_handler_t; -struct pal_handler_t { - /* Reset */ - void (*reset)(CPUAlphaState *env); - /* Uncorrectable hardware error */ - void (*machine_check)(CPUAlphaState *env); - /* Arithmetic exception */ - void (*arithmetic)(CPUAlphaState *env); - /* Interrupt / correctable hardware error */ - void (*interrupt)(CPUAlphaState *env); - /* Data fault */ - void (*dfault)(CPUAlphaState *env); - /* DTB miss pal */ - void (*dtb_miss_pal)(CPUAlphaState *env); - /* DTB miss native */ - void (*dtb_miss_native)(CPUAlphaState *env); - /* Unaligned access */ - void (*unalign)(CPUAlphaState *env); - /* ITB miss */ - void (*itb_miss)(CPUAlphaState *env); - /* Instruction stream access violation */ - void (*itb_acv)(CPUAlphaState *env); - /* Reserved or privileged opcode */ - void (*opcdec)(CPUAlphaState *env); - /* Floating point exception */ - void (*fen)(CPUAlphaState *env); - /* Call pal instruction */ - void (*call_pal)(CPUAlphaState *env, uint32_t palcode); -}; + PALcode itself uses physical mode for code and kernel mode for data; + there are PALmode instructions that can access data via physical mode + or via an os-installed "alternate mode", which is one of the 4 above. -#define NB_MMU_MODES 4 + QEMU does not currently properly distinguish between code/data when + looking up addresses. To avoid having to address this issue, our + emulated PALcode will cheat and use the KSEG mapping for its code+data + rather than physical addresses. + + Moreover, we're only emulating Unix PALcode, and not attempting VMS. + + All of which allows us to drop all but kernel and user modes. + Elide the unused MMU modes to save space. */ + +#define NB_MMU_MODES 2 + +#define MMU_MODE0_SUFFIX _kernel +#define MMU_MODE1_SUFFIX _user +#define MMU_KERNEL_IDX 0 +#define MMU_USER_IDX 1 + +typedef struct CPUAlphaState CPUAlphaState; struct CPUAlphaState { uint64_t ir[31]; float64 fir[31]; uint64_t pc; - uint64_t ipr[IPR_LAST]; - uint64_t ps; uint64_t unique; uint64_t lock_addr; uint64_t lock_st_addr; @@ -371,10 +237,33 @@ uint8_t fpcr_dnod; uint8_t fpcr_undz; - /* Used for HW_LD / HW_ST */ - uint8_t saved_mode; - /* For RC and RS */ + /* The Internal Processor Registers. Some of these we assume always + exist for use in user-mode. */ + uint8_t ps; uint8_t intr_flag; + uint8_t pal_mode; + uint8_t fen; + + uint32_t pcc_ofs; + + /* These pass data from the exception logic in the translator and + helpers to the OS entry point. This is used for both system + emulation and user-mode. */ + uint64_t trap_arg0; + uint64_t trap_arg1; + uint64_t trap_arg2; + +#if !defined(CONFIG_USER_ONLY) + /* The internal data required by our emulation of the Unix PALcode. */ + uint64_t exc_addr; + uint64_t palbr; + uint64_t ptbr; + uint64_t vptptr; + uint64_t sysval; + uint64_t usp; + uint64_t shadow[8]; + uint64_t scratch[24]; +#endif #if TARGET_LONG_BITS > HOST_LONG_BITS /* temporary fixed-point registers @@ -386,14 +275,11 @@ /* Those resources are used only in Qemu core */ CPU_COMMON - uint32_t hflags; - int error_code; uint32_t features; uint32_t amask; int implver; - pal_handler_t *pal_handler; }; #define cpu_init cpu_alpha_init @@ -401,17 +287,6 @@ #define cpu_gen_code cpu_alpha_gen_code #define cpu_signal_handler cpu_alpha_signal_handler -/* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _executive -#define MMU_MODE2_SUFFIX _supervisor -#define MMU_MODE3_SUFFIX _user -#define MMU_USER_IDX 3 -static inline int cpu_mmu_index (CPUState *env) -{ - return (env->ps >> 3) & 3; -} - #include "cpu-all.h" enum { @@ -422,36 +297,89 @@ }; enum { - EXCP_RESET = 0x0000, - EXCP_MCHK = 0x0020, - EXCP_ARITH = 0x0060, - EXCP_HW_INTERRUPT = 0x00E0, - EXCP_DFAULT = 0x01E0, - EXCP_DTB_MISS_PAL = 0x09E0, - EXCP_ITB_MISS = 0x03E0, - EXCP_ITB_ACV = 0x07E0, - EXCP_DTB_MISS_NATIVE = 0x08E0, - EXCP_UNALIGN = 0x11E0, - EXCP_OPCDEC = 0x13E0, - EXCP_FEN = 0x17E0, - EXCP_CALL_PAL = 0x2000, - EXCP_CALL_PALP = 0x3000, - EXCP_CALL_PALE = 0x4000, - /* Pseudo exception for console */ - EXCP_CONSOLE_DISPATCH = 0x4001, - EXCP_CONSOLE_FIXUP = 0x4002, - EXCP_STL_C = 0x4003, - EXCP_STQ_C = 0x4004, -}; - -/* Arithmetic exception */ -#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 */ + EXCP_RESET, + EXCP_MCHK, + EXCP_SMP_INTERRUPT, + EXCP_CLK_INTERRUPT, + EXCP_DEV_INTERRUPT, + EXCP_MMFAULT, + EXCP_UNALIGN, + EXCP_OPCDEC, + EXCP_ARITH, + EXCP_FEN, + EXCP_CALL_PAL, + /* For Usermode emulation. */ + EXCP_STL_C, + EXCP_STQ_C, +}; + +/* Alpha-specific interrupt pending bits. */ +#define CPU_INTERRUPT_TIMER CPU_INTERRUPT_TGT_EXT_0 +#define CPU_INTERRUPT_SMP CPU_INTERRUPT_TGT_EXT_1 +#define CPU_INTERRUPT_MCHK CPU_INTERRUPT_TGT_EXT_2 + +/* OSF/1 Page table bits. */ +enum { + PTE_VALID = 0x0001, + PTE_FOR = 0x0002, /* used for page protection (fault on read) */ + PTE_FOW = 0x0004, /* used for page protection (fault on write) */ + PTE_FOE = 0x0008, /* used for page protection (fault on exec) */ + PTE_ASM = 0x0010, + PTE_KRE = 0x0100, + PTE_URE = 0x0200, + PTE_KWE = 0x1000, + PTE_UWE = 0x2000 +}; + +/* Hardware interrupt (entInt) constants. */ +enum { + INT_K_IP, + INT_K_CLK, + INT_K_MCHK, + INT_K_DEV, + INT_K_PERF, +}; + +/* Memory management (entMM) constants. */ +enum { + MM_K_TNV, + MM_K_ACV, + MM_K_FOR, + MM_K_FOE, + MM_K_FOW +}; + +/* Arithmetic exception (entArith) constants. */ +enum { + EXC_M_SWC = 1, /* Software completion */ + EXC_M_INV = 2, /* Invalid operation */ + EXC_M_DZE = 4, /* Division by zero */ + EXC_M_FOV = 8, /* Overflow */ + EXC_M_UNF = 16, /* Underflow */ + EXC_M_INE = 32, /* Inexact result */ + EXC_M_IOV = 64 /* Integer Overflow */ +}; + +/* Processor status constants. */ +enum { + /* Low 3 bits are interrupt mask level. */ + PS_INT_MASK = 7, + + /* Bits 4 and 5 are the mmu mode. The VMS PALcode uses all 4 modes; + The Unix PALcode only uses bit 4. */ + PS_USER_MODE = 8 +}; + +static inline int cpu_mmu_index(CPUState *env) +{ + if (env->pal_mode) { + return MMU_KERNEL_IDX; + } else if (env->ps & PS_USER_MODE) { + return MMU_USER_IDX; + } else { + return MMU_KERNEL_IDX; + } +} enum { IR_V0 = 0, @@ -504,19 +432,47 @@ 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); -#if !defined (CONFIG_USER_ONLY) -void pal_init (CPUState *env); -void call_pal (CPUState *env); +#ifndef CONFIG_USER_ONLY +void swap_shadow_regs(CPUState *env); +QEMU_NORETURN void cpu_unassigned_access(CPUState *env1, + target_phys_addr_t addr, int is_write, + int is_exec, int unused, int size); #endif +/* Bits in TB->FLAGS that control how translation is processed. */ +enum { + TB_FLAGS_PAL_MODE = 1, + TB_FLAGS_FEN = 2, + TB_FLAGS_USER_MODE = 8, + + TB_FLAGS_AMASK_SHIFT = 4, + TB_FLAGS_AMASK_BWX = AMASK_BWX << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_FIX = AMASK_FIX << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_CIX = AMASK_CIX << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_MVI = AMASK_MVI << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_TRAP = AMASK_TRAP << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_PREFETCH = AMASK_PREFETCH << TB_FLAGS_AMASK_SHIFT, +}; + static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, - target_ulong *cs_base, int *flags) + target_ulong *cs_base, int *pflags) { + int flags = 0; + *pc = env->pc; *cs_base = 0; - *flags = env->ps; + + if (env->pal_mode) { + flags = TB_FLAGS_PAL_MODE; + } else { + flags = env->ps & PS_USER_MODE; + } + if (env->fen) { + flags |= TB_FLAGS_FEN; + } + flags |= env->amask << TB_FLAGS_AMASK_SHIFT; + + *pflags = flags; } #if defined(CONFIG_USER_ONLY) @@ -535,4 +491,26 @@ } #endif +static inline bool cpu_has_work(CPUState *env) +{ + /* Here we are checking to see if the CPU should wake up from HALT. + We will have gotten into this state only for WTINT from PALmode. */ + /* ??? I'm not sure how the IPL state works with WTINT to keep a CPU + asleep even if (some) interrupts have been asserted. For now, + assume that if a CPU really wants to stay asleep, it will mask + interrupts at the chipset level, which will prevent these bits + from being set in the first place. */ + return env->interrupt_request & (CPU_INTERRUPT_HARD + | CPU_INTERRUPT_TIMER + | CPU_INTERRUPT_SMP + | CPU_INTERRUPT_MCHK); +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + #endif /* !defined (__CPU_ALPHA_H__) */ diff -Nru qemu-kvm-0.14.1+noroms/target-alpha/exec.h qemu-kvm-0.15.0+noroms/target-alpha/exec.h --- qemu-kvm-0.14.1+noroms/target-alpha/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-alpha/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -31,31 +31,9 @@ #define FP_STATUS (env->fp_status) #include "cpu.h" -#include "exec-all.h" #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -static inline int cpu_has_work(CPUState *env) -{ - return (env->interrupt_request & CPU_INTERRUPT_HARD); -} - -static inline int cpu_halted(CPUState *env) -{ - if (!env->halted) - return 0; - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - 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.14.1+noroms/target-alpha/helper.c qemu-kvm-0.15.0+noroms/target-alpha/helper.c --- qemu-kvm-0.14.1+noroms/target-alpha/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-alpha/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -22,7 +22,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "softfloat.h" uint64_t cpu_alpha_load_fpcr (CPUState *env) @@ -160,382 +159,299 @@ } #if defined(CONFIG_USER_ONLY) - int cpu_alpha_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu) { - if (rw == 2) - env->exception_index = EXCP_ITB_MISS; - else - env->exception_index = EXCP_DFAULT; - env->ipr[IPR_EXC_ADDR] = address; - + env->exception_index = EXCP_MMFAULT; + env->trap_arg0 = address; return 1; } - -void do_interrupt (CPUState *env) +#else +void swap_shadow_regs(CPUState *env) { - env->exception_index = -1; + uint64_t i0, i1, i2, i3, i4, i5, i6, i7; + + i0 = env->ir[8]; + i1 = env->ir[9]; + i2 = env->ir[10]; + i3 = env->ir[11]; + i4 = env->ir[12]; + i5 = env->ir[13]; + i6 = env->ir[14]; + i7 = env->ir[25]; + + env->ir[8] = env->shadow[0]; + env->ir[9] = env->shadow[1]; + env->ir[10] = env->shadow[2]; + env->ir[11] = env->shadow[3]; + env->ir[12] = env->shadow[4]; + env->ir[13] = env->shadow[5]; + env->ir[14] = env->shadow[6]; + env->ir[25] = env->shadow[7]; + + env->shadow[0] = i0; + env->shadow[1] = i1; + env->shadow[2] = i2; + env->shadow[3] = i3; + env->shadow[4] = i4; + env->shadow[5] = i5; + env->shadow[6] = i6; + env->shadow[7] = i7; } -#else +/* Returns the OSF/1 entMM failure indication, or -1 on success. */ +static int get_physical_address(CPUState *env, target_ulong addr, + int prot_need, int mmu_idx, + target_ulong *pphys, int *pprot) +{ + target_long saddr = addr; + target_ulong phys = 0; + target_ulong L1pte, L2pte, L3pte; + target_ulong pt, index; + int prot = 0; + int ret = MM_K_ACV; + + /* Ensure that the virtual address is properly sign-extended from + the last implemented virtual address bit. */ + if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) { + goto exit; + } + + /* Translate the superpage. */ + /* ??? When we do more than emulate Unix PALcode, we'll need to + determine which KSEG is actually active. */ + if (saddr < 0 && ((saddr >> 41) & 3) == 2) { + /* User-space cannot access KSEG addresses. */ + if (mmu_idx != MMU_KERNEL_IDX) { + goto exit; + } + + /* For the benefit of the Typhoon chipset, move bit 40 to bit 43. + We would not do this if the 48-bit KSEG is enabled. */ + phys = saddr & ((1ull << 40) - 1); + phys |= (saddr & (1ull << 40)) << 3; + + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + ret = -1; + goto exit; + } + + /* Interpret the page table exactly like PALcode does. */ + + pt = env->ptbr; + + /* L1 page table read. */ + index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff; + L1pte = ldq_phys(pt + index*8); + + if (unlikely((L1pte & PTE_VALID) == 0)) { + ret = MM_K_TNV; + goto exit; + } + if (unlikely((L1pte & PTE_KRE) == 0)) { + goto exit; + } + pt = L1pte >> 32 << TARGET_PAGE_BITS; + + /* L2 page table read. */ + index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff; + L2pte = ldq_phys(pt + index*8); + + if (unlikely((L2pte & PTE_VALID) == 0)) { + ret = MM_K_TNV; + goto exit; + } + if (unlikely((L2pte & PTE_KRE) == 0)) { + goto exit; + } + pt = L2pte >> 32 << TARGET_PAGE_BITS; + + /* L3 page table read. */ + index = (addr >> TARGET_PAGE_BITS) & 0x3ff; + L3pte = ldq_phys(pt + index*8); + + phys = L3pte >> 32 << TARGET_PAGE_BITS; + if (unlikely((L3pte & PTE_VALID) == 0)) { + ret = MM_K_TNV; + goto exit; + } + +#if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4 +# error page bits out of date +#endif -target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) + /* Check access violations. */ + if (L3pte & (PTE_KRE << mmu_idx)) { + prot |= PAGE_READ | PAGE_EXEC; + } + if (L3pte & (PTE_KWE << mmu_idx)) { + prot |= PAGE_WRITE; + } + if (unlikely((prot & prot_need) == 0 && prot_need)) { + goto exit; + } + + /* Check fault-on-operation violations. */ + prot &= ~(L3pte >> 1); + ret = -1; + if (unlikely((prot & prot_need) == 0)) { + ret = (prot_need & PAGE_EXEC ? MM_K_FOE : + prot_need & PAGE_WRITE ? MM_K_FOW : + prot_need & PAGE_READ ? MM_K_FOR : -1); + } + + exit: + *pphys = phys; + *pprot = prot; + return ret; +} + +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { - return -1; + target_ulong phys; + int prot, fail; + + fail = get_physical_address(env, addr, 0, 0, &phys, &prot); + return (fail >= 0 ? -1 : phys); } -int cpu_alpha_handle_mmu_fault (CPUState *env, target_ulong address, int rw, - int mmu_idx, int is_softmmu) +int cpu_alpha_handle_mmu_fault(CPUState *env, target_ulong addr, int rw, + int mmu_idx, int is_softmmu) { - uint32_t opc; + target_ulong phys; + int prot, fail; - if (rw == 2) { - /* Instruction translation buffer miss */ - env->exception_index = EXCP_ITB_MISS; - } else { - if (env->ipr[IPR_EXC_ADDR] & 1) - env->exception_index = EXCP_DTB_MISS_PAL; - else - env->exception_index = EXCP_DTB_MISS_NATIVE; - opc = (ldl_code(env->pc) >> 21) << 4; - if (rw) { - opc |= 0x9; - } else { - opc |= 0x4; - } - env->ipr[IPR_MM_STAT] = opc; + fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot); + if (unlikely(fail >= 0)) { + env->exception_index = EXCP_MMFAULT; + env->trap_arg0 = addr; + env->trap_arg1 = fail; + env->trap_arg2 = (rw == 2 ? -1 : rw); + return 1; } - return 1; + tlb_set_page(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); + return 0; } +#endif /* USER_ONLY */ -int cpu_alpha_mfpr (CPUState *env, int iprn, uint64_t *valp) +void do_interrupt (CPUState *env) { - uint64_t hwpcb; - int ret = 0; + int i = env->exception_index; - hwpcb = env->ipr[IPR_PCBB]; - switch (iprn) { - case IPR_ASN: - if (env->features & FEATURE_ASN) - *valp = env->ipr[IPR_ASN]; - else - *valp = 0; - break; - case IPR_ASTEN: - *valp = ((int64_t)(env->ipr[IPR_ASTEN] << 60)) >> 60; - break; - case IPR_ASTSR: - *valp = ((int64_t)(env->ipr[IPR_ASTSR] << 60)) >> 60; - break; - case IPR_DATFX: - /* Write only */ - ret = -1; - break; - case IPR_ESP: - if (env->features & FEATURE_SPS) - *valp = env->ipr[IPR_ESP]; - else - *valp = ldq_raw(hwpcb + 8); - break; - case IPR_FEN: - *valp = ((int64_t)(env->ipr[IPR_FEN] << 63)) >> 63; - break; - case IPR_IPIR: - /* Write-only */ - ret = -1; - break; - case IPR_IPL: - *valp = ((int64_t)(env->ipr[IPR_IPL] << 59)) >> 59; - break; - case IPR_KSP: - if (!(env->ipr[IPR_EXC_ADDR] & 1)) { - ret = -1; - } else { - if (env->features & FEATURE_SPS) - *valp = env->ipr[IPR_KSP]; - else - *valp = ldq_raw(hwpcb + 0); + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + const char *name = ""; + + switch (i) { + case EXCP_RESET: + name = "reset"; + break; + case EXCP_MCHK: + name = "mchk"; + break; + case EXCP_SMP_INTERRUPT: + name = "smp_interrupt"; + break; + case EXCP_CLK_INTERRUPT: + name = "clk_interrupt"; + break; + case EXCP_DEV_INTERRUPT: + name = "dev_interrupt"; + break; + case EXCP_MMFAULT: + name = "mmfault"; + break; + case EXCP_UNALIGN: + name = "unalign"; + break; + case EXCP_OPCDEC: + name = "opcdec"; + break; + case EXCP_ARITH: + name = "arith"; + break; + case EXCP_FEN: + name = "fen"; + break; + case EXCP_CALL_PAL: + name = "call_pal"; + break; + case EXCP_STL_C: + name = "stl_c"; + break; + case EXCP_STQ_C: + name = "stq_c"; + break; } - break; - case IPR_MCES: - *valp = ((int64_t)(env->ipr[IPR_MCES] << 59)) >> 59; - break; - case IPR_PERFMON: - /* Implementation specific */ - *valp = 0; - break; - case IPR_PCBB: - *valp = ((int64_t)env->ipr[IPR_PCBB] << 16) >> 16; - break; - case IPR_PRBR: - *valp = env->ipr[IPR_PRBR]; - break; - case IPR_PTBR: - *valp = env->ipr[IPR_PTBR]; - break; - case IPR_SCBB: - *valp = (int64_t)((int32_t)env->ipr[IPR_SCBB]); - break; - case IPR_SIRR: - /* Write-only */ - ret = -1; - break; - case IPR_SISR: - *valp = (int64_t)((int16_t)env->ipr[IPR_SISR]); - case IPR_SSP: - if (env->features & FEATURE_SPS) - *valp = env->ipr[IPR_SSP]; - else - *valp = ldq_raw(hwpcb + 16); - break; - case IPR_SYSPTBR: - if (env->features & FEATURE_VIRBND) - *valp = env->ipr[IPR_SYSPTBR]; - else - ret = -1; - break; - case IPR_TBCHK: - if ((env->features & FEATURE_TBCHK)) { - /* XXX: TODO */ - *valp = 0; - ret = -1; - } else { - ret = -1; - } - break; - case IPR_TBIA: - /* Write-only */ - ret = -1; - break; - case IPR_TBIAP: - /* Write-only */ - ret = -1; - break; - case IPR_TBIS: - /* Write-only */ - ret = -1; - break; - case IPR_TBISD: - /* Write-only */ - ret = -1; - break; - case IPR_TBISI: - /* Write-only */ - ret = -1; - break; - case IPR_USP: - if (env->features & FEATURE_SPS) - *valp = env->ipr[IPR_USP]; - else - *valp = ldq_raw(hwpcb + 24); - break; - case IPR_VIRBND: - if (env->features & FEATURE_VIRBND) - *valp = env->ipr[IPR_VIRBND]; - else - ret = -1; - break; - case IPR_VPTB: - *valp = env->ipr[IPR_VPTB]; - break; - case IPR_WHAMI: - *valp = env->ipr[IPR_WHAMI]; - break; - default: - /* Invalid */ - ret = -1; - break; + qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n", + ++count, name, env->error_code, env->pc, env->ir[IR_SP]); } - return ret; -} + env->exception_index = -1; -int cpu_alpha_mtpr (CPUState *env, int iprn, uint64_t val, uint64_t *oldvalp) -{ - uint64_t hwpcb, tmp64; - uint8_t tmp8; - int ret = 0; - - hwpcb = env->ipr[IPR_PCBB]; - switch (iprn) { - case IPR_ASN: - /* Read-only */ - ret = -1; - break; - case IPR_ASTEN: - tmp8 = ((int8_t)(env->ipr[IPR_ASTEN] << 4)) >> 4; - *oldvalp = tmp8; - tmp8 &= val & 0xF; - tmp8 |= (val >> 4) & 0xF; - env->ipr[IPR_ASTEN] &= ~0xF; - env->ipr[IPR_ASTEN] |= tmp8; - ret = 1; - break; - case IPR_ASTSR: - tmp8 = ((int8_t)(env->ipr[IPR_ASTSR] << 4)) >> 4; - *oldvalp = tmp8; - tmp8 &= val & 0xF; - tmp8 |= (val >> 4) & 0xF; - env->ipr[IPR_ASTSR] &= ~0xF; - env->ipr[IPR_ASTSR] |= tmp8; - ret = 1; - case IPR_DATFX: - env->ipr[IPR_DATFX] &= ~0x1; - env->ipr[IPR_DATFX] |= val & 1; - tmp64 = ldq_raw(hwpcb + 56); - tmp64 &= ~0x8000000000000000ULL; - tmp64 |= (val & 1) << 63; - stq_raw(hwpcb + 56, tmp64); - break; - case IPR_ESP: - if (env->features & FEATURE_SPS) - env->ipr[IPR_ESP] = val; - else - stq_raw(hwpcb + 8, val); - break; - case IPR_FEN: - env->ipr[IPR_FEN] = val & 1; - tmp64 = ldq_raw(hwpcb + 56); - tmp64 &= ~1; - tmp64 |= val & 1; - stq_raw(hwpcb + 56, tmp64); - break; - case IPR_IPIR: - /* XXX: TODO: Send IRQ to CPU #ir[16] */ - break; - case IPR_IPL: - *oldvalp = ((int64_t)(env->ipr[IPR_IPL] << 59)) >> 59; - env->ipr[IPR_IPL] &= ~0x1F; - env->ipr[IPR_IPL] |= val & 0x1F; - /* XXX: may issue an interrupt or ASR _now_ */ - ret = 1; - break; - case IPR_KSP: - if (!(env->ipr[IPR_EXC_ADDR] & 1)) { - ret = -1; - } else { - if (env->features & FEATURE_SPS) - env->ipr[IPR_KSP] = val; - else - stq_raw(hwpcb + 0, val); - } - break; - case IPR_MCES: - env->ipr[IPR_MCES] &= ~((val & 0x7) | 0x18); - env->ipr[IPR_MCES] |= val & 0x18; - break; - case IPR_PERFMON: - /* Implementation specific */ - *oldvalp = 0; - ret = 1; - break; - case IPR_PCBB: - /* Read-only */ - ret = -1; - break; - case IPR_PRBR: - env->ipr[IPR_PRBR] = val; - break; - case IPR_PTBR: - /* Read-only */ - ret = -1; - break; - case IPR_SCBB: - env->ipr[IPR_SCBB] = (uint32_t)val; +#if !defined(CONFIG_USER_ONLY) + switch (i) { + case EXCP_RESET: + i = 0x0000; break; - case IPR_SIRR: - if (val & 0xF) { - env->ipr[IPR_SISR] |= 1 << (val & 0xF); - /* XXX: request a software interrupt _now_ */ - } + case EXCP_MCHK: + i = 0x0080; break; - case IPR_SISR: - /* Read-only */ - ret = -1; + case EXCP_SMP_INTERRUPT: + i = 0x0100; break; - case IPR_SSP: - if (env->features & FEATURE_SPS) - env->ipr[IPR_SSP] = val; - else - stq_raw(hwpcb + 16, val); - break; - case IPR_SYSPTBR: - if (env->features & FEATURE_VIRBND) - env->ipr[IPR_SYSPTBR] = val; - else - ret = -1; + case EXCP_CLK_INTERRUPT: + i = 0x0180; break; - case IPR_TBCHK: - /* Read-only */ - ret = -1; + case EXCP_DEV_INTERRUPT: + i = 0x0200; break; - case IPR_TBIA: - tlb_flush(env, 1); + case EXCP_MMFAULT: + i = 0x0280; break; - case IPR_TBIAP: - tlb_flush(env, 1); + case EXCP_UNALIGN: + i = 0x0300; break; - case IPR_TBIS: - tlb_flush_page(env, val); + case EXCP_OPCDEC: + i = 0x0380; break; - case IPR_TBISD: - tlb_flush_page(env, val); - break; - case IPR_TBISI: - tlb_flush_page(env, val); - break; - case IPR_USP: - if (env->features & FEATURE_SPS) - env->ipr[IPR_USP] = val; - else - stq_raw(hwpcb + 24, val); - break; - case IPR_VIRBND: - if (env->features & FEATURE_VIRBND) - env->ipr[IPR_VIRBND] = val; - else - ret = -1; + case EXCP_ARITH: + i = 0x0400; break; - case IPR_VPTB: - env->ipr[IPR_VPTB] = val; + case EXCP_FEN: + i = 0x0480; break; - case IPR_WHAMI: - /* Read-only */ - ret = -1; + case EXCP_CALL_PAL: + i = env->error_code; + /* There are 64 entry points for both privileged and unprivileged, + with bit 0x80 indicating unprivileged. Each entry point gets + 64 bytes to do its job. */ + if (i & 0x80) { + i = 0x2000 + (i - 0x80) * 64; + } else { + i = 0x1000 + i * 64; + } break; default: - /* Invalid */ - ret = -1; - break; + cpu_abort(env, "Unhandled CPU exception"); } - return ret; -} + /* Remember where the exception happened. Emulate real hardware in + that the low bit of the PC indicates PALmode. */ + env->exc_addr = env->pc | env->pal_mode; -void do_interrupt (CPUState *env) -{ - int excp; + /* Continue execution at the PALcode entry point. */ + env->pc = env->palbr + i; - env->ipr[IPR_EXC_ADDR] = env->pc | 1; - excp = env->exception_index; - env->exception_index = -1; - env->error_code = 0; - /* XXX: disable interrupts and memory mapping */ - if (env->ipr[IPR_PAL_BASE] != -1ULL) { - /* We use native PALcode */ - env->pc = env->ipr[IPR_PAL_BASE] + excp; - } else { - /* We use emulated PALcode */ - call_pal(env); - /* Emulate REI */ - env->pc = env->ipr[IPR_EXC_ADDR] & ~7; - env->ipr[IPR_EXC_ADDR] = env->ipr[IPR_EXC_ADDR] & 1; - /* XXX: re-enable interrupts and memory mapping */ + /* Switch to PALmode. */ + if (!env->pal_mode) { + env->pal_mode = 1; + swap_shadow_regs(env); } +#endif /* !USER_ONLY */ } -#endif void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) @@ -548,7 +464,7 @@ }; int i; - cpu_fprintf(f, " PC " TARGET_FMT_lx " PS " TARGET_FMT_lx "\n", + cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n", env->pc, env->ps); for (i = 0; i < 31; i++) { cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i, diff -Nru qemu-kvm-0.14.1+noroms/target-alpha/helper.h qemu-kvm-0.15.0+noroms/target-alpha/helper.h --- qemu-kvm-0.14.1+noroms/target-alpha/helper.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-alpha/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -100,27 +100,19 @@ DEF_HELPER_1(ieee_input_s, i64, i64) #if !defined (CONFIG_USER_ONLY) -DEF_HELPER_0(hw_rei, void) DEF_HELPER_1(hw_ret, void, i64) -DEF_HELPER_2(mfpr, i64, int, i64) -DEF_HELPER_2(mtpr, void, int, i64) -DEF_HELPER_0(set_alt_mode, void) -DEF_HELPER_0(restore_mode, void) -DEF_HELPER_1(ld_virt_to_phys, i64, i64) -DEF_HELPER_1(st_virt_to_phys, i64, i64) -DEF_HELPER_2(ldl_raw, void, i64, i64) -DEF_HELPER_2(ldq_raw, void, i64, i64) -DEF_HELPER_2(ldl_l_raw, void, i64, i64) -DEF_HELPER_2(ldq_l_raw, void, i64, i64) -DEF_HELPER_2(ldl_kernel, void, i64, i64) -DEF_HELPER_2(ldq_kernel, void, i64, i64) -DEF_HELPER_2(ldl_data, void, i64, i64) -DEF_HELPER_2(ldq_data, void, i64, i64) -DEF_HELPER_2(stl_raw, void, i64, i64) -DEF_HELPER_2(stq_raw, void, i64, i64) -DEF_HELPER_2(stl_c_raw, i64, i64, i64) -DEF_HELPER_2(stq_c_raw, i64, i64, i64) +DEF_HELPER_1(ldl_phys, i64, i64) +DEF_HELPER_1(ldq_phys, i64, i64) +DEF_HELPER_1(ldl_l_phys, i64, i64) +DEF_HELPER_1(ldq_l_phys, i64, i64) +DEF_HELPER_2(stl_phys, void, i64, i64) +DEF_HELPER_2(stq_phys, void, i64, i64) +DEF_HELPER_2(stl_c_phys, i64, i64, i64) +DEF_HELPER_2(stq_c_phys, i64, i64, i64) + +DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void) +DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64) #endif #include "def-helper.h" diff -Nru qemu-kvm-0.14.1+noroms/target-alpha/machine.c qemu-kvm-0.15.0+noroms/target-alpha/machine.c --- qemu-kvm-0.14.1+noroms/target-alpha/machine.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-alpha/machine.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,87 @@ +#include "hw/hw.h" +#include "hw/boards.h" + +static int get_fpcr(QEMUFile *f, void *opaque, size_t size) +{ + CPUAlphaState *env = opaque; + cpu_alpha_store_fpcr(env, qemu_get_be64(f)); + return 0; +} + +static void put_fpcr(QEMUFile *f, void *opaque, size_t size) +{ + CPUAlphaState *env = opaque; + qemu_put_be64(f, cpu_alpha_load_fpcr(env)); +} + +static const VMStateInfo vmstate_fpcr = { + .name = "fpcr", + .get = get_fpcr, + .put = put_fpcr, +}; + +static VMStateField vmstate_cpu_fields[] = { + VMSTATE_UINTTL_ARRAY(ir, CPUState, 31), + VMSTATE_UINTTL_ARRAY(fir, CPUState, 31), + /* Save the architecture value of the fpcr, not the internally + expanded version. Since this architecture value does not + exist in memory to be stored, this requires a but of hoop + jumping. We want OFFSET=0 so that we effectively pass ENV + to the helper functions, and we need to fill in the name by + hand since there's no field of that name. */ + { + .name = "fpcr", + .version_id = 0, + .size = sizeof(uint64_t), + .info = &vmstate_fpcr, + .flags = VMS_SINGLE, + .offset = 0 + }, + VMSTATE_UINTTL(pc, CPUState), + VMSTATE_UINTTL(unique, CPUState), + VMSTATE_UINTTL(lock_addr, CPUState), + VMSTATE_UINTTL(lock_value, CPUState), + /* Note that lock_st_addr is not saved; it is a temporary + used during the execution of the st[lq]_c insns. */ + + VMSTATE_UINT8(ps, CPUState), + VMSTATE_UINT8(intr_flag, CPUState), + VMSTATE_UINT8(pal_mode, CPUState), + VMSTATE_UINT8(fen, CPUState), + + VMSTATE_UINT32(pcc_ofs, CPUState), + + VMSTATE_UINTTL(trap_arg0, CPUState), + VMSTATE_UINTTL(trap_arg1, CPUState), + VMSTATE_UINTTL(trap_arg2, CPUState), + + VMSTATE_UINTTL(exc_addr, CPUState), + VMSTATE_UINTTL(palbr, CPUState), + VMSTATE_UINTTL(ptbr, CPUState), + VMSTATE_UINTTL(vptptr, CPUState), + VMSTATE_UINTTL(sysval, CPUState), + VMSTATE_UINTTL(usp, CPUState), + + VMSTATE_UINTTL_ARRAY(shadow, CPUState, 8), + VMSTATE_UINTTL_ARRAY(scratch, CPUState, 24), + + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = vmstate_cpu_fields, +}; + +void cpu_save(QEMUFile *f, void *opaque) +{ + vmstate_save_state(f, &vmstate_cpu, opaque); +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return vmstate_load_state(f, &vmstate_cpu, opaque, version_id); +} diff -Nru qemu-kvm-0.14.1+noroms/target-alpha/op_helper.c qemu-kvm-0.15.0+noroms/target-alpha/op_helper.c --- qemu-kvm-0.14.1+noroms/target-alpha/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-alpha/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,17 +25,57 @@ /*****************************************************************************/ /* Exceptions processing helpers */ -void QEMU_NORETURN helper_excp (int excp, int error) + +/* This should only be called from translate, via gen_excp. + We expect that ENV->PC has already been updated. */ +void QEMU_NORETURN helper_excp(int excp, int error) { env->exception_index = excp; env->error_code = error; - cpu_loop_exit(); + cpu_loop_exit(env); +} + +static void do_restore_state(void *retaddr) +{ + unsigned long pc = (unsigned long)retaddr; + + if (pc) { + TranslationBlock *tb = tb_find_pc(pc); + if (tb) { + cpu_restore_state(tb, env, pc); + } + } +} + +/* This may be called from any of the helpers to set up EXCEPTION_INDEX. */ +static void QEMU_NORETURN dynamic_excp(int excp, int error) +{ + env->exception_index = excp; + env->error_code = error; + do_restore_state(GETPC()); + cpu_loop_exit(env); +} + +static void QEMU_NORETURN arith_excp(int exc, uint64_t mask) +{ + env->trap_arg0 = exc; + env->trap_arg1 = mask; + dynamic_excp(EXCP_ARITH, 0); } uint64_t helper_load_pcc (void) { - /* ??? This isn't a timer for which we have any rate info. */ +#ifndef CONFIG_USER_ONLY + /* In system mode we have access to a decent high-resolution clock. + In order to make OS-level time accounting work with the RPCC, + present it with a well-timed clock fixed at 250MHz. */ + return (((uint64_t)env->pcc_ofs << 32) + | (uint32_t)(qemu_get_clock_ns(vm_clock) >> 2)); +#else + /* In user-mode, vm_clock doesn't exist. Just pass through the host cpu + clock ticks. Also, don't bother taking PCC_OFS into account. */ return (uint32_t)cpu_get_real_ticks(); +#endif } uint64_t helper_load_fpcr (void) @@ -53,7 +93,7 @@ uint64_t tmp = op1; op1 += op2; if (unlikely((tmp ^ op2 ^ (-1ULL)) & (tmp ^ op1) & (1ULL << 63))) { - helper_excp(EXCP_ARITH, EXC_M_IOV); + arith_excp(EXC_M_IOV, 0); } return op1; } @@ -63,7 +103,7 @@ uint64_t tmp = op1; op1 = (uint32_t)(op1 + op2); if (unlikely((tmp ^ op2 ^ (-1UL)) & (tmp ^ op1) & (1UL << 31))) { - helper_excp(EXCP_ARITH, EXC_M_IOV); + arith_excp(EXC_M_IOV, 0); } return op1; } @@ -73,7 +113,7 @@ uint64_t res; res = op1 - op2; if (unlikely((op1 ^ op2) & (res ^ op1) & (1ULL << 63))) { - helper_excp(EXCP_ARITH, EXC_M_IOV); + arith_excp(EXC_M_IOV, 0); } return res; } @@ -83,7 +123,7 @@ uint32_t res; res = op1 - op2; if (unlikely((op1 ^ op2) & (res ^ op1) & (1UL << 31))) { - helper_excp(EXCP_ARITH, EXC_M_IOV); + arith_excp(EXC_M_IOV, 0); } return res; } @@ -93,7 +133,7 @@ int64_t res = (int64_t)op1 * (int64_t)op2; if (unlikely((int32_t)res != res)) { - helper_excp(EXCP_ARITH, EXC_M_IOV); + arith_excp(EXC_M_IOV, 0); } return (int64_t)((int32_t)res); } @@ -105,7 +145,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, EXC_M_IOV); + arith_excp(EXC_M_IOV, 0); } return tl; } @@ -373,8 +413,6 @@ if (exc) { uint32_t hw_exc = 0; - env->ipr[IPR_EXC_MASK] |= 1ull << regno; - if (exc & float_flag_invalid) { hw_exc |= EXC_M_INV; } @@ -390,7 +428,8 @@ if (exc & float_flag_inexact) { hw_exc |= EXC_M_INE; } - helper_excp(EXCP_ARITH, hw_exc); + + arith_excp(hw_exc, 1ull << regno); } } @@ -420,7 +459,7 @@ if (env->fpcr_dnz) { val &= 1ull << 63; } else { - helper_excp(EXCP_ARITH, EXC_M_UNF); + arith_excp(EXC_M_UNF, 0); } } } else if (exp == 0x7ff) { @@ -428,7 +467,7 @@ /* ??? 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); + arith_excp(frac ? EXC_M_INV : EXC_M_FOV, 0); } return val; } @@ -445,12 +484,12 @@ if (env->fpcr_dnz) { val &= 1ull << 63; } else { - helper_excp(EXCP_ARITH, EXC_M_UNF); + arith_excp(EXC_M_UNF, 0); } } } else if (exp == 0x7ff && frac) { /* NaN. */ - helper_excp(EXCP_ARITH, EXC_M_INV); + arith_excp(EXC_M_INV, 0); } return val; } @@ -513,7 +552,7 @@ if (unlikely(!exp && mant_sig)) { /* Reserved operands / Dirty zero */ - helper_excp(EXCP_OPCDEC, 0); + dynamic_excp(EXCP_OPCDEC, 0); } if (exp < 3) { @@ -643,7 +682,7 @@ if (!exp && mant_sig) { /* Reserved operands / Dirty zero */ - helper_excp(EXCP_OPCDEC, 0); + dynamic_excp(EXCP_OPCDEC, 0); } if (exp < 3) { @@ -904,10 +943,11 @@ fa = t_to_float64(a); fb = t_to_float64(b); - if (float64_is_quiet_nan(fa) || float64_is_quiet_nan(fb)) + if (float64_unordered_quiet(fa, fb, &FP_STATUS)) { return 0x4000000000000000ULL; - else + } else { return 0; + } } uint64_t helper_cmpteq(uint64_t a, uint64_t b) @@ -917,7 +957,7 @@ fa = t_to_float64(a); fb = t_to_float64(b); - if (float64_eq(fa, fb, &FP_STATUS)) + if (float64_eq_quiet(fa, fb, &FP_STATUS)) return 0x4000000000000000ULL; else return 0; @@ -956,7 +996,7 @@ fa = g_to_float64(a); fb = g_to_float64(b); - if (float64_eq(fa, fb, &FP_STATUS)) + if (float64_eq_quiet(fa, fb, &FP_STATUS)) return 0x4000000000000000ULL; else return 0; @@ -1155,187 +1195,124 @@ /* 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 */ -} - void helper_hw_ret (uint64_t a) { 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 */ -} - -uint64_t helper_mfpr (int iprn, uint64_t val) -{ - uint64_t tmp; - - if (cpu_alpha_mfpr(env, iprn, &tmp) == 0) - val = tmp; - - return val; -} - -void helper_mtpr (int iprn, uint64_t val) -{ - cpu_alpha_mtpr(env, iprn, val, NULL); + if ((a & 1) == 0) { + env->pal_mode = 0; + swap_shadow_regs(env); + } } -void helper_set_alt_mode (void) +void helper_tbia(void) { - env->saved_mode = env->ps & 0xC; - env->ps = (env->ps & ~0xC) | (env->ipr[IPR_ALT_MODE] & 0xC); + tlb_flush(env, 1); } -void helper_restore_mode (void) +void helper_tbis(uint64_t p) { - env->ps = (env->ps & ~0xC) | env->saved_mode; + tlb_flush_page(env, p); } - #endif /*****************************************************************************/ /* Softmmu support */ #if !defined (CONFIG_USER_ONLY) - -/* XXX: the two following helpers are pure hacks. - * Hopefully, we emulate the PALcode, then we should never see - * HW_LD / HW_ST instructions. - */ -uint64_t helper_ld_virt_to_phys (uint64_t virtaddr) -{ - uint64_t tlb_addr, physaddr; - int index, mmu_idx; - void *retaddr; - - mmu_idx = cpu_mmu_index(env); - index = (virtaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - redo: - tlb_addr = env->tlb_table[mmu_idx][index].addr_read; - if ((virtaddr & TARGET_PAGE_MASK) == - (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - physaddr = virtaddr + env->tlb_table[mmu_idx][index].addend; - } else { - /* the page is not in the TLB : fill it */ - retaddr = GETPC(); - tlb_fill(virtaddr, 0, mmu_idx, retaddr); - goto redo; - } - return physaddr; -} - -uint64_t helper_st_virt_to_phys (uint64_t virtaddr) -{ - uint64_t tlb_addr, physaddr; - int index, mmu_idx; - void *retaddr; - - mmu_idx = cpu_mmu_index(env); - index = (virtaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - redo: - tlb_addr = env->tlb_table[mmu_idx][index].addr_write; - if ((virtaddr & TARGET_PAGE_MASK) == - (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - physaddr = virtaddr + env->tlb_table[mmu_idx][index].addend; - } else { - /* the page is not in the TLB : fill it */ - retaddr = GETPC(); - tlb_fill(virtaddr, 1, mmu_idx, retaddr); - goto redo; - } - return physaddr; -} - -void helper_ldl_raw(uint64_t t0, uint64_t t1) -{ - ldl_raw(t1, t0); -} - -void helper_ldq_raw(uint64_t t0, uint64_t t1) +uint64_t helper_ldl_phys(uint64_t p) { - ldq_raw(t1, t0); + return (int32_t)ldl_phys(p); } -void helper_ldl_l_raw(uint64_t t0, uint64_t t1) +uint64_t helper_ldq_phys(uint64_t p) { - env->lock = t1; - ldl_raw(t1, t0); + return ldq_phys(p); } -void helper_ldq_l_raw(uint64_t t0, uint64_t t1) +uint64_t helper_ldl_l_phys(uint64_t p) { - env->lock = t1; - ldl_raw(t1, t0); + env->lock_addr = p; + return env->lock_value = (int32_t)ldl_phys(p); } -void helper_ldl_kernel(uint64_t t0, uint64_t t1) +uint64_t helper_ldq_l_phys(uint64_t p) { - ldl_kernel(t1, t0); + env->lock_addr = p; + return env->lock_value = ldl_phys(p); } -void helper_ldq_kernel(uint64_t t0, uint64_t t1) +void helper_stl_phys(uint64_t p, uint64_t v) { - ldq_kernel(t1, t0); + stl_phys(p, v); } -void helper_ldl_data(uint64_t t0, uint64_t t1) +void helper_stq_phys(uint64_t p, uint64_t v) { - ldl_data(t1, t0); + stq_phys(p, v); } -void helper_ldq_data(uint64_t t0, uint64_t t1) +uint64_t helper_stl_c_phys(uint64_t p, uint64_t v) { - ldq_data(t1, t0); -} + uint64_t ret = 0; -void helper_stl_raw(uint64_t t0, uint64_t t1) -{ - stl_raw(t1, t0); -} + if (p == env->lock_addr) { + int32_t old = ldl_phys(p); + if (old == (int32_t)env->lock_value) { + stl_phys(p, v); + ret = 1; + } + } + env->lock_addr = -1; -void helper_stq_raw(uint64_t t0, uint64_t t1) -{ - stq_raw(t1, t0); + return ret; } -uint64_t helper_stl_c_raw(uint64_t t0, uint64_t t1) +uint64_t helper_stq_c_phys(uint64_t p, uint64_t v) { - uint64_t ret; - - if (t1 == env->lock) { - stl_raw(t1, t0); - ret = 0; - } else - ret = 1; + uint64_t ret = 0; - env->lock = 1; + if (p == env->lock_addr) { + uint64_t old = ldq_phys(p); + if (old == env->lock_value) { + stq_phys(p, v); + ret = 1; + } + } + env->lock_addr = -1; return ret; } -uint64_t helper_stq_c_raw(uint64_t t0, uint64_t t1) +static void QEMU_NORETURN do_unaligned_access(target_ulong addr, int is_write, + int is_user, void *retaddr) { - uint64_t ret; + uint64_t pc; + uint32_t insn; - if (t1 == env->lock) { - stq_raw(t1, t0); - ret = 0; - } else - ret = 1; + do_restore_state(retaddr); - env->lock = 1; + pc = env->pc; + insn = ldl_code(pc); - return ret; + env->trap_arg0 = addr; + env->trap_arg1 = insn >> 26; /* opcode */ + env->trap_arg2 = (insn >> 21) & 31; /* dest regno */ + helper_excp(EXCP_UNALIGN, 0); +} + +void QEMU_NORETURN cpu_unassigned_access(CPUState *env1, + target_phys_addr_t addr, int is_write, + int is_exec, int unused, int size) +{ + env = env1; + env->trap_arg0 = addr; + env->trap_arg1 = is_write; + dynamic_excp(EXCP_MCHK, 0); } #define MMUSUFFIX _mmu +#define ALIGNED_ONLY #define SHIFT 0 #include "softmmu_template.h" @@ -1355,9 +1332,7 @@ /* XXX: fix it to restore all registers */ 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 @@ -1365,21 +1340,11 @@ saved_env = env; env = cpu_single_env; ret = cpu_alpha_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); - if (!likely(ret == 0)) { - if (likely(retaddr)) { - /* now we have a real cpu fault */ - pc = (unsigned long)retaddr; - tb = tb_find_pc(pc); - if (likely(tb)) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); - } - } + if (unlikely(ret != 0)) { + do_restore_state(retaddr); /* Exception index and error code are already set */ - cpu_loop_exit(); + cpu_loop_exit(env); } env = saved_env; } - #endif diff -Nru qemu-kvm-0.14.1+noroms/target-alpha/translate.c qemu-kvm-0.15.0+noroms/target-alpha/translate.c --- qemu-kvm-0.14.1+noroms/target-alpha/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-alpha/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -22,7 +22,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "host-utils.h" #include "tcg-op.h" @@ -47,10 +46,6 @@ CPUAlphaState *env; uint64_t pc; int mem_idx; -#if !defined (CONFIG_USER_ONLY) - int pal_mode; -#endif - uint32_t amask; /* Current rounding mode for this TB. */ int tb_rm; @@ -89,8 +84,10 @@ static TCGv cpu_lock_addr; static TCGv cpu_lock_st_addr; static TCGv cpu_lock_value; -#ifdef CONFIG_USER_ONLY -static TCGv cpu_uniq; +static TCGv cpu_unique; +#ifndef CONFIG_USER_ONLY +static TCGv cpu_sysval; +static TCGv cpu_usp; #endif /* register names */ @@ -135,9 +132,13 @@ offsetof(CPUState, lock_value), "lock_value"); -#ifdef CONFIG_USER_ONLY - cpu_uniq = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUState, unique), "uniq"); + cpu_unique = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUState, unique), "unique"); +#ifndef CONFIG_USER_ONLY + cpu_sysval = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUState, sysval), "sysval"); + cpu_usp = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUState, usp), "usp"); #endif /* register helpers */ @@ -147,17 +148,21 @@ done_init = 1; } -static ExitStatus gen_excp(DisasContext *ctx, int exception, int error_code) +static void gen_excp_1(int exception, int error_code) { TCGv_i32 tmp1, tmp2; - tcg_gen_movi_i64(cpu_pc, ctx->pc); tmp1 = tcg_const_i32(exception); tmp2 = tcg_const_i32(error_code); gen_helper_excp(tmp1, tmp2); tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp1); +} +static ExitStatus gen_excp(DisasContext *ctx, int exception, int error_code) +{ + tcg_gen_movi_i64(cpu_pc, ctx->pc); + gen_excp_1(exception, error_code); return EXIT_NORETURN; } @@ -322,7 +327,7 @@ #if defined(CONFIG_USER_ONLY) addr = cpu_lock_st_addr; #else - addr = tcg_local_new(); + addr = tcg_temp_local_new(); #endif if (rb != 31) { @@ -345,7 +350,7 @@ lab_fail = gen_new_label(); lab_done = gen_new_label(); - tcg_gen_brcond(TCG_COND_NE, addr, cpu_lock_addr, lab_fail); + tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_lock_addr, lab_fail); val = tcg_temp_new(); if (quad) { @@ -353,7 +358,7 @@ } else { tcg_gen_qemu_ld32s(val, addr, ctx->mem_idx); } - tcg_gen_brcond(TCG_COND_NE, val, cpu_lock_value, lab_fail); + tcg_gen_brcond_i64(TCG_COND_NE, val, cpu_lock_value, lab_fail); if (quad) { tcg_gen_qemu_st64(cpu_ir[ra], addr, ctx->mem_idx); @@ -398,7 +403,7 @@ } 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); + tcg_gen_exit_tb((tcg_target_long)ctx->tb); return EXIT_GOTO_TB; } else { tcg_gen_movi_i64(cpu_pc, dest); @@ -417,12 +422,12 @@ tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, ctx->pc); - tcg_gen_exit_tb((long)ctx->tb); + tcg_gen_exit_tb((tcg_target_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); + tcg_gen_exit_tb((tcg_target_long)ctx->tb + 1); return EXIT_GOTO_TB; } else { @@ -1464,12 +1469,203 @@ tcg_temp_free_i32(tmp); } +static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) +{ + /* We're emulating OSF/1 PALcode. Many of these are trivial access + to internal cpu registers. */ + + /* Unprivileged PAL call */ + if (palcode >= 0x80 && palcode < 0xC0) { + switch (palcode) { + case 0x86: + /* IMB */ + /* No-op inside QEMU. */ + break; + case 0x9E: + /* RDUNIQUE */ + tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_unique); + break; + case 0x9F: + /* WRUNIQUE */ + tcg_gen_mov_i64(cpu_unique, cpu_ir[IR_A0]); + break; + default: + return gen_excp(ctx, EXCP_CALL_PAL, palcode & 0xbf); + } + return NO_EXIT; + } + +#ifndef CONFIG_USER_ONLY + /* Privileged PAL code */ + if (palcode < 0x40 && (ctx->tb->flags & TB_FLAGS_USER_MODE) == 0) { + switch (palcode) { + case 0x01: + /* CFLUSH */ + /* No-op inside QEMU. */ + break; + case 0x02: + /* DRAINA */ + /* No-op inside QEMU. */ + break; + case 0x2D: + /* WRVPTPTR */ + tcg_gen_st_i64(cpu_ir[IR_A0], cpu_env, offsetof(CPUState, vptptr)); + break; + case 0x31: + /* WRVAL */ + tcg_gen_mov_i64(cpu_sysval, cpu_ir[IR_A0]); + break; + case 0x32: + /* RDVAL */ + tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_sysval); + break; + + case 0x35: { + /* SWPIPL */ + TCGv tmp; + + /* Note that we already know we're in kernel mode, so we know + that PS only contains the 3 IPL bits. */ + tcg_gen_ld8u_i64(cpu_ir[IR_V0], cpu_env, offsetof(CPUState, ps)); + + /* But make sure and store only the 3 IPL bits from the user. */ + tmp = tcg_temp_new(); + tcg_gen_andi_i64(tmp, cpu_ir[IR_A0], PS_INT_MASK); + tcg_gen_st8_i64(tmp, cpu_env, offsetof(CPUState, ps)); + tcg_temp_free(tmp); + break; + } + + case 0x36: + /* RDPS */ + tcg_gen_ld8u_i64(cpu_ir[IR_V0], cpu_env, offsetof(CPUState, ps)); + break; + case 0x38: + /* WRUSP */ + tcg_gen_mov_i64(cpu_usp, cpu_ir[IR_A0]); + break; + case 0x3A: + /* RDUSP */ + tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_usp); + break; + case 0x3C: + /* WHAMI */ + tcg_gen_ld32s_i64(cpu_ir[IR_V0], cpu_env, + offsetof(CPUState, cpu_index)); + break; + + default: + return gen_excp(ctx, EXCP_CALL_PAL, palcode & 0x3f); + } + return NO_EXIT; + } +#endif + + return gen_invalid(ctx); +} + +#ifndef CONFIG_USER_ONLY + +#define PR_BYTE 0x100000 +#define PR_LONG 0x200000 + +static int cpu_pr_data(int pr) +{ + switch (pr) { + case 0: return offsetof(CPUAlphaState, ps) | PR_BYTE; + case 1: return offsetof(CPUAlphaState, fen) | PR_BYTE; + case 2: return offsetof(CPUAlphaState, pcc_ofs) | PR_LONG; + case 3: return offsetof(CPUAlphaState, trap_arg0); + case 4: return offsetof(CPUAlphaState, trap_arg1); + case 5: return offsetof(CPUAlphaState, trap_arg2); + case 6: return offsetof(CPUAlphaState, exc_addr); + case 7: return offsetof(CPUAlphaState, palbr); + case 8: return offsetof(CPUAlphaState, ptbr); + case 9: return offsetof(CPUAlphaState, vptptr); + case 10: return offsetof(CPUAlphaState, unique); + case 11: return offsetof(CPUAlphaState, sysval); + case 12: return offsetof(CPUAlphaState, usp); + + case 32 ... 39: + return offsetof(CPUAlphaState, shadow[pr - 32]); + case 40 ... 63: + return offsetof(CPUAlphaState, scratch[pr - 40]); + } + return 0; +} + +static void gen_mfpr(int ra, int regno) +{ + int data = cpu_pr_data(regno); + + /* In our emulated PALcode, these processor registers have no + side effects from reading. */ + if (ra == 31) { + return; + } + + /* The basic registers are data only, and unknown registers + are read-zero, write-ignore. */ + if (data == 0) { + tcg_gen_movi_i64(cpu_ir[ra], 0); + } else if (data & PR_BYTE) { + tcg_gen_ld8u_i64(cpu_ir[ra], cpu_env, data & ~PR_BYTE); + } else if (data & PR_LONG) { + tcg_gen_ld32s_i64(cpu_ir[ra], cpu_env, data & ~PR_LONG); + } else { + tcg_gen_ld_i64(cpu_ir[ra], cpu_env, data); + } +} + +static void gen_mtpr(int rb, int regno) +{ + TCGv tmp; + + if (rb == 31) { + tmp = tcg_const_i64(0); + } else { + tmp = cpu_ir[rb]; + } + + /* These two register numbers perform a TLB cache flush. Thankfully we + can only do this inside PALmode, which means that the current basic + block cannot be affected by the change in mappings. */ + if (regno == 255) { + /* TBIA */ + gen_helper_tbia(); + } else if (regno == 254) { + /* TBIS */ + gen_helper_tbis(tmp); + } else { + /* The basic registers are data only, and unknown registers + are read-zero, write-ignore. */ + int data = cpu_pr_data(regno); + if (data != 0) { + if (data & PR_BYTE) { + tcg_gen_st8_i64(tmp, cpu_env, data & ~PR_BYTE); + } else if (data & PR_LONG) { + tcg_gen_st32_i64(tmp, cpu_env, data & ~PR_LONG); + } else { + tcg_gen_st_i64(tmp, cpu_env, data); + } + } + } + + if (rb == 31) { + tcg_temp_free(tmp); + } +} +#endif /* !USER_ONLY*/ + static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) { uint32_t palcode; - int32_t disp21, disp16, disp12; + int32_t disp21, disp16; +#ifndef CONFIG_USER_ONLY + int32_t disp12; +#endif uint16_t fn11; - uint8_t opc, ra, rb, rc, fpfn, fn7, fn2, islit, real_islit; + uint8_t opc, ra, rb, rc, fpfn, fn7, islit, real_islit; uint8_t lit; ExitStatus ret; @@ -1487,11 +1683,12 @@ palcode = insn & 0x03FFFFFF; disp21 = ((int32_t)((insn & 0x001FFFFF) << 11)) >> 11; disp16 = (int16_t)(insn & 0x0000FFFF); +#ifndef CONFIG_USER_ONLY disp12 = (int32_t)((insn & 0x00000FFF) << 20) >> 20; +#endif fn11 = (insn >> 5) & 0x000007FF; fpfn = fn11 & 0x3F; fn7 = (insn >> 5) & 0x0000007F; - fn2 = (insn >> 5) & 0x00000003; LOG_DISAS("opc %02x ra %2d rb %2d rc %2d disp16 %6d\n", opc, ra, rb, rc, disp16); @@ -1499,32 +1696,8 @@ 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 */ - 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; - ret = gen_excp(ctx, EXCP_CALL_PALP + ((palcode & 0x3F) << 6), 0); - } -#endif - /* Invalid PAL call */ - goto invalid_opc; + ret = gen_call_pal(ctx, palcode); + break; case 0x01: /* OPC01 */ goto invalid_opc; @@ -1566,20 +1739,22 @@ break; case 0x0A: /* LDBU */ - if (!(ctx->amask & AMASK_BWX)) - goto invalid_opc; - gen_load_mem(ctx, &tcg_gen_qemu_ld8u, ra, rb, disp16, 0, 0); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_BWX) { + gen_load_mem(ctx, &tcg_gen_qemu_ld8u, ra, rb, disp16, 0, 0); + break; + } + goto invalid_opc; case 0x0B: /* LDQ_U */ gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 1); break; case 0x0C: /* LDWU */ - if (!(ctx->amask & AMASK_BWX)) - goto invalid_opc; - gen_load_mem(ctx, &tcg_gen_qemu_ld16u, ra, rb, disp16, 0, 0); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_BWX) { + gen_load_mem(ctx, &tcg_gen_qemu_ld16u, ra, rb, disp16, 0, 0); + break; + } + goto invalid_opc; case 0x0D: /* STW */ gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0); @@ -1983,20 +2158,12 @@ case 0x61: /* AMASK */ if (likely(rc != 31)) { - if (islit) - tcg_gen_movi_i64(cpu_ir[rc], lit); - else - tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]); - switch (ctx->env->implver) { - case IMPLVER_2106x: - /* EV4, EV45, LCA, LCA45 & EV5 */ - break; - case IMPLVER_21164: - case IMPLVER_21264: - case IMPLVER_21364: - tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[rc], - ~(uint64_t)ctx->amask); - break; + uint64_t amask = ctx->tb->flags >> TB_FLAGS_AMASK_SHIFT; + + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], lit & ~amask); + } else { + tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[rb], ~amask); } } break; @@ -2210,8 +2377,9 @@ switch (fpfn) { /* fn11 & 0x3F */ case 0x04: /* ITOFS */ - if (!(ctx->amask & AMASK_FIX)) + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { goto invalid_opc; + } if (likely(rc != 31)) { if (ra != 31) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -2224,20 +2392,23 @@ break; case 0x0A: /* SQRTF */ - if (!(ctx->amask & AMASK_FIX)) - goto invalid_opc; - gen_fsqrtf(rb, rc); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrtf(rb, rc); + break; + } + goto invalid_opc; case 0x0B: /* SQRTS */ - if (!(ctx->amask & AMASK_FIX)) - goto invalid_opc; - gen_fsqrts(ctx, rb, rc, fn11); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrts(ctx, rb, rc, fn11); + break; + } + goto invalid_opc; case 0x14: /* ITOFF */ - if (!(ctx->amask & AMASK_FIX)) + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { goto invalid_opc; + } if (likely(rc != 31)) { if (ra != 31) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -2250,8 +2421,9 @@ break; case 0x24: /* ITOFT */ - if (!(ctx->amask & AMASK_FIX)) + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { goto invalid_opc; + } if (likely(rc != 31)) { if (ra != 31) tcg_gen_mov_i64(cpu_fir[rc], cpu_ir[ra]); @@ -2261,16 +2433,18 @@ break; case 0x2A: /* SQRTG */ - if (!(ctx->amask & AMASK_FIX)) - goto invalid_opc; - gen_fsqrtg(rb, rc); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrtg(rb, rc); + break; + } + goto invalid_opc; case 0x02B: /* SQRTT */ - if (!(ctx->amask & AMASK_FIX)) - goto invalid_opc; - gen_fsqrtt(ctx, rb, rc, fn11); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrtt(ctx, rb, rc, fn11); + break; + } + goto invalid_opc; default: goto invalid_opc; } @@ -2571,18 +2745,13 @@ break; case 0x19: /* HW_MFPR (PALcode) */ -#if defined (CONFIG_USER_ONLY) - goto invalid_opc; -#else - if (!ctx->pal_mode) - goto invalid_opc; - if (ra != 31) { - TCGv tmp = tcg_const_i32(insn & 0xFF); - gen_helper_mfpr(cpu_ir[ra], tmp, cpu_ir[ra]); - tcg_temp_free(tmp); +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + gen_mfpr(ra, insn & 0xffff); + break; } - break; #endif + goto invalid_opc; case 0x1A: /* JMP, JSR, RET, JSR_COROUTINE. These only differ by the branch prediction stack action, which of course we don't implement. */ @@ -2598,13 +2767,15 @@ break; case 0x1B: /* HW_LD (PALcode) */ -#if defined (CONFIG_USER_ONLY) - goto invalid_opc; -#else - if (!ctx->pal_mode) - goto invalid_opc; - if (ra != 31) { - TCGv addr = tcg_temp_new(); +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + TCGv addr; + + if (ra == 31) { + break; + } + + addr = tcg_temp_new(); if (rb != 31) tcg_gen_addi_i64(addr, cpu_ir[rb], disp12); else @@ -2612,27 +2783,26 @@ switch ((insn >> 12) & 0xF) { case 0x0: /* Longword physical access (hw_ldl/p) */ - gen_helper_ldl_raw(cpu_ir[ra], addr); + gen_helper_ldl_phys(cpu_ir[ra], addr); break; case 0x1: /* Quadword physical access (hw_ldq/p) */ - gen_helper_ldq_raw(cpu_ir[ra], addr); + gen_helper_ldq_phys(cpu_ir[ra], addr); break; case 0x2: /* Longword physical access with lock (hw_ldl_l/p) */ - gen_helper_ldl_l_raw(cpu_ir[ra], addr); + gen_helper_ldl_l_phys(cpu_ir[ra], addr); break; case 0x3: /* Quadword physical access with lock (hw_ldq_l/p) */ - gen_helper_ldq_l_raw(cpu_ir[ra], addr); + gen_helper_ldq_l_phys(cpu_ir[ra], addr); break; case 0x4: /* Longword virtual PTE fetch (hw_ldl/v) */ - tcg_gen_qemu_ld32s(cpu_ir[ra], addr, 0); - break; + goto invalid_opc; case 0x5: /* Quadword virtual PTE fetch (hw_ldq/v) */ - tcg_gen_qemu_ld64(cpu_ir[ra], addr, 0); + goto invalid_opc; break; case 0x6: /* Incpu_ir[ra]id */ @@ -2642,63 +2812,47 @@ goto invalid_opc; case 0x8: /* Longword virtual access (hw_ldl) */ - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_ldl_raw(cpu_ir[ra], addr); - break; + goto invalid_opc; case 0x9: /* Quadword virtual access (hw_ldq) */ - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_ldq_raw(cpu_ir[ra], addr); - break; + goto invalid_opc; case 0xA: /* Longword virtual access with protection check (hw_ldl/w) */ - tcg_gen_qemu_ld32s(cpu_ir[ra], addr, 0); + tcg_gen_qemu_ld32s(cpu_ir[ra], addr, MMU_KERNEL_IDX); break; case 0xB: /* Quadword virtual access with protection check (hw_ldq/w) */ - tcg_gen_qemu_ld64(cpu_ir[ra], addr, 0); + tcg_gen_qemu_ld64(cpu_ir[ra], addr, MMU_KERNEL_IDX); break; case 0xC: /* Longword virtual access with alt access mode (hw_ldl/a)*/ - gen_helper_set_alt_mode(); - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_ldl_raw(cpu_ir[ra], addr); - gen_helper_restore_mode(); - break; + goto invalid_opc; case 0xD: /* Quadword virtual access with alt access mode (hw_ldq/a) */ - gen_helper_set_alt_mode(); - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_ldq_raw(cpu_ir[ra], addr); - gen_helper_restore_mode(); - break; + goto invalid_opc; case 0xE: /* Longword virtual access with alternate access mode and - * protection checks (hw_ldl/wa) - */ - gen_helper_set_alt_mode(); - gen_helper_ldl_data(cpu_ir[ra], addr); - gen_helper_restore_mode(); + protection checks (hw_ldl/wa) */ + tcg_gen_qemu_ld32s(cpu_ir[ra], addr, MMU_USER_IDX); break; case 0xF: /* Quadword virtual access with alternate access mode and - * protection checks (hw_ldq/wa) - */ - gen_helper_set_alt_mode(); - gen_helper_ldq_data(cpu_ir[ra], addr); - gen_helper_restore_mode(); + protection checks (hw_ldq/wa) */ + tcg_gen_qemu_ld64(cpu_ir[ra], addr, MMU_USER_IDX); break; } tcg_temp_free(addr); + break; } - break; #endif + goto invalid_opc; case 0x1C: switch (fn7) { case 0x00: /* SEXTB */ - if (!(ctx->amask & AMASK_BWX)) + if ((ctx->tb->flags & TB_FLAGS_AMASK_BWX) == 0) { goto invalid_opc; + } if (likely(rc != 31)) { if (islit) tcg_gen_movi_i64(cpu_ir[rc], (int64_t)((int8_t)lit)); @@ -2708,138 +2862,164 @@ break; case 0x01: /* SEXTW */ - if (!(ctx->amask & AMASK_BWX)) - goto invalid_opc; - if (likely(rc != 31)) { - if (islit) - tcg_gen_movi_i64(cpu_ir[rc], (int64_t)((int16_t)lit)); - else - tcg_gen_ext16s_i64(cpu_ir[rc], cpu_ir[rb]); + if (ctx->tb->flags & TB_FLAGS_AMASK_BWX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], (int64_t)((int16_t)lit)); + } else { + tcg_gen_ext16s_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; } - break; + goto invalid_opc; case 0x30: /* CTPOP */ - if (!(ctx->amask & AMASK_CIX)) - goto invalid_opc; - if (likely(rc != 31)) { - if (islit) - tcg_gen_movi_i64(cpu_ir[rc], ctpop64(lit)); - else - gen_helper_ctpop(cpu_ir[rc], cpu_ir[rb]); + if (ctx->tb->flags & TB_FLAGS_AMASK_CIX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], ctpop64(lit)); + } else { + gen_helper_ctpop(cpu_ir[rc], cpu_ir[rb]); + } + } + break; } - break; + goto invalid_opc; case 0x31: /* PERR */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_perr(ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_perr(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x32: /* CTLZ */ - if (!(ctx->amask & AMASK_CIX)) - goto invalid_opc; - if (likely(rc != 31)) { - if (islit) - tcg_gen_movi_i64(cpu_ir[rc], clz64(lit)); - else - gen_helper_ctlz(cpu_ir[rc], cpu_ir[rb]); + if (ctx->tb->flags & TB_FLAGS_AMASK_CIX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], clz64(lit)); + } else { + gen_helper_ctlz(cpu_ir[rc], cpu_ir[rb]); + } + } + break; } - break; + goto invalid_opc; case 0x33: /* CTTZ */ - if (!(ctx->amask & AMASK_CIX)) - goto invalid_opc; - if (likely(rc != 31)) { - if (islit) - tcg_gen_movi_i64(cpu_ir[rc], ctz64(lit)); - else - gen_helper_cttz(cpu_ir[rc], cpu_ir[rb]); + if (ctx->tb->flags & TB_FLAGS_AMASK_CIX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], ctz64(lit)); + } else { + gen_helper_cttz(cpu_ir[rc], cpu_ir[rb]); + } + } + break; } - break; + goto invalid_opc; case 0x34: /* UNPKBW */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - if (real_islit || ra != 31) - goto invalid_opc; - gen_unpkbw (rb, rc); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_unpkbw(rb, rc); + break; + } + goto invalid_opc; case 0x35: /* UNPKBL */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - if (real_islit || ra != 31) - goto invalid_opc; - gen_unpkbl (rb, rc); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_unpkbl(rb, rc); + break; + } + goto invalid_opc; case 0x36: /* PKWB */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - if (real_islit || ra != 31) - goto invalid_opc; - gen_pkwb (rb, rc); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_pkwb(rb, rc); + break; + } + goto invalid_opc; case 0x37: /* PKLB */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - if (real_islit || ra != 31) - goto invalid_opc; - gen_pklb (rb, rc); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_pklb(rb, rc); + break; + } + goto invalid_opc; case 0x38: /* MINSB8 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_minsb8 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minsb8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x39: /* MINSW4 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_minsw4 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minsw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x3A: /* MINUB8 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_minub8 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minub8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x3B: /* MINUW4 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_minuw4 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minuw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x3C: /* MAXUB8 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_maxub8 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxub8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x3D: /* MAXUW4 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_maxuw4 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxuw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x3E: /* MAXSB8 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_maxsb8 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxsb8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x3F: /* MAXSW4 */ - if (!(ctx->amask & AMASK_MVI)) - goto invalid_opc; - gen_maxsw4 (ra, rb, rc, islit, lit); - break; + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxsw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; case 0x70: /* FTOIT */ - if (!(ctx->amask & AMASK_FIX)) + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { goto invalid_opc; + } if (likely(rc != 31)) { if (ra != 31) tcg_gen_mov_i64(cpu_ir[rc], cpu_fir[ra]); @@ -2849,8 +3029,9 @@ break; case 0x78: /* FTOIS */ - if (!(ctx->amask & AMASK_FIX)) + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { goto invalid_opc; + } if (rc != 31) { TCGv_i32 tmp1 = tcg_temp_new_i32(); if (ra != 31) @@ -2870,57 +3051,37 @@ break; case 0x1D: /* HW_MTPR (PALcode) */ -#if defined (CONFIG_USER_ONLY) - goto invalid_opc; -#else - if (!ctx->pal_mode) - goto invalid_opc; - else { - TCGv tmp1 = tcg_const_i32(insn & 0xFF); - if (ra != 31) - gen_helper_mtpr(tmp1, cpu_ir[ra]); - else { - TCGv tmp2 = tcg_const_i64(0); - gen_helper_mtpr(tmp1, tmp2); - tcg_temp_free(tmp2); - } - tcg_temp_free(tmp1); - ret = EXIT_PC_STALE; +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + gen_mtpr(rb, insn & 0xffff); + break; } - break; #endif - case 0x1E: - /* HW_REI (PALcode) */ -#if defined (CONFIG_USER_ONLY) goto invalid_opc; -#else - if (!ctx->pal_mode) - goto invalid_opc; - if (rb == 31) { - /* "Old" alpha */ - gen_helper_hw_rei(); - } else { - TCGv tmp; - - if (ra != 31) { - tmp = tcg_temp_new(); - tcg_gen_addi_i64(tmp, cpu_ir[rb], (((int64_t)insn << 51) >> 51)); - } else - tmp = tcg_const_i64(((int64_t)insn << 51) >> 51); - gen_helper_hw_ret(tmp); - tcg_temp_free(tmp); + case 0x1E: + /* HW_RET (PALcode) */ +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + if (rb == 31) { + /* Pre-EV6 CPUs interpreted this as HW_REI, loading the return + address from EXC_ADDR. This turns out to be useful for our + emulation PALcode, so continue to accept it. */ + TCGv tmp = tcg_temp_new(); + tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUState, exc_addr)); + gen_helper_hw_ret(tmp); + tcg_temp_free(tmp); + } else { + gen_helper_hw_ret(cpu_ir[rb]); + } + ret = EXIT_PC_UPDATED; + break; } - ret = EXIT_PC_UPDATED; - break; #endif + goto invalid_opc; case 0x1F: /* HW_ST (PALcode) */ -#if defined (CONFIG_USER_ONLY) - goto invalid_opc; -#else - if (!ctx->pal_mode) - goto invalid_opc; - else { +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { TCGv addr, val; addr = tcg_temp_new(); if (rb != 31) @@ -2936,30 +3097,26 @@ switch ((insn >> 12) & 0xF) { case 0x0: /* Longword physical access */ - gen_helper_stl_raw(val, addr); + gen_helper_stl_phys(addr, val); break; case 0x1: /* Quadword physical access */ - gen_helper_stq_raw(val, addr); + gen_helper_stq_phys(addr, val); break; case 0x2: /* Longword physical access with lock */ - gen_helper_stl_c_raw(val, val, addr); + gen_helper_stl_c_phys(val, addr, val); break; case 0x3: /* Quadword physical access with lock */ - gen_helper_stq_c_raw(val, val, addr); + gen_helper_stq_c_phys(val, addr, val); break; case 0x4: /* Longword virtual access */ - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_stl_raw(val, addr); - break; + goto invalid_opc; case 0x5: /* Quadword virtual access */ - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_stq_raw(val, addr); - break; + goto invalid_opc; case 0x6: /* Invalid */ goto invalid_opc; @@ -2980,18 +3137,10 @@ goto invalid_opc; case 0xC: /* Longword virtual access with alternate access mode */ - gen_helper_set_alt_mode(); - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_stl_raw(val, addr); - gen_helper_restore_mode(); - break; + goto invalid_opc; case 0xD: /* Quadword virtual access with alternate access mode */ - gen_helper_set_alt_mode(); - gen_helper_st_virt_to_phys(addr, addr); - gen_helper_stl_raw(val, addr); - gen_helper_restore_mode(); - break; + goto invalid_opc; case 0xE: /* Invalid */ goto invalid_opc; @@ -3002,9 +3151,10 @@ if (ra == 31) tcg_temp_free(val); tcg_temp_free(addr); + break; } - break; #endif + goto invalid_opc; case 0x20: /* LDF */ gen_load_mem(ctx, &gen_qemu_ldf, ra, rb, disp16, 1, 0); @@ -3155,13 +3305,7 @@ ctx.tb = tb; ctx.env = env; ctx.pc = pc_start; - ctx.amask = env->amask; -#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 + ctx.mem_idx = cpu_mmu_index(env); /* ??? Every TB begins with unset rounding mode, to be initialized on the first fp insn of the TB. Alternately we could define a proper @@ -3211,18 +3355,15 @@ ctx.pc += 4; ret = translate_one(ctxp, insn); - 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; - } + /* If we reach a page boundary, are single stepping, + or exhaust instruction count, stop generation. */ + if (ret == NO_EXIT + && ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0 + || gen_opc_ptr >= gen_opc_end + || num_insns >= max_insns + || singlestep + || env->singlestep_enabled)) { + ret = EXIT_PC_STALE; } } while (ret == NO_EXIT); @@ -3238,7 +3379,11 @@ tcg_gen_movi_i64(cpu_pc, ctx.pc); /* FALLTHRU */ case EXIT_PC_UPDATED: - tcg_gen_exit_tb(0); + if (env->singlestep_enabled) { + gen_excp_1(EXCP_DEBUG, 0); + } else { + tcg_gen_exit_tb(0); + } break; default: abort(); @@ -3325,50 +3470,19 @@ env->implver = implver; env->amask = amask; - env->ps = 0x1F00; #if defined (CONFIG_USER_ONLY) - env->ps |= 1 << 3; + env->ps = PS_USER_MODE; 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 */ -#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 + env->fen = 1; qemu_init_vcpu(env); return env; } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->pc = gen_opc_pc[pc_pos]; } diff -Nru qemu-kvm-0.14.1+noroms/target-arm/cpu.h qemu-kvm-0.15.0+noroms/target-arm/cpu.h --- qemu-kvm-0.14.1+noroms/target-arm/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -55,6 +55,10 @@ #define ARMV7M_EXCP_PENDSV 14 #define ARMV7M_EXCP_SYSTICK 15 +/* ARM-specific interrupt pending bits. */ +#define CPU_INTERRUPT_FIQ CPU_INTERRUPT_TGT_EXT_1 + + typedef void ARMWriteCPFunc(void *opaque, int cp_info, int srcreg, int operand, uint32_t value); typedef uint32_t ARMReadCPFunc(void *opaque, int cp_info, @@ -126,8 +130,15 @@ uint32_t c6_region[8]; /* MPU base/size registers. */ uint32_t c6_insn; /* Fault address registers. */ uint32_t c6_data; + uint32_t c7_par; /* Translation result. */ uint32_t c9_insn; /* Cache lockdown registers. */ uint32_t c9_data; + uint32_t c9_pmcr; /* performance monitor control register */ + uint32_t c9_pmcnten; /* perf monitor counter enables */ + uint32_t c9_pmovsr; /* perf monitor overflow status */ + uint32_t c9_pmxevtyper; /* perf monitor event type */ + uint32_t c9_pmuserenr; /* perf monitor user enable */ + uint32_t c9_pminten; /* perf monitor interrupt enables */ uint32_t c13_fcse; /* FCSE PID. */ uint32_t c13_context; /* Context ID. */ uint32_t c13_tls1; /* User RW Thread register. */ @@ -157,10 +168,6 @@ /* Internal CPU feature flags. */ uint32_t features; - /* Callback for vectored interrupt controller. */ - int (*get_irq_vector)(struct CPUARMState *); - void *irq_opaque; - /* VFP coprocessor state. */ struct { float64 regs[32]; @@ -220,7 +227,7 @@ void *opaque; } cp[15]; void *nvic; - struct arm_boot_info *boot_info; + const struct arm_boot_info *boot_info; } CPUARMState; CPUARMState *cpu_arm_init(const char *cpu_model); @@ -362,7 +369,12 @@ ARM_FEATURE_DIV, ARM_FEATURE_M, /* Microcontroller profile. */ ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */ - ARM_FEATURE_THUMB2EE + ARM_FEATURE_THUMB2EE, + ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */ + ARM_FEATURE_V4T, + ARM_FEATURE_V5, + ARM_FEATURE_STRONGARM, + ARM_FEATURE_VAPA, /* cp15 VA to PA lookups */ }; static inline int arm_feature(CPUARMState *env, int feature) @@ -393,6 +405,8 @@ #define ARM_CPUID_ARM946 0x41059461 #define ARM_CPUID_TI915T 0x54029152 #define ARM_CPUID_TI925T 0x54029252 +#define ARM_CPUID_SA1100 0x4401A11B +#define ARM_CPUID_SA1110 0x6901B119 #define ARM_CPUID_PXA250 0x69052100 #define ARM_CPUID_PXA255 0x69052d00 #define ARM_CPUID_PXA260 0x69052903 @@ -431,7 +445,7 @@ #define cpu_signal_handler cpu_arm_signal_handler #define cpu_list arm_cpu_list -#define CPU_SAVE_VERSION 2 +#define CPU_SAVE_VERSION 4 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel @@ -505,4 +519,17 @@ } } +static inline bool cpu_has_work(CPUState *env) +{ + return env->interrupt_request & + (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB); +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->regs[15] = tb->pc; +} + #endif diff -Nru qemu-kvm-0.14.1+noroms/target-arm/exec.h qemu-kvm-0.15.0+noroms/target-arm/exec.h --- qemu-kvm-0.14.1+noroms/target-arm/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -21,38 +21,10 @@ register struct CPUARMState *env asm(AREG0); -#define M0 env->iwmmxt.val - #include "cpu.h" -#include "exec-all.h" - -static inline int cpu_has_work(CPUState *env) -{ - return (env->interrupt_request & - (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB)); -} - -static inline int cpu_halted(CPUState *env) { - if (!env->halted) - return 0; - /* An interrupt wakes the CPU even if the I and F CPSR bits are - set. We use EXITTB to silently wake CPU without causing an - actual interrupt. */ - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; -} #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #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.14.1+noroms/target-arm/helper.c qemu-kvm-0.15.0+noroms/target-arm/helper.c --- qemu-kvm-0.14.1+noroms/target-arm/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,9 +3,8 @@ #include #include "cpu.h" -#include "exec-all.h" #include "gdbstub.h" -#include "helpers.h" +#include "helper.h" #include "qemu-common.h" #include "host-utils.h" #if !defined(CONFIG_USER_ONLY) @@ -48,28 +47,47 @@ env->cp15.c0_cpuid = id; switch (id) { case ARM_CPUID_ARM926: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_VFP); env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090; env->cp15.c0_cachetype = 0x1dd20d2; env->cp15.c1_sys = 0x00090078; break; case ARM_CPUID_ARM946: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_MPU); env->cp15.c0_cachetype = 0x0f004006; env->cp15.c1_sys = 0x00000078; break; case ARM_CPUID_ARM1026: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_VFP); set_feature(env, ARM_FEATURE_AUXCR); env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0; env->cp15.c0_cachetype = 0x1dd20d2; env->cp15.c1_sys = 0x00090078; break; - case ARM_CPUID_ARM1136_R2: case ARM_CPUID_ARM1136: + /* This is the 1136 r1, which is a v6K core */ + set_feature(env, ARM_FEATURE_V6K); + /* Fall through */ + case ARM_CPUID_ARM1136_R2: + /* What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an + * older core than plain "arm1136". In particular this does not + * have the v6K features. + */ + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_V6); set_feature(env, ARM_FEATURE_VFP); set_feature(env, ARM_FEATURE_AUXCR); + /* These ID register values are correct for 1136 but may be wrong + * for 1136_r2 (in particular r0p2 does not actually implement most + * of the ID registers). + */ env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4; env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111; env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000; @@ -79,10 +97,13 @@ env->cp15.c1_sys = 0x00050078; break; case ARM_CPUID_ARM11MPCORE: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_V6); set_feature(env, ARM_FEATURE_V6K); set_feature(env, ARM_FEATURE_VFP); set_feature(env, ARM_FEATURE_AUXCR); + set_feature(env, ARM_FEATURE_VAPA); env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4; env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111; env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000; @@ -91,6 +112,8 @@ env->cp15.c0_cachetype = 0x1dd20d2; break; case ARM_CPUID_CORTEXA8: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_V6); set_feature(env, ARM_FEATURE_V6K); set_feature(env, ARM_FEATURE_V7); @@ -113,6 +136,8 @@ env->cp15.c1_sys = 0x00c50078; break; case ARM_CPUID_CORTEXA9: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_V6); set_feature(env, ARM_FEATURE_V6K); set_feature(env, ARM_FEATURE_V7); @@ -123,6 +148,11 @@ set_feature(env, ARM_FEATURE_VFP_FP16); set_feature(env, ARM_FEATURE_NEON); set_feature(env, ARM_FEATURE_THUMB2EE); + /* Note that A9 supports the MP extensions even for + * A9UP and single-core A9MP (which are both different + * and valid configurations; we don't model A9UP). + */ + set_feature(env, ARM_FEATURE_V7MP); env->vfp.xregs[ARM_VFP_FPSID] = 0x41034000; /* Guess */ env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222; env->vfp.xregs[ARM_VFP_MVFR1] = 0x01111111; @@ -135,6 +165,8 @@ env->cp15.c1_sys = 0x00c50078; break; case ARM_CPUID_CORTEXM3: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_V6); set_feature(env, ARM_FEATURE_THUMB2); set_feature(env, ARM_FEATURE_V7); @@ -142,6 +174,8 @@ set_feature(env, ARM_FEATURE_DIV); break; case ARM_CPUID_ANY: /* For userspace emulation. */ + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_V6); set_feature(env, ARM_FEATURE_V6K); set_feature(env, ARM_FEATURE_V7); @@ -152,9 +186,11 @@ set_feature(env, ARM_FEATURE_NEON); set_feature(env, ARM_FEATURE_THUMB2EE); set_feature(env, ARM_FEATURE_DIV); + set_feature(env, ARM_FEATURE_V7MP); break; case ARM_CPUID_TI915T: case ARM_CPUID_TI925T: + set_feature(env, ARM_FEATURE_V4T); set_feature(env, ARM_FEATURE_OMAPCP); env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */ env->cp15.c0_cachetype = 0x5109149; @@ -167,6 +203,8 @@ case ARM_CPUID_PXA260: case ARM_CPUID_PXA261: case ARM_CPUID_PXA262: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_XSCALE); /* JTAG_ID is ((id << 28) | 0x09265013) */ env->cp15.c0_cachetype = 0xd172172; @@ -178,6 +216,8 @@ case ARM_CPUID_PXA270_B1: case ARM_CPUID_PXA270_C0: case ARM_CPUID_PXA270_C5: + set_feature(env, ARM_FEATURE_V4T); + set_feature(env, ARM_FEATURE_V5); set_feature(env, ARM_FEATURE_XSCALE); /* JTAG_ID is ((id << 28) | 0x09265013) */ set_feature(env, ARM_FEATURE_IWMMXT); @@ -185,10 +225,20 @@ env->cp15.c0_cachetype = 0xd172172; env->cp15.c1_sys = 0x00000078; break; + case ARM_CPUID_SA1100: + case ARM_CPUID_SA1110: + set_feature(env, ARM_FEATURE_STRONGARM); + env->cp15.c1_sys = 0x00000070; + break; default: cpu_abort(env, "Bad CPU ID: %x\n", id); break; } + + /* Some features automatically imply others: */ + if (arm_feature(env, ARM_FEATURE_V7)) { + set_feature(env, ARM_FEATURE_VAPA); + } } void cpu_reset(CPUARMState *env) @@ -236,10 +286,18 @@ } env->vfp.xregs[ARM_VFP_FPEXC] = 0; env->cp15.c2_base_mask = 0xffffc000u; + /* v7 performance monitor control register: same implementor + * field as main ID register, and we implement no event counters. + */ + env->cp15.c9_pmcr = (id & 0xff000000); #endif 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); + set_float_detect_tininess(float_tininess_before_rounding, + &env->vfp.fp_status); + set_float_detect_tininess(float_tininess_before_rounding, + &env->vfp.standard_fp_status); tlb_flush(env, 1); } @@ -345,6 +403,8 @@ { ARM_CPUID_CORTEXA9, "cortex-a9"}, { ARM_CPUID_TI925T, "ti925t" }, { ARM_CPUID_PXA250, "pxa250" }, + { ARM_CPUID_SA1100, "sa1100" }, + { ARM_CPUID_SA1110, "sa1110" }, { ARM_CPUID_PXA255, "pxa255" }, { ARM_CPUID_PXA260, "pxa260" }, { ARM_CPUID_PXA261, "pxa261" }, @@ -808,6 +868,7 @@ return; } } + env->cp15.c5_insn = 2; /* Fall through to prefetch abort. */ case EXCP_PREFETCH_ABORT: new_mode = ARM_CPU_MODE_ABT; @@ -850,7 +911,11 @@ /* 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 = (env->cp15.c1_sys & (1 << 30)) != 0; + /* this is a lie, as the was no c1_sys on V4T/V5, but who cares + * and we should just guard the thumb mode on V4 */ + if (arm_feature(env, ARM_FEATURE_V4T)) { + 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; @@ -1341,7 +1406,7 @@ /* This may enable/disable the MMU, so do a TLB flush. */ tlb_flush(env, 1); break; - case 1: /* Auxiliary cotrol register. */ + case 1: /* Auxiliary control register. */ if (arm_feature(env, ARM_FEATURE_XSCALE)) { env->cp15.c1_xscaleauxcr = val; break; @@ -1450,8 +1515,49 @@ case 7: /* Cache control. */ env->cp15.c15_i_max = 0x000; env->cp15.c15_i_min = 0xff0; - /* No cache, so nothing to do. */ - /* ??? MPCore has VA to PA translation functions. */ + if (op1 != 0) { + goto bad_reg; + } + /* No cache, so nothing to do except VA->PA translations. */ + if (arm_feature(env, ARM_FEATURE_VAPA)) { + switch (crm) { + case 4: + if (arm_feature(env, ARM_FEATURE_V7)) { + env->cp15.c7_par = val & 0xfffff6ff; + } else { + env->cp15.c7_par = val & 0xfffff1ff; + } + break; + case 8: { + uint32_t phys_addr; + target_ulong page_size; + int prot; + int ret, is_user = op2 & 2; + int access_type = op2 & 1; + + if (op2 & 4) { + /* Other states are only available with TrustZone */ + goto bad_reg; + } + ret = get_phys_addr(env, val, access_type, is_user, + &phys_addr, &prot, &page_size); + if (ret == 0) { + /* We do not set any attribute bits in the PAR */ + if (page_size == (1 << 24) + && arm_feature(env, ARM_FEATURE_V7)) { + env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1; + } else { + env->cp15.c7_par = phys_addr & 0xfffff000; + } + } else { + env->cp15.c7_par = ((ret & (10 << 1)) >> 5) | + ((ret & (12 << 1)) >> 6) | + ((ret & 0xf) << 1) | 1; + } + break; + } + } + } break; case 8: /* MMU TLB control. */ switch (op2) { @@ -1475,6 +1581,8 @@ case 9: if (arm_feature(env, ARM_FEATURE_OMAPCP)) break; + if (arm_feature(env, ARM_FEATURE_STRONGARM)) + break; /* Ignore ReadBuffer access */ switch (crm) { case 0: /* Cache lockdown. */ switch (op1) { @@ -1500,6 +1608,81 @@ case 1: /* TCM memory region registers. */ /* Not implemented. */ goto bad_reg; + case 12: /* Performance monitor control */ + /* Performance monitors are implementation defined in v7, + * but with an ARM recommended set of registers, which we + * follow (although we don't actually implement any counters) + */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* performance monitor control register */ + /* only the DP, X, D and E bits are writable */ + env->cp15.c9_pmcr &= ~0x39; + env->cp15.c9_pmcr |= (val & 0x39); + break; + case 1: /* Count enable set register */ + val &= (1 << 31); + env->cp15.c9_pmcnten |= val; + break; + case 2: /* Count enable clear */ + val &= (1 << 31); + env->cp15.c9_pmcnten &= ~val; + break; + case 3: /* Overflow flag status */ + env->cp15.c9_pmovsr &= ~val; + break; + case 4: /* Software increment */ + /* RAZ/WI since we don't implement the software-count event */ + break; + case 5: /* Event counter selection register */ + /* Since we don't implement any events, writing to this register + * is actually UNPREDICTABLE. So we choose to RAZ/WI. + */ + break; + default: + goto bad_reg; + } + break; + case 13: /* Performance counters */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* Cycle count register: not implemented, so RAZ/WI */ + break; + case 1: /* Event type select */ + env->cp15.c9_pmxevtyper = val & 0xff; + break; + case 2: /* Event count register */ + /* Unimplemented (we have no events), RAZ/WI */ + break; + default: + goto bad_reg; + } + break; + case 14: /* Performance monitor control */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* user enable */ + env->cp15.c9_pmuserenr = val & 1; + /* changes access rights for cp registers, so flush tbs */ + tb_flush(env); + break; + case 1: /* interrupt enable set */ + /* We have no event counters so only the C bit can be changed */ + val &= (1 << 31); + env->cp15.c9_pminten |= val; + break; + case 2: /* interrupt enable clear */ + val &= (1 << 31); + env->cp15.c9_pminten &= ~val; + break; + } + break; default: goto bad_reg; } @@ -1602,12 +1785,28 @@ return 0; case 3: /* TLB type register. */ return 0; /* No lockable TLB entries. */ - case 5: /* CPU ID */ - if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) { - return env->cpu_index | 0x80000900; - } else { - return env->cpu_index; + case 5: /* MPIDR */ + /* The MPIDR was standardised in v7; prior to + * this it was implemented only in the 11MPCore. + * For all other pre-v7 cores it does not exist. + */ + if (arm_feature(env, ARM_FEATURE_V7) || + ARM_CPUID(env) == ARM_CPUID_ARM11MPCORE) { + int mpidr = env->cpu_index; + /* We don't support setting cluster ID ([8..11]) + * so these bits always RAZ. + */ + if (arm_feature(env, ARM_FEATURE_V7MP)) { + mpidr |= (1 << 31); + /* Cores which are uniprocessor (non-coherent) + * but still implement the MP extensions set + * bit 30. (For instance, A9UP.) However we do + * not currently model any of those cores. + */ + } + return mpidr; } + /* otherwise fall through to the unimplemented-reg case */ default: goto bad_reg; } @@ -1767,32 +1966,89 @@ } } case 7: /* Cache control. */ + if (crm == 4 && op1 == 0 && op2 == 0) { + return env->cp15.c7_par; + } /* FIXME: Should only clear Z flag if destination is r15. */ env->ZF = 0; return 0; case 8: /* MMU TLB control. */ goto bad_reg; - case 9: /* Cache lockdown. */ - switch (op1) { - case 0: /* L1 cache. */ - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - return 0; + case 9: + switch (crm) { + case 0: /* Cache lockdown */ + switch (op1) { + case 0: /* L1 cache. */ + if (arm_feature(env, ARM_FEATURE_OMAPCP)) { + return 0; + } + switch (op2) { + case 0: + return env->cp15.c9_data; + case 1: + return env->cp15.c9_insn; + default: + goto bad_reg; + } + case 1: /* L2 cache */ + if (crm != 0) { + goto bad_reg; + } + /* L2 Lockdown and Auxiliary control. */ + return 0; + default: + goto bad_reg; + } + break; + case 12: /* Performance monitor control */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } switch (op2) { - case 0: - return env->cp15.c9_data; - case 1: - return env->cp15.c9_insn; + case 0: /* performance monitor control register */ + return env->cp15.c9_pmcr; + case 1: /* count enable set */ + case 2: /* count enable clear */ + return env->cp15.c9_pmcnten; + case 3: /* overflow flag status */ + return env->cp15.c9_pmovsr; + case 4: /* software increment */ + case 5: /* event counter selection register */ + return 0; /* Unimplemented, RAZ/WI */ default: goto bad_reg; } - case 1: /* L2 cache */ - if (crm != 0) + case 13: /* Performance counters */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 1: /* Event type select */ + return env->cp15.c9_pmxevtyper; + case 0: /* Cycle count register */ + case 2: /* Event count register */ + /* Unimplemented, so RAZ/WI */ + return 0; + default: + goto bad_reg; + } + case 14: /* Performance monitor control */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* user enable */ + return env->cp15.c9_pmuserenr; + case 1: /* interrupt enable set */ + case 2: /* interrupt enable clear */ + return env->cp15.c9_pminten; + default: goto bad_reg; - /* L2 Lockdown and Auxiliary control. */ - return 0; + } default: goto bad_reg; } + break; case 10: /* MMU TLB lockdown. */ /* ??? TLB lockdown not implemented. */ return 0; @@ -1888,11 +2144,11 @@ return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp; case 16: /* PRIMASK */ return (env->uncached_cpsr & CPSR_I) != 0; - case 17: /* FAULTMASK */ - return (env->uncached_cpsr & CPSR_F) != 0; - case 18: /* BASEPRI */ - case 19: /* BASEPRI_MAX */ + case 17: /* BASEPRI */ + case 18: /* BASEPRI_MAX */ return env->v7m.basepri; + case 19: /* FAULTMASK */ + return (env->uncached_cpsr & CPSR_F) != 0; case 20: /* CONTROL */ return env->v7m.control; default: @@ -1944,20 +2200,20 @@ else env->uncached_cpsr &= ~CPSR_I; break; - case 17: /* FAULTMASK */ - if (val & 1) - env->uncached_cpsr |= CPSR_F; - else - env->uncached_cpsr &= ~CPSR_F; - break; - case 18: /* BASEPRI */ + case 17: /* BASEPRI */ env->v7m.basepri = val & 0xff; break; - case 19: /* BASEPRI_MAX */ + case 18: /* BASEPRI_MAX */ val &= 0xff; if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0)) env->v7m.basepri = val; break; + case 19: /* FAULTMASK */ + if (val & 1) + env->uncached_cpsr |= CPSR_F; + else + env->uncached_cpsr &= ~CPSR_F; + break; case 20: /* CONTROL */ env->v7m.control = val & 3; switch_v7m_sp(env, (val & 2) != 0); @@ -2105,7 +2361,7 @@ /* Signed modulo arithmetic. */ #define SARITH16(a, b, n, op) do { \ int32_t sum; \ - sum = (int16_t)((uint16_t)(a) op (uint16_t)(b)); \ + sum = (int32_t)(int16_t)(a) op (int32_t)(int16_t)(b); \ RESULT(sum, n, 16); \ if (sum >= 0) \ ge |= 3 << (n * 2); \ @@ -2113,7 +2369,7 @@ #define SARITH8(a, b, n, op) do { \ int32_t sum; \ - sum = (int8_t)((uint8_t)(a) op (uint8_t)(b)); \ + sum = (int32_t)(int8_t)(a) op (int32_t)(int8_t)(b); \ RESULT(sum, n, 8); \ if (sum >= 0) \ ge |= 1 << n; \ @@ -2249,7 +2505,7 @@ target_bits |= 2; if (host_bits & float_flag_overflow) target_bits |= 4; - if (host_bits & float_flag_underflow) + if (host_bits & (float_flag_underflow | float_flag_output_denormal)) target_bits |= 8; if (host_bits & float_flag_inexact) target_bits |= 0x10; @@ -2346,13 +2602,15 @@ #define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) #define VFP_BINOP(name) \ -float32 VFP_HELPER(name, s)(float32 a, float32 b, CPUState *env) \ +float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \ { \ - return float32_ ## name (a, b, &env->vfp.fp_status); \ + float_status *fpst = fpstp; \ + return float32_ ## name(a, b, fpst); \ } \ -float64 VFP_HELPER(name, d)(float64 a, float64 b, CPUState *env) \ +float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \ { \ - return float64_ ## name (a, b, &env->vfp.fp_status); \ + float_status *fpst = fpstp; \ + return float64_ ## name(a, b, fpst); \ } VFP_BINOP(add) VFP_BINOP(sub) @@ -2420,136 +2678,39 @@ DO_VFP_cmp(d, float64) #undef DO_VFP_cmp -/* Helper routines to perform bitwise copies between float and int. */ -static inline float32 vfp_itos(uint32_t i) -{ - union { - uint32_t i; - float32 s; - } v; - - v.i = i; - return v.s; -} - -static inline uint32_t vfp_stoi(float32 s) -{ - union { - uint32_t i; - float32 s; - } v; - - v.s = s; - return v.i; -} - -static inline float64 vfp_itod(uint64_t i) -{ - union { - uint64_t i; - float64 d; - } v; +/* Integer to float and float to integer conversions */ - v.i = i; - return v.d; -} - -static inline uint64_t vfp_dtoi(float64 d) -{ - union { - uint64_t i; - float64 d; - } v; - - v.d = d; - return v.i; -} - -/* Integer to float conversion. */ -float32 VFP_HELPER(uito, s)(float32 x, CPUState *env) -{ - return uint32_to_float32(vfp_stoi(x), &env->vfp.fp_status); -} - -float64 VFP_HELPER(uito, d)(float32 x, CPUState *env) -{ - return uint32_to_float64(vfp_stoi(x), &env->vfp.fp_status); -} - -float32 VFP_HELPER(sito, s)(float32 x, CPUState *env) -{ - return int32_to_float32(vfp_stoi(x), &env->vfp.fp_status); -} - -float64 VFP_HELPER(sito, d)(float32 x, CPUState *env) -{ - return int32_to_float64(vfp_stoi(x), &env->vfp.fp_status); -} - -/* 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)); +#define CONV_ITOF(name, fsz, sign) \ + float##fsz HELPER(name)(uint32_t x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return sign##int32_to_##float##fsz(x, fpst); \ } -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)); +#define CONV_FTOI(name, fsz, sign, round) \ +uint32_t HELPER(name)(float##fsz x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##sign##int32##round(x, fpst); \ } -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)); -} +#define FLOAT_CONVS(name, p, fsz, sign) \ +CONV_ITOF(vfp_##name##to##p, fsz, sign) \ +CONV_FTOI(vfp_to##name##p, fsz, sign, ) \ +CONV_FTOI(vfp_to##name##z##p, fsz, sign, _round_to_zero) + +FLOAT_CONVS(si, s, 32, ) +FLOAT_CONVS(si, d, 64, ) +FLOAT_CONVS(ui, s, 32, u) +FLOAT_CONVS(ui, d, 64, u) + +#undef CONV_ITOF +#undef CONV_FTOI +#undef FLOAT_CONVS /* floating point conversion */ float64 VFP_HELPER(fcvtd, s)(float32 x, CPUState *env) @@ -2571,110 +2732,330 @@ } /* VFP3 fixed point conversion. */ -#define VFP_CONV_FIX(name, p, ftype, itype, sign) \ -ftype VFP_HELPER(name##to, p)(ftype x, uint32_t shift, CPUState *env) \ +#define VFP_CONV_FIX(name, p, fsz, itype, sign) \ +float##fsz HELPER(vfp_##name##to##p)(uint##fsz##_t x, uint32_t shift, \ + void *fpstp) \ { \ - ftype tmp; \ - 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); \ + float_status *fpst = fpstp; \ + float##fsz tmp; \ + tmp = sign##int32_to_##float##fsz((itype##_t)x, fpst); \ + return float##fsz##_scalbn(tmp, -(int)shift, fpst); \ } \ -ftype VFP_HELPER(to##name, p)(ftype x, uint32_t shift, CPUState *env) \ +uint##fsz##_t HELPER(vfp_to##name##p)(float##fsz x, uint32_t shift, \ + void *fpstp) \ { \ - ftype tmp; \ - if (ftype##_is_any_nan(x)) { \ - return ftype##_zero; \ + float_status *fpst = fpstp; \ + float##fsz tmp; \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ } \ - tmp = ftype##_scalbn(x, shift, &env->vfp.fp_status); \ - return vfp_ito##p(ftype##_to_##itype##_round_to_zero(tmp, \ - &env->vfp.fp_status)); \ + tmp = float##fsz##_scalbn(x, shift, fpst); \ + return float##fsz##_to_##itype##_round_to_zero(tmp, fpst); \ } -VFP_CONV_FIX(sh, d, float64, int16, ) -VFP_CONV_FIX(sl, d, float64, int32, ) -VFP_CONV_FIX(uh, d, float64, uint16, u) -VFP_CONV_FIX(ul, d, float64, uint32, u) -VFP_CONV_FIX(sh, s, float32, int16, ) -VFP_CONV_FIX(sl, s, float32, int32, ) -VFP_CONV_FIX(uh, s, float32, uint16, u) -VFP_CONV_FIX(ul, s, float32, uint32, u) +VFP_CONV_FIX(sh, d, 64, int16, ) +VFP_CONV_FIX(sl, d, 64, int32, ) +VFP_CONV_FIX(uh, d, 64, uint16, u) +VFP_CONV_FIX(ul, d, 64, uint32, u) +VFP_CONV_FIX(sh, s, 32, int16, ) +VFP_CONV_FIX(sl, s, 32, int32, ) +VFP_CONV_FIX(uh, s, 32, uint16, u) +VFP_CONV_FIX(ul, s, 32, uint32, u) #undef VFP_CONV_FIX /* Half precision conversions. */ -float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, CPUState *env) +static float32 do_fcvt_f16_to_f32(uint32_t a, CPUState *env, float_status *s) { - float_status *s = &env->vfp.fp_status; int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - return float16_to_float32(a, ieee, s); + float32 r = float16_to_float32(make_float16(a), ieee, s); + if (ieee) { + return float32_maybe_silence_nan(r); + } + return r; } -uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, CPUState *env) +static uint32_t do_fcvt_f32_to_f16(float32 a, CPUState *env, float_status *s) { - float_status *s = &env->vfp.fp_status; int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - return float32_to_float16(a, ieee, s); + float16 r = float32_to_float16(a, ieee, s); + if (ieee) { + r = float16_maybe_silence_nan(r); + } + return float16_val(r); } +float32 HELPER(neon_fcvt_f16_to_f32)(uint32_t a, CPUState *env) +{ + return do_fcvt_f16_to_f32(a, env, &env->vfp.standard_fp_status); +} + +uint32_t HELPER(neon_fcvt_f32_to_f16)(float32 a, CPUState *env) +{ + return do_fcvt_f32_to_f16(a, env, &env->vfp.standard_fp_status); +} + +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, CPUState *env) +{ + return do_fcvt_f16_to_f32(a, env, &env->vfp.fp_status); +} + +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, CPUState *env) +{ + return do_fcvt_f32_to_f16(a, env, &env->vfp.fp_status); +} + +#define float32_two make_float32(0x40000000) +#define float32_three make_float32(0x40400000) +#define float32_one_point_five make_float32(0x3fc00000) + float32 HELPER(recps_f32)(float32 a, float32 b, CPUState *env) { - float_status *s = &env->vfp.fp_status; - float32 two = int32_to_float32(2, s); - return float32_sub(two, float32_mul(a, b, s), s); + float_status *s = &env->vfp.standard_fp_status; + if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) || + (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) { + if (!(float32_is_zero(a) || float32_is_zero(b))) { + float_raise(float_flag_input_denormal, s); + } + return float32_two; + } + return float32_sub(float32_two, float32_mul(a, b, s), s); } float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUState *env) { float_status *s = &env->vfp.standard_fp_status; - float32 two = int32_to_float32(2, s); - float32 three = int32_to_float32(3, 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); + if (!(float32_is_zero(a) || float32_is_zero(b))) { + float_raise(float_flag_input_denormal, s); + } + return float32_one_point_five; } - return float32_div(float32_sub(three, product, s), two, s); + product = float32_mul(a, b, s); + return float32_div(float32_sub(float32_three, product, s), float32_two, s); } /* NEON helpers. */ -/* TODO: The architecture specifies the value that the estimate functions - should return. We return the exact reciprocal/root instead. */ +/* Constants 256 and 512 are used in some helpers; we avoid relying on + * int->float conversions at run-time. */ +#define float64_256 make_float64(0x4070000000000000LL) +#define float64_512 make_float64(0x4080000000000000LL) + +/* The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM. + */ +static float64 recip_estimate(float64 a, CPUState *env) +{ + /* These calculations mustn't set any fp exception flags, + * so we use a local copy of the fp_status. + */ + float_status dummy_status = env->vfp.standard_fp_status; + float_status *s = &dummy_status; + /* q = (int)(a * 512.0) */ + float64 q = float64_mul(float64_512, a, s); + int64_t q_int = float64_to_int64_round_to_zero(q, s); + + /* r = 1.0 / (((double)q + 0.5) / 512.0) */ + q = int64_to_float64(q_int, s); + q = float64_add(q, float64_half, s); + q = float64_div(q, float64_512, s); + q = float64_div(float64_one, q, s); + + /* s = (int)(256.0 * r + 0.5) */ + q = float64_mul(q, float64_256, s); + q = float64_add(q, float64_half, s); + q_int = float64_to_int64_round_to_zero(q, s); + + /* return (double)s / 256.0 */ + return float64_div(int64_to_float64(q_int, s), float64_256, s); +} + float32 HELPER(recpe_f32)(float32 a, CPUState *env) { - float_status *s = &env->vfp.fp_status; - float32 one = int32_to_float32(1, s); - return float32_div(one, a, s); + float_status *s = &env->vfp.standard_fp_status; + float64 f64; + uint32_t val32 = float32_val(a); + + int result_exp; + int a_exp = (val32 & 0x7f800000) >> 23; + int sign = val32 & 0x80000000; + + if (float32_is_any_nan(a)) { + if (float32_is_signaling_nan(a)) { + float_raise(float_flag_invalid, s); + } + return float32_default_nan; + } else if (float32_is_infinity(a)) { + return float32_set_sign(float32_zero, float32_is_neg(a)); + } else if (float32_is_zero_or_denormal(a)) { + if (!float32_is_zero(a)) { + float_raise(float_flag_input_denormal, s); + } + float_raise(float_flag_divbyzero, s); + return float32_set_sign(float32_infinity, float32_is_neg(a)); + } else if (a_exp >= 253) { + float_raise(float_flag_underflow, s); + return float32_set_sign(float32_zero, float32_is_neg(a)); + } + + f64 = make_float64((0x3feULL << 52) + | ((int64_t)(val32 & 0x7fffff) << 29)); + + result_exp = 253 - a_exp; + + f64 = recip_estimate(f64, env); + + val32 = sign + | ((result_exp & 0xff) << 23) + | ((float64_val(f64) >> 29) & 0x7fffff); + return make_float32(val32); +} + +/* The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM. + */ +static float64 recip_sqrt_estimate(float64 a, CPUState *env) +{ + /* These calculations mustn't set any fp exception flags, + * so we use a local copy of the fp_status. + */ + float_status dummy_status = env->vfp.standard_fp_status; + float_status *s = &dummy_status; + float64 q; + int64_t q_int; + + if (float64_lt(a, float64_half, s)) { + /* range 0.25 <= a < 0.5 */ + + /* a in units of 1/512 rounded down */ + /* q0 = (int)(a * 512.0); */ + q = float64_mul(float64_512, a, s); + q_int = float64_to_int64_round_to_zero(q, s); + + /* reciprocal root r */ + /* r = 1.0 / sqrt(((double)q0 + 0.5) / 512.0); */ + q = int64_to_float64(q_int, s); + q = float64_add(q, float64_half, s); + q = float64_div(q, float64_512, s); + q = float64_sqrt(q, s); + q = float64_div(float64_one, q, s); + } else { + /* range 0.5 <= a < 1.0 */ + + /* a in units of 1/256 rounded down */ + /* q1 = (int)(a * 256.0); */ + q = float64_mul(float64_256, a, s); + int64_t q_int = float64_to_int64_round_to_zero(q, s); + + /* reciprocal root r */ + /* r = 1.0 /sqrt(((double)q1 + 0.5) / 256); */ + q = int64_to_float64(q_int, s); + q = float64_add(q, float64_half, s); + q = float64_div(q, float64_256, s); + q = float64_sqrt(q, s); + q = float64_div(float64_one, q, s); + } + /* r in units of 1/256 rounded to nearest */ + /* s = (int)(256.0 * r + 0.5); */ + + q = float64_mul(q, float64_256,s ); + q = float64_add(q, float64_half, s); + q_int = float64_to_int64_round_to_zero(q, s); + + /* return (double)s / 256.0;*/ + return float64_div(int64_to_float64(q_int, s), float64_256, s); } float32 HELPER(rsqrte_f32)(float32 a, CPUState *env) { - float_status *s = &env->vfp.fp_status; - float32 one = int32_to_float32(1, s); - return float32_div(one, float32_sqrt(a, s), s); + float_status *s = &env->vfp.standard_fp_status; + int result_exp; + float64 f64; + uint32_t val; + uint64_t val64; + + val = float32_val(a); + + if (float32_is_any_nan(a)) { + if (float32_is_signaling_nan(a)) { + float_raise(float_flag_invalid, s); + } + return float32_default_nan; + } else if (float32_is_zero_or_denormal(a)) { + if (!float32_is_zero(a)) { + float_raise(float_flag_input_denormal, s); + } + float_raise(float_flag_divbyzero, s); + return float32_set_sign(float32_infinity, float32_is_neg(a)); + } else if (float32_is_neg(a)) { + float_raise(float_flag_invalid, s); + return float32_default_nan; + } else if (float32_is_infinity(a)) { + return float32_zero; + } + + /* Normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + if ((val & 0x800000) == 0) { + f64 = make_float64(((uint64_t)(val & 0x80000000) << 32) + | (0x3feULL << 52) + | ((uint64_t)(val & 0x7fffff) << 29)); + } else { + f64 = make_float64(((uint64_t)(val & 0x80000000) << 32) + | (0x3fdULL << 52) + | ((uint64_t)(val & 0x7fffff) << 29)); + } + + result_exp = (380 - ((val & 0x7f800000) >> 23)) / 2; + + f64 = recip_sqrt_estimate(f64, env); + + val64 = float64_val(f64); + + val = ((val64 >> 63) & 0x80000000) + | ((result_exp & 0xff) << 23) + | ((val64 >> 29) & 0x7fffff); + return make_float32(val); } uint32_t HELPER(recpe_u32)(uint32_t a, CPUState *env) { - float_status *s = &env->vfp.fp_status; - float32 tmp; - tmp = int32_to_float32(a, s); - tmp = float32_scalbn(tmp, -32, s); - tmp = helper_recpe_f32(tmp, env); - tmp = float32_scalbn(tmp, 31, s); - return float32_to_int32(tmp, s); + float64 f64; + + if ((a & 0x80000000) == 0) { + return 0xffffffff; + } + + f64 = make_float64((0x3feULL << 52) + | ((int64_t)(a & 0x7fffffff) << 21)); + + f64 = recip_estimate (f64, env); + + return 0x80000000 | ((float64_val(f64) >> 21) & 0x7fffffff); } uint32_t HELPER(rsqrte_u32)(uint32_t a, CPUState *env) { - float_status *s = &env->vfp.fp_status; - float32 tmp; - tmp = int32_to_float32(a, s); - tmp = float32_scalbn(tmp, -32, s); - tmp = helper_rsqrte_f32(tmp, env); - tmp = float32_scalbn(tmp, 31, s); - return float32_to_int32(tmp, s); + float64 f64; + + if ((a & 0xc0000000) == 0) { + return 0xffffffff; + } + + if (a & 0x80000000) { + f64 = make_float64((0x3feULL << 52) + | ((uint64_t)(a & 0x7fffffff) << 21)); + } else { /* bits 31-30 == '01' */ + f64 = make_float64((0x3fdULL << 52) + | ((uint64_t)(a & 0x3fffffff) << 22)); + } + + f64 = recip_sqrt_estimate(f64, env); + + return 0x80000000 | ((float64_val(f64) >> 21) & 0x7fffffff); } void HELPER(set_teecr)(CPUState *env, uint32_t val) diff -Nru qemu-kvm-0.14.1+noroms/target-arm/helper.h qemu-kvm-0.15.0+noroms/target-arm/helper.h --- qemu-kvm-0.14.1+noroms/target-arm/helper.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-arm/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,472 @@ +#include "def-helper.h" + +DEF_HELPER_1(clz, i32, i32) +DEF_HELPER_1(sxtb16, i32, i32) +DEF_HELPER_1(uxtb16, i32, i32) + +DEF_HELPER_2(add_setq, i32, i32, i32) +DEF_HELPER_2(add_saturate, i32, i32, i32) +DEF_HELPER_2(sub_saturate, i32, i32, i32) +DEF_HELPER_2(add_usaturate, i32, i32, i32) +DEF_HELPER_2(sub_usaturate, i32, i32, i32) +DEF_HELPER_1(double_saturate, i32, s32) +DEF_HELPER_2(sdiv, s32, s32, s32) +DEF_HELPER_2(udiv, i32, i32, i32) +DEF_HELPER_1(rbit, i32, i32) +DEF_HELPER_1(abs, i32, i32) + +#define PAS_OP(pfx) \ + DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## sub16, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## add16, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## addsubx, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## subaddx, i32, i32, i32, ptr) + +PAS_OP(s) +PAS_OP(u) +#undef PAS_OP + +#define PAS_OP(pfx) \ + DEF_HELPER_2(pfx ## add8, i32, i32, i32) \ + DEF_HELPER_2(pfx ## sub8, i32, i32, i32) \ + DEF_HELPER_2(pfx ## sub16, i32, i32, i32) \ + DEF_HELPER_2(pfx ## add16, i32, i32, i32) \ + DEF_HELPER_2(pfx ## addsubx, i32, i32, i32) \ + DEF_HELPER_2(pfx ## subaddx, i32, i32, i32) +PAS_OP(q) +PAS_OP(sh) +PAS_OP(uq) +PAS_OP(uh) +#undef PAS_OP + +DEF_HELPER_2(ssat, i32, i32, i32) +DEF_HELPER_2(usat, i32, i32, i32) +DEF_HELPER_2(ssat16, i32, i32, i32) +DEF_HELPER_2(usat16, i32, i32, i32) + +DEF_HELPER_2(usad8, i32, i32, i32) + +DEF_HELPER_1(logicq_cc, i32, i64) + +DEF_HELPER_3(sel_flags, i32, i32, i32, i32) +DEF_HELPER_1(exception, void, i32) +DEF_HELPER_0(wfi, void) + +DEF_HELPER_2(cpsr_write, void, i32, i32) +DEF_HELPER_0(cpsr_read, i32) + +DEF_HELPER_3(v7m_msr, void, env, i32, i32) +DEF_HELPER_2(v7m_mrs, i32, env, i32) + +DEF_HELPER_3(set_cp15, void, env, i32, i32) +DEF_HELPER_2(get_cp15, i32, env, i32) + +DEF_HELPER_3(set_cp, void, env, i32, i32) +DEF_HELPER_2(get_cp, i32, env, i32) + +DEF_HELPER_2(get_r13_banked, i32, env, i32) +DEF_HELPER_3(set_r13_banked, void, env, i32, i32) + +DEF_HELPER_1(get_user_reg, i32, i32) +DEF_HELPER_2(set_user_reg, void, i32, i32) + +DEF_HELPER_1(vfp_get_fpscr, i32, env) +DEF_HELPER_2(vfp_set_fpscr, void, env, i32) + +DEF_HELPER_3(vfp_adds, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_addd, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_subs, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_subd, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_muls, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_muld, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_divs, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_divd, f64, f64, f64, ptr) +DEF_HELPER_1(vfp_negs, f32, f32) +DEF_HELPER_1(vfp_negd, f64, f64) +DEF_HELPER_1(vfp_abss, f32, f32) +DEF_HELPER_1(vfp_absd, f64, f64) +DEF_HELPER_2(vfp_sqrts, f32, f32, env) +DEF_HELPER_2(vfp_sqrtd, f64, f64, env) +DEF_HELPER_3(vfp_cmps, void, f32, f32, env) +DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) +DEF_HELPER_3(vfp_cmpes, void, f32, f32, env) +DEF_HELPER_3(vfp_cmped, void, f64, f64, env) + +DEF_HELPER_2(vfp_fcvtds, f64, f32, env) +DEF_HELPER_2(vfp_fcvtsd, f32, f64, env) + +DEF_HELPER_2(vfp_uitos, f32, i32, ptr) +DEF_HELPER_2(vfp_uitod, f64, i32, ptr) +DEF_HELPER_2(vfp_sitos, f32, i32, ptr) +DEF_HELPER_2(vfp_sitod, f64, i32, ptr) + +DEF_HELPER_2(vfp_touis, i32, f32, ptr) +DEF_HELPER_2(vfp_touid, i32, f64, ptr) +DEF_HELPER_2(vfp_touizs, i32, f32, ptr) +DEF_HELPER_2(vfp_touizd, i32, f64, ptr) +DEF_HELPER_2(vfp_tosis, i32, f32, ptr) +DEF_HELPER_2(vfp_tosid, i32, f64, ptr) +DEF_HELPER_2(vfp_tosizs, i32, f32, ptr) +DEF_HELPER_2(vfp_tosizd, i32, f64, ptr) + +DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) +DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) +DEF_HELPER_3(vfp_touhs, i32, f32, i32, ptr) +DEF_HELPER_3(vfp_touls, i32, f32, i32, ptr) +DEF_HELPER_3(vfp_toshd, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_tosld, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_touhd, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_tould, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_shtos, f32, i32, i32, ptr) +DEF_HELPER_3(vfp_sltos, f32, i32, i32, ptr) +DEF_HELPER_3(vfp_uhtos, f32, i32, i32, ptr) +DEF_HELPER_3(vfp_ultos, f32, i32, i32, ptr) +DEF_HELPER_3(vfp_shtod, f64, i64, i32, ptr) +DEF_HELPER_3(vfp_sltod, f64, i64, i32, ptr) +DEF_HELPER_3(vfp_uhtod, f64, i64, i32, ptr) +DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr) + +DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env) +DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env) +DEF_HELPER_2(neon_fcvt_f16_to_f32, f32, i32, env) +DEF_HELPER_2(neon_fcvt_f32_to_f16, i32, f32, env) + +DEF_HELPER_3(recps_f32, f32, f32, f32, env) +DEF_HELPER_3(rsqrts_f32, f32, f32, f32, env) +DEF_HELPER_2(recpe_f32, f32, f32, env) +DEF_HELPER_2(rsqrte_f32, f32, f32, env) +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(add_cc, i32, i32, i32) +DEF_HELPER_2(adc_cc, i32, i32, i32) +DEF_HELPER_2(sub_cc, i32, i32, i32) +DEF_HELPER_2(sbc_cc, i32, i32, i32) + +DEF_HELPER_2(shl, i32, i32, i32) +DEF_HELPER_2(shr, i32, i32, i32) +DEF_HELPER_2(sar, i32, i32, i32) +DEF_HELPER_2(shl_cc, i32, i32, i32) +DEF_HELPER_2(shr_cc, i32, i32, i32) +DEF_HELPER_2(sar_cc, i32, i32, i32) +DEF_HELPER_2(ror_cc, i32, i32, i32) + +/* neon_helper.c */ +DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32) +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) +DEF_HELPER_2(neon_hadd_s16, i32, i32, i32) +DEF_HELPER_2(neon_hadd_u16, i32, i32, i32) +DEF_HELPER_2(neon_hadd_s32, s32, s32, s32) +DEF_HELPER_2(neon_hadd_u32, i32, i32, i32) +DEF_HELPER_2(neon_rhadd_s8, i32, i32, i32) +DEF_HELPER_2(neon_rhadd_u8, i32, i32, i32) +DEF_HELPER_2(neon_rhadd_s16, i32, i32, i32) +DEF_HELPER_2(neon_rhadd_u16, i32, i32, i32) +DEF_HELPER_2(neon_rhadd_s32, s32, s32, s32) +DEF_HELPER_2(neon_rhadd_u32, i32, i32, i32) +DEF_HELPER_2(neon_hsub_s8, i32, i32, i32) +DEF_HELPER_2(neon_hsub_u8, i32, i32, i32) +DEF_HELPER_2(neon_hsub_s16, i32, i32, i32) +DEF_HELPER_2(neon_hsub_u16, i32, i32, i32) +DEF_HELPER_2(neon_hsub_s32, s32, s32, s32) +DEF_HELPER_2(neon_hsub_u32, i32, i32, i32) + +DEF_HELPER_2(neon_cgt_u8, i32, i32, i32) +DEF_HELPER_2(neon_cgt_s8, i32, i32, i32) +DEF_HELPER_2(neon_cgt_u16, i32, i32, i32) +DEF_HELPER_2(neon_cgt_s16, i32, i32, i32) +DEF_HELPER_2(neon_cgt_u32, i32, i32, i32) +DEF_HELPER_2(neon_cgt_s32, i32, i32, i32) +DEF_HELPER_2(neon_cge_u8, i32, i32, i32) +DEF_HELPER_2(neon_cge_s8, i32, i32, i32) +DEF_HELPER_2(neon_cge_u16, i32, i32, i32) +DEF_HELPER_2(neon_cge_s16, i32, i32, i32) +DEF_HELPER_2(neon_cge_u32, i32, i32, i32) +DEF_HELPER_2(neon_cge_s32, i32, i32, i32) + +DEF_HELPER_2(neon_min_u8, i32, i32, i32) +DEF_HELPER_2(neon_min_s8, i32, i32, i32) +DEF_HELPER_2(neon_min_u16, i32, i32, i32) +DEF_HELPER_2(neon_min_s16, i32, i32, i32) +DEF_HELPER_2(neon_min_u32, i32, i32, i32) +DEF_HELPER_2(neon_min_s32, i32, i32, i32) +DEF_HELPER_2(neon_max_u8, i32, i32, i32) +DEF_HELPER_2(neon_max_s8, i32, i32, i32) +DEF_HELPER_2(neon_max_u16, i32, i32, i32) +DEF_HELPER_2(neon_max_s16, i32, i32, i32) +DEF_HELPER_2(neon_max_u32, i32, i32, i32) +DEF_HELPER_2(neon_max_s32, i32, i32, i32) +DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) +DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) +DEF_HELPER_2(neon_pmin_u16, i32, i32, i32) +DEF_HELPER_2(neon_pmin_s16, i32, i32, i32) +DEF_HELPER_2(neon_pmax_u8, i32, i32, i32) +DEF_HELPER_2(neon_pmax_s8, i32, i32, i32) +DEF_HELPER_2(neon_pmax_u16, i32, i32, i32) +DEF_HELPER_2(neon_pmax_s16, i32, i32, i32) + +DEF_HELPER_2(neon_abd_u8, i32, i32, i32) +DEF_HELPER_2(neon_abd_s8, i32, i32, i32) +DEF_HELPER_2(neon_abd_u16, i32, i32, i32) +DEF_HELPER_2(neon_abd_s16, i32, i32, i32) +DEF_HELPER_2(neon_abd_u32, i32, i32, i32) +DEF_HELPER_2(neon_abd_s32, i32, i32, i32) + +DEF_HELPER_2(neon_shl_u8, i32, i32, i32) +DEF_HELPER_2(neon_shl_s8, i32, i32, i32) +DEF_HELPER_2(neon_shl_u16, i32, i32, i32) +DEF_HELPER_2(neon_shl_s16, i32, i32, i32) +DEF_HELPER_2(neon_shl_u32, i32, i32, i32) +DEF_HELPER_2(neon_shl_s32, i32, i32, i32) +DEF_HELPER_2(neon_shl_u64, i64, i64, i64) +DEF_HELPER_2(neon_shl_s64, i64, i64, i64) +DEF_HELPER_2(neon_rshl_u8, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s8, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u32, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s32, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u64, i64, i64, i64) +DEF_HELPER_2(neon_rshl_s64, i64, i64, i64) +DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32) +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) +DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) + +DEF_HELPER_2(neon_add_u8, i32, i32, i32) +DEF_HELPER_2(neon_add_u16, i32, i32, i32) +DEF_HELPER_2(neon_padd_u8, i32, i32, i32) +DEF_HELPER_2(neon_padd_u16, i32, i32, i32) +DEF_HELPER_2(neon_sub_u8, i32, i32, i32) +DEF_HELPER_2(neon_sub_u16, i32, i32, i32) +DEF_HELPER_2(neon_mul_u8, i32, i32, i32) +DEF_HELPER_2(neon_mul_u16, i32, i32, i32) +DEF_HELPER_2(neon_mul_p8, i32, i32, i32) +DEF_HELPER_2(neon_mull_p8, i64, i32, i32) + +DEF_HELPER_2(neon_tst_u8, i32, i32, i32) +DEF_HELPER_2(neon_tst_u16, i32, i32, i32) +DEF_HELPER_2(neon_tst_u32, i32, i32, i32) +DEF_HELPER_2(neon_ceq_u8, i32, i32, i32) +DEF_HELPER_2(neon_ceq_u16, i32, i32, i32) +DEF_HELPER_2(neon_ceq_u32, i32, i32, i32) + +DEF_HELPER_1(neon_abs_s8, i32, i32) +DEF_HELPER_1(neon_abs_s16, i32, i32) +DEF_HELPER_1(neon_clz_u8, i32, i32) +DEF_HELPER_1(neon_clz_u16, i32, i32) +DEF_HELPER_1(neon_cls_s8, i32, i32) +DEF_HELPER_1(neon_cls_s16, i32, i32) +DEF_HELPER_1(neon_cls_s32, i32, i32) +DEF_HELPER_1(neon_cnt_u8, i32, i32) + +DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32) + +DEF_HELPER_1(neon_narrow_u8, i32, i64) +DEF_HELPER_1(neon_narrow_u16, i32, i64) +DEF_HELPER_2(neon_unarrow_sat8, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64) +DEF_HELPER_2(neon_unarrow_sat16, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64) +DEF_HELPER_2(neon_unarrow_sat32, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64) +DEF_HELPER_1(neon_narrow_high_u8, i32, i64) +DEF_HELPER_1(neon_narrow_high_u16, i32, i64) +DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64) +DEF_HELPER_1(neon_narrow_round_high_u16, i32, i64) +DEF_HELPER_1(neon_widen_u8, i64, i32) +DEF_HELPER_1(neon_widen_s8, i64, i32) +DEF_HELPER_1(neon_widen_u16, i64, i32) +DEF_HELPER_1(neon_widen_s16, i64, i32) + +DEF_HELPER_2(neon_addl_u16, i64, i64, i64) +DEF_HELPER_2(neon_addl_u32, i64, i64, i64) +DEF_HELPER_2(neon_paddl_u16, i64, i64, i64) +DEF_HELPER_2(neon_paddl_u32, i64, i64, i64) +DEF_HELPER_2(neon_subl_u16, i64, i64, i64) +DEF_HELPER_2(neon_subl_u32, i64, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) +DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s16, i64, i32, i32) +DEF_HELPER_2(neon_abdl_u32, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s32, i64, i32, i32) +DEF_HELPER_2(neon_abdl_u64, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s64, i64, i32, i32) +DEF_HELPER_2(neon_mull_u8, i64, i32, i32) +DEF_HELPER_2(neon_mull_s8, i64, i32, i32) +DEF_HELPER_2(neon_mull_u16, i64, i32, i32) +DEF_HELPER_2(neon_mull_s16, i64, i32, i32) + +DEF_HELPER_1(neon_negl_u16, i64, i64) +DEF_HELPER_1(neon_negl_u32, i64, i64) +DEF_HELPER_1(neon_negl_u64, i64, i64) + +DEF_HELPER_2(neon_qabs_s8, i32, env, i32) +DEF_HELPER_2(neon_qabs_s16, i32, env, i32) +DEF_HELPER_2(neon_qabs_s32, i32, env, i32) +DEF_HELPER_2(neon_qneg_s8, i32, env, i32) +DEF_HELPER_2(neon_qneg_s16, i32, env, i32) +DEF_HELPER_2(neon_qneg_s32, i32, env, i32) + +DEF_HELPER_3(neon_min_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_max_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_abd_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_cge_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_acge_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, ptr) + +/* iwmmxt_helper.c */ +DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) +DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) +DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) +DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) +DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) + +#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ +DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ + +DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) +DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) + +DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) + +DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) + +DEF_IWMMXT_HELPER_SIZE_ENV(mins) +DEF_IWMMXT_HELPER_SIZE_ENV(minu) +DEF_IWMMXT_HELPER_SIZE_ENV(maxs) +DEF_IWMMXT_HELPER_SIZE_ENV(maxu) + +DEF_IWMMXT_HELPER_SIZE_ENV(subn) +DEF_IWMMXT_HELPER_SIZE_ENV(addn) +DEF_IWMMXT_HELPER_SIZE_ENV(subu) +DEF_IWMMXT_HELPER_SIZE_ENV(addu) +DEF_IWMMXT_HELPER_SIZE_ENV(subs) +DEF_IWMMXT_HELPER_SIZE_ENV(adds) + +DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) + +DEF_HELPER_2(iwmmxt_msadb, i64, i64, i64) + +DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) +DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) + +DEF_HELPER_1(iwmmxt_bcstb, i64, i32) +DEF_HELPER_1(iwmmxt_bcstw, i64, i32) +DEF_HELPER_1(iwmmxt_bcstl, i64, i32) + +DEF_HELPER_1(iwmmxt_addcb, i64, i64) +DEF_HELPER_1(iwmmxt_addcw, i64, i64) +DEF_HELPER_1(iwmmxt_addcl, i64, i64) + +DEF_HELPER_1(iwmmxt_msbb, i32, i64) +DEF_HELPER_1(iwmmxt_msbw, i32, i64) +DEF_HELPER_1(iwmmxt_msbl, i32, i64) + +DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) + +DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) + +DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) +DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) +DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) + +DEF_HELPER_2(set_teecr, void, env, i32) + +DEF_HELPER_3(neon_unzip8, void, env, i32, i32) +DEF_HELPER_3(neon_unzip16, void, env, i32, i32) +DEF_HELPER_3(neon_qunzip8, void, env, i32, i32) +DEF_HELPER_3(neon_qunzip16, void, env, i32, i32) +DEF_HELPER_3(neon_qunzip32, void, env, i32, i32) +DEF_HELPER_3(neon_zip8, void, env, i32, i32) +DEF_HELPER_3(neon_zip16, void, env, i32, i32) +DEF_HELPER_3(neon_qzip8, void, env, i32, i32) +DEF_HELPER_3(neon_qzip16, void, env, i32, i32) +DEF_HELPER_3(neon_qzip32, void, env, i32, i32) + +#include "def-helper.h" diff -Nru qemu-kvm-0.14.1+noroms/target-arm/helpers.h qemu-kvm-0.15.0+noroms/target-arm/helpers.h --- qemu-kvm-0.14.1+noroms/target-arm/helpers.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/helpers.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,458 +0,0 @@ -#include "def-helper.h" - -DEF_HELPER_1(clz, i32, i32) -DEF_HELPER_1(sxtb16, i32, i32) -DEF_HELPER_1(uxtb16, i32, i32) - -DEF_HELPER_2(add_setq, i32, i32, i32) -DEF_HELPER_2(add_saturate, i32, i32, i32) -DEF_HELPER_2(sub_saturate, i32, i32, i32) -DEF_HELPER_2(add_usaturate, i32, i32, i32) -DEF_HELPER_2(sub_usaturate, i32, i32, i32) -DEF_HELPER_1(double_saturate, i32, s32) -DEF_HELPER_2(sdiv, s32, s32, s32) -DEF_HELPER_2(udiv, i32, i32, i32) -DEF_HELPER_1(rbit, i32, i32) -DEF_HELPER_1(abs, i32, i32) - -#define PAS_OP(pfx) \ - DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## sub16, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## add16, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## addsubx, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## subaddx, i32, i32, i32, ptr) - -PAS_OP(s) -PAS_OP(u) -#undef PAS_OP - -#define PAS_OP(pfx) \ - DEF_HELPER_2(pfx ## add8, i32, i32, i32) \ - DEF_HELPER_2(pfx ## sub8, i32, i32, i32) \ - DEF_HELPER_2(pfx ## sub16, i32, i32, i32) \ - DEF_HELPER_2(pfx ## add16, i32, i32, i32) \ - DEF_HELPER_2(pfx ## addsubx, i32, i32, i32) \ - DEF_HELPER_2(pfx ## subaddx, i32, i32, i32) -PAS_OP(q) -PAS_OP(sh) -PAS_OP(uq) -PAS_OP(uh) -#undef PAS_OP - -DEF_HELPER_2(ssat, i32, i32, i32) -DEF_HELPER_2(usat, i32, i32, i32) -DEF_HELPER_2(ssat16, i32, i32, i32) -DEF_HELPER_2(usat16, i32, i32, i32) - -DEF_HELPER_2(usad8, i32, i32, i32) - -DEF_HELPER_1(logicq_cc, i32, i64) - -DEF_HELPER_3(sel_flags, i32, i32, i32, i32) -DEF_HELPER_1(exception, void, i32) -DEF_HELPER_0(wfi, void) - -DEF_HELPER_2(cpsr_write, void, i32, i32) -DEF_HELPER_0(cpsr_read, i32) - -DEF_HELPER_3(v7m_msr, void, env, i32, i32) -DEF_HELPER_2(v7m_mrs, i32, env, i32) - -DEF_HELPER_3(set_cp15, void, env, i32, i32) -DEF_HELPER_2(get_cp15, i32, env, i32) - -DEF_HELPER_3(set_cp, void, env, i32, i32) -DEF_HELPER_2(get_cp, i32, env, i32) - -DEF_HELPER_2(get_r13_banked, i32, env, i32) -DEF_HELPER_3(set_r13_banked, void, env, i32, i32) - -DEF_HELPER_1(get_user_reg, i32, i32) -DEF_HELPER_2(set_user_reg, void, i32, i32) - -DEF_HELPER_1(vfp_get_fpscr, i32, env) -DEF_HELPER_2(vfp_set_fpscr, void, env, i32) - -DEF_HELPER_3(vfp_adds, f32, f32, f32, env) -DEF_HELPER_3(vfp_addd, f64, f64, f64, env) -DEF_HELPER_3(vfp_subs, f32, f32, f32, env) -DEF_HELPER_3(vfp_subd, f64, f64, f64, env) -DEF_HELPER_3(vfp_muls, f32, f32, f32, env) -DEF_HELPER_3(vfp_muld, f64, f64, f64, env) -DEF_HELPER_3(vfp_divs, f32, f32, f32, env) -DEF_HELPER_3(vfp_divd, f64, f64, f64, env) -DEF_HELPER_1(vfp_negs, f32, f32) -DEF_HELPER_1(vfp_negd, f64, f64) -DEF_HELPER_1(vfp_abss, f32, f32) -DEF_HELPER_1(vfp_absd, f64, f64) -DEF_HELPER_2(vfp_sqrts, f32, f32, env) -DEF_HELPER_2(vfp_sqrtd, f64, f64, env) -DEF_HELPER_3(vfp_cmps, void, f32, f32, env) -DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) -DEF_HELPER_3(vfp_cmpes, void, f32, f32, env) -DEF_HELPER_3(vfp_cmped, void, f64, f64, env) - -DEF_HELPER_2(vfp_fcvtds, f64, f32, env) -DEF_HELPER_2(vfp_fcvtsd, f32, f64, env) - -DEF_HELPER_2(vfp_uitos, f32, f32, env) -DEF_HELPER_2(vfp_uitod, f64, f32, env) -DEF_HELPER_2(vfp_sitos, f32, f32, env) -DEF_HELPER_2(vfp_sitod, f64, f32, env) - -DEF_HELPER_2(vfp_touis, f32, f32, env) -DEF_HELPER_2(vfp_touid, f32, f64, env) -DEF_HELPER_2(vfp_touizs, f32, f32, env) -DEF_HELPER_2(vfp_touizd, f32, f64, env) -DEF_HELPER_2(vfp_tosis, f32, f32, env) -DEF_HELPER_2(vfp_tosid, f32, f64, env) -DEF_HELPER_2(vfp_tosizs, f32, f32, env) -DEF_HELPER_2(vfp_tosizd, f32, f64, env) - -DEF_HELPER_3(vfp_toshs, f32, f32, i32, env) -DEF_HELPER_3(vfp_tosls, f32, f32, i32, env) -DEF_HELPER_3(vfp_touhs, f32, f32, i32, env) -DEF_HELPER_3(vfp_touls, f32, f32, i32, env) -DEF_HELPER_3(vfp_toshd, f64, f64, i32, env) -DEF_HELPER_3(vfp_tosld, f64, f64, i32, env) -DEF_HELPER_3(vfp_touhd, f64, f64, i32, env) -DEF_HELPER_3(vfp_tould, f64, f64, i32, env) -DEF_HELPER_3(vfp_shtos, f32, f32, i32, env) -DEF_HELPER_3(vfp_sltos, f32, f32, i32, env) -DEF_HELPER_3(vfp_uhtos, f32, f32, i32, env) -DEF_HELPER_3(vfp_ultos, f32, f32, i32, env) -DEF_HELPER_3(vfp_shtod, f64, f64, i32, env) -DEF_HELPER_3(vfp_sltod, f64, f64, i32, env) -DEF_HELPER_3(vfp_uhtod, f64, f64, i32, env) -DEF_HELPER_3(vfp_ultod, f64, f64, i32, env) - -DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env) -DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env) - -DEF_HELPER_3(recps_f32, f32, f32, f32, env) -DEF_HELPER_3(rsqrts_f32, f32, f32, f32, env) -DEF_HELPER_2(recpe_f32, f32, f32, env) -DEF_HELPER_2(rsqrte_f32, f32, f32, env) -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(add_cc, i32, i32, i32) -DEF_HELPER_2(adc_cc, i32, i32, i32) -DEF_HELPER_2(sub_cc, i32, i32, i32) -DEF_HELPER_2(sbc_cc, i32, i32, i32) - -DEF_HELPER_2(shl, i32, i32, i32) -DEF_HELPER_2(shr, i32, i32, i32) -DEF_HELPER_2(sar, i32, i32, i32) -DEF_HELPER_2(shl_cc, i32, i32, i32) -DEF_HELPER_2(shr_cc, i32, i32, i32) -DEF_HELPER_2(sar_cc, i32, i32, i32) -DEF_HELPER_2(ror_cc, i32, i32, i32) - -/* neon_helper.c */ -DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32) -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) -DEF_HELPER_2(neon_hadd_s16, i32, i32, i32) -DEF_HELPER_2(neon_hadd_u16, i32, i32, i32) -DEF_HELPER_2(neon_hadd_s32, s32, s32, s32) -DEF_HELPER_2(neon_hadd_u32, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s8, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_u8, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s16, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_u16, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s32, s32, s32, s32) -DEF_HELPER_2(neon_rhadd_u32, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s8, i32, i32, i32) -DEF_HELPER_2(neon_hsub_u8, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s16, i32, i32, i32) -DEF_HELPER_2(neon_hsub_u16, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s32, s32, s32, s32) -DEF_HELPER_2(neon_hsub_u32, i32, i32, i32) - -DEF_HELPER_2(neon_cgt_u8, i32, i32, i32) -DEF_HELPER_2(neon_cgt_s8, i32, i32, i32) -DEF_HELPER_2(neon_cgt_u16, i32, i32, i32) -DEF_HELPER_2(neon_cgt_s16, i32, i32, i32) -DEF_HELPER_2(neon_cgt_u32, i32, i32, i32) -DEF_HELPER_2(neon_cgt_s32, i32, i32, i32) -DEF_HELPER_2(neon_cge_u8, i32, i32, i32) -DEF_HELPER_2(neon_cge_s8, i32, i32, i32) -DEF_HELPER_2(neon_cge_u16, i32, i32, i32) -DEF_HELPER_2(neon_cge_s16, i32, i32, i32) -DEF_HELPER_2(neon_cge_u32, i32, i32, i32) -DEF_HELPER_2(neon_cge_s32, i32, i32, i32) - -DEF_HELPER_2(neon_min_u8, i32, i32, i32) -DEF_HELPER_2(neon_min_s8, i32, i32, i32) -DEF_HELPER_2(neon_min_u16, i32, i32, i32) -DEF_HELPER_2(neon_min_s16, i32, i32, i32) -DEF_HELPER_2(neon_min_u32, i32, i32, i32) -DEF_HELPER_2(neon_min_s32, i32, i32, i32) -DEF_HELPER_2(neon_max_u8, i32, i32, i32) -DEF_HELPER_2(neon_max_s8, i32, i32, i32) -DEF_HELPER_2(neon_max_u16, i32, i32, i32) -DEF_HELPER_2(neon_max_s16, i32, i32, i32) -DEF_HELPER_2(neon_max_u32, i32, i32, i32) -DEF_HELPER_2(neon_max_s32, i32, i32, i32) -DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) -DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) -DEF_HELPER_2(neon_pmin_u16, i32, i32, i32) -DEF_HELPER_2(neon_pmin_s16, i32, i32, i32) -DEF_HELPER_2(neon_pmax_u8, i32, i32, i32) -DEF_HELPER_2(neon_pmax_s8, i32, i32, i32) -DEF_HELPER_2(neon_pmax_u16, i32, i32, i32) -DEF_HELPER_2(neon_pmax_s16, i32, i32, i32) - -DEF_HELPER_2(neon_abd_u8, i32, i32, i32) -DEF_HELPER_2(neon_abd_s8, i32, i32, i32) -DEF_HELPER_2(neon_abd_u16, i32, i32, i32) -DEF_HELPER_2(neon_abd_s16, i32, i32, i32) -DEF_HELPER_2(neon_abd_u32, i32, i32, i32) -DEF_HELPER_2(neon_abd_s32, i32, i32, i32) - -DEF_HELPER_2(neon_shl_u8, i32, i32, i32) -DEF_HELPER_2(neon_shl_s8, i32, i32, i32) -DEF_HELPER_2(neon_shl_u16, i32, i32, i32) -DEF_HELPER_2(neon_shl_s16, i32, i32, i32) -DEF_HELPER_2(neon_shl_u32, i32, i32, i32) -DEF_HELPER_2(neon_shl_s32, i32, i32, i32) -DEF_HELPER_2(neon_shl_u64, i64, i64, i64) -DEF_HELPER_2(neon_shl_s64, i64, i64, i64) -DEF_HELPER_2(neon_rshl_u8, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s8, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u32, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s32, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u64, i64, i64, i64) -DEF_HELPER_2(neon_rshl_s64, i64, i64, i64) -DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32) -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) -DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) - -DEF_HELPER_2(neon_add_u8, i32, i32, i32) -DEF_HELPER_2(neon_add_u16, i32, i32, i32) -DEF_HELPER_2(neon_padd_u8, i32, i32, i32) -DEF_HELPER_2(neon_padd_u16, i32, i32, i32) -DEF_HELPER_2(neon_sub_u8, i32, i32, i32) -DEF_HELPER_2(neon_sub_u16, i32, i32, i32) -DEF_HELPER_2(neon_mul_u8, i32, i32, i32) -DEF_HELPER_2(neon_mul_u16, i32, i32, i32) -DEF_HELPER_2(neon_mul_p8, i32, i32, i32) - -DEF_HELPER_2(neon_tst_u8, i32, i32, i32) -DEF_HELPER_2(neon_tst_u16, i32, i32, i32) -DEF_HELPER_2(neon_tst_u32, i32, i32, i32) -DEF_HELPER_2(neon_ceq_u8, i32, i32, i32) -DEF_HELPER_2(neon_ceq_u16, i32, i32, i32) -DEF_HELPER_2(neon_ceq_u32, i32, i32, i32) - -DEF_HELPER_1(neon_abs_s8, i32, i32) -DEF_HELPER_1(neon_abs_s16, i32, i32) -DEF_HELPER_1(neon_clz_u8, i32, i32) -DEF_HELPER_1(neon_clz_u16, i32, i32) -DEF_HELPER_1(neon_cls_s8, i32, i32) -DEF_HELPER_1(neon_cls_s16, i32, i32) -DEF_HELPER_1(neon_cls_s32, i32, i32) -DEF_HELPER_1(neon_cnt_u8, i32, i32) - -DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32) - -DEF_HELPER_1(neon_narrow_u8, i32, i64) -DEF_HELPER_1(neon_narrow_u16, i32, i64) -DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64) -DEF_HELPER_1(neon_narrow_high_u8, i32, i64) -DEF_HELPER_1(neon_narrow_high_u16, i32, i64) -DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64) -DEF_HELPER_1(neon_narrow_round_high_u16, i32, i64) -DEF_HELPER_1(neon_widen_u8, i64, i32) -DEF_HELPER_1(neon_widen_s8, i64, i32) -DEF_HELPER_1(neon_widen_u16, i64, i32) -DEF_HELPER_1(neon_widen_s16, i64, i32) - -DEF_HELPER_2(neon_addl_u16, i64, i64, i64) -DEF_HELPER_2(neon_addl_u32, i64, i64, i64) -DEF_HELPER_2(neon_paddl_u16, i64, i64, i64) -DEF_HELPER_2(neon_paddl_u32, i64, i64, i64) -DEF_HELPER_2(neon_subl_u16, i64, i64, i64) -DEF_HELPER_2(neon_subl_u32, i64, i64, i64) -DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) -DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) -DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s16, i64, i32, i32) -DEF_HELPER_2(neon_abdl_u32, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s32, i64, i32, i32) -DEF_HELPER_2(neon_abdl_u64, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s64, i64, i32, i32) -DEF_HELPER_2(neon_mull_u8, i64, i32, i32) -DEF_HELPER_2(neon_mull_s8, i64, i32, i32) -DEF_HELPER_2(neon_mull_u16, i64, i32, i32) -DEF_HELPER_2(neon_mull_s16, i64, i32, i32) - -DEF_HELPER_1(neon_negl_u16, i64, i64) -DEF_HELPER_1(neon_negl_u32, i64, i64) -DEF_HELPER_1(neon_negl_u64, i64, i64) - -DEF_HELPER_2(neon_qabs_s8, i32, env, i32) -DEF_HELPER_2(neon_qabs_s16, i32, env, i32) -DEF_HELPER_2(neon_qabs_s32, i32, env, i32) -DEF_HELPER_2(neon_qneg_s8, i32, env, i32) -DEF_HELPER_2(neon_qneg_s16, i32, env, i32) -DEF_HELPER_2(neon_qneg_s32, i32, env, i32) - -DEF_HELPER_2(neon_min_f32, i32, i32, i32) -DEF_HELPER_2(neon_max_f32, i32, i32, i32) -DEF_HELPER_2(neon_abd_f32, i32, i32, i32) -DEF_HELPER_2(neon_add_f32, i32, i32, i32) -DEF_HELPER_2(neon_sub_f32, i32, i32, i32) -DEF_HELPER_2(neon_mul_f32, i32, i32, i32) -DEF_HELPER_2(neon_ceq_f32, i32, i32, i32) -DEF_HELPER_2(neon_cge_f32, i32, i32, i32) -DEF_HELPER_2(neon_cgt_f32, i32, i32, i32) -DEF_HELPER_2(neon_acge_f32, i32, i32, i32) -DEF_HELPER_2(neon_acgt_f32, i32, i32, i32) - -/* iwmmxt_helper.c */ -DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) -DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) - -#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ -DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ - -DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) -DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) - -DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) - -DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) - -DEF_IWMMXT_HELPER_SIZE_ENV(mins) -DEF_IWMMXT_HELPER_SIZE_ENV(minu) -DEF_IWMMXT_HELPER_SIZE_ENV(maxs) -DEF_IWMMXT_HELPER_SIZE_ENV(maxu) - -DEF_IWMMXT_HELPER_SIZE_ENV(subn) -DEF_IWMMXT_HELPER_SIZE_ENV(addn) -DEF_IWMMXT_HELPER_SIZE_ENV(subu) -DEF_IWMMXT_HELPER_SIZE_ENV(addu) -DEF_IWMMXT_HELPER_SIZE_ENV(subs) -DEF_IWMMXT_HELPER_SIZE_ENV(adds) - -DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) - -DEF_HELPER_2(iwmmxt_msadb, i64, i64, i64) - -DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) -DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) - -DEF_HELPER_1(iwmmxt_bcstb, i64, i32) -DEF_HELPER_1(iwmmxt_bcstw, i64, i32) -DEF_HELPER_1(iwmmxt_bcstl, i64, i32) - -DEF_HELPER_1(iwmmxt_addcb, i64, i64) -DEF_HELPER_1(iwmmxt_addcw, i64, i64) -DEF_HELPER_1(iwmmxt_addcl, i64, i64) - -DEF_HELPER_1(iwmmxt_msbb, i32, i64) -DEF_HELPER_1(iwmmxt_msbw, i32, i64) -DEF_HELPER_1(iwmmxt_msbl, i32, i64) - -DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) - -DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) - -DEF_HELPER_2(set_teecr, void, env, i32) - -#include "def-helper.h" diff -Nru qemu-kvm-0.14.1+noroms/target-arm/iwmmxt_helper.c qemu-kvm-0.15.0+noroms/target-arm/iwmmxt_helper.c --- qemu-kvm-0.14.1+noroms/target-arm/iwmmxt_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/iwmmxt_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,7 @@ #include "cpu.h" #include "exec-all.h" -#include "helpers.h" +#include "helper.h" /* iwMMXt macros extracted from GNU gdb. */ diff -Nru qemu-kvm-0.14.1+noroms/target-arm/machine.c qemu-kvm-0.15.0+noroms/target-arm/machine.c --- qemu-kvm-0.14.1+noroms/target-arm/machine.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/machine.c 2011-08-09 07:40:29.000000000 -0500 @@ -41,8 +41,15 @@ } qemu_put_be32(f, env->cp15.c6_insn); qemu_put_be32(f, env->cp15.c6_data); + qemu_put_be32(f, env->cp15.c7_par); qemu_put_be32(f, env->cp15.c9_insn); qemu_put_be32(f, env->cp15.c9_data); + qemu_put_be32(f, env->cp15.c9_pmcr); + qemu_put_be32(f, env->cp15.c9_pmcnten); + qemu_put_be32(f, env->cp15.c9_pmovsr); + qemu_put_be32(f, env->cp15.c9_pmxevtyper); + qemu_put_be32(f, env->cp15.c9_pmuserenr); + qemu_put_be32(f, env->cp15.c9_pminten); qemu_put_be32(f, env->cp15.c13_fcse); qemu_put_be32(f, env->cp15.c13_context); qemu_put_be32(f, env->cp15.c13_tls1); @@ -148,8 +155,15 @@ } env->cp15.c6_insn = qemu_get_be32(f); env->cp15.c6_data = qemu_get_be32(f); + env->cp15.c7_par = qemu_get_be32(f); env->cp15.c9_insn = qemu_get_be32(f); env->cp15.c9_data = qemu_get_be32(f); + env->cp15.c9_pmcr = qemu_get_be32(f); + env->cp15.c9_pmcnten = qemu_get_be32(f); + env->cp15.c9_pmovsr = qemu_get_be32(f); + env->cp15.c9_pmxevtyper = qemu_get_be32(f); + env->cp15.c9_pmuserenr = qemu_get_be32(f); + env->cp15.c9_pminten = qemu_get_be32(f); env->cp15.c13_fcse = qemu_get_be32(f); env->cp15.c13_context = qemu_get_be32(f); env->cp15.c13_tls1 = qemu_get_be32(f); diff -Nru qemu-kvm-0.14.1+noroms/target-arm/neon_helper.c qemu-kvm-0.15.0+noroms/target-arm/neon_helper.c --- qemu-kvm-0.14.1+noroms/target-arm/neon_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/neon_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -4,46 +4,20 @@ * Copyright (c) 2007, 2008 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include #include #include "cpu.h" #include "exec-all.h" -#include "helpers.h" +#include "helper.h" #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) #define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] = CPSR_Q -static float_status neon_float_status; -#define NFS &neon_float_status - -/* Helper routines to perform bitwise copies between float and int. */ -static inline float32 vfp_itos(uint32_t i) -{ - union { - uint32_t i; - float32 s; - } v; - - v.i = i; - return v.s; -} - -static inline uint32_t vfp_stoi(float32 s) -{ - union { - uint32_t i; - float32 s; - } v; - - v.s = s; - return v.i; -} - #define NEON_TYPE1(name, type) \ typedef struct \ { \ @@ -543,14 +517,9 @@ #define NEON_FN(dest, src1, src2) do { \ int8_t tmp; \ tmp = (int8_t)src2; \ - if (tmp >= (ssize_t)sizeof(src1) * 8) { \ + if ((tmp >= (ssize_t)sizeof(src1) * 8) \ + || (tmp <= -(ssize_t)sizeof(src1) * 8)) { \ dest = 0; \ - } else if (tmp < -(ssize_t)sizeof(src1) * 8) { \ - dest = src1 >> (sizeof(src1) * 8 - 1); \ - } else if (tmp == -(ssize_t)sizeof(src1) * 8) { \ - dest = src1 >> (tmp - 1); \ - dest++; \ - dest >>= 1; \ } else if (tmp < 0) { \ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \ } else { \ @@ -558,23 +527,45 @@ }} while (0) NEON_VOP(rshl_s8, neon_s8, 4) NEON_VOP(rshl_s16, neon_s16, 2) -NEON_VOP(rshl_s32, neon_s32, 1) #undef NEON_FN +/* The addition of the rounding constant may overflow, so we use an + * intermediate 64 bits accumulator. */ +uint32_t HELPER(neon_rshl_s32)(uint32_t valop, uint32_t shiftop) +{ + int32_t dest; + int32_t val = (int32_t)valop; + int8_t shift = (int8_t)shiftop; + if ((shift >= 32) || (shift <= -32)) { + dest = 0; + } else if (shift < 0) { + int64_t big_dest = ((int64_t)val + (1 << (-1 - shift))); + dest = big_dest >> -shift; + } else { + dest = val << shift; + } + return dest; +} + +/* Handling addition overflow with 64 bits inputs values is more + * tricky than with 32 bits values. */ uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop) { int8_t shift = (int8_t)shiftop; int64_t val = valop; - if (shift >= 64) { + if ((shift >= 64) || (shift <= -64)) { val = 0; - } else if (shift < -64) { - val >>= 63; - } else if (shift == -63) { - val >>= 63; - val++; - val >>= 1; } else if (shift < 0) { - val = (val + ((int64_t)1 << (-1 - shift))) >> -shift; + val >>= (-shift - 1); + if (val == INT64_MAX) { + /* In this case, it means that the rounding constant is 1, + * and the addition would overflow. Return the actual + * result directly. */ + val = 0x4000000000000000LL; + } else { + val++; + val >>= 1; + } } else { val <<= shift; } @@ -588,7 +579,7 @@ tmp < -(ssize_t)sizeof(src1) * 8) { \ dest = 0; \ } else if (tmp == -(ssize_t)sizeof(src1) * 8) { \ - dest = src1 >> (tmp - 1); \ + dest = src1 >> (-tmp - 1); \ } else if (tmp < 0) { \ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \ } else { \ @@ -596,20 +587,48 @@ }} while (0) NEON_VOP(rshl_u8, neon_u8, 4) NEON_VOP(rshl_u16, neon_u16, 2) -NEON_VOP(rshl_u32, neon_u32, 1) #undef NEON_FN +/* The addition of the rounding constant may overflow, so we use an + * intermediate 64 bits accumulator. */ +uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shiftop) +{ + uint32_t dest; + int8_t shift = (int8_t)shiftop; + if (shift >= 32 || shift < -32) { + dest = 0; + } else if (shift == -32) { + dest = val >> 31; + } else if (shift < 0) { + uint64_t big_dest = ((uint64_t)val + (1 << (-1 - shift))); + dest = big_dest >> -shift; + } else { + dest = val << shift; + } + return dest; +} + +/* Handling addition overflow with 64 bits inputs values is more + * tricky than with 32 bits values. */ uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop) { int8_t shift = (uint8_t)shiftop; - if (shift >= 64 || shift < 64) { + if (shift >= 64 || shift < -64) { val = 0; } else if (shift == -64) { /* Rounding a 1-bit result just preserves that bit. */ val >>= 63; - } if (shift < 0) { - val = (val + ((uint64_t)1 << (-1 - shift))) >> -shift; - val >>= -shift; + } else if (shift < 0) { + val >>= (-shift - 1); + if (val == UINT64_MAX) { + /* In this case, it means that the rounding constant is 1, + * and the addition would overflow. Return the actual + * result directly. */ + val = 0x8000000000000000ULL; + } else { + val++; + val >>= 1; + } } else { val <<= shift; } @@ -773,7 +792,18 @@ #define NEON_FN(dest, src1, src2) do { \ int8_t tmp; \ tmp = (int8_t)src2; \ - if (tmp < 0) { \ + 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 == -(ssize_t)sizeof(src1) * 8) { \ + dest = src1 >> (sizeof(src1) * 8 - 1); \ + } else if (tmp < 0) { \ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \ } else { \ dest = src1 << tmp; \ @@ -784,14 +814,63 @@ }} while (0) NEON_VOP_ENV(qrshl_u8, neon_u8, 4) NEON_VOP_ENV(qrshl_u16, neon_u16, 2) -NEON_VOP_ENV(qrshl_u32, neon_u32, 1) #undef NEON_FN +/* The addition of the rounding constant may overflow, so we use an + * intermediate 64 bits accumulator. */ +uint32_t HELPER(neon_qrshl_u32)(CPUState *env, uint32_t val, uint32_t shiftop) +{ + uint32_t dest; + int8_t shift = (int8_t)shiftop; + if (shift >= 32) { + if (val) { + SET_QC(); + dest = ~0; + } else { + dest = 0; + } + } else if (shift < -32) { + dest = 0; + } else if (shift == -32) { + dest = val >> 31; + } else if (shift < 0) { + uint64_t big_dest = ((uint64_t)val + (1 << (-1 - shift))); + dest = big_dest >> -shift; + } else { + dest = val << shift; + if ((dest >> shift) != val) { + SET_QC(); + dest = ~0; + } + } + return dest; +} + +/* Handling addition overflow with 64 bits inputs values is more + * tricky than with 32 bits values. */ uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop) { int8_t shift = (int8_t)shiftop; - if (shift < 0) { - val = (val + (1 << (-1 - shift))) >> -shift; + if (shift >= 64) { + if (val) { + SET_QC(); + val = ~0; + } + } else if (shift < -64) { + val = 0; + } else if (shift == -64) { + val >>= 63; + } else if (shift < 0) { + val >>= (-shift - 1); + if (val == UINT64_MAX) { + /* In this case, it means that the rounding constant is 1, + * and the addition would overflow. Return the actual + * result directly. */ + val = 0x8000000000000000ULL; + } else { + val++; + val >>= 1; + } } else { \ uint64_t tmp = val; val <<= shift; @@ -806,33 +885,94 @@ #define NEON_FN(dest, src1, src2) do { \ int8_t tmp; \ tmp = (int8_t)src2; \ - if (tmp < 0) { \ + if (tmp >= (ssize_t)sizeof(src1) * 8) { \ + if (src1) { \ + SET_QC(); \ + dest = (1 << (sizeof(src1) * 8 - 1)); \ + if (src1 > 0) { \ + dest--; \ + } \ + } else { \ + dest = 0; \ + } \ + } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \ + dest = 0; \ + } else if (tmp < 0) { \ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \ } else { \ dest = src1 << tmp; \ if ((dest >> tmp) != src1) { \ SET_QC(); \ - dest = src1 >> 31; \ + dest = (uint32_t)(1 << (sizeof(src1) * 8 - 1)); \ + if (src1 > 0) { \ + dest--; \ + } \ } \ }} while (0) NEON_VOP_ENV(qrshl_s8, neon_s8, 4) NEON_VOP_ENV(qrshl_s16, neon_s16, 2) -NEON_VOP_ENV(qrshl_s32, neon_s32, 1) #undef NEON_FN +/* The addition of the rounding constant may overflow, so we use an + * intermediate 64 bits accumulator. */ +uint32_t HELPER(neon_qrshl_s32)(CPUState *env, uint32_t valop, uint32_t shiftop) +{ + int32_t dest; + int32_t val = (int32_t)valop; + int8_t shift = (int8_t)shiftop; + if (shift >= 32) { + if (val) { + SET_QC(); + dest = (val >> 31) ^ ~SIGNBIT; + } else { + dest = 0; + } + } else if (shift <= -32) { + dest = 0; + } else if (shift < 0) { + int64_t big_dest = ((int64_t)val + (1 << (-1 - shift))); + dest = big_dest >> -shift; + } else { + dest = val << shift; + if ((dest >> shift) != val) { + SET_QC(); + dest = (val >> 31) ^ ~SIGNBIT; + } + } + return dest; +} + +/* Handling addition overflow with 64 bits inputs values is more + * tricky than with 32 bits values. */ uint64_t HELPER(neon_qrshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop) { int8_t shift = (uint8_t)shiftop; int64_t val = valop; - if (shift < 0) { - val = (val + (1 << (-1 - shift))) >> -shift; + if (shift >= 64) { + if (val) { + SET_QC(); + val = (val >> 63) ^ ~SIGNBIT64; + } + } else if (shift <= -64) { + val = 0; + } else if (shift < 0) { + val >>= (-shift - 1); + if (val == INT64_MAX) { + /* In this case, it means that the rounding constant is 1, + * and the addition would overflow. Return the actual + * result directly. */ + val = 0x4000000000000000ULL; + } else { + val++; + val >>= 1; + } } else { - int64_t tmp = val;; + int64_t tmp = val; val <<= shift; if ((val >> shift) != tmp) { SET_QC(); - val = tmp >> 31; + val = (tmp >> 63) ^ ~SIGNBIT64; } } return val; @@ -895,6 +1035,36 @@ return result; } +uint64_t HELPER(neon_mull_p8)(uint32_t op1, uint32_t op2) +{ + uint64_t result = 0; + uint64_t mask; + uint64_t op2ex = op2; + op2ex = (op2ex & 0xff) | + ((op2ex & 0xff00) << 8) | + ((op2ex & 0xff0000) << 16) | + ((op2ex & 0xff000000) << 24); + while (op1) { + mask = 0; + if (op1 & 1) { + mask |= 0xffff; + } + if (op1 & (1 << 8)) { + mask |= (0xffffU << 16); + } + if (op1 & (1 << 16)) { + mask |= (0xffffULL << 32); + } + if (op1 & (1 << 24)) { + mask |= (0xffffULL << 48); + } + result ^= op2ex & mask; + op1 = (op1 >> 1) & 0x7f7f7f7f; + op2ex <<= 1; + } + return result; +} + #define NEON_FN(dest, src1, src2) dest = (src1 & src2) ? -1 : 0 NEON_VOP(tst_u8, neon_u8, 4) NEON_VOP(tst_u16, neon_u16, 2) @@ -1053,6 +1223,33 @@ return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000); } +uint32_t HELPER(neon_unarrow_sat8)(CPUState *env, uint64_t x) +{ + uint16_t s; + uint8_t d; + uint32_t res = 0; +#define SAT8(n) \ + s = x >> n; \ + if (s & 0x8000) { \ + SET_QC(); \ + } else { \ + if (s > 0xff) { \ + d = 0xff; \ + SET_QC(); \ + } else { \ + d = s; \ + } \ + res |= (uint32_t)d << (n / 2); \ + } + + SAT8(0); + SAT8(16); + SAT8(32); + SAT8(48); +#undef SAT8 + return res; +} + uint32_t HELPER(neon_narrow_sat_u8)(CPUState *env, uint64_t x) { uint16_t s; @@ -1099,6 +1296,29 @@ return res; } +uint32_t HELPER(neon_unarrow_sat16)(CPUState *env, uint64_t x) +{ + uint32_t high; + uint32_t low; + low = x; + if (low & 0x80000000) { + low = 0; + SET_QC(); + } else if (low > 0xffff) { + low = 0xffff; + SET_QC(); + } + high = x >> 32; + if (high & 0x80000000) { + high = 0; + SET_QC(); + } else if (high > 0xffff) { + high = 0xffff; + SET_QC(); + } + return low | (high << 16); +} + uint32_t HELPER(neon_narrow_sat_u16)(CPUState *env, uint64_t x) { uint32_t high; @@ -1133,6 +1353,19 @@ return (uint16_t)low | (high << 16); } +uint32_t HELPER(neon_unarrow_sat32)(CPUState *env, uint64_t x) +{ + if (x & 0x8000000000000000ull) { + SET_QC(); + return 0; + } + if (x > 0xffffffffu) { + SET_QC(); + return 0xffffffffu; + } + return x; +} + uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x) { if (x > 0xffffffffu) { @@ -1146,7 +1379,7 @@ { if ((int64_t)x != (int32_t)x) { SET_QC(); - return (x >> 63) ^ 0x7fffffff; + return ((int64_t)x >> 63) ^ 0x7fffffff; } return x; } @@ -1283,9 +1516,13 @@ return result; } -#define DO_ABD(dest, x, y, type) do { \ - type tmp_x = x; \ - type tmp_y = y; \ +/* We have to do the arithmetic in a larger type than + * the input type, because for example with a signed 32 bit + * op the absolute difference can overflow a signed 32 bit value. + */ +#define DO_ABD(dest, x, y, intype, arithtype) do { \ + arithtype tmp_x = (intype)(x); \ + arithtype tmp_y = (intype)(y); \ dest = ((tmp_x > tmp_y) ? tmp_x - tmp_y : tmp_y - tmp_x); \ } while(0) @@ -1293,12 +1530,12 @@ { uint64_t tmp; uint64_t result; - DO_ABD(result, a, b, uint8_t); - DO_ABD(tmp, a >> 8, b >> 8, uint8_t); + DO_ABD(result, a, b, uint8_t, uint32_t); + DO_ABD(tmp, a >> 8, b >> 8, uint8_t, uint32_t); result |= tmp << 16; - DO_ABD(tmp, a >> 16, b >> 16, uint8_t); + DO_ABD(tmp, a >> 16, b >> 16, uint8_t, uint32_t); result |= tmp << 32; - DO_ABD(tmp, a >> 24, b >> 24, uint8_t); + DO_ABD(tmp, a >> 24, b >> 24, uint8_t, uint32_t); result |= tmp << 48; return result; } @@ -1307,12 +1544,12 @@ { uint64_t tmp; uint64_t result; - DO_ABD(result, a, b, int8_t); - DO_ABD(tmp, a >> 8, b >> 8, int8_t); + DO_ABD(result, a, b, int8_t, int32_t); + DO_ABD(tmp, a >> 8, b >> 8, int8_t, int32_t); result |= tmp << 16; - DO_ABD(tmp, a >> 16, b >> 16, int8_t); + DO_ABD(tmp, a >> 16, b >> 16, int8_t, int32_t); result |= tmp << 32; - DO_ABD(tmp, a >> 24, b >> 24, int8_t); + DO_ABD(tmp, a >> 24, b >> 24, int8_t, int32_t); result |= tmp << 48; return result; } @@ -1321,8 +1558,8 @@ { uint64_t tmp; uint64_t result; - DO_ABD(result, a, b, uint16_t); - DO_ABD(tmp, a >> 16, b >> 16, uint16_t); + DO_ABD(result, a, b, uint16_t, uint32_t); + DO_ABD(tmp, a >> 16, b >> 16, uint16_t, uint32_t); return result | (tmp << 32); } @@ -1330,22 +1567,22 @@ { uint64_t tmp; uint64_t result; - DO_ABD(result, a, b, int16_t); - DO_ABD(tmp, a >> 16, b >> 16, int16_t); + DO_ABD(result, a, b, int16_t, int32_t); + DO_ABD(tmp, a >> 16, b >> 16, int16_t, int32_t); return result | (tmp << 32); } uint64_t HELPER(neon_abdl_u64)(uint32_t a, uint32_t b) { uint64_t result; - DO_ABD(result, a, b, uint32_t); + DO_ABD(result, a, b, uint32_t, uint64_t); return result; } uint64_t HELPER(neon_abdl_s64)(uint32_t a, uint32_t b) { uint64_t result; - DO_ABD(result, a, b, int32_t); + DO_ABD(result, a, b, int32_t, int64_t); return result; } #undef DO_ABD @@ -1421,7 +1658,6 @@ return result; } -#include uint64_t HELPER(neon_negl_u32)(uint64_t x) { uint32_t low = -x; @@ -1536,68 +1772,246 @@ } /* NEON Float helpers. */ -uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b) -{ - float32 f0 = vfp_itos(a); - float32 f1 = vfp_itos(b); - return (float32_compare_quiet(f0, f1, NFS) == -1) ? a : b; -} - -uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b) -{ - float32 f0 = vfp_itos(a); - float32 f1 = vfp_itos(b); - return (float32_compare_quiet(f0, f1, NFS) == 1) ? a : b; -} - -uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b) -{ - float32 f0 = vfp_itos(a); - float32 f1 = vfp_itos(b); - return vfp_stoi((float32_compare_quiet(f0, f1, NFS) == 1) - ? float32_sub(f0, f1, NFS) - : float32_sub(f1, f0, NFS)); -} - -uint32_t HELPER(neon_add_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b, void *fpstp) { - return vfp_stoi(float32_add(vfp_itos(a), vfp_itos(b), NFS)); + float_status *fpst = fpstp; + return float32_val(float32_min(make_float32(a), make_float32(b), fpst)); } -uint32_t HELPER(neon_sub_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b, void *fpstp) { - return vfp_stoi(float32_sub(vfp_itos(a), vfp_itos(b), NFS)); + float_status *fpst = fpstp; + return float32_val(float32_max(make_float32(a), make_float32(b), fpst)); } -uint32_t HELPER(neon_mul_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b, void *fpstp) { - return vfp_stoi(float32_mul(vfp_itos(a), vfp_itos(b), NFS)); -} - -/* Floating point comparisons produce an integer result. */ -#define NEON_VOP_FCMP(name, cmp) \ -uint32_t HELPER(neon_##name)(uint32_t a, uint32_t b) \ -{ \ - if (float32_compare_quiet(vfp_itos(a), vfp_itos(b), NFS) cmp 0) \ - return ~0; \ - else \ - return 0; \ + float_status *fpst = fpstp; + float32 f0 = make_float32(a); + float32 f1 = make_float32(b); + return float32_val(float32_abs(float32_sub(f0, f1, fpst))); } -NEON_VOP_FCMP(ceq_f32, ==) -NEON_VOP_FCMP(cge_f32, >=) -NEON_VOP_FCMP(cgt_f32, >) - -uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b) +/* Floating point comparisons produce an integer result. + * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do. + * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires. + */ +uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, void *fpstp) { - float32 f0 = float32_abs(vfp_itos(a)); - float32 f1 = float32_abs(vfp_itos(b)); - return (float32_compare_quiet(f0, f1,NFS) >= 0) ? ~0 : 0; + float_status *fpst = fpstp; + return -float32_eq_quiet(make_float32(a), make_float32(b), fpst); } -uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, void *fpstp) { - float32 f0 = float32_abs(vfp_itos(a)); - float32 f1 = float32_abs(vfp_itos(b)); - return (float32_compare_quiet(f0, f1, NFS) > 0) ? ~0 : 0; + float_status *fpst = fpstp; + return -float32_le(make_float32(b), make_float32(a), fpst); +} + +uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + return -float32_lt(make_float32(b), make_float32(a), fpst); +} + +uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float32 f0 = float32_abs(make_float32(a)); + float32 f1 = float32_abs(make_float32(b)); + return -float32_le(f1, f0, fpst); +} + +uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float32 f0 = float32_abs(make_float32(a)); + float32 f1 = float32_abs(make_float32(b)); + return -float32_lt(f1, f0, fpst); +} + +#define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1)) + +void HELPER(neon_qunzip8)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm0 = float64_val(env->vfp.regs[rm]); + uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); + uint64_t zd0 = float64_val(env->vfp.regs[rd]); + uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]); + uint64_t d0 = ELEM(zd0, 0, 8) | (ELEM(zd0, 2, 8) << 8) + | (ELEM(zd0, 4, 8) << 16) | (ELEM(zd0, 6, 8) << 24) + | (ELEM(zd1, 0, 8) << 32) | (ELEM(zd1, 2, 8) << 40) + | (ELEM(zd1, 4, 8) << 48) | (ELEM(zd1, 6, 8) << 56); + uint64_t d1 = ELEM(zm0, 0, 8) | (ELEM(zm0, 2, 8) << 8) + | (ELEM(zm0, 4, 8) << 16) | (ELEM(zm0, 6, 8) << 24) + | (ELEM(zm1, 0, 8) << 32) | (ELEM(zm1, 2, 8) << 40) + | (ELEM(zm1, 4, 8) << 48) | (ELEM(zm1, 6, 8) << 56); + uint64_t m0 = ELEM(zd0, 1, 8) | (ELEM(zd0, 3, 8) << 8) + | (ELEM(zd0, 5, 8) << 16) | (ELEM(zd0, 7, 8) << 24) + | (ELEM(zd1, 1, 8) << 32) | (ELEM(zd1, 3, 8) << 40) + | (ELEM(zd1, 5, 8) << 48) | (ELEM(zd1, 7, 8) << 56); + uint64_t m1 = ELEM(zm0, 1, 8) | (ELEM(zm0, 3, 8) << 8) + | (ELEM(zm0, 5, 8) << 16) | (ELEM(zm0, 7, 8) << 24) + | (ELEM(zm1, 1, 8) << 32) | (ELEM(zm1, 3, 8) << 40) + | (ELEM(zm1, 5, 8) << 48) | (ELEM(zm1, 7, 8) << 56); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rm + 1] = make_float64(m1); + env->vfp.regs[rd] = make_float64(d0); + env->vfp.regs[rd + 1] = make_float64(d1); +} + +void HELPER(neon_qunzip16)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm0 = float64_val(env->vfp.regs[rm]); + uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); + uint64_t zd0 = float64_val(env->vfp.regs[rd]); + uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]); + uint64_t d0 = ELEM(zd0, 0, 16) | (ELEM(zd0, 2, 16) << 16) + | (ELEM(zd1, 0, 16) << 32) | (ELEM(zd1, 2, 16) << 48); + uint64_t d1 = ELEM(zm0, 0, 16) | (ELEM(zm0, 2, 16) << 16) + | (ELEM(zm1, 0, 16) << 32) | (ELEM(zm1, 2, 16) << 48); + uint64_t m0 = ELEM(zd0, 1, 16) | (ELEM(zd0, 3, 16) << 16) + | (ELEM(zd1, 1, 16) << 32) | (ELEM(zd1, 3, 16) << 48); + uint64_t m1 = ELEM(zm0, 1, 16) | (ELEM(zm0, 3, 16) << 16) + | (ELEM(zm1, 1, 16) << 32) | (ELEM(zm1, 3, 16) << 48); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rm + 1] = make_float64(m1); + env->vfp.regs[rd] = make_float64(d0); + env->vfp.regs[rd + 1] = make_float64(d1); +} + +void HELPER(neon_qunzip32)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm0 = float64_val(env->vfp.regs[rm]); + uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); + uint64_t zd0 = float64_val(env->vfp.regs[rd]); + uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]); + uint64_t d0 = ELEM(zd0, 0, 32) | (ELEM(zd1, 0, 32) << 32); + uint64_t d1 = ELEM(zm0, 0, 32) | (ELEM(zm1, 0, 32) << 32); + uint64_t m0 = ELEM(zd0, 1, 32) | (ELEM(zd1, 1, 32) << 32); + uint64_t m1 = ELEM(zm0, 1, 32) | (ELEM(zm1, 1, 32) << 32); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rm + 1] = make_float64(m1); + env->vfp.regs[rd] = make_float64(d0); + env->vfp.regs[rd + 1] = make_float64(d1); +} + +void HELPER(neon_unzip8)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm = float64_val(env->vfp.regs[rm]); + uint64_t zd = float64_val(env->vfp.regs[rd]); + uint64_t d0 = ELEM(zd, 0, 8) | (ELEM(zd, 2, 8) << 8) + | (ELEM(zd, 4, 8) << 16) | (ELEM(zd, 6, 8) << 24) + | (ELEM(zm, 0, 8) << 32) | (ELEM(zm, 2, 8) << 40) + | (ELEM(zm, 4, 8) << 48) | (ELEM(zm, 6, 8) << 56); + uint64_t m0 = ELEM(zd, 1, 8) | (ELEM(zd, 3, 8) << 8) + | (ELEM(zd, 5, 8) << 16) | (ELEM(zd, 7, 8) << 24) + | (ELEM(zm, 1, 8) << 32) | (ELEM(zm, 3, 8) << 40) + | (ELEM(zm, 5, 8) << 48) | (ELEM(zm, 7, 8) << 56); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rd] = make_float64(d0); +} + +void HELPER(neon_unzip16)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm = float64_val(env->vfp.regs[rm]); + uint64_t zd = float64_val(env->vfp.regs[rd]); + uint64_t d0 = ELEM(zd, 0, 16) | (ELEM(zd, 2, 16) << 16) + | (ELEM(zm, 0, 16) << 32) | (ELEM(zm, 2, 16) << 48); + uint64_t m0 = ELEM(zd, 1, 16) | (ELEM(zd, 3, 16) << 16) + | (ELEM(zm, 1, 16) << 32) | (ELEM(zm, 3, 16) << 48); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rd] = make_float64(d0); +} + +void HELPER(neon_qzip8)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm0 = float64_val(env->vfp.regs[rm]); + uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); + uint64_t zd0 = float64_val(env->vfp.regs[rd]); + uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]); + uint64_t d0 = ELEM(zd0, 0, 8) | (ELEM(zm0, 0, 8) << 8) + | (ELEM(zd0, 1, 8) << 16) | (ELEM(zm0, 1, 8) << 24) + | (ELEM(zd0, 2, 8) << 32) | (ELEM(zm0, 2, 8) << 40) + | (ELEM(zd0, 3, 8) << 48) | (ELEM(zm0, 3, 8) << 56); + uint64_t d1 = ELEM(zd0, 4, 8) | (ELEM(zm0, 4, 8) << 8) + | (ELEM(zd0, 5, 8) << 16) | (ELEM(zm0, 5, 8) << 24) + | (ELEM(zd0, 6, 8) << 32) | (ELEM(zm0, 6, 8) << 40) + | (ELEM(zd0, 7, 8) << 48) | (ELEM(zm0, 7, 8) << 56); + uint64_t m0 = ELEM(zd1, 0, 8) | (ELEM(zm1, 0, 8) << 8) + | (ELEM(zd1, 1, 8) << 16) | (ELEM(zm1, 1, 8) << 24) + | (ELEM(zd1, 2, 8) << 32) | (ELEM(zm1, 2, 8) << 40) + | (ELEM(zd1, 3, 8) << 48) | (ELEM(zm1, 3, 8) << 56); + uint64_t m1 = ELEM(zd1, 4, 8) | (ELEM(zm1, 4, 8) << 8) + | (ELEM(zd1, 5, 8) << 16) | (ELEM(zm1, 5, 8) << 24) + | (ELEM(zd1, 6, 8) << 32) | (ELEM(zm1, 6, 8) << 40) + | (ELEM(zd1, 7, 8) << 48) | (ELEM(zm1, 7, 8) << 56); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rm + 1] = make_float64(m1); + env->vfp.regs[rd] = make_float64(d0); + env->vfp.regs[rd + 1] = make_float64(d1); +} + +void HELPER(neon_qzip16)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm0 = float64_val(env->vfp.regs[rm]); + uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); + uint64_t zd0 = float64_val(env->vfp.regs[rd]); + uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]); + uint64_t d0 = ELEM(zd0, 0, 16) | (ELEM(zm0, 0, 16) << 16) + | (ELEM(zd0, 1, 16) << 32) | (ELEM(zm0, 1, 16) << 48); + uint64_t d1 = ELEM(zd0, 2, 16) | (ELEM(zm0, 2, 16) << 16) + | (ELEM(zd0, 3, 16) << 32) | (ELEM(zm0, 3, 16) << 48); + uint64_t m0 = ELEM(zd1, 0, 16) | (ELEM(zm1, 0, 16) << 16) + | (ELEM(zd1, 1, 16) << 32) | (ELEM(zm1, 1, 16) << 48); + uint64_t m1 = ELEM(zd1, 2, 16) | (ELEM(zm1, 2, 16) << 16) + | (ELEM(zd1, 3, 16) << 32) | (ELEM(zm1, 3, 16) << 48); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rm + 1] = make_float64(m1); + env->vfp.regs[rd] = make_float64(d0); + env->vfp.regs[rd + 1] = make_float64(d1); +} + +void HELPER(neon_qzip32)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm0 = float64_val(env->vfp.regs[rm]); + uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); + uint64_t zd0 = float64_val(env->vfp.regs[rd]); + uint64_t zd1 = float64_val(env->vfp.regs[rd + 1]); + uint64_t d0 = ELEM(zd0, 0, 32) | (ELEM(zm0, 0, 32) << 32); + uint64_t d1 = ELEM(zd0, 1, 32) | (ELEM(zm0, 1, 32) << 32); + uint64_t m0 = ELEM(zd1, 0, 32) | (ELEM(zm1, 0, 32) << 32); + uint64_t m1 = ELEM(zd1, 1, 32) | (ELEM(zm1, 1, 32) << 32); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rm + 1] = make_float64(m1); + env->vfp.regs[rd] = make_float64(d0); + env->vfp.regs[rd + 1] = make_float64(d1); +} + +void HELPER(neon_zip8)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm = float64_val(env->vfp.regs[rm]); + uint64_t zd = float64_val(env->vfp.regs[rd]); + uint64_t d0 = ELEM(zd, 0, 8) | (ELEM(zm, 0, 8) << 8) + | (ELEM(zd, 1, 8) << 16) | (ELEM(zm, 1, 8) << 24) + | (ELEM(zd, 2, 8) << 32) | (ELEM(zm, 2, 8) << 40) + | (ELEM(zd, 3, 8) << 48) | (ELEM(zm, 3, 8) << 56); + uint64_t m0 = ELEM(zd, 4, 8) | (ELEM(zm, 4, 8) << 8) + | (ELEM(zd, 5, 8) << 16) | (ELEM(zm, 5, 8) << 24) + | (ELEM(zd, 6, 8) << 32) | (ELEM(zm, 6, 8) << 40) + | (ELEM(zd, 7, 8) << 48) | (ELEM(zm, 7, 8) << 56); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rd] = make_float64(d0); +} + +void HELPER(neon_zip16)(CPUState *env, uint32_t rd, uint32_t rm) +{ + uint64_t zm = float64_val(env->vfp.regs[rm]); + uint64_t zd = float64_val(env->vfp.regs[rd]); + uint64_t d0 = ELEM(zd, 0, 16) | (ELEM(zm, 0, 16) << 16) + | (ELEM(zd, 1, 16) << 32) | (ELEM(zm, 1, 16) << 48); + uint64_t m0 = ELEM(zd, 2, 16) | (ELEM(zm, 2, 16) << 16) + | (ELEM(zd, 3, 16) << 32) | (ELEM(zm, 3, 16) << 48); + env->vfp.regs[rm] = make_float64(m0); + env->vfp.regs[rd] = make_float64(d0); } diff -Nru qemu-kvm-0.14.1+noroms/target-arm/op_addsub.h qemu-kvm-0.15.0+noroms/target-arm/op_addsub.h --- qemu-kvm-0.14.1+noroms/target-arm/op_addsub.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/op_addsub.h 2011-08-09 07:40:29.000000000 -0500 @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #ifdef ARITH_GE diff -Nru qemu-kvm-0.14.1+noroms/target-arm/op_helper.c qemu-kvm-0.15.0+noroms/target-arm/op_helper.c --- qemu-kvm-0.14.1+noroms/target-arm/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,7 +17,7 @@ * License along with this library; if not, see . */ #include "exec.h" -#include "helpers.h" +#include "helper.h" #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) @@ -25,7 +25,7 @@ void raise_exception(int tt) { env->exception_index = tt; - cpu_loop_exit(); + cpu_loop_exit(env); } uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, @@ -90,7 +90,7 @@ 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); + cpu_restore_state(tb, env, pc); } } raise_exception(env->exception_index); @@ -234,13 +234,13 @@ { env->exception_index = EXCP_HLT; env->halted = 1; - cpu_loop_exit(); + cpu_loop_exit(env); } void HELPER(exception)(uint32_t excp) { env->exception_index = excp; - cpu_loop_exit(); + cpu_loop_exit(env); } uint32_t HELPER(cpsr_read)(void) diff -Nru qemu-kvm-0.14.1+noroms/target-arm/translate.c qemu-kvm-0.15.0+noroms/target-arm/translate.c --- qemu-kvm-0.14.1+noroms/target-arm/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-arm/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,15 +25,18 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "qemu-log.h" -#include "helpers.h" +#include "helper.h" #define GEN_HELPER 1 -#include "helpers.h" +#include "helper.h" +#define ENABLE_ARCH_4T arm_feature(env, ARM_FEATURE_V4T) +#define ENABLE_ARCH_5 arm_feature(env, ARM_FEATURE_V5) +/* currently all emulated v5 cores are also v5TE, so don't bother */ +#define ENABLE_ARCH_5TE arm_feature(env, ARM_FEATURE_V5) #define ENABLE_ARCH_5J 0 #define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6) #define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K) @@ -125,28 +128,12 @@ #endif #define GEN_HELPER 2 -#include "helpers.h" -} - -static int num_temps; - -/* Allocate a temporary variable. */ -static TCGv_i32 new_tmp(void) -{ - num_temps++; - return tcg_temp_new_i32(); -} - -/* Release a temporary variable. */ -static void dead_tmp(TCGv tmp) -{ - tcg_temp_free(tmp); - num_temps--; +#include "helper.h" } static inline TCGv load_cpu_offset(int offset) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_ld_i32(tmp, cpu_env, offset); return tmp; } @@ -156,7 +143,7 @@ static inline void store_cpu_offset(TCGv var, int offset) { tcg_gen_st_i32(var, cpu_env, offset); - dead_tmp(var); + tcg_temp_free_i32(var); } #define store_cpu_field(var, name) \ @@ -181,7 +168,7 @@ /* Create a new temporary and set it to the value of a CPU register. */ static inline TCGv load_reg(DisasContext *s, int reg) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); load_reg_var(s, tmp, reg); return tmp; } @@ -195,7 +182,7 @@ s->is_jmp = DISAS_JUMP; } tcg_gen_mov_i32(cpu_R[reg], var); - dead_tmp(var); + tcg_temp_free_i32(var); } /* Value extensions. */ @@ -219,37 +206,37 @@ static void gen_exception(int excp) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, excp); gen_helper_exception(tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } static void gen_smul_dual(TCGv a, TCGv b) { - TCGv tmp1 = new_tmp(); - TCGv tmp2 = new_tmp(); + TCGv tmp1 = tcg_temp_new_i32(); + TCGv tmp2 = tcg_temp_new_i32(); tcg_gen_ext16s_i32(tmp1, a); tcg_gen_ext16s_i32(tmp2, b); tcg_gen_mul_i32(tmp1, tmp1, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_sari_i32(a, a, 16); tcg_gen_sari_i32(b, b, 16); tcg_gen_mul_i32(b, b, a); tcg_gen_mov_i32(a, tmp1); - dead_tmp(tmp1); + tcg_temp_free_i32(tmp1); } /* Byteswap each halfword. */ static void gen_rev16(TCGv var) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp, var, 8); tcg_gen_andi_i32(tmp, tmp, 0x00ff00ff); tcg_gen_shli_i32(var, var, 8); tcg_gen_andi_i32(var, var, 0xff00ff00); tcg_gen_or_i32(var, var, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* Byteswap low halfword and sign extend. */ @@ -298,7 +285,7 @@ TCGv_i64 tmp64 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(tmp64, b); - dead_tmp(b); + tcg_temp_free_i32(b); tcg_gen_shli_i64(tmp64, tmp64, 32); tcg_gen_add_i64(a, tmp64, a); @@ -312,7 +299,7 @@ TCGv_i64 tmp64 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(tmp64, b); - dead_tmp(b); + tcg_temp_free_i32(b); tcg_gen_shli_i64(tmp64, tmp64, 32); tcg_gen_sub_i64(a, tmp64, a); @@ -329,9 +316,9 @@ TCGv_i64 tmp2 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(tmp1, a); - dead_tmp(a); + tcg_temp_free_i32(a); tcg_gen_extu_i32_i64(tmp2, b); - dead_tmp(b); + tcg_temp_free_i32(b); tcg_gen_mul_i64(tmp1, tmp1, tmp2); tcg_temp_free_i64(tmp2); return tmp1; @@ -343,9 +330,9 @@ TCGv_i64 tmp2 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp1, a); - dead_tmp(a); + tcg_temp_free_i32(a); tcg_gen_ext_i32_i64(tmp2, b); - dead_tmp(b); + tcg_temp_free_i32(b); tcg_gen_mul_i64(tmp1, tmp1, tmp2); tcg_temp_free_i64(tmp2); return tmp1; @@ -354,11 +341,11 @@ /* Swap low and high halfwords. */ static void gen_swap_half(TCGv var) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp, var, 16); tcg_gen_shli_i32(var, var, 16); tcg_gen_or_i32(var, var, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* Dual 16-bit add. Result placed in t0 and t1 is marked as dead. @@ -370,15 +357,15 @@ static void gen_add16(TCGv t0, TCGv t1) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, t0, t1); tcg_gen_andi_i32(tmp, tmp, 0x8000); tcg_gen_andi_i32(t0, t0, ~0x8000); tcg_gen_andi_i32(t1, t1, ~0x8000); tcg_gen_add_i32(t0, t0, t1); tcg_gen_xor_i32(t0, t0, tmp); - dead_tmp(tmp); - dead_tmp(t1); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(t1); } #define gen_set_CF(var) tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, CF)) @@ -386,10 +373,10 @@ /* Set CF to the top bit of var. */ static void gen_set_CF_bit31(TCGv var) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp, var, 31); gen_set_CF(tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* Set N and Z flags from var. */ @@ -406,7 +393,7 @@ tcg_gen_add_i32(t0, t0, t1); tmp = load_cpu_field(CF); tcg_gen_add_i32(t0, t0, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* dest = T0 + T1 + CF. */ @@ -416,7 +403,7 @@ tcg_gen_add_i32(dest, t0, t1); tmp = load_cpu_field(CF); tcg_gen_add_i32(dest, dest, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* dest = T0 - T1 + CF - 1. */ @@ -427,7 +414,7 @@ tmp = load_cpu_field(CF); tcg_gen_add_i32(dest, dest, tmp); tcg_gen_subi_i32(dest, dest, 1); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* FIXME: Implement this natively. */ @@ -435,7 +422,7 @@ static void shifter_out_im(TCGv var, int shift) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); if (shift == 0) { tcg_gen_andi_i32(tmp, var, 1); } else { @@ -444,7 +431,7 @@ tcg_gen_andi_i32(tmp, tmp, 1); } gen_set_CF(tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* Shift by immediate. Includes special handling for shift == 0. */ @@ -492,7 +479,7 @@ tcg_gen_shri_i32(var, var, 1); tcg_gen_shli_i32(tmp, tmp, 31); tcg_gen_or_i32(var, var, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } } }; @@ -516,7 +503,7 @@ tcg_gen_rotr_i32(var, var, shift); break; } } - dead_tmp(shift); + tcg_temp_free_i32(shift); } #define PAS_OP(pfx) \ @@ -655,7 +642,7 @@ inv = gen_new_label(); tmp = load_cpu_field(CF); tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tmp = load_cpu_field(ZF); tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label); gen_set_label(inv); @@ -663,7 +650,7 @@ case 9: /* ls: !C || Z */ tmp = load_cpu_field(CF); tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tmp = load_cpu_field(ZF); tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); break; @@ -671,43 +658,43 @@ tmp = load_cpu_field(VF); tmp2 = load_cpu_field(NF); tcg_gen_xor_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); break; case 11: /* lt: N != V -> N ^ V != 0 */ tmp = load_cpu_field(VF); tmp2 = load_cpu_field(NF); tcg_gen_xor_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); break; case 12: /* gt: !Z && N == V */ inv = gen_new_label(); tmp = load_cpu_field(ZF); tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tmp = load_cpu_field(VF); tmp2 = load_cpu_field(NF); tcg_gen_xor_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); gen_set_label(inv); break; case 13: /* le: Z || N != V */ tmp = load_cpu_field(ZF); tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tmp = load_cpu_field(VF); tmp2 = load_cpu_field(NF); tcg_gen_xor_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); break; default: fprintf(stderr, "Bad condition code 0x%x\n", cc); abort(); } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } static const uint8_t table_logic_cc[16] = { @@ -736,10 +723,10 @@ s->is_jmp = DISAS_UPDATE; if (s->thumb != (addr & 1)) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, addr & 1); tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, thumb)); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } tcg_gen_movi_i32(cpu_R[15], addr & ~1); } @@ -766,33 +753,47 @@ } } +/* Variant of store_reg which uses branch&exchange logic when storing + * to r15 in ARM architecture v5T and above. This is used for storing + * the results of a LDR/LDM/POP into r15, and corresponds to the cases + * in the ARM ARM which use the LoadWritePC() pseudocode function. */ +static inline void store_reg_from_load(CPUState *env, DisasContext *s, + int reg, TCGv var) +{ + if (reg == 15 && ENABLE_ARCH_5) { + gen_bx(s, var); + } else { + store_reg(s, reg, var); + } +} + static inline TCGv gen_ld8s(TCGv addr, int index) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_qemu_ld8s(tmp, addr, index); return tmp; } static inline TCGv gen_ld8u(TCGv addr, int index) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_qemu_ld8u(tmp, addr, index); return tmp; } static inline TCGv gen_ld16s(TCGv addr, int index) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_qemu_ld16s(tmp, addr, index); return tmp; } static inline TCGv gen_ld16u(TCGv addr, int index) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_qemu_ld16u(tmp, addr, index); return tmp; } static inline TCGv gen_ld32(TCGv addr, int index) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_qemu_ld32u(tmp, addr, index); return tmp; } @@ -805,17 +806,17 @@ static inline void gen_st8(TCGv val, TCGv addr, int index) { tcg_gen_qemu_st8(val, addr, index); - dead_tmp(val); + tcg_temp_free_i32(val); } static inline void gen_st16(TCGv val, TCGv addr, int index) { tcg_gen_qemu_st16(val, addr, index); - dead_tmp(val); + tcg_temp_free_i32(val); } static inline void gen_st32(TCGv val, TCGv addr, int index) { tcg_gen_qemu_st32(val, addr, index); - dead_tmp(val); + tcg_temp_free_i32(val); } static inline void gen_st64(TCGv_i64 val, TCGv addr, int index) { @@ -859,7 +860,7 @@ tcg_gen_sub_i32(var, var, offset); else tcg_gen_add_i32(var, var, offset); - dead_tmp(offset); + tcg_temp_free_i32(offset); } } @@ -887,17 +888,33 @@ tcg_gen_sub_i32(var, var, offset); else tcg_gen_add_i32(var, var, offset); - dead_tmp(offset); + tcg_temp_free_i32(offset); + } +} + +static TCGv_ptr get_fpstatus_ptr(int neon) +{ + TCGv_ptr statusptr = tcg_temp_new_ptr(); + int offset; + if (neon) { + offset = offsetof(CPUState, vfp.standard_fp_status); + } else { + offset = offsetof(CPUState, vfp.fp_status); } + tcg_gen_addi_ptr(statusptr, cpu_env, offset); + return statusptr; } #define VFP_OP2(name) \ static inline void gen_vfp_##name(int dp) \ { \ - if (dp) \ - gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, cpu_env); \ - else \ - gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, cpu_env); \ + TCGv_ptr fpst = get_fpstatus_ptr(0); \ + if (dp) { \ + gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, fpst); \ + } else { \ + gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, fpst); \ + } \ + tcg_temp_free_ptr(fpst); \ } VFP_OP2(add) @@ -907,6 +924,28 @@ #undef VFP_OP2 +static inline void gen_vfp_F1_mul(int dp) +{ + /* Like gen_vfp_mul() but put result in F1 */ + TCGv_ptr fpst = get_fpstatus_ptr(0); + if (dp) { + gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, fpst); + } else { + gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, fpst); + } + tcg_temp_free_ptr(fpst); +} + +static inline void gen_vfp_F1_neg(int dp) +{ + /* Like gen_vfp_neg() but put result in F1 */ + if (dp) { + gen_helper_vfp_negd(cpu_F1d, cpu_F0d); + } else { + gen_helper_vfp_negs(cpu_F1s, cpu_F0s); + } +} + static inline void gen_vfp_abs(int dp) { if (dp) @@ -955,63 +994,52 @@ tcg_gen_movi_i32(cpu_F1s, 0); } -static inline void gen_vfp_uito(int dp) -{ - if (dp) - gen_helper_vfp_uitod(cpu_F0d, cpu_F0s, cpu_env); - else - gen_helper_vfp_uitos(cpu_F0s, cpu_F0s, cpu_env); -} - -static inline void gen_vfp_sito(int dp) -{ - if (dp) - gen_helper_vfp_sitod(cpu_F0d, cpu_F0s, cpu_env); - else - gen_helper_vfp_sitos(cpu_F0s, cpu_F0s, cpu_env); -} - -static inline void gen_vfp_toui(int dp) -{ - if (dp) - gen_helper_vfp_touid(cpu_F0s, cpu_F0d, cpu_env); - else - gen_helper_vfp_touis(cpu_F0s, cpu_F0s, cpu_env); +#define VFP_GEN_ITOF(name) \ +static inline void gen_vfp_##name(int dp, int neon) \ +{ \ + TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ + if (dp) { \ + gen_helper_vfp_##name##d(cpu_F0d, cpu_F0s, statusptr); \ + } else { \ + gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \ + } \ + tcg_temp_free_ptr(statusptr); \ } -static inline void gen_vfp_touiz(int dp) -{ - if (dp) - gen_helper_vfp_touizd(cpu_F0s, cpu_F0d, cpu_env); - else - gen_helper_vfp_touizs(cpu_F0s, cpu_F0s, cpu_env); -} +VFP_GEN_ITOF(uito) +VFP_GEN_ITOF(sito) +#undef VFP_GEN_ITOF -static inline void gen_vfp_tosi(int dp) -{ - if (dp) - gen_helper_vfp_tosid(cpu_F0s, cpu_F0d, cpu_env); - else - gen_helper_vfp_tosis(cpu_F0s, cpu_F0s, cpu_env); +#define VFP_GEN_FTOI(name) \ +static inline void gen_vfp_##name(int dp, int neon) \ +{ \ + TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ + if (dp) { \ + gen_helper_vfp_##name##d(cpu_F0s, cpu_F0d, statusptr); \ + } else { \ + gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \ + } \ + tcg_temp_free_ptr(statusptr); \ } -static inline void gen_vfp_tosiz(int dp) -{ - if (dp) - gen_helper_vfp_tosizd(cpu_F0s, cpu_F0d, cpu_env); - else - gen_helper_vfp_tosizs(cpu_F0s, cpu_F0s, cpu_env); -} +VFP_GEN_FTOI(toui) +VFP_GEN_FTOI(touiz) +VFP_GEN_FTOI(tosi) +VFP_GEN_FTOI(tosiz) +#undef VFP_GEN_FTOI #define VFP_GEN_FIX(name) \ -static inline void gen_vfp_##name(int dp, int shift) \ +static inline void gen_vfp_##name(int dp, int shift, int neon) \ { \ TCGv tmp_shift = tcg_const_i32(shift); \ - if (dp) \ - gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, cpu_env);\ - else \ - gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tmp_shift, cpu_env);\ + TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ + if (dp) { \ + gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, statusptr); \ + } else { \ + gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tmp_shift, statusptr); \ + } \ tcg_temp_free_i32(tmp_shift); \ + tcg_temp_free_ptr(statusptr); \ } VFP_GEN_FIX(tosh) VFP_GEN_FIX(tosl) @@ -1065,7 +1093,7 @@ static TCGv neon_load_reg(int reg, int pass) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_ld_i32(tmp, cpu_env, neon_reg_offset(reg, pass)); return tmp; } @@ -1073,7 +1101,7 @@ static void neon_store_reg(int reg, int pass, TCGv var) { tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass)); - dead_tmp(var); + tcg_temp_free_i32(var); } static inline void neon_load_reg64(TCGv_i64 var, int reg) @@ -1129,7 +1157,7 @@ static inline TCGv iwmmxt_load_creg(int reg) { - TCGv var = new_tmp(); + TCGv var = tcg_temp_new_i32(); tcg_gen_ld_i32(var, cpu_env, offsetof(CPUState, iwmmxt.cregs[reg])); return var; } @@ -1137,7 +1165,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); + tcg_temp_free_i32(var); } static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) @@ -1268,7 +1296,7 @@ static void gen_op_iwmmxt_setpsr_nz(void) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0); store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]); } @@ -1300,7 +1328,7 @@ if (insn & (1 << 21)) store_reg(s, rd, tmp); else - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else if (insn & (1 << 21)) { /* Post indexed */ tcg_gen_mov_i32(dest, tmp); @@ -1326,17 +1354,17 @@ tmp = iwmmxt_load_creg(rd); } } else { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); iwmmxt_load_reg(cpu_V0, rd); tcg_gen_trunc_i64_i32(tmp, cpu_V0); } tcg_gen_andi_i32(tmp, tmp, mask); tcg_gen_mov_i32(dest, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 0; } -/* Disassemble an iwMMXt instruction. Returns nonzero if an error occured +/* Disassemble an iwMMXt instruction. Returns nonzero if an error occurred (ie. an undefined instruction). */ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn) { @@ -1364,14 +1392,14 @@ } wrd = (insn >> 12) & 0xf; - addr = new_tmp(); + addr = tcg_temp_new_i32(); if (gen_iwmmxt_address(s, insn, addr)) { - dead_tmp(addr); + tcg_temp_free_i32(addr); return 1; } if (insn & ARM_CP_RW_BIT) { if ((insn >> 28) == 0xf) { /* WLDRW wCx */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); iwmmxt_store_creg(wrd, tmp); } else { @@ -1392,7 +1420,7 @@ } if (i) { tcg_gen_extu_i32_i64(cpu_M0, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } gen_op_iwmmxt_movq_wRn_M0(wrd); } @@ -1402,10 +1430,10 @@ gen_st32(tmp, addr, IS_USER(s)); } else { gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (insn & (1 << 8)) { if (insn & (1 << 22)) { /* WSTRD */ - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tcg_gen_qemu_st64(cpu_M0, addr, IS_USER(s)); } else { /* WSTRW wRd */ tcg_gen_trunc_i64_i32(tmp, cpu_M0); @@ -1422,7 +1450,7 @@ } } } - dead_tmp(addr); + tcg_temp_free_i32(addr); return 0; } @@ -1457,7 +1485,7 @@ tmp = iwmmxt_load_creg(wrd); tmp2 = load_reg(s, rd); tcg_gen_andc_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); iwmmxt_store_creg(wrd, tmp); break; case ARM_IWMMXT_wCGR0: @@ -1670,7 +1698,7 @@ tcg_gen_andi_i32(tmp, tmp, 7); iwmmxt_load_reg(cpu_V1, rd1); gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -1701,7 +1729,7 @@ gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3); tcg_temp_free(tmp3); tcg_temp_free(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -1711,7 +1739,7 @@ if (rd == 15 || ((insn >> 22) & 3) == 3) return 1; gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); switch ((insn >> 22) & 3) { case 0: tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3); @@ -1755,7 +1783,7 @@ } tcg_gen_shli_i32(tmp, tmp, 28); gen_set_nzcv(tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ if (((insn >> 6) & 3) == 3) @@ -1774,7 +1802,7 @@ gen_helper_iwmmxt_bcstl(cpu_M0, tmp); break; } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -1782,7 +1810,7 @@ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp2, tmp); switch ((insn >> 22) & 3) { case 0: @@ -1803,8 +1831,8 @@ break; } gen_set_nzcv(tmp); - dead_tmp(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); break; case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ wrd = (insn >> 12) & 0xf; @@ -1830,7 +1858,7 @@ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp2, tmp); switch ((insn >> 22) & 3) { case 0: @@ -1851,8 +1879,8 @@ break; } gen_set_nzcv(tmp); - dead_tmp(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); break; case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ rd = (insn >> 12) & 0xf; @@ -1860,7 +1888,7 @@ if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) return 1; gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); switch ((insn >> 22) & 3) { case 0: gen_helper_iwmmxt_msbb(tmp, cpu_M0); @@ -1975,9 +2003,9 @@ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 1; } switch ((insn >> 22) & 3) { @@ -1991,7 +2019,7 @@ gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp); break; } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2003,9 +2031,9 @@ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 1; } switch ((insn >> 22) & 3) { @@ -2019,7 +2047,7 @@ gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp); break; } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2031,9 +2059,9 @@ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 1; } switch ((insn >> 22) & 3) { @@ -2047,7 +2075,7 @@ gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp); break; } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2059,31 +2087,31 @@ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); switch ((insn >> 22) & 3) { case 1: if (gen_iwmmxt_shift(insn, 0xf, tmp)) { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 1; } gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp); break; case 2: if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 1; } gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp); break; case 3: if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 1; } gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp); break; } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); @@ -2324,12 +2352,12 @@ gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); break; default: - dead_tmp(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); return 1; } - dead_tmp(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; @@ -2340,7 +2368,7 @@ return 0; } -/* Disassemble an XScale DSP instruction. Returns nonzero if an error occured +/* Disassemble an XScale DSP instruction. Returns nonzero if an error occurred (ie. an undefined instruction). */ static int disas_dsp_insn(CPUState *env, DisasContext *s, uint32_t insn) { @@ -2378,8 +2406,8 @@ default: return 1; } - dead_tmp(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); gen_op_iwmmxt_movq_wRn_M0(acc); return 0; @@ -2425,7 +2453,7 @@ if (!env->cp[cp].cp_read) return 1; gen_set_pc_im(s->pc); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tmp2 = tcg_const_i32(insn); gen_helper_get_cp(tmp, cpu_env, tmp2); tcg_temp_free(tmp2); @@ -2438,28 +2466,38 @@ tmp2 = tcg_const_i32(insn); gen_helper_set_cp(cpu_env, tmp2, tmp); tcg_temp_free(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } return 0; } -static int cp15_user_ok(uint32_t insn) +static int cp15_user_ok(CPUState *env, uint32_t insn) { int cpn = (insn >> 16) & 0xf; int cpm = insn & 0xf; int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38); + if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) { + /* Performance monitor registers fall into three categories: + * (a) always UNDEF in usermode + * (b) UNDEF only if PMUSERENR.EN is 0 + * (c) always read OK and UNDEF on write (PMUSERENR only) + */ + if ((cpm == 12 && (op < 6)) || + (cpm == 13 && (op < 3))) { + return env->cp15.c9_pmuserenr; + } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) { + /* PMUSERENR, read only */ + return 1; + } + return 0; + } + if (cpn == 13 && cpm == 0) { /* TLS register. */ if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT))) return 1; } - if (cpn == 7) { - /* ISB, DSB, DMB. */ - if ((cpm == 5 && op == 4) - || (cpm == 10 && (op == 4 || op == 5))) - return 1; - } return 0; } @@ -2505,7 +2543,7 @@ store_cpu_field(tmp, cp15.c13_tls3); break; default: - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 0; } } @@ -2535,16 +2573,62 @@ /* cdp */ return 1; } - if (IS_USER(s) && !cp15_user_ok(insn)) { - return 1; - } - if ((insn & 0x0fff0fff) == 0x0e070f90 - || (insn & 0x0fff0fff) == 0x0e070f58) { - /* Wait for interrupt. */ - gen_set_pc_im(s->pc); - s->is_jmp = DISAS_WFI; + /* We special case a number of cp15 instructions which were used + * for things which are real instructions in ARMv7. This allows + * them to work in linux-user mode which doesn't provide functional + * get_cp15/set_cp15 helpers, and is more efficient anyway. + */ + switch ((insn & 0x0fff0fff)) { + case 0x0e070f90: + /* 0,c7,c0,4: Standard v6 WFI (also used in some pre-v6 cores). + * In v7, this must NOP. + */ + if (IS_USER(s)) { + return 1; + } + if (!arm_feature(env, ARM_FEATURE_V7)) { + /* Wait for interrupt. */ + gen_set_pc_im(s->pc); + s->is_jmp = DISAS_WFI; + } return 0; + case 0x0e070f58: + /* 0,c7,c8,2: Not all pre-v6 cores implemented this WFI, + * so this is slightly over-broad. + */ + if (!IS_USER(s) && !arm_feature(env, ARM_FEATURE_V6)) { + /* Wait for interrupt. */ + gen_set_pc_im(s->pc); + s->is_jmp = DISAS_WFI; + return 0; + } + /* Otherwise continue to handle via helper function. + * In particular, on v7 and some v6 cores this is one of + * the VA-PA registers. + */ + break; + case 0x0e070f3d: + /* 0,c7,c13,1: prefetch-by-MVA in v6, NOP in v7 */ + if (arm_feature(env, ARM_FEATURE_V6)) { + return IS_USER(s) ? 1 : 0; + } + break; + case 0x0e070f95: /* 0,c7,c5,4 : ISB */ + case 0x0e070f9a: /* 0,c7,c10,4: DSB */ + case 0x0e070fba: /* 0,c7,c10,5: DMB */ + /* Barriers in both v6 and v7 */ + if (arm_feature(env, ARM_FEATURE_V6)) { + return 0; + } + break; + default: + break; } + + if (IS_USER(s) && !cp15_user_ok(env, insn)) { + return 1; + } + rd = (insn >> 12) & 0xf; if (cp15_tls_load_store(env, s, insn, rd)) @@ -2552,17 +2636,17 @@ tmp2 = tcg_const_i32(insn); if (insn & ARM_CP_RW_BIT) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); gen_helper_get_cp15(tmp, cpu_env, tmp2); /* If the destination register is r15 then sets condition codes. */ if (rd != 15) store_reg(s, rd, tmp); else - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else { tmp = load_reg(s, rd); gen_helper_set_cp15(cpu_env, tmp2, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); /* Normally we would always end the TB here, but Linux * arch/arm/mach-pxa/sleep.S expects two instructions following * an MMU enable to execute from cache. Imitate this behaviour. */ @@ -2597,7 +2681,7 @@ /* Move between integer and VFP cores. */ static TCGv gen_vfp_mrs(void) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp, cpu_F0s); return tmp; } @@ -2605,12 +2689,12 @@ static void gen_vfp_msr(TCGv tmp) { tcg_gen_mov_i32(cpu_F0s, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } static void gen_neon_dup_u8(TCGv var, int shift) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); if (shift) tcg_gen_shri_i32(var, var, shift); tcg_gen_ext8u_i32(var, var); @@ -2618,28 +2702,50 @@ tcg_gen_or_i32(var, var, tmp); tcg_gen_shli_i32(tmp, var, 16); tcg_gen_or_i32(var, var, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } static void gen_neon_dup_low16(TCGv var) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_ext16u_i32(var, var); tcg_gen_shli_i32(tmp, var, 16); tcg_gen_or_i32(var, var, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } static void gen_neon_dup_high16(TCGv var) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_andi_i32(var, var, 0xffff0000); tcg_gen_shri_i32(tmp, var, 16); tcg_gen_or_i32(var, var, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); +} + +static TCGv gen_load_and_replicate(DisasContext *s, TCGv addr, int size) +{ + /* Load a single Neon element and replicate into a 32 bit TCG reg */ + TCGv tmp; + switch (size) { + case 0: + tmp = gen_ld8u(addr, IS_USER(s)); + gen_neon_dup_u8(tmp, 0); + break; + case 1: + tmp = gen_ld16u(addr, IS_USER(s)); + gen_neon_dup_low16(tmp); + break; + case 2: + tmp = gen_ld32(addr, IS_USER(s)); + break; + default: /* Avoid compiler warnings. */ + abort(); + } + return tmp; } -/* Disassemble a VFP instruction. Returns nonzero if an error occured +/* Disassemble a VFP instruction. Returns nonzero if an error occurred (ie. an undefined instruction). */ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) { @@ -2731,7 +2837,7 @@ gen_neon_dup_low16(tmp); } for (n = 0; n <= pass * 2; n++) { - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp2, tmp); neon_store_reg(rn, n, tmp2); } @@ -2742,12 +2848,12 @@ case 0: tmp2 = neon_load_reg(rn, pass); gen_bfi(tmp, tmp2, tmp, offset, 0xff); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); break; case 1: tmp2 = neon_load_reg(rn, pass); gen_bfi(tmp, tmp2, tmp, offset, 0xffff); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); break; case 2: break; @@ -2793,7 +2899,7 @@ tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]); tcg_gen_andi_i32(tmp, tmp, 0xf0000000); } else { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); gen_helper_vfp_get_fpscr(tmp, cpu_env); } break; @@ -2814,7 +2920,7 @@ if (rd == 15) { /* Set the 4 flag bits in the CPSR. */ gen_set_nzcv(tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else { store_reg(s, rd, tmp); } @@ -2832,7 +2938,7 @@ break; case ARM_VFP_FPSCR: gen_helper_vfp_set_fpscr(cpu_env, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_lookup_tb(s); break; case ARM_VFP_FPEXC: @@ -2965,6 +3071,17 @@ /* Source and destination the same. */ gen_mov_F0_vreg(dp, rd); break; + case 4: + case 5: + case 6: + case 7: + /* VCVTB, VCVTT: only present with the halfprec extension, + * UNPREDICTABLE if bit 8 is set (we choose to UNDEF) + */ + if (dp || !arm_feature(env, ARM_FEATURE_VFP_FP16)) { + return 1; + } + /* Otherwise fall through */ default: /* One source operand. */ gen_mov_F0_vreg(dp, rm); @@ -2979,27 +3096,34 @@ for (;;) { /* Perform the calculation. */ switch (op) { - case 0: /* mac: fd + (fn * fm) */ - gen_vfp_mul(dp); - gen_mov_F1_vreg(dp, rd); + case 0: /* VMLA: fd + (fn * fm) */ + /* Note that order of inputs to the add matters for NaNs */ + gen_vfp_F1_mul(dp); + gen_mov_F0_vreg(dp, rd); gen_vfp_add(dp); break; - case 1: /* nmac: fd - (fn * fm) */ + case 1: /* VMLS: fd + -(fn * fm) */ gen_vfp_mul(dp); - gen_vfp_neg(dp); - gen_mov_F1_vreg(dp, rd); + gen_vfp_F1_neg(dp); + gen_mov_F0_vreg(dp, rd); gen_vfp_add(dp); break; - case 2: /* msc: -fd + (fn * fm) */ - gen_vfp_mul(dp); - gen_mov_F1_vreg(dp, rd); - gen_vfp_sub(dp); + case 2: /* VNMLS: -fd + (fn * fm) */ + /* Note that it isn't valid to replace (-A + B) with (B - A) + * or similar plausible looking simplifications + * because this will give wrong results for NaNs. + */ + gen_vfp_F1_mul(dp); + gen_mov_F0_vreg(dp, rd); + gen_vfp_neg(dp); + gen_vfp_add(dp); break; - case 3: /* nmsc: -fd - (fn * fm) */ + case 3: /* VNMLA: -fd + -(fn * fm) */ gen_vfp_mul(dp); + gen_vfp_F1_neg(dp); + gen_mov_F0_vreg(dp, rd); gen_vfp_neg(dp); - gen_mov_F1_vreg(dp, rd); - gen_vfp_sub(dp); + gen_vfp_add(dp); break; case 4: /* mul: fn * fm */ gen_vfp_mul(dp); @@ -3054,44 +3178,36 @@ gen_vfp_sqrt(dp); break; case 4: /* vcvtb.f32.f16 */ - if (!arm_feature(env, ARM_FEATURE_VFP_FP16)) - return 1; tmp = gen_vfp_mrs(); tcg_gen_ext16u_i32(tmp, tmp); gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, cpu_env); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; case 5: /* vcvtt.f32.f16 */ - if (!arm_feature(env, ARM_FEATURE_VFP_FP16)) - return 1; tmp = gen_vfp_mrs(); tcg_gen_shri_i32(tmp, tmp, 16); gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, cpu_env); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; case 6: /* vcvtb.f16.f32 */ - if (!arm_feature(env, ARM_FEATURE_VFP_FP16)) - return 1; - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); gen_mov_F0_vreg(0, rd); tmp2 = gen_vfp_mrs(); tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); tcg_gen_or_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); gen_vfp_msr(tmp); break; case 7: /* vcvtt.f16.f32 */ - if (!arm_feature(env, ARM_FEATURE_VFP_FP16)) - return 1; - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); tcg_gen_shli_i32(tmp, tmp, 16); gen_mov_F0_vreg(0, rd); tmp2 = gen_vfp_mrs(); tcg_gen_ext16u_i32(tmp2, tmp2); tcg_gen_or_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); gen_vfp_msr(tmp); break; case 8: /* cmp */ @@ -3114,62 +3230,62 @@ gen_helper_vfp_fcvtds(cpu_F0d, cpu_F0s, cpu_env); break; case 16: /* fuito */ - gen_vfp_uito(dp); + gen_vfp_uito(dp, 0); break; case 17: /* fsito */ - gen_vfp_sito(dp); + gen_vfp_sito(dp, 0); break; case 20: /* fshto */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_shto(dp, 16 - rm); + gen_vfp_shto(dp, 16 - rm, 0); break; case 21: /* fslto */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_slto(dp, 32 - rm); + gen_vfp_slto(dp, 32 - rm, 0); break; case 22: /* fuhto */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_uhto(dp, 16 - rm); + gen_vfp_uhto(dp, 16 - rm, 0); break; case 23: /* fulto */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_ulto(dp, 32 - rm); + gen_vfp_ulto(dp, 32 - rm, 0); break; case 24: /* ftoui */ - gen_vfp_toui(dp); + gen_vfp_toui(dp, 0); break; case 25: /* ftouiz */ - gen_vfp_touiz(dp); + gen_vfp_touiz(dp, 0); break; case 26: /* ftosi */ - gen_vfp_tosi(dp); + gen_vfp_tosi(dp, 0); break; case 27: /* ftosiz */ - gen_vfp_tosiz(dp); + gen_vfp_tosiz(dp, 0); break; case 28: /* ftosh */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_tosh(dp, 16 - rm); + gen_vfp_tosh(dp, 16 - rm, 0); break; case 29: /* ftosl */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_tosl(dp, 32 - rm); + gen_vfp_tosl(dp, 32 - rm, 0); break; case 30: /* ftouh */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_touh(dp, 16 - rm); + gen_vfp_touh(dp, 16 - rm, 0); break; case 31: /* ftoul */ if (!arm_feature(env, ARM_FEATURE_VFP3)) return 1; - gen_vfp_toul(dp, 32 - rm); + gen_vfp_toul(dp, 32 - rm, 0); break; default: /* undefined */ printf ("rn:%d\n", rn); @@ -3232,7 +3348,7 @@ break; case 0xc: case 0xd: - if (dp && (insn & 0x03e00000) == 0x00400000) { + if ((insn & 0x03e00000) == 0x00400000) { /* two-register transfer */ rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; @@ -3254,10 +3370,10 @@ } else { gen_mov_F0_vreg(0, rm); tmp = gen_vfp_mrs(); - store_reg(s, rn, tmp); + store_reg(s, rd, tmp); gen_mov_F0_vreg(0, rm + 1); tmp = gen_vfp_mrs(); - store_reg(s, rd, tmp); + store_reg(s, rn, tmp); } } else { /* arm->vfp */ @@ -3269,10 +3385,10 @@ gen_vfp_msr(tmp); gen_mov_vreg_F0(0, rm * 2 + 1); } else { - tmp = load_reg(s, rn); + tmp = load_reg(s, rd); gen_vfp_msr(tmp); gen_mov_vreg_F0(0, rm); - tmp = load_reg(s, rd); + tmp = load_reg(s, rn); gen_vfp_msr(tmp); gen_mov_vreg_F0(0, rm + 1); } @@ -3284,17 +3400,18 @@ VFP_DREG_D(rd, insn); else rd = VFP_SREG_D(insn); - if (s->thumb && rn == 15) { - addr = new_tmp(); - tcg_gen_movi_i32(addr, s->pc & ~2); - } else { - addr = load_reg(s, rn); - } if ((insn & 0x01200000) == 0x01000000) { /* Single load/store */ offset = (insn & 0xff) << 2; if ((insn & (1 << 23)) == 0) offset = -offset; + if (s->thumb && rn == 15) { + /* This is actually UNPREDICTABLE */ + addr = tcg_temp_new_i32(); + tcg_gen_movi_i32(addr, s->pc & ~2); + } else { + addr = load_reg(s, rn); + } tcg_gen_addi_i32(addr, addr, offset); if (insn & (1 << 20)) { gen_vfp_ld(s, dp, addr); @@ -3303,14 +3420,37 @@ gen_mov_F0_vreg(dp, rd); gen_vfp_st(s, dp, addr); } - dead_tmp(addr); + tcg_temp_free_i32(addr); } else { /* load/store multiple */ + int w = insn & (1 << 21); if (dp) n = (insn >> 1) & 0x7f; else n = insn & 0xff; + if (w && !(((insn >> 23) ^ (insn >> 24)) & 1)) { + /* P == U , W == 1 => UNDEF */ + return 1; + } + if (n == 0 || (rd + n) > 32 || (dp && n > 16)) { + /* UNPREDICTABLE cases for bad immediates: we choose to + * UNDEF to avoid generating huge numbers of TCG ops + */ + return 1; + } + if (rn == 15 && w) { + /* writeback to PC is UNPREDICTABLE, we choose to UNDEF */ + return 1; + } + + if (s->thumb && rn == 15) { + /* This is actually UNPREDICTABLE */ + addr = tcg_temp_new_i32(); + tcg_gen_movi_i32(addr, s->pc & ~2); + } else { + addr = load_reg(s, rn); + } if (insn & (1 << 24)) /* pre-decrement */ tcg_gen_addi_i32(addr, addr, -((insn & 0xff) << 2)); @@ -3330,7 +3470,7 @@ } tcg_gen_addi_i32(addr, addr, offset); } - if (insn & (1 << 21)) { + if (w) { /* writeback */ if (insn & (1 << 24)) offset = -offset * n; @@ -3343,7 +3483,7 @@ tcg_gen_addi_i32(addr, addr, offset); store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } } } @@ -3363,7 +3503,7 @@ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { tcg_gen_goto_tb(n); gen_set_pc_im(dest); - tcg_gen_exit_tb((long)tb + n); + tcg_gen_exit_tb((tcg_target_long)tb + n); } else { gen_set_pc_im(dest); tcg_gen_exit_tb(0); @@ -3412,6 +3552,10 @@ /* Mask out undefined bits. */ mask &= ~CPSR_RESERVED; + if (!arm_feature(env, ARM_FEATURE_V4T)) + mask &= ~CPSR_T; + if (!arm_feature(env, ARM_FEATURE_V5)) + mask &= ~CPSR_Q; /* V5TE in reality*/ if (!arm_feature(env, ARM_FEATURE_V6)) mask &= ~(CPSR_E | CPSR_GE); if (!arm_feature(env, ARM_FEATURE_THUMB2)) @@ -3442,7 +3586,7 @@ } else { gen_set_cpsr(t0, mask); } - dead_tmp(t0); + tcg_temp_free_i32(t0); gen_lookup_tb(s); return 0; } @@ -3451,7 +3595,7 @@ static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val) { TCGv tmp; - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); return gen_set_psr(s, mask, spsr, tmp); } @@ -3463,7 +3607,7 @@ store_reg(s, 15, pc); tmp = load_cpu_field(spsr); gen_set_cpsr(tmp, 0xffffffff); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); s->is_jmp = DISAS_UPDATE; } @@ -3471,7 +3615,7 @@ static void gen_rfe(DisasContext *s, TCGv pc, TCGv cpsr) { gen_set_cpsr(cpsr, 0xffffffff); - dead_tmp(cpsr); + tcg_temp_free_i32(cpsr); store_reg(s, 15, pc); s->is_jmp = DISAS_UPDATE; } @@ -3481,7 +3625,7 @@ { if (s->condexec_mask) { uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); store_cpu_field(tmp, condexec_bits); } @@ -3512,15 +3656,14 @@ #define CPU_V001 cpu_V0, cpu_V0, cpu_V1 -static inline int gen_neon_add(int size, TCGv t0, TCGv t1) +static inline void gen_neon_add(int size, TCGv t0, TCGv t1) { switch (size) { case 0: gen_helper_neon_add_u8(t0, t0, t1); break; case 1: gen_helper_neon_add_u16(t0, t0, t1); break; case 2: tcg_gen_add_i32(t0, t0, t1); break; - default: return 1; + default: abort(); } - return 0; } static inline void gen_neon_rsb(int size, TCGv t0, TCGv t1) @@ -3587,7 +3730,7 @@ static TCGv neon_load_scratch(int scratch) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_ld_i32(tmp, cpu_env, offsetof(CPUARMState, vfp.scratch[scratch])); return tmp; } @@ -3595,7 +3738,7 @@ static void neon_store_scratch(int scratch, TCGv var) { tcg_gen_st_i32(var, cpu_env, offsetof(CPUARMState, vfp.scratch[scratch])); - dead_tmp(var); + tcg_temp_free_i32(var); } static inline TCGv neon_get_scalar(int size, int reg) @@ -3614,122 +3757,90 @@ return tmp; } -static void gen_neon_unzip_u8(TCGv t0, TCGv t1) -{ - TCGv rd, rm, tmp; - - rd = new_tmp(); - rm = new_tmp(); - tmp = new_tmp(); - - tcg_gen_andi_i32(rd, t0, 0xff); - tcg_gen_shri_i32(tmp, t0, 8); - tcg_gen_andi_i32(tmp, tmp, 0xff00); - tcg_gen_or_i32(rd, rd, tmp); - tcg_gen_shli_i32(tmp, t1, 16); - tcg_gen_andi_i32(tmp, tmp, 0xff0000); - tcg_gen_or_i32(rd, rd, tmp); - tcg_gen_shli_i32(tmp, t1, 8); - tcg_gen_andi_i32(tmp, tmp, 0xff000000); - tcg_gen_or_i32(rd, rd, tmp); - - tcg_gen_shri_i32(rm, t0, 8); - tcg_gen_andi_i32(rm, rm, 0xff); - tcg_gen_shri_i32(tmp, t0, 16); - tcg_gen_andi_i32(tmp, tmp, 0xff00); - tcg_gen_or_i32(rm, rm, tmp); - tcg_gen_shli_i32(tmp, t1, 8); - tcg_gen_andi_i32(tmp, tmp, 0xff0000); - tcg_gen_or_i32(rm, rm, tmp); - tcg_gen_andi_i32(tmp, t1, 0xff000000); - tcg_gen_or_i32(t1, rm, tmp); - tcg_gen_mov_i32(t0, rd); - - dead_tmp(tmp); - dead_tmp(rm); - dead_tmp(rd); -} - -static void gen_neon_zip_u8(TCGv t0, TCGv t1) -{ - TCGv rd, rm, tmp; - - rd = new_tmp(); - rm = new_tmp(); - tmp = new_tmp(); - - tcg_gen_andi_i32(rd, t0, 0xff); - tcg_gen_shli_i32(tmp, t1, 8); - tcg_gen_andi_i32(tmp, tmp, 0xff00); - tcg_gen_or_i32(rd, rd, tmp); - tcg_gen_shli_i32(tmp, t0, 16); - tcg_gen_andi_i32(tmp, tmp, 0xff0000); - tcg_gen_or_i32(rd, rd, tmp); - tcg_gen_shli_i32(tmp, t1, 24); - tcg_gen_andi_i32(tmp, tmp, 0xff000000); - tcg_gen_or_i32(rd, rd, tmp); - - tcg_gen_andi_i32(rm, t1, 0xff000000); - tcg_gen_shri_i32(tmp, t0, 8); - tcg_gen_andi_i32(tmp, tmp, 0xff0000); - tcg_gen_or_i32(rm, rm, tmp); - tcg_gen_shri_i32(tmp, t1, 8); - tcg_gen_andi_i32(tmp, tmp, 0xff00); - tcg_gen_or_i32(rm, rm, tmp); - tcg_gen_shri_i32(tmp, t0, 16); - tcg_gen_andi_i32(tmp, tmp, 0xff); - tcg_gen_or_i32(t1, rm, tmp); - tcg_gen_mov_i32(t0, rd); - - dead_tmp(tmp); - dead_tmp(rm); - dead_tmp(rd); -} - -static void gen_neon_zip_u16(TCGv t0, TCGv t1) +static int gen_neon_unzip(int rd, int rm, int size, int q) { TCGv tmp, tmp2; - - tmp = new_tmp(); - tmp2 = new_tmp(); - - tcg_gen_andi_i32(tmp, t0, 0xffff); - tcg_gen_shli_i32(tmp2, t1, 16); - tcg_gen_or_i32(tmp, tmp, tmp2); - tcg_gen_andi_i32(t1, t1, 0xffff0000); - tcg_gen_shri_i32(tmp2, t0, 16); - tcg_gen_or_i32(t1, t1, tmp2); - tcg_gen_mov_i32(t0, tmp); - - dead_tmp(tmp2); - dead_tmp(tmp); + if (!q && size == 2) { + return 1; + } + tmp = tcg_const_i32(rd); + tmp2 = tcg_const_i32(rm); + if (q) { + switch (size) { + case 0: + gen_helper_neon_qunzip8(cpu_env, tmp, tmp2); + break; + case 1: + gen_helper_neon_qunzip16(cpu_env, tmp, tmp2); + break; + case 2: + gen_helper_neon_qunzip32(cpu_env, tmp, tmp2); + break; + default: + abort(); + } + } else { + switch (size) { + case 0: + gen_helper_neon_unzip8(cpu_env, tmp, tmp2); + break; + case 1: + gen_helper_neon_unzip16(cpu_env, tmp, tmp2); + break; + default: + abort(); + } + } + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + return 0; } -static void gen_neon_unzip(int reg, int q, int tmp, int size) +static int gen_neon_zip(int rd, int rm, int size, int q) { - int n; - TCGv t0, t1; - - for (n = 0; n < q + 1; n += 2) { - t0 = neon_load_reg(reg, n); - t1 = neon_load_reg(reg, n + 1); + TCGv tmp, tmp2; + if (!q && size == 2) { + return 1; + } + tmp = tcg_const_i32(rd); + tmp2 = tcg_const_i32(rm); + if (q) { switch (size) { - case 0: gen_neon_unzip_u8(t0, t1); break; - case 1: gen_neon_zip_u16(t0, t1); break; /* zip and unzip are the same. */ - case 2: /* no-op */; break; - default: abort(); + case 0: + gen_helper_neon_qzip8(cpu_env, tmp, tmp2); + break; + case 1: + gen_helper_neon_qzip16(cpu_env, tmp, tmp2); + break; + case 2: + gen_helper_neon_qzip32(cpu_env, tmp, tmp2); + break; + default: + abort(); + } + } else { + switch (size) { + case 0: + gen_helper_neon_zip8(cpu_env, tmp, tmp2); + break; + case 1: + gen_helper_neon_zip16(cpu_env, tmp, tmp2); + break; + default: + abort(); } - neon_store_scratch(tmp + n, t0); - neon_store_scratch(tmp + n + 1, t1); } + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + return 0; } static void gen_neon_trn_u8(TCGv t0, TCGv t1) { TCGv rd, tmp; - rd = new_tmp(); - tmp = new_tmp(); + rd = tcg_temp_new_i32(); + tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(rd, t0, 8); tcg_gen_andi_i32(rd, rd, 0xff00ff00); @@ -3742,16 +3853,16 @@ tcg_gen_or_i32(t1, t1, tmp); tcg_gen_mov_i32(t0, rd); - dead_tmp(tmp); - dead_tmp(rd); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(rd); } static void gen_neon_trn_u16(TCGv t0, TCGv t1) { TCGv rd, tmp; - rd = new_tmp(); - tmp = new_tmp(); + rd = tcg_temp_new_i32(); + tmp = tcg_temp_new_i32(); tcg_gen_shli_i32(rd, t0, 16); tcg_gen_andi_i32(tmp, t1, 0xffff); @@ -3761,8 +3872,8 @@ tcg_gen_or_i32(t1, t1, tmp); tcg_gen_mov_i32(t0, rd); - dead_tmp(tmp); - dead_tmp(rd); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(rd); } @@ -3811,18 +3922,33 @@ rn = (insn >> 16) & 0xf; rm = insn & 0xf; load = (insn & (1 << 21)) != 0; - addr = new_tmp(); if ((insn & (1 << 23)) == 0) { /* Load store all elements. */ op = (insn >> 8) & 0xf; size = (insn >> 6) & 3; if (op > 10) return 1; + /* Catch UNDEF cases for bad values of align field */ + switch (op & 0xc) { + case 4: + if (((insn >> 5) & 1) == 1) { + return 1; + } + break; + case 8: + if (((insn >> 4) & 3) == 3) { + return 1; + } + break; + default: + break; + } nregs = neon_ls_element_type[op].nregs; interleave = neon_ls_element_type[op].interleave; spacing = neon_ls_element_type[op].spacing; if (size == 3 && (interleave | spacing) != 1) return 1; + addr = tcg_temp_new_i32(); load_reg_var(s, addr, rn); stride = (1 << size) * interleave; for (reg = 0; reg < nregs; reg++) { @@ -3863,11 +3989,11 @@ tcg_gen_addi_i32(addr, addr, stride); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); neon_store_reg(rd, pass, tmp); } else { tmp = neon_load_reg(rd, pass); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp2, tmp, 16); gen_st16(tmp, addr, IS_USER(s)); tcg_gen_addi_i32(addr, addr, stride); @@ -3885,14 +4011,14 @@ } else { tcg_gen_shli_i32(tmp, tmp, n * 8); tcg_gen_or_i32(tmp2, tmp2, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } } neon_store_reg(rd, pass, tmp2); } else { tmp2 = neon_load_reg(rd, pass); for (n = 0; n < 4; n++) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (n == 0) { tcg_gen_mov_i32(tmp, tmp2); } else { @@ -3901,52 +4027,68 @@ gen_st8(tmp, addr, IS_USER(s)); tcg_gen_addi_i32(addr, addr, stride); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } } } } rd += spacing; } + tcg_temp_free_i32(addr); stride = nregs * 8; } else { size = (insn >> 10) & 3; if (size == 3) { /* Load single element to all lanes. */ - if (!load) + int a = (insn >> 4) & 1; + if (!load) { return 1; + } size = (insn >> 6) & 3; nregs = ((insn >> 8) & 3) + 1; - stride = (insn & (1 << 5)) ? 2 : 1; - load_reg_var(s, addr, rn); - for (reg = 0; reg < nregs; reg++) { - switch (size) { - case 0: - tmp = gen_ld8u(addr, IS_USER(s)); - gen_neon_dup_u8(tmp, 0); - break; - case 1: - tmp = gen_ld16u(addr, IS_USER(s)); - gen_neon_dup_low16(tmp); - break; - case 2: - tmp = gen_ld32(addr, IS_USER(s)); - break; - case 3: + + if (size == 3) { + if (nregs != 4 || a == 0) { return 1; - default: /* Avoid compiler warnings. */ - abort(); } - tcg_gen_addi_i32(addr, addr, 1 << size); - tmp2 = new_tmp(); - tcg_gen_mov_i32(tmp2, tmp); - neon_store_reg(rd, 0, tmp2); - neon_store_reg(rd, 1, tmp); - rd += stride; + /* For VLD4 size==3 a == 1 means 32 bits at 16 byte alignment */ + size = 2; + } + if (nregs == 1 && a == 1 && size == 0) { + return 1; + } + if (nregs == 3 && a == 1) { + return 1; + } + addr = tcg_temp_new_i32(); + load_reg_var(s, addr, rn); + if (nregs == 1) { + /* VLD1 to all lanes: bit 5 indicates how many Dregs to write */ + tmp = gen_load_and_replicate(s, addr, size); + tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 0)); + tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 1)); + if (insn & (1 << 5)) { + tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd + 1, 0)); + tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd + 1, 1)); + } + tcg_temp_free_i32(tmp); + } else { + /* VLD2/3/4 to all lanes: bit 5 indicates register stride */ + stride = (insn & (1 << 5)) ? 2 : 1; + for (reg = 0; reg < nregs; reg++) { + tmp = gen_load_and_replicate(s, addr, size); + tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 0)); + tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 1)); + tcg_temp_free_i32(tmp); + tcg_gen_addi_i32(addr, addr, 1 << size); + rd += stride; + } } + tcg_temp_free_i32(addr); stride = (1 << size) * nregs; } else { /* Single element. */ + int idx = (insn >> 4) & 0xf; pass = (insn >> 7) & 1; switch (size) { case 0: @@ -3965,6 +4107,40 @@ abort(); } nregs = ((insn >> 8) & 3) + 1; + /* Catch the UNDEF cases. This is unavoidably a bit messy. */ + switch (nregs) { + case 1: + if (((idx & (1 << size)) != 0) || + (size == 2 && ((idx & 3) == 1 || (idx & 3) == 2))) { + return 1; + } + break; + case 3: + if ((idx & 1) != 0) { + return 1; + } + /* fall through */ + case 2: + if (size == 2 && (idx & 2) != 0) { + return 1; + } + break; + case 4: + if ((size == 2) && ((idx & 3) == 3)) { + return 1; + } + break; + default: + abort(); + } + if ((rd + stride * (nregs - 1)) > 31) { + /* Attempts to write off the end of the register file + * are UNPREDICTABLE; we choose to UNDEF because otherwise + * the neon_load_reg() would write off the end of the array. + */ + return 1; + } + addr = tcg_temp_new_i32(); load_reg_var(s, addr, rn); for (reg = 0; reg < nregs; reg++) { if (load) { @@ -3984,7 +4160,7 @@ if (size != 2) { tmp2 = neon_load_reg(rd, pass); gen_bfi(tmp, tmp2, tmp, shift, size ? 0xffff : 0xff); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } neon_store_reg(rd, pass, tmp); } else { /* Store */ @@ -4006,10 +4182,10 @@ rd += stride; tcg_gen_addi_i32(addr, addr, 1 << size); } + tcg_temp_free_i32(addr); stride = nregs * (1 << size); } } - dead_tmp(addr); if (rm != 15) { TCGv base; @@ -4020,7 +4196,7 @@ TCGv index; index = load_reg(s, rm); tcg_gen_add_i32(base, base, index); - dead_tmp(index); + tcg_temp_free_i32(index); } store_reg(s, rn, base); } @@ -4065,6 +4241,16 @@ } } +static inline void gen_neon_unarrow_sats(int size, TCGv dest, TCGv_i64 src) +{ + switch (size) { + case 0: gen_helper_neon_unarrow_sat8(dest, cpu_env, src); break; + case 1: gen_helper_neon_unarrow_sat16(dest, cpu_env, src); break; + case 2: gen_helper_neon_unarrow_sat32(dest, cpu_env, src); break; + default: abort(); + } +} + static inline void gen_neon_shift_narrow(int size, TCGv var, TCGv shift, int q, int u) { @@ -4085,8 +4271,8 @@ } else { if (u) { switch (size) { - case 1: gen_helper_neon_rshl_u16(var, var, shift); break; - case 2: gen_helper_neon_rshl_u32(var, var, shift); break; + case 1: gen_helper_neon_shl_u16(var, var, shift); break; + case 2: gen_helper_neon_shl_u32(var, var, shift); break; default: abort(); } } else { @@ -4116,7 +4302,7 @@ default: abort(); } } - dead_tmp(src); + tcg_temp_free_i32(src); } static inline void gen_neon_addl(int size) @@ -4170,10 +4356,12 @@ case 4: tmp = gen_muls_i64_i32(a, b); tcg_gen_mov_i64(dest, tmp); + tcg_temp_free_i64(tmp); break; case 5: tmp = gen_mulu_i64_i32(a, b); tcg_gen_mov_i64(dest, tmp); + tcg_temp_free_i64(tmp); break; default: abort(); } @@ -4181,11 +4369,203 @@ /* 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); + tcg_temp_free_i32(a); + tcg_temp_free_i32(b); } } +static void gen_neon_narrow_op(int op, int u, int size, TCGv dest, TCGv_i64 src) +{ + if (op) { + if (u) { + gen_neon_unarrow_sats(size, dest, src); + } else { + gen_neon_narrow(size, dest, src); + } + } else { + if (u) { + gen_neon_narrow_satu(size, dest, src); + } else { + gen_neon_narrow_sats(size, dest, src); + } + } +} + +/* Symbolic constants for op fields for Neon 3-register same-length. + * The values correspond to bits [11:8,4]; see the ARM ARM DDI0406B + * table A7-9. + */ +#define NEON_3R_VHADD 0 +#define NEON_3R_VQADD 1 +#define NEON_3R_VRHADD 2 +#define NEON_3R_LOGIC 3 /* VAND,VBIC,VORR,VMOV,VORN,VEOR,VBIF,VBIT,VBSL */ +#define NEON_3R_VHSUB 4 +#define NEON_3R_VQSUB 5 +#define NEON_3R_VCGT 6 +#define NEON_3R_VCGE 7 +#define NEON_3R_VSHL 8 +#define NEON_3R_VQSHL 9 +#define NEON_3R_VRSHL 10 +#define NEON_3R_VQRSHL 11 +#define NEON_3R_VMAX 12 +#define NEON_3R_VMIN 13 +#define NEON_3R_VABD 14 +#define NEON_3R_VABA 15 +#define NEON_3R_VADD_VSUB 16 +#define NEON_3R_VTST_VCEQ 17 +#define NEON_3R_VML 18 /* VMLA, VMLAL, VMLS, VMLSL */ +#define NEON_3R_VMUL 19 +#define NEON_3R_VPMAX 20 +#define NEON_3R_VPMIN 21 +#define NEON_3R_VQDMULH_VQRDMULH 22 +#define NEON_3R_VPADD 23 +#define NEON_3R_FLOAT_ARITH 26 /* float VADD, VSUB, VPADD, VABD */ +#define NEON_3R_FLOAT_MULTIPLY 27 /* float VMLA, VMLS, VMUL */ +#define NEON_3R_FLOAT_CMP 28 /* float VCEQ, VCGE, VCGT */ +#define NEON_3R_FLOAT_ACMP 29 /* float VACGE, VACGT, VACLE, VACLT */ +#define NEON_3R_FLOAT_MINMAX 30 /* float VMIN, VMAX */ +#define NEON_3R_VRECPS_VRSQRTS 31 /* float VRECPS, VRSQRTS */ + +static const uint8_t neon_3r_sizes[] = { + [NEON_3R_VHADD] = 0x7, + [NEON_3R_VQADD] = 0xf, + [NEON_3R_VRHADD] = 0x7, + [NEON_3R_LOGIC] = 0xf, /* size field encodes op type */ + [NEON_3R_VHSUB] = 0x7, + [NEON_3R_VQSUB] = 0xf, + [NEON_3R_VCGT] = 0x7, + [NEON_3R_VCGE] = 0x7, + [NEON_3R_VSHL] = 0xf, + [NEON_3R_VQSHL] = 0xf, + [NEON_3R_VRSHL] = 0xf, + [NEON_3R_VQRSHL] = 0xf, + [NEON_3R_VMAX] = 0x7, + [NEON_3R_VMIN] = 0x7, + [NEON_3R_VABD] = 0x7, + [NEON_3R_VABA] = 0x7, + [NEON_3R_VADD_VSUB] = 0xf, + [NEON_3R_VTST_VCEQ] = 0x7, + [NEON_3R_VML] = 0x7, + [NEON_3R_VMUL] = 0x7, + [NEON_3R_VPMAX] = 0x7, + [NEON_3R_VPMIN] = 0x7, + [NEON_3R_VQDMULH_VQRDMULH] = 0x6, + [NEON_3R_VPADD] = 0x7, + [NEON_3R_FLOAT_ARITH] = 0x5, /* size bit 1 encodes op */ + [NEON_3R_FLOAT_MULTIPLY] = 0x5, /* size bit 1 encodes op */ + [NEON_3R_FLOAT_CMP] = 0x5, /* size bit 1 encodes op */ + [NEON_3R_FLOAT_ACMP] = 0x5, /* size bit 1 encodes op */ + [NEON_3R_FLOAT_MINMAX] = 0x5, /* size bit 1 encodes op */ + [NEON_3R_VRECPS_VRSQRTS] = 0x5, /* size bit 1 encodes op */ +}; + +/* Symbolic constants for op fields for Neon 2-register miscellaneous. + * The values correspond to bits [17:16,10:7]; see the ARM ARM DDI0406B + * table A7-13. + */ +#define NEON_2RM_VREV64 0 +#define NEON_2RM_VREV32 1 +#define NEON_2RM_VREV16 2 +#define NEON_2RM_VPADDL 4 +#define NEON_2RM_VPADDL_U 5 +#define NEON_2RM_VCLS 8 +#define NEON_2RM_VCLZ 9 +#define NEON_2RM_VCNT 10 +#define NEON_2RM_VMVN 11 +#define NEON_2RM_VPADAL 12 +#define NEON_2RM_VPADAL_U 13 +#define NEON_2RM_VQABS 14 +#define NEON_2RM_VQNEG 15 +#define NEON_2RM_VCGT0 16 +#define NEON_2RM_VCGE0 17 +#define NEON_2RM_VCEQ0 18 +#define NEON_2RM_VCLE0 19 +#define NEON_2RM_VCLT0 20 +#define NEON_2RM_VABS 22 +#define NEON_2RM_VNEG 23 +#define NEON_2RM_VCGT0_F 24 +#define NEON_2RM_VCGE0_F 25 +#define NEON_2RM_VCEQ0_F 26 +#define NEON_2RM_VCLE0_F 27 +#define NEON_2RM_VCLT0_F 28 +#define NEON_2RM_VABS_F 30 +#define NEON_2RM_VNEG_F 31 +#define NEON_2RM_VSWP 32 +#define NEON_2RM_VTRN 33 +#define NEON_2RM_VUZP 34 +#define NEON_2RM_VZIP 35 +#define NEON_2RM_VMOVN 36 /* Includes VQMOVN, VQMOVUN */ +#define NEON_2RM_VQMOVN 37 /* Includes VQMOVUN */ +#define NEON_2RM_VSHLL 38 +#define NEON_2RM_VCVT_F16_F32 44 +#define NEON_2RM_VCVT_F32_F16 46 +#define NEON_2RM_VRECPE 56 +#define NEON_2RM_VRSQRTE 57 +#define NEON_2RM_VRECPE_F 58 +#define NEON_2RM_VRSQRTE_F 59 +#define NEON_2RM_VCVT_FS 60 +#define NEON_2RM_VCVT_FU 61 +#define NEON_2RM_VCVT_SF 62 +#define NEON_2RM_VCVT_UF 63 + +static int neon_2rm_is_float_op(int op) +{ + /* Return true if this neon 2reg-misc op is float-to-float */ + return (op == NEON_2RM_VABS_F || op == NEON_2RM_VNEG_F || + op >= NEON_2RM_VRECPE_F); +} + +/* Each entry in this array has bit n set if the insn allows + * size value n (otherwise it will UNDEF). Since unallocated + * op values will have no bits set they always UNDEF. + */ +static const uint8_t neon_2rm_sizes[] = { + [NEON_2RM_VREV64] = 0x7, + [NEON_2RM_VREV32] = 0x3, + [NEON_2RM_VREV16] = 0x1, + [NEON_2RM_VPADDL] = 0x7, + [NEON_2RM_VPADDL_U] = 0x7, + [NEON_2RM_VCLS] = 0x7, + [NEON_2RM_VCLZ] = 0x7, + [NEON_2RM_VCNT] = 0x1, + [NEON_2RM_VMVN] = 0x1, + [NEON_2RM_VPADAL] = 0x7, + [NEON_2RM_VPADAL_U] = 0x7, + [NEON_2RM_VQABS] = 0x7, + [NEON_2RM_VQNEG] = 0x7, + [NEON_2RM_VCGT0] = 0x7, + [NEON_2RM_VCGE0] = 0x7, + [NEON_2RM_VCEQ0] = 0x7, + [NEON_2RM_VCLE0] = 0x7, + [NEON_2RM_VCLT0] = 0x7, + [NEON_2RM_VABS] = 0x7, + [NEON_2RM_VNEG] = 0x7, + [NEON_2RM_VCGT0_F] = 0x4, + [NEON_2RM_VCGE0_F] = 0x4, + [NEON_2RM_VCEQ0_F] = 0x4, + [NEON_2RM_VCLE0_F] = 0x4, + [NEON_2RM_VCLT0_F] = 0x4, + [NEON_2RM_VABS_F] = 0x4, + [NEON_2RM_VNEG_F] = 0x4, + [NEON_2RM_VSWP] = 0x1, + [NEON_2RM_VTRN] = 0x7, + [NEON_2RM_VUZP] = 0x7, + [NEON_2RM_VZIP] = 0x7, + [NEON_2RM_VMOVN] = 0x7, + [NEON_2RM_VQMOVN] = 0x7, + [NEON_2RM_VSHLL] = 0x7, + [NEON_2RM_VCVT_F16_F32] = 0x2, + [NEON_2RM_VCVT_F32_F16] = 0x2, + [NEON_2RM_VRECPE] = 0x4, + [NEON_2RM_VRSQRTE] = 0x4, + [NEON_2RM_VRECPE_F] = 0x4, + [NEON_2RM_VRSQRTE_F] = 0x4, + [NEON_2RM_VCVT_FS] = 0x4, + [NEON_2RM_VCVT_FU] = 0x4, + [NEON_2RM_VCVT_SF] = 0x4, + [NEON_2RM_VCVT_UF] = 0x4, +}; + /* Translate a NEON data processing instruction. Return nonzero if the instruction is invalid. We process data in a mixture of 32-bit and 64-bit chunks. @@ -4202,7 +4582,6 @@ int count; int pairwise; int u; - int n; uint32_t imm, mask; TCGv tmp, tmp2, tmp3, tmp4, tmp5; TCGv_i64 tmp64; @@ -4218,14 +4597,23 @@ if ((insn & (1 << 23)) == 0) { /* Three register same length. */ op = ((insn >> 7) & 0x1e) | ((insn >> 4) & 1); - if (size == 3 && (op == 1 || op == 5 || op == 8 || op == 9 - || op == 10 || op == 11 || op == 16)) { - /* 64-bit element instructions. */ + /* Catch invalid op and bad size combinations: UNDEF */ + if ((neon_3r_sizes[op] & (1 << size)) == 0) { + return 1; + } + /* All insns of this form UNDEF for either this condition or the + * superset of cases "Q==1"; we catch the latter later. + */ + if (q && ((rd | rn | rm) & 1)) { + return 1; + } + if (size == 3 && op != NEON_3R_LOGIC) { + /* 64-bit element instructions. */ for (pass = 0; pass < (q ? 2 : 1); pass++) { neon_load_reg64(cpu_V0, rn + pass); neon_load_reg64(cpu_V1, rm + pass); switch (op) { - case 1: /* VQADD */ + case NEON_3R_VQADD: if (u) { gen_helper_neon_qadd_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1); @@ -4234,7 +4622,7 @@ cpu_V0, cpu_V1); } break; - case 5: /* VQSUB */ + case NEON_3R_VQSUB: if (u) { gen_helper_neon_qsub_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1); @@ -4243,14 +4631,14 @@ cpu_V0, cpu_V1); } break; - case 8: /* VSHL */ + case NEON_3R_VSHL: if (u) { gen_helper_neon_shl_u64(cpu_V0, cpu_V1, cpu_V0); } else { gen_helper_neon_shl_s64(cpu_V0, cpu_V1, cpu_V0); } break; - case 9: /* VQSHL */ + case NEON_3R_VQSHL: if (u) { gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V1, cpu_V0); @@ -4259,14 +4647,14 @@ cpu_V1, cpu_V0); } break; - case 10: /* VRSHL */ + case NEON_3R_VRSHL: if (u) { gen_helper_neon_rshl_u64(cpu_V0, cpu_V1, cpu_V0); } else { gen_helper_neon_rshl_s64(cpu_V0, cpu_V1, cpu_V0); } break; - case 11: /* VQRSHL */ + case NEON_3R_VQRSHL: if (u) { gen_helper_neon_qrshl_u64(cpu_V0, cpu_env, cpu_V1, cpu_V0); @@ -4275,7 +4663,7 @@ cpu_V1, cpu_V0); } break; - case 16: + case NEON_3R_VADD_VSUB: if (u) { tcg_gen_sub_i64(CPU_V001); } else { @@ -4289,50 +4677,76 @@ } return 0; } + pairwise = 0; switch (op) { - case 8: /* VSHL */ - case 9: /* VQSHL */ - case 10: /* VRSHL */ - case 11: /* VQRSHL */ + case NEON_3R_VSHL: + case NEON_3R_VQSHL: + case NEON_3R_VRSHL: + case NEON_3R_VQRSHL: { int rtmp; /* Shift instruction operands are reversed. */ rtmp = rn; rn = rm; rm = rtmp; - pairwise = 0; } break; - case 20: /* VPMAX */ - case 21: /* VPMIN */ - case 23: /* VPADD */ + case NEON_3R_VPADD: + if (u) { + return 1; + } + /* Fall through */ + case NEON_3R_VPMAX: + case NEON_3R_VPMIN: pairwise = 1; break; - case 26: /* VPADD (float) */ - pairwise = (u && size < 2); + case NEON_3R_FLOAT_ARITH: + pairwise = (u && size < 2); /* if VPADD (float) */ break; - case 30: /* VPMIN/VPMAX (float) */ - pairwise = u; + case NEON_3R_FLOAT_MINMAX: + pairwise = u; /* if VPMIN/VPMAX (float) */ + break; + case NEON_3R_FLOAT_CMP: + if (!u && size) { + /* no encoding for U=0 C=1x */ + return 1; + } + break; + case NEON_3R_FLOAT_ACMP: + if (!u) { + return 1; + } + break; + case NEON_3R_VRECPS_VRSQRTS: + if (u) { + return 1; + } + break; + case NEON_3R_VMUL: + if (u && (size != 0)) { + /* UNDEF on invalid size for polynomial subcase */ + return 1; + } break; default: - pairwise = 0; break; } + if (pairwise && q) { + /* All the pairwise insns UNDEF if Q is set */ + return 1; + } + for (pass = 0; pass < (q ? 4 : 2); pass++) { if (pairwise) { /* Pairwise. */ - if (q) - n = (pass & 1) * 2; - else - n = 0; - if (pass < q + 1) { - tmp = neon_load_reg(rn, n); - tmp2 = neon_load_reg(rn, n + 1); + if (pass < 1) { + tmp = neon_load_reg(rn, 0); + tmp2 = neon_load_reg(rn, 1); } else { - tmp = neon_load_reg(rm, n); - tmp2 = neon_load_reg(rm, n + 1); + tmp = neon_load_reg(rm, 0); + tmp2 = neon_load_reg(rm, 1); } } else { /* Elementwise. */ @@ -4340,16 +4754,16 @@ tmp2 = neon_load_reg(rm, pass); } switch (op) { - case 0: /* VHADD */ + case NEON_3R_VHADD: GEN_NEON_INTEGER_OP(hadd); break; - case 1: /* VQADD */ + case NEON_3R_VQADD: GEN_NEON_INTEGER_OP_ENV(qadd); break; - case 2: /* VRHADD */ + case NEON_3R_VRHADD: GEN_NEON_INTEGER_OP(rhadd); break; - case 3: /* Logic ops. */ + case NEON_3R_LOGIC: /* Logic ops. */ switch ((u << 2) | size) { case 0: /* VAND */ tcg_gen_and_i32(tmp, tmp, tmp2); @@ -4369,97 +4783,96 @@ case 5: /* VBSL */ tmp3 = neon_load_reg(rd, pass); gen_neon_bsl(tmp, tmp, tmp2, tmp3); - dead_tmp(tmp3); + tcg_temp_free_i32(tmp3); break; case 6: /* VBIT */ tmp3 = neon_load_reg(rd, pass); gen_neon_bsl(tmp, tmp, tmp3, tmp2); - dead_tmp(tmp3); + tcg_temp_free_i32(tmp3); break; case 7: /* VBIF */ tmp3 = neon_load_reg(rd, pass); gen_neon_bsl(tmp, tmp3, tmp, tmp2); - dead_tmp(tmp3); + tcg_temp_free_i32(tmp3); break; } break; - case 4: /* VHSUB */ + case NEON_3R_VHSUB: GEN_NEON_INTEGER_OP(hsub); break; - case 5: /* VQSUB */ + case NEON_3R_VQSUB: GEN_NEON_INTEGER_OP_ENV(qsub); break; - case 6: /* VCGT */ + case NEON_3R_VCGT: GEN_NEON_INTEGER_OP(cgt); break; - case 7: /* VCGE */ + case NEON_3R_VCGE: GEN_NEON_INTEGER_OP(cge); break; - case 8: /* VSHL */ + case NEON_3R_VSHL: GEN_NEON_INTEGER_OP(shl); break; - case 9: /* VQSHL */ + case NEON_3R_VQSHL: GEN_NEON_INTEGER_OP_ENV(qshl); break; - case 10: /* VRSHL */ + case NEON_3R_VRSHL: GEN_NEON_INTEGER_OP(rshl); break; - case 11: /* VQRSHL */ + case NEON_3R_VQRSHL: GEN_NEON_INTEGER_OP_ENV(qrshl); break; - case 12: /* VMAX */ + case NEON_3R_VMAX: GEN_NEON_INTEGER_OP(max); break; - case 13: /* VMIN */ + case NEON_3R_VMIN: GEN_NEON_INTEGER_OP(min); break; - case 14: /* VABD */ + case NEON_3R_VABD: GEN_NEON_INTEGER_OP(abd); break; - case 15: /* VABA */ + case NEON_3R_VABA: GEN_NEON_INTEGER_OP(abd); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tmp2 = neon_load_reg(rd, pass); gen_neon_add(size, tmp, tmp2); break; - case 16: + case NEON_3R_VADD_VSUB: if (!u) { /* VADD */ - if (gen_neon_add(size, tmp, tmp2)) - return 1; + gen_neon_add(size, tmp, tmp2); } else { /* VSUB */ switch (size) { case 0: gen_helper_neon_sub_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_sub_u16(tmp, tmp, tmp2); break; case 2: tcg_gen_sub_i32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } } break; - case 17: + case NEON_3R_VTST_VCEQ: if (!u) { /* VTST */ switch (size) { case 0: gen_helper_neon_tst_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_tst_u16(tmp, tmp, tmp2); break; case 2: gen_helper_neon_tst_u32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } } else { /* VCEQ */ switch (size) { case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break; case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } } break; - case 18: /* Multiply. */ + case NEON_3R_VML: /* VMLA, VMLAL, VMLS,VMLSL */ switch (size) { case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break; case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tmp2 = neon_load_reg(rd, pass); if (u) { /* VMLS */ gen_neon_rsb(size, tmp, tmp2); @@ -4467,7 +4880,7 @@ gen_neon_add(size, tmp, tmp2); } break; - case 19: /* VMUL */ + case NEON_3R_VMUL: if (u) { /* polynomial */ gen_helper_neon_mul_p8(tmp, tmp, tmp2); } else { /* Integer */ @@ -4475,96 +4888,121 @@ case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break; case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } } break; - case 20: /* VPMAX */ + case NEON_3R_VPMAX: GEN_NEON_INTEGER_OP(pmax); break; - case 21: /* VPMIN */ + case NEON_3R_VPMIN: GEN_NEON_INTEGER_OP(pmin); break; - case 22: /* Hultiply high. */ + case NEON_3R_VQDMULH_VQRDMULH: /* Multiply high. */ if (!u) { /* VQDMULH */ switch (size) { - case 1: gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2); break; - case 2: gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2); break; - default: return 1; + case 1: + gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2); + break; + case 2: + gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2); + break; + default: abort(); } - } else { /* VQRDHMUL */ + } else { /* VQRDMULH */ switch (size) { - case 1: gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2); break; - case 2: gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2); break; - default: return 1; + case 1: + gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2); + break; + case 2: + gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2); + break; + default: abort(); } } break; - case 23: /* VPADD */ - if (u) - return 1; + case NEON_3R_VPADD: switch (size) { case 0: gen_helper_neon_padd_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_padd_u16(tmp, tmp, tmp2); break; case 2: tcg_gen_add_i32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } break; - case 26: /* Floating point arithnetic. */ + case NEON_3R_FLOAT_ARITH: /* Floating point arithmetic. */ + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); switch ((u << 2) | size) { case 0: /* VADD */ - gen_helper_neon_add_f32(tmp, tmp, tmp2); + case 4: /* VPADD */ + gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus); break; case 2: /* VSUB */ - gen_helper_neon_sub_f32(tmp, tmp, tmp2); - break; - case 4: /* VPADD */ - gen_helper_neon_add_f32(tmp, tmp, tmp2); + gen_helper_vfp_subs(tmp, tmp, tmp2, fpstatus); break; case 6: /* VABD */ - gen_helper_neon_abd_f32(tmp, tmp, tmp2); + gen_helper_neon_abd_f32(tmp, tmp, tmp2, fpstatus); break; default: - return 1; + abort(); } + tcg_temp_free_ptr(fpstatus); break; - case 27: /* Float multiply. */ - gen_helper_neon_mul_f32(tmp, tmp, tmp2); + } + case NEON_3R_FLOAT_MULTIPLY: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus); if (!u) { - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tmp2 = neon_load_reg(rd, pass); if (size == 0) { - gen_helper_neon_add_f32(tmp, tmp, tmp2); + gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus); } else { - gen_helper_neon_sub_f32(tmp, tmp2, tmp); + gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus); } } + tcg_temp_free_ptr(fpstatus); break; - case 28: /* Float compare. */ + } + case NEON_3R_FLOAT_CMP: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); if (!u) { - gen_helper_neon_ceq_f32(tmp, tmp, tmp2); + gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus); } else { - if (size == 0) - gen_helper_neon_cge_f32(tmp, tmp, tmp2); - else - gen_helper_neon_cgt_f32(tmp, tmp, tmp2); + if (size == 0) { + gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus); + } else { + gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus); + } } + tcg_temp_free_ptr(fpstatus); break; - case 29: /* Float compare absolute. */ - if (!u) - return 1; - if (size == 0) - gen_helper_neon_acge_f32(tmp, tmp, tmp2); - else - gen_helper_neon_acgt_f32(tmp, tmp, tmp2); + } + case NEON_3R_FLOAT_ACMP: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + if (size == 0) { + gen_helper_neon_acge_f32(tmp, tmp, tmp2, fpstatus); + } else { + gen_helper_neon_acgt_f32(tmp, tmp, tmp2, fpstatus); + } + tcg_temp_free_ptr(fpstatus); break; - case 30: /* Float min/max. */ - if (size == 0) - gen_helper_neon_max_f32(tmp, tmp, tmp2); - else - gen_helper_neon_min_f32(tmp, tmp, tmp2); + } + case NEON_3R_FLOAT_MINMAX: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + if (size == 0) { + gen_helper_neon_max_f32(tmp, tmp, tmp2, fpstatus); + } else { + gen_helper_neon_min_f32(tmp, tmp, tmp2, fpstatus); + } + tcg_temp_free_ptr(fpstatus); break; - case 31: + } + case NEON_3R_VRECPS_VRSQRTS: if (size == 0) gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env); else @@ -4573,7 +5011,7 @@ default: abort(); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); /* Save the result. For elementwise operations we can put it straight into the destination register. For pairwise operations @@ -4597,7 +5035,10 @@ /* Two registers and shift. */ op = (insn >> 8) & 0xf; if (insn & (1 << 7)) { - /* 64-bit shift. */ + /* 64-bit shift. */ + if (op > 7) { + return 1; + } size = 3; } else { size = 2; @@ -4610,6 +5051,12 @@ if (op < 8) { /* Shift by immediate: VSHR, VSRA, VRSHR, VRSRA, VSRI, VSHL, VQSHL, VQSHLU. */ + if (q && ((rd | rm) & 1)) { + return 1; + } + if (!u && (op == 4 || op == 6)) { + return 1; + } /* Right shifts are encoded as N - shift, where N is the element size in bits. */ if (op <= 4) @@ -4657,20 +5104,12 @@ gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, cpu_V1); break; case 4: /* VSRI */ - if (!u) - return 1; - gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1); - break; case 5: /* VSHL, VSLI */ gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1); break; case 6: /* VQSHLU */ - if (u) { - gen_helper_neon_qshlu_s64(cpu_V0, cpu_env, - cpu_V0, cpu_V1); - } else { - return 1; - } + gen_helper_neon_qshlu_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); break; case 7: /* VQSHL */ if (u) { @@ -4688,13 +5127,25 @@ tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1); } else if (op == 4 || (op == 5 && u)) { /* Insert */ - cpu_abort(env, "VS[LR]I.64 not implemented"); + neon_load_reg64(cpu_V1, rd + pass); + uint64_t mask; + if (shift < -63 || shift > 63) { + mask = 0; + } else { + if (op == 4) { + mask = 0xffffffffffffffffull >> -shift; + } else { + mask = 0xffffffffffffffffull << shift; + } + } + tcg_gen_andi_i64(cpu_V1, cpu_V1, ~mask); + tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1); } neon_store_reg64(cpu_V0, rd + pass); } else { /* size < 3 */ /* Operands in T0 and T1. */ tmp = neon_load_reg(rm, pass); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, imm); switch (op) { case 0: /* VSHR */ @@ -4706,22 +5157,15 @@ GEN_NEON_INTEGER_OP(rshl); break; case 4: /* VSRI */ - if (!u) - return 1; - GEN_NEON_INTEGER_OP(shl); - break; case 5: /* VSHL, VSLI */ switch (size) { case 0: gen_helper_neon_shl_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_shl_u16(tmp, tmp, tmp2); break; case 2: gen_helper_neon_shl_u32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } break; case 6: /* VQSHLU */ - if (!u) { - return 1; - } switch (size) { case 0: gen_helper_neon_qshlu_s8(tmp, cpu_env, @@ -4736,20 +5180,20 @@ tmp, tmp2); break; default: - return 1; + abort(); } break; case 7: /* VQSHL */ GEN_NEON_INTEGER_OP_ENV(qshl); break; } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (op == 1 || op == 3) { /* Accumulate. */ tmp2 = neon_load_reg(rd, pass); gen_neon_add(size, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } else if (op == 4 || (op == 5 && u)) { /* Insert */ switch (size) { @@ -4785,7 +5229,7 @@ tcg_gen_andi_i32(tmp, tmp, mask); tcg_gen_andi_i32(tmp2, tmp2, ~mask); tcg_gen_or_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } neon_store_reg(rd, pass, tmp); } @@ -4793,71 +5237,81 @@ } else if (op < 10) { /* Shift by immediate and narrow: VSHRN, VRSHRN, VQSHRN, VQRSHRN. */ + int input_unsigned = (op == 8) ? !u : u; + if (rm & 1) { + return 1; + } shift = shift - (1 << (size + 3)); size++; - switch (size) { - case 1: - imm = (uint16_t)shift; - imm |= imm << 16; - tmp2 = tcg_const_i32(imm); - TCGV_UNUSED_I64(tmp64); - break; - case 2: - imm = (uint32_t)shift; - tmp2 = tcg_const_i32(imm); - TCGV_UNUSED_I64(tmp64); - break; - case 3: + if (size == 3) { tmp64 = tcg_const_i64(shift); - TCGV_UNUSED(tmp2); - break; - default: - abort(); - } - - for (pass = 0; pass < 2; pass++) { - if (size == 3) { - neon_load_reg64(cpu_V0, rm + pass); + neon_load_reg64(cpu_V0, rm); + neon_load_reg64(cpu_V1, rm + 1); + for (pass = 0; pass < 2; pass++) { + TCGv_i64 in; + if (pass == 0) { + in = cpu_V0; + } else { + in = cpu_V1; + } if (q) { - if (u) - gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, tmp64); - else - gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, tmp64); + if (input_unsigned) { + gen_helper_neon_rshl_u64(cpu_V0, in, tmp64); + } else { + gen_helper_neon_rshl_s64(cpu_V0, in, tmp64); + } } else { - if (u) - gen_helper_neon_shl_u64(cpu_V0, cpu_V0, tmp64); - else - gen_helper_neon_shl_s64(cpu_V0, cpu_V0, tmp64); + if (input_unsigned) { + gen_helper_neon_shl_u64(cpu_V0, in, tmp64); + } else { + gen_helper_neon_shl_s64(cpu_V0, in, tmp64); + } } - } else { - tmp = neon_load_reg(rm + pass, 0); - gen_neon_shift_narrow(size, tmp, tmp2, q, u); - tmp3 = neon_load_reg(rm + pass, 1); - gen_neon_shift_narrow(size, tmp3, tmp2, q, u); - tcg_gen_concat_i32_i64(cpu_V0, tmp, tmp3); - dead_tmp(tmp); - dead_tmp(tmp3); - } - tmp = new_tmp(); - if (op == 8 && !u) { - gen_neon_narrow(size - 1, tmp, cpu_V0); - } else { - if (op == 8) - gen_neon_narrow_sats(size - 1, tmp, cpu_V0); - else - gen_neon_narrow_satu(size - 1, tmp, cpu_V0); - } - neon_store_reg(rd, pass, tmp); - } /* for pass */ - if (size == 3) { + tmp = tcg_temp_new_i32(); + gen_neon_narrow_op(op == 8, u, size - 1, tmp, cpu_V0); + neon_store_reg(rd, pass, tmp); + } /* for pass */ tcg_temp_free_i64(tmp64); } else { + if (size == 1) { + imm = (uint16_t)shift; + imm |= imm << 16; + } else { + /* size == 2 */ + imm = (uint32_t)shift; + } + tmp2 = tcg_const_i32(imm); + tmp4 = neon_load_reg(rm + 1, 0); + tmp5 = neon_load_reg(rm + 1, 1); + for (pass = 0; pass < 2; pass++) { + if (pass == 0) { + tmp = neon_load_reg(rm, 0); + } else { + tmp = tmp4; + } + gen_neon_shift_narrow(size, tmp, tmp2, q, + input_unsigned); + if (pass == 0) { + tmp3 = neon_load_reg(rm, 1); + } else { + tmp3 = tmp5; + } + gen_neon_shift_narrow(size, tmp3, tmp2, q, + input_unsigned); + tcg_gen_concat_i32_i64(cpu_V0, tmp, tmp3); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp3); + tmp = tcg_temp_new_i32(); + gen_neon_narrow_op(op == 8, u, size - 1, tmp, cpu_V0); + neon_store_reg(rd, pass, tmp); + } /* for pass */ tcg_temp_free_i32(tmp2); } } else if (op == 10) { - /* VSHLL */ - if (q || size == 3) + /* VSHLL, VMOVL */ + if (q || (rd & 1)) { return 1; + } tmp = neon_load_reg(rm, 0); tmp2 = neon_load_reg(rm, 1); for (pass = 0; pass < 2; pass++) { @@ -4870,22 +5324,37 @@ /* The shift is less than the width of the source type, so we can just shift the whole register. */ tcg_gen_shli_i64(cpu_V0, cpu_V0, shift); + /* Widen the result of shift: we need to clear + * the potential overflow bits resulting from + * left bits of the narrow input appearing as + * right bits of left the neighbour narrow + * input. */ if (size < 2 || !u) { uint64_t imm64; if (size == 0) { imm = (0xffu >> (8 - shift)); imm |= imm << 16; - } else { + } else if (size == 1) { imm = 0xffff >> (16 - shift); + } else { + /* size == 2 */ + imm = 0xffffffff >> (32 - shift); + } + if (size < 2) { + imm64 = imm | (((uint64_t)imm) << 32); + } else { + imm64 = imm; } - imm64 = imm | (((uint64_t)imm) << 32); - tcg_gen_andi_i64(cpu_V0, cpu_V0, imm64); + tcg_gen_andi_i64(cpu_V0, cpu_V0, ~imm64); } } neon_store_reg64(cpu_V0, rd + pass); } } else if (op >= 14) { /* VCVT fixed-point. */ + if (!(insn & (1 << 21)) || (q && ((rd | rm) & 1))) { + return 1; + } /* We have already masked out the must-be-1 top bit of imm6, * hence this 32-shift where the ARM ARM has 64-imm6. */ @@ -4894,14 +5363,14 @@ tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass)); if (!(op & 1)) { if (u) - gen_vfp_ulto(0, shift); + gen_vfp_ulto(0, shift, 1); else - gen_vfp_slto(0, shift); + gen_vfp_slto(0, shift, 1); } else { if (u) - gen_vfp_toul(0, shift); + gen_vfp_toul(0, shift, 1); else - gen_vfp_tosl(0, shift); + gen_vfp_tosl(0, shift, 1); } tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, pass)); } @@ -4910,11 +5379,18 @@ } } else { /* (insn & 0x00380080) == 0 */ int invert; + if (q && (rd & 1)) { + return 1; + } op = (insn >> 8) & 0xf; /* One register and immediate. */ imm = (u << 7) | ((insn >> 12) & 0x70) | (insn & 0xf); invert = (insn & (1 << 5)) != 0; + /* Note that op = 2,3,4,5,6,7,10,11,12,13 imm=0 is UNPREDICTABLE. + * We choose to not special-case this and will behave as if a + * valid constant encoding of 0 had been given. + */ switch (op) { case 0: case 1: /* no-op */ @@ -4946,6 +5422,9 @@ imm = ~imm; break; case 15: + if (invert) { + return 1; + } imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19) | ((imm & 0x40) ? (0x1f << 25) : (1 << 30)); break; @@ -4965,8 +5444,9 @@ } } else { /* VMOV, VMVN. */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (op == 14 && invert) { + int n; uint32_t val; val = 0; for (n = 0; n < 4; n++) { @@ -4989,31 +5469,47 @@ int src1_wide; int src2_wide; int prewiden; - /* prewiden, src1_wide, src2_wide */ - static const int neon_3reg_wide[16][3] = { - {1, 0, 0}, /* VADDL */ - {1, 1, 0}, /* VADDW */ - {1, 0, 0}, /* VSUBL */ - {1, 1, 0}, /* VSUBW */ - {0, 1, 1}, /* VADDHN */ - {0, 0, 0}, /* VABAL */ - {0, 1, 1}, /* VSUBHN */ - {0, 0, 0}, /* VABDL */ - {0, 0, 0}, /* VMLAL */ - {0, 0, 0}, /* VQDMLAL */ - {0, 0, 0}, /* VMLSL */ - {0, 0, 0}, /* VQDMLSL */ - {0, 0, 0}, /* Integer VMULL */ - {0, 0, 0}, /* VQDMULL */ - {0, 0, 0} /* Polynomial VMULL */ + /* undefreq: bit 0 : UNDEF if size != 0 + * bit 1 : UNDEF if size == 0 + * bit 2 : UNDEF if U == 1 + * Note that [1:0] set implies 'always UNDEF' + */ + int undefreq; + /* prewiden, src1_wide, src2_wide, undefreq */ + static const int neon_3reg_wide[16][4] = { + {1, 0, 0, 0}, /* VADDL */ + {1, 1, 0, 0}, /* VADDW */ + {1, 0, 0, 0}, /* VSUBL */ + {1, 1, 0, 0}, /* VSUBW */ + {0, 1, 1, 0}, /* VADDHN */ + {0, 0, 0, 0}, /* VABAL */ + {0, 1, 1, 0}, /* VSUBHN */ + {0, 0, 0, 0}, /* VABDL */ + {0, 0, 0, 0}, /* VMLAL */ + {0, 0, 0, 6}, /* VQDMLAL */ + {0, 0, 0, 0}, /* VMLSL */ + {0, 0, 0, 6}, /* VQDMLSL */ + {0, 0, 0, 0}, /* Integer VMULL */ + {0, 0, 0, 2}, /* VQDMULL */ + {0, 0, 0, 5}, /* Polynomial VMULL */ + {0, 0, 0, 3}, /* Reserved: always UNDEF */ }; prewiden = neon_3reg_wide[op][0]; src1_wide = neon_3reg_wide[op][1]; src2_wide = neon_3reg_wide[op][2]; + undefreq = neon_3reg_wide[op][3]; - if (size == 0 && (op == 9 || op == 11 || op == 13)) + if (((undefreq & 1) && (size != 0)) || + ((undefreq & 2) && (size == 0)) || + ((undefreq & 4) && u)) { return 1; + } + if ((src1_wide && (rn & 1)) || + (src2_wide && (rm & 1)) || + (!src2_wide && (rd & 1))) { + return 1; + } /* Avoid overlapping operands. Wide source operands are always aligned so will never overlap with wide @@ -5082,48 +5578,49 @@ break; default: abort(); } - dead_tmp(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); break; 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); break; case 14: /* Polynomial VMULL */ - cpu_abort(env, "Polynomial VMULL not implemented"); - - default: /* 15 is RESERVED. */ - return 1; + gen_helper_neon_mull_p8(cpu_V0, tmp, tmp2); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); + break; + default: /* 15 is RESERVED: caught earlier */ + abort(); } - if (op == 5 || op == 13 || (op >= 8 && op <= 11)) { + if (op == 13) { + /* VQDMULL */ + gen_neon_addl_saturate(cpu_V0, cpu_V0, size); + neon_store_reg64(cpu_V0, rd + pass); + } else if (op == 5 || (op >= 8 && op <= 11)) { /* Accumulate. */ - if (op == 10 || op == 11) { - gen_neon_negl(cpu_V0, size); - } - - if (op != 13) { - neon_load_reg64(cpu_V1, rd + pass); - } - + neon_load_reg64(cpu_V1, rd + pass); switch (op) { - case 5: case 8: case 10: /* VABAL, VMLAL, VMLSL */ + case 10: /* VMLSL */ + gen_neon_negl(cpu_V0, size); + /* Fall through */ + case 5: case 8: /* VABAL, VMLAL */ gen_neon_addl(size); break; case 9: case 11: /* VQDMLAL, VQDMLSL */ gen_neon_addl_saturate(cpu_V0, cpu_V0, size); + if (op == 11) { + gen_neon_negl(cpu_V0, size); + } gen_neon_addl_saturate(cpu_V0, cpu_V1, size); break; - /* Fall through. */ - case 13: /* VQDMULL */ - gen_neon_addl_saturate(cpu_V0, cpu_V0, size); - break; default: abort(); } neon_store_reg64(cpu_V0, rd + pass); } else if (op == 4 || op == 6) { /* Narrowing operation. */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (!u) { switch (size) { case 0: @@ -5166,16 +5663,29 @@ } } } else { - /* Two registers and a scalar. */ + /* Two registers and a scalar. NB that for ops of this form + * the ARM ARM labels bit 24 as Q, but it is in our variable + * 'u', not 'q'. + */ + if (size == 0) { + return 1; + } switch (op) { - case 0: /* Integer VMLA scalar */ case 1: /* Float VMLA scalar */ - case 4: /* Integer VMLS scalar */ case 5: /* Floating point VMLS scalar */ - case 8: /* Integer VMUL scalar */ case 9: /* Floating point VMUL scalar */ + if (size == 1) { + return 1; + } + /* fall through */ + case 0: /* Integer VMLA scalar */ + case 4: /* Integer VMLS scalar */ + case 8: /* Integer VMUL scalar */ case 12: /* VQDMULH scalar */ case 13: /* VQRDMULH scalar */ + if (u && ((rd | rn) & 1)) { + return 1; + } tmp = neon_get_scalar(size, rm); neon_store_scratch(0, tmp); for (pass = 0; pass < (u ? 4 : 2); pass++) { @@ -5194,16 +5704,18 @@ gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2); } } else if (op & 1) { - gen_helper_neon_mul_f32(tmp, tmp, tmp2); + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus); + tcg_temp_free_ptr(fpstatus); } else { switch (size) { case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break; case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (op < 8) { /* Accumulate. */ tmp2 = neon_load_reg(rd, pass); @@ -5212,35 +5724,47 @@ gen_neon_add(size, tmp, tmp2); break; case 1: - gen_helper_neon_add_f32(tmp, tmp, tmp2); + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus); + tcg_temp_free_ptr(fpstatus); break; + } case 4: gen_neon_rsb(size, tmp, tmp2); break; case 5: - gen_helper_neon_sub_f32(tmp, tmp2, tmp); + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus); + tcg_temp_free_ptr(fpstatus); break; + } default: abort(); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } neon_store_reg(rd, pass, tmp); } break; - case 2: /* VMLAL sclar */ case 3: /* VQDMLAL scalar */ - case 6: /* VMLSL scalar */ case 7: /* VQDMLSL scalar */ - case 10: /* VMULL scalar */ case 11: /* VQDMULL scalar */ - if (size == 0 && (op == 3 || op == 7 || op == 11)) + if (u == 1) { return 1; - + } + /* fall through */ + case 2: /* VMLAL sclar */ + case 6: /* VMLSL scalar */ + case 10: /* VMULL scalar */ + if (rd & 1) { + 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(); + tmp4 = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp4, tmp2); tmp3 = neon_load_reg(rn, 1); @@ -5252,18 +5776,21 @@ tmp2 = tmp4; } gen_neon_mull(cpu_V0, tmp, tmp2, size, u); - if (op == 6 || op == 7) { - gen_neon_negl(cpu_V0, size); - } if (op != 11) { neon_load_reg64(cpu_V1, rd + pass); } switch (op) { - case 2: case 6: + case 6: + gen_neon_negl(cpu_V0, size); + /* Fall through */ + case 2: gen_neon_addl(size); break; case 3: case 7: gen_neon_addl_saturate(cpu_V0, cpu_V0, size); + if (op == 7) { + gen_neon_negl(cpu_V0, size); + } gen_neon_addl_saturate(cpu_V0, cpu_V1, size); break; case 10: @@ -5292,6 +5819,10 @@ if (imm > 7 && !q) return 1; + if (q && ((rd | rn | rm) & 1)) { + return 1; + } + if (imm == 0) { neon_load_reg64(cpu_V0, rn); if (q) { @@ -5340,10 +5871,16 @@ /* Two register misc. */ op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf); size = (insn >> 18) & 3; + /* UNDEF for unknown op values and bad op-size combinations */ + if ((neon_2rm_sizes[op] & (1 << size)) == 0) { + return 1; + } + if ((op != NEON_2RM_VMOVN && op != NEON_2RM_VQMOVN) && + q && ((rm | rd) & 1)) { + return 1; + } switch (op) { - case 0: /* VREV64 */ - if (size == 3) - return 1; + case NEON_2RM_VREV64: for (pass = 0; pass < (q ? 2 : 1); pass++) { tmp = neon_load_reg(rm, pass * 2); tmp2 = neon_load_reg(rm, pass * 2 + 1); @@ -5366,10 +5903,8 @@ } } break; - case 4: case 5: /* VPADDL */ - case 12: case 13: /* VPADAL */ - if (size == 3) - return 1; + case NEON_2RM_VPADDL: case NEON_2RM_VPADDL_U: + case NEON_2RM_VPADAL: case NEON_2RM_VPADAL_U: for (pass = 0; pass < q + 1; pass++) { tmp = neon_load_reg(rm, pass * 2); gen_neon_widen(cpu_V0, tmp, size, op & 1); @@ -5381,7 +5916,7 @@ case 2: tcg_gen_add_i64(CPU_V001); break; default: abort(); } - if (op >= 12) { + if (op >= NEON_2RM_VPADAL) { /* Accumulate. */ neon_load_reg64(cpu_V1, rd + pass); gen_neon_addl(size); @@ -5389,8 +5924,9 @@ neon_store_reg64(cpu_V0, rd + pass); } break; - case 33: /* VTRN */ + case NEON_2RM_VTRN: if (size == 2) { + int n; for (n = 0; n < (q ? 4 : 2); n += 2) { tmp = neon_load_reg(rm, n); tmp2 = neon_load_reg(rd, n + 1); @@ -5401,73 +5937,27 @@ goto elementwise; } break; - case 34: /* VUZP */ - /* Reg Before After - Rd A3 A2 A1 A0 B2 B0 A2 A0 - Rm B3 B2 B1 B0 B3 B1 A3 A1 - */ - if (size == 3) + case NEON_2RM_VUZP: + if (gen_neon_unzip(rd, rm, size, q)) { return 1; - gen_neon_unzip(rd, q, 0, size); - gen_neon_unzip(rm, q, 4, size); - if (q) { - static int unzip_order_q[8] = - {0, 2, 4, 6, 1, 3, 5, 7}; - for (n = 0; n < 8; n++) { - int reg = (n < 4) ? rd : rm; - tmp = neon_load_scratch(unzip_order_q[n]); - neon_store_reg(reg, n % 4, tmp); - } - } else { - static int unzip_order[4] = - {0, 4, 1, 5}; - for (n = 0; n < 4; n++) { - int reg = (n < 2) ? rd : rm; - tmp = neon_load_scratch(unzip_order[n]); - neon_store_reg(reg, n % 2, tmp); - } } break; - case 35: /* VZIP */ - /* Reg Before After - Rd A3 A2 A1 A0 B1 A1 B0 A0 - Rm B3 B2 B1 B0 B3 A3 B2 A2 - */ - if (size == 3) + case NEON_2RM_VZIP: + if (gen_neon_zip(rd, rm, size, q)) { return 1; - count = (q ? 4 : 2); - for (n = 0; n < count; n++) { - tmp = neon_load_reg(rd, n); - tmp2 = neon_load_reg(rd, n); - switch (size) { - case 0: gen_neon_zip_u8(tmp, tmp2); break; - case 1: gen_neon_zip_u16(tmp, tmp2); break; - case 2: /* no-op */; break; - default: abort(); - } - neon_store_scratch(n * 2, tmp); - neon_store_scratch(n * 2 + 1, tmp2); - } - for (n = 0; n < count * 2; n++) { - int reg = (n < count) ? rd : rm; - tmp = neon_load_scratch(n); - neon_store_reg(reg, n % count, tmp); } break; - case 36: case 37: /* VMOVN, VQMOVUN, VQMOVN */ - if (size == 3) + case NEON_2RM_VMOVN: case NEON_2RM_VQMOVN: + /* also VQMOVUN; op field and mnemonics don't line up */ + if (rm & 1) { return 1; + } TCGV_UNUSED(tmp2); for (pass = 0; pass < 2; pass++) { neon_load_reg64(cpu_V0, rm + pass); - tmp = new_tmp(); - if (op == 36 && q == 0) { - gen_neon_narrow(size, tmp, cpu_V0); - } else if (q) { - gen_neon_narrow_satu(size, tmp, cpu_V0); - } else { - gen_neon_narrow_sats(size, tmp, cpu_V0); - } + tmp = tcg_temp_new_i32(); + gen_neon_narrow_op(op == NEON_2RM_VMOVN, q, size, + tmp, cpu_V0); if (pass == 0) { tmp2 = tmp; } else { @@ -5476,9 +5966,10 @@ } } break; - case 38: /* VSHLL */ - if (q || size == 3) + case NEON_2RM_VSHLL: + if (q || (rd & 1)) { return 1; + } tmp = neon_load_reg(rm, 0); tmp2 = neon_load_reg(rm, 1); for (pass = 0; pass < 2; pass++) { @@ -5489,54 +5980,58 @@ neon_store_reg64(cpu_V0, rd + pass); } break; - case 44: /* VCVT.F16.F32 */ - if (!arm_feature(env, ARM_FEATURE_VFP_FP16)) - return 1; - tmp = new_tmp(); - tmp2 = new_tmp(); + case NEON_2RM_VCVT_F16_F32: + if (!arm_feature(env, ARM_FEATURE_VFP_FP16) || + q || (rm & 1)) { + return 1; + } + tmp = tcg_temp_new_i32(); + tmp2 = tcg_temp_new_i32(); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 0)); - gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); + gen_helper_neon_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 1)); - gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); + gen_helper_neon_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp2, tmp2, tmp); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 2)); - gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); + gen_helper_neon_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 3)); neon_store_reg(rd, 0, tmp2); - tmp2 = new_tmp(); - gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); + tmp2 = tcg_temp_new_i32(); + gen_helper_neon_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp2, tmp2, tmp); neon_store_reg(rd, 1, tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; - case 46: /* VCVT.F32.F16 */ - if (!arm_feature(env, ARM_FEATURE_VFP_FP16)) - return 1; - tmp3 = new_tmp(); + case NEON_2RM_VCVT_F32_F16: + if (!arm_feature(env, ARM_FEATURE_VFP_FP16) || + q || (rd & 1)) { + return 1; + } + tmp3 = tcg_temp_new_i32(); tmp = neon_load_reg(rm, 0); tmp2 = neon_load_reg(rm, 1); tcg_gen_ext16u_i32(tmp3, tmp); - gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 0)); tcg_gen_shri_i32(tmp3, tmp, 16); - gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 1)); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tcg_gen_ext16u_i32(tmp3, tmp2); - gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 2)); tcg_gen_shri_i32(tmp3, tmp2, 16); - gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 3)); - dead_tmp(tmp2); - dead_tmp(tmp3); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp3); break; default: elementwise: for (pass = 0; pass < (q ? 4 : 2); pass++) { - if (op == 30 || op == 31 || op >= 58) { + if (neon_2rm_is_float_op(op)) { tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass)); TCGV_UNUSED(tmp); @@ -5544,177 +6039,210 @@ tmp = neon_load_reg(rm, pass); } switch (op) { - case 1: /* VREV32 */ + case NEON_2RM_VREV32: switch (size) { case 0: tcg_gen_bswap32_i32(tmp, tmp); break; case 1: gen_swap_half(tmp); break; - default: return 1; + default: abort(); } break; - case 2: /* VREV16 */ - if (size != 0) - return 1; + case NEON_2RM_VREV16: gen_rev16(tmp); break; - case 8: /* CLS */ + case NEON_2RM_VCLS: switch (size) { case 0: gen_helper_neon_cls_s8(tmp, tmp); break; case 1: gen_helper_neon_cls_s16(tmp, tmp); break; case 2: gen_helper_neon_cls_s32(tmp, tmp); break; - default: return 1; + default: abort(); } break; - case 9: /* CLZ */ + case NEON_2RM_VCLZ: switch (size) { case 0: gen_helper_neon_clz_u8(tmp, tmp); break; case 1: gen_helper_neon_clz_u16(tmp, tmp); break; case 2: gen_helper_clz(tmp, tmp); break; - default: return 1; + default: abort(); } break; - case 10: /* CNT */ - if (size != 0) - return 1; + case NEON_2RM_VCNT: gen_helper_neon_cnt_u8(tmp, tmp); break; - case 11: /* VNOT */ - if (size != 0) - return 1; + case NEON_2RM_VMVN: tcg_gen_not_i32(tmp, tmp); break; - case 14: /* VQABS */ + case NEON_2RM_VQABS: switch (size) { - case 0: gen_helper_neon_qabs_s8(tmp, cpu_env, tmp); break; - case 1: gen_helper_neon_qabs_s16(tmp, cpu_env, tmp); break; - case 2: gen_helper_neon_qabs_s32(tmp, cpu_env, tmp); break; - default: return 1; + case 0: + gen_helper_neon_qabs_s8(tmp, cpu_env, tmp); + break; + case 1: + gen_helper_neon_qabs_s16(tmp, cpu_env, tmp); + break; + case 2: + gen_helper_neon_qabs_s32(tmp, cpu_env, tmp); + break; + default: abort(); } break; - case 15: /* VQNEG */ + case NEON_2RM_VQNEG: switch (size) { - case 0: gen_helper_neon_qneg_s8(tmp, cpu_env, tmp); break; - case 1: gen_helper_neon_qneg_s16(tmp, cpu_env, tmp); break; - case 2: gen_helper_neon_qneg_s32(tmp, cpu_env, tmp); break; - default: return 1; + case 0: + gen_helper_neon_qneg_s8(tmp, cpu_env, tmp); + break; + case 1: + gen_helper_neon_qneg_s16(tmp, cpu_env, tmp); + break; + case 2: + gen_helper_neon_qneg_s32(tmp, cpu_env, tmp); + break; + default: abort(); } break; - case 16: case 19: /* VCGT #0, VCLE #0 */ + case NEON_2RM_VCGT0: case NEON_2RM_VCLE0: tmp2 = tcg_const_i32(0); switch(size) { case 0: gen_helper_neon_cgt_s8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_cgt_s16(tmp, tmp, tmp2); break; case 2: gen_helper_neon_cgt_s32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } tcg_temp_free(tmp2); - if (op == 19) + if (op == NEON_2RM_VCLE0) { tcg_gen_not_i32(tmp, tmp); + } break; - case 17: case 20: /* VCGE #0, VCLT #0 */ + case NEON_2RM_VCGE0: case NEON_2RM_VCLT0: tmp2 = tcg_const_i32(0); switch(size) { case 0: gen_helper_neon_cge_s8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_cge_s16(tmp, tmp, tmp2); break; case 2: gen_helper_neon_cge_s32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } tcg_temp_free(tmp2); - if (op == 20) + if (op == NEON_2RM_VCLT0) { tcg_gen_not_i32(tmp, tmp); + } break; - case 18: /* VCEQ #0 */ + case NEON_2RM_VCEQ0: tmp2 = tcg_const_i32(0); switch(size) { case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break; case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break; case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break; - default: return 1; + default: abort(); } tcg_temp_free(tmp2); break; - case 22: /* VABS */ + case NEON_2RM_VABS: switch(size) { case 0: gen_helper_neon_abs_s8(tmp, tmp); break; case 1: gen_helper_neon_abs_s16(tmp, tmp); break; case 2: tcg_gen_abs_i32(tmp, tmp); break; - default: return 1; + default: abort(); } break; - case 23: /* VNEG */ - if (size == 3) - return 1; + case NEON_2RM_VNEG: tmp2 = tcg_const_i32(0); gen_neon_rsb(size, tmp, tmp2); tcg_temp_free(tmp2); break; - case 24: case 27: /* Float VCGT #0, Float VCLE #0 */ + case NEON_2RM_VCGT0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_cgt_f32(tmp, tmp, tmp2); + gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus); tcg_temp_free(tmp2); - if (op == 27) - tcg_gen_not_i32(tmp, tmp); + tcg_temp_free_ptr(fpstatus); break; - case 25: case 28: /* Float VCGE #0, Float VCLT #0 */ + } + case NEON_2RM_VCGE0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_cge_f32(tmp, tmp, tmp2); + gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus); tcg_temp_free(tmp2); - if (op == 28) - tcg_gen_not_i32(tmp, tmp); + tcg_temp_free_ptr(fpstatus); + break; + } + case NEON_2RM_VCEQ0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + tmp2 = tcg_const_i32(0); + gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus); + tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); + break; + } + case NEON_2RM_VCLE0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + tmp2 = tcg_const_i32(0); + gen_helper_neon_cge_f32(tmp, tmp2, tmp, fpstatus); + tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); break; - case 26: /* Float VCEQ #0 */ + } + case NEON_2RM_VCLT0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_ceq_f32(tmp, tmp, tmp2); + gen_helper_neon_cgt_f32(tmp, tmp2, tmp, fpstatus); tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); break; - case 30: /* Float VABS */ + } + case NEON_2RM_VABS_F: gen_vfp_abs(0); break; - case 31: /* Float VNEG */ + case NEON_2RM_VNEG_F: gen_vfp_neg(0); break; - case 32: /* VSWP */ + case NEON_2RM_VSWP: tmp2 = neon_load_reg(rd, pass); neon_store_reg(rm, pass, tmp2); break; - case 33: /* VTRN */ + case NEON_2RM_VTRN: tmp2 = neon_load_reg(rd, pass); switch (size) { case 0: gen_neon_trn_u8(tmp, tmp2); break; case 1: gen_neon_trn_u16(tmp, tmp2); break; - case 2: abort(); - default: return 1; + default: abort(); } neon_store_reg(rm, pass, tmp2); break; - case 56: /* Integer VRECPE */ + case NEON_2RM_VRECPE: gen_helper_recpe_u32(tmp, tmp, cpu_env); break; - case 57: /* Integer VRSQRTE */ + case NEON_2RM_VRSQRTE: gen_helper_rsqrte_u32(tmp, tmp, cpu_env); break; - case 58: /* Float VRECPE */ + case NEON_2RM_VRECPE_F: gen_helper_recpe_f32(cpu_F0s, cpu_F0s, cpu_env); break; - case 59: /* Float VRSQRTE */ + case NEON_2RM_VRSQRTE_F: gen_helper_rsqrte_f32(cpu_F0s, cpu_F0s, cpu_env); break; - case 60: /* VCVT.F32.S32 */ - gen_vfp_sito(0); + case NEON_2RM_VCVT_FS: /* VCVT.F32.S32 */ + gen_vfp_sito(0, 1); break; - case 61: /* VCVT.F32.U32 */ - gen_vfp_uito(0); + case NEON_2RM_VCVT_FU: /* VCVT.F32.U32 */ + gen_vfp_uito(0, 1); break; - case 62: /* VCVT.S32.F32 */ - gen_vfp_tosiz(0); + case NEON_2RM_VCVT_SF: /* VCVT.S32.F32 */ + gen_vfp_tosiz(0, 1); break; - case 63: /* VCVT.U32.F32 */ - gen_vfp_touiz(0); + case NEON_2RM_VCVT_UF: /* VCVT.U32.F32 */ + gen_vfp_touiz(0, 1); break; default: - /* Reserved: 21, 29, 39-56 */ - return 1; + /* Reserved op values were caught by the + * neon_2rm_sizes[] check earlier. + */ + abort(); } - if (op == 30 || op == 31 || op >= 58) { + if (neon_2rm_is_float_op(op)) { tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, pass)); } else { @@ -5725,22 +6253,29 @@ } } else if ((insn & (1 << 10)) == 0) { /* VTBL, VTBX. */ - n = ((insn >> 5) & 0x18) + 8; + int n = ((insn >> 8) & 3) + 1; + if ((rn + n) > 32) { + /* This is UNPREDICTABLE; we choose to UNDEF to avoid the + * helper function running off the end of the register file. + */ + return 1; + } + n <<= 3; if (insn & (1 << 6)) { tmp = neon_load_reg(rd, 0); } else { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } tmp2 = neon_load_reg(rm, 0); tmp4 = tcg_const_i32(rn); tmp5 = tcg_const_i32(n); gen_helper_neon_tbl(tmp2, tmp2, tmp, tmp4, tmp5); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); if (insn & (1 << 6)) { tmp = neon_load_reg(rd, 1); } else { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } tmp3 = neon_load_reg(rm, 1); @@ -5749,9 +6284,12 @@ tcg_temp_free_i32(tmp4); neon_store_reg(rd, 0, tmp2); neon_store_reg(rd, 1, tmp3); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else if ((insn & 0x380) == 0) { /* VDUP */ + if ((insn & (7 << 16)) == 0 || (q && (rd & 1))) { + return 1; + } if (insn & (1 << 19)) { tmp = neon_load_reg(rm, 1); } else { @@ -5766,11 +6304,11 @@ gen_neon_dup_low16(tmp); } for (pass = 0; pass < (q ? 4 : 2); pass++) { - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp2, tmp); neon_store_reg(rd, pass, tmp2); } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else { return 1; } @@ -5788,6 +6326,34 @@ int rt = (insn >> 12) & 0xf; TCGv tmp; + /* Minimal set of debug registers, since we don't support debug */ + if (op1 == 0 && crn == 0 && op2 == 0) { + switch (crm) { + case 0: + /* DBGDIDR: just RAZ. In particular this means the + * "debug architecture version" bits will read as + * a reserved value, which should cause Linux to + * not try to use the debug hardware. + */ + tmp = tcg_const_i32(0); + store_reg(s, rt, tmp); + return 0; + case 1: + case 2: + /* DBGDRAR and DBGDSAR: v7 only. Always RAZ since we + * don't implement memory mapped debug components + */ + if (ENABLE_ARCH_7) { + tmp = tcg_const_i32(0); + store_reg(s, rt, tmp); + return 0; + } + break; + default: + break; + } + } + if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) { /* TEECR */ @@ -5827,7 +6393,7 @@ return 1; tmp = load_reg(s, rt); gen_helper_set_teecr(cpu_env, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); return 0; } if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) { @@ -5888,10 +6454,10 @@ static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) { TCGv tmp; - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_trunc_i64_i32(tmp, val); store_reg(s, rlow, tmp); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_shri_i64(val, val, 32); tcg_gen_trunc_i64_i32(tmp, val); store_reg(s, rhigh, tmp); @@ -5907,7 +6473,7 @@ tmp = tcg_temp_new_i64(); tmp2 = load_reg(s, rlow); tcg_gen_extu_i32_i64(tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_add_i64(val, val, tmp); tcg_temp_free_i64(tmp); } @@ -5924,8 +6490,8 @@ tmph = load_reg(s, rhigh); tmp = tcg_temp_new_i64(); tcg_gen_concat_i32_i64(tmp, tmpl, tmph); - dead_tmp(tmpl); - dead_tmp(tmph); + tcg_temp_free_i32(tmpl); + tcg_temp_free_i32(tmph); tcg_gen_add_i64(val, val, tmp); tcg_temp_free_i64(tmp); } @@ -5933,10 +6499,10 @@ /* Set N and Z flags from a 64-bit value. */ static void gen_logicq_cc(TCGv_i64 val) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); gen_helper_logicq_cc(tmp, val); gen_logic_CC(tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } /* Load/Store exclusive instructions are implemented by remembering @@ -5970,10 +6536,10 @@ tcg_gen_mov_i32(cpu_exclusive_val, tmp); store_reg(s, rt, tmp); if (size == 3) { - TCGv tmp2 = new_tmp(); + TCGv tmp2 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); tmp = gen_ld32(tmp2, IS_USER(s)); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_mov_i32(cpu_exclusive_high, tmp); store_reg(s, rt2, tmp); } @@ -6026,14 +6592,14 @@ abort(); } tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); if (size == 3) { - TCGv tmp2 = new_tmp(); + TCGv tmp2 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); tmp = gen_ld32(tmp2, IS_USER(s)); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_high, fail_label); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } tmp = load_reg(s, rt); switch (size) { @@ -6081,6 +6647,12 @@ goto illegal_op; cond = insn >> 28; if (cond == 0xf){ + /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we + * choose to UNDEF. In ARMv5 and above the space is used + * for miscellaneous unconditional instructions. + */ + ARCH(5); + /* Unconditional instructions. */ if (((insn >> 25) & 7) == 1) { /* NEON Data processing. */ @@ -6100,9 +6672,32 @@ goto illegal_op; return; } - if ((insn & 0x0d70f000) == 0x0550f000) - return; /* PLD */ - else if ((insn & 0x0ffffdff) == 0x01010000) { + if (((insn & 0x0f30f000) == 0x0510f000) || + ((insn & 0x0f30f010) == 0x0710f000)) { + if ((insn & (1 << 22)) == 0) { + /* PLDW; v7MP */ + if (!arm_feature(env, ARM_FEATURE_V7MP)) { + goto illegal_op; + } + } + /* Otherwise PLD; v5TE+ */ + ARCH(5TE); + return; + } + if (((insn & 0x0f70f000) == 0x0450f000) || + ((insn & 0x0f70f010) == 0x0650f000)) { + ARCH(7); + return; /* PLI; V7 */ + } + if (((insn & 0x0f700000) == 0x04100000) || + ((insn & 0x0f700010) == 0x06100000)) { + if (!arm_feature(env, ARM_FEATURE_V7MP)) { + goto illegal_op; + } + return; /* v7MP: Unallocated memory hint: must NOP */ + } + + if ((insn & 0x0ffffdff) == 0x01010000) { ARCH(6); /* setend */ if (insn & (1 << 9)) { @@ -6132,7 +6727,7 @@ goto illegal_op; ARCH(6); op1 = (insn & 0x1f); - addr = new_tmp(); + addr = tcg_temp_new_i32(); tmp = tcg_const_i32(op1); gen_helper_get_r13_banked(addr, cpu_env, tmp); tcg_temp_free_i32(tmp); @@ -6165,9 +6760,9 @@ tmp = tcg_const_i32(op1); gen_helper_set_r13_banked(cpu_env, tmp, addr); tcg_temp_free_i32(tmp); - dead_tmp(addr); + tcg_temp_free_i32(addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } return; } else if ((insn & 0x0e50ffe0) == 0x08100a00) { @@ -6205,7 +6800,7 @@ tcg_gen_addi_i32(addr, addr, offset); store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } gen_rfe(s, tmp, tmp2); return; @@ -6214,7 +6809,7 @@ int32_t offset; val = (uint32_t)s->pc; - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); store_reg(s, 14, tmp); /* Sign-extend the 24-bit offset */ @@ -6223,6 +6818,7 @@ val += (offset << 2) | ((insn >> 23) & 2) | 1; /* pipeline offset */ val += 4; + /* protected by ARCH(5); above, near the start of uncond block */ gen_bx_im(s, val); return; } else if ((insn & 0x0e000f00) == 0x0c000100) { @@ -6234,6 +6830,7 @@ } } else if ((insn & 0x0fe00000) == 0x0c400000) { /* Coprocessor double register transfer. */ + ARCH(5TE); } else if ((insn & 0x0f000010) == 0x0e000010) { /* Additional coprocessor register transfer. */ } else if ((insn & 0x0ff10020) == 0x01000000) { @@ -6278,7 +6875,7 @@ val = ((insn >> 4) & 0xf000) | (insn & 0xfff); if ((insn & (1 << 22)) == 0) { /* MOVW */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); } else { /* MOVT */ @@ -6325,7 +6922,7 @@ goto illegal_op; tmp = load_cpu_field(spsr); } else { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); gen_helper_cpsr_read(tmp); } store_reg(s, rd, tmp); @@ -6334,10 +6931,12 @@ case 0x1: if (op1 == 1) { /* branch/exchange thumb (bx). */ + ARCH(4T); tmp = load_reg(s, rm); gen_bx(s, tmp); } else if (op1 == 3) { /* clz */ + ARCH(5); rd = (insn >> 12) & 0xf; tmp = load_reg(s, rm); gen_helper_clz(tmp, tmp); @@ -6360,14 +6959,16 @@ if (op1 != 1) goto illegal_op; + ARCH(5); /* branch link/exchange thumb (blx) */ tmp = load_reg(s, rm); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, s->pc); store_reg(s, 14, tmp2); gen_bx(s, tmp); break; case 0x5: /* saturating add/subtract */ + ARCH(5TE); rd = (insn >> 12) & 0xf; rn = (insn >> 16) & 0xf; tmp = load_reg(s, rm); @@ -6378,7 +6979,7 @@ gen_helper_sub_saturate(tmp, tmp, tmp2); else gen_helper_add_saturate(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 7: @@ -6389,12 +6990,14 @@ goto illegal_op; } /* bkpt */ + ARCH(5); gen_exception_insn(s, 4, EXCP_BKPT); break; case 0x8: /* signed multiply */ case 0xa: case 0xc: case 0xe: + ARCH(5TE); rs = (insn >> 8) & 0xf; rn = (insn >> 12) & 0xf; rd = (insn >> 16) & 0xf; @@ -6408,13 +7011,13 @@ gen_sxth(tmp2); tmp64 = gen_muls_i64_i32(tmp, tmp2); tcg_gen_shri_i64(tmp64, tmp64, 16); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_trunc_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); if ((sh & 2) == 0) { tmp2 = load_reg(s, rn); gen_helper_add_setq(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } store_reg(s, rd, tmp); } else { @@ -6422,11 +7025,11 @@ tmp = load_reg(s, rm); tmp2 = load_reg(s, rs); gen_mulxy(tmp, tmp2, sh & 2, sh & 4); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (op1 == 2) { tmp64 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_addq(s, tmp64, rn, rd); gen_storeq_reg(s, rn, rd, tmp64); tcg_temp_free_i64(tmp64); @@ -6434,7 +7037,7 @@ if (op1 == 0) { tmp2 = load_reg(s, rn); gen_helper_add_setq(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } store_reg(s, rd, tmp); } @@ -6460,7 +7063,7 @@ if (shift) { val = (val >> shift) | (val << (32 - shift)); } - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, val); if (logic_cc && shift) { gen_set_CF_bit31(tmp2); @@ -6563,26 +7166,26 @@ tcg_gen_and_i32(tmp, tmp, tmp2); gen_logic_CC(tmp); } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; case 0x09: if (set_cc) { tcg_gen_xor_i32(tmp, tmp, tmp2); gen_logic_CC(tmp); } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; case 0x0a: if (set_cc) { gen_helper_sub_cc(tmp, tmp, tmp2); } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; case 0x0b: if (set_cc) { gen_helper_add_cc(tmp, tmp, tmp2); } - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; case 0x0c: tcg_gen_or_i32(tmp, tmp, tmp2); @@ -6622,7 +7225,7 @@ break; } if (op1 != 0x0f && op1 != 0x0d) { - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } } else { /* other instructions */ @@ -6645,18 +7248,18 @@ tmp = load_reg(s, rs); tmp2 = load_reg(s, rm); tcg_gen_mul_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (insn & (1 << 22)) { /* Subtract (mls) */ ARCH(6T2); tmp2 = load_reg(s, rn); tcg_gen_sub_i32(tmp, tmp2, tmp); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } else if (insn & (1 << 21)) { /* Add */ tmp2 = load_reg(s, rn); tcg_gen_add_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } if (insn & (1 << 20)) gen_logic_CC(tmp); @@ -6760,7 +7363,7 @@ tmp2 = gen_ld32(addr, IS_USER(s)); gen_st32(tmp, addr, IS_USER(s)); } - dead_tmp(addr); + tcg_temp_free_i32(addr); store_reg(s, rd, tmp2); } } @@ -6790,6 +7393,7 @@ } load = 1; } else if (sh & 2) { + ARCH(5TE); /* doubleword */ if (sh & 1) { /* store */ @@ -6827,7 +7431,7 @@ tcg_gen_addi_i32(addr, addr, address_offset); store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } if (load) { /* Complete the load. */ @@ -6856,7 +7460,7 @@ if ((op1 & 3) == 0 || sh == 5 || sh == 6) goto illegal_op; gen_arm_parallel_addsub(op1, sh, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 1: @@ -6880,7 +7484,7 @@ tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); } tcg_gen_or_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00200020) == 0x00200000) { /* [us]sat */ @@ -6916,16 +7520,16 @@ /* Select bytes. */ tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); - tmp3 = new_tmp(); + tmp3 = tcg_temp_new_i32(); tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE)); gen_helper_sel_flags(tmp, tmp3, tmp, tmp2); - dead_tmp(tmp3); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp3); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x000003e0) == 0x00000060) { tmp = load_reg(s, rm); shift = (insn >> 10) & 3; - /* ??? In many cases it's not neccessary to do a + /* ??? In many cases it's not necessary to do a rotate, a shift is sufficient. */ if (shift != 0) tcg_gen_rotri_i32(tmp, tmp, shift * 8); @@ -6945,7 +7549,7 @@ gen_add16(tmp, tmp2); } else { tcg_gen_add_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } } store_reg(s, rd, tmp); @@ -6990,7 +7594,7 @@ tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u); } tcg_gen_shri_i64(tmp64, tmp64, 32); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_trunc_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); store_reg(s, rn, tmp); @@ -6998,18 +7602,22 @@ if (insn & (1 << 5)) gen_swap_half(tmp2); gen_smul_dual(tmp, tmp2); - /* This addition cannot overflow. */ if (insn & (1 << 6)) { + /* This subtraction cannot overflow. */ tcg_gen_sub_i32(tmp, tmp, tmp2); } else { - tcg_gen_add_i32(tmp, tmp, tmp2); + /* This addition cannot overflow 32 bits; + * however it may overflow considered as a signed + * operation, in which case we must set the Q flag. + */ + gen_helper_add_setq(tmp, tmp, tmp2); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (insn & (1 << 22)) { /* smlald, smlsld */ tmp64 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_addq(s, tmp64, rd, rn); gen_storeq_reg(s, rd, rn, tmp64); tcg_temp_free_i64(tmp64); @@ -7019,7 +7627,7 @@ { tmp2 = load_reg(s, rd); gen_helper_add_setq(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } store_reg(s, rn, tmp); } @@ -7033,11 +7641,11 @@ tmp = load_reg(s, rm); tmp2 = load_reg(s, rs); gen_helper_usad8(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (rd != 15) { tmp2 = load_reg(s, rd); tcg_gen_add_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } store_reg(s, rn, tmp); break; @@ -7048,7 +7656,7 @@ i = (insn >> 16) & 0x1f; i = i + 1 - shift; if (rm == 15) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } else { tmp = load_reg(s, rm); @@ -7056,7 +7664,7 @@ if (i != 32) { tmp2 = load_reg(s, rd); gen_bfi(tmp, tmp2, tmp, shift, (1u << i) - 1); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } store_reg(s, rd, tmp); break; @@ -7122,14 +7730,11 @@ } else if (insn & (1 << 21)) { store_reg(s, rn, tmp2); } else { - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } if (insn & (1 << 20)) { /* Complete the load. */ - if (rd == 15) - gen_bx(s, tmp); - else - store_reg(s, rd, tmp); + store_reg_from_load(env, s, rd, tmp); } break; case 0x08: @@ -7182,28 +7787,26 @@ if (insn & (1 << 20)) { /* load */ tmp = gen_ld32(addr, IS_USER(s)); - if (i == 15) { - gen_bx(s, tmp); - } else if (user) { + if (user) { tmp2 = tcg_const_i32(i); gen_helper_set_user_reg(tmp2, tmp); tcg_temp_free_i32(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else if (i == rn) { loaded_var = tmp; loaded_base = 1; } else { - store_reg(s, i, tmp); + store_reg_from_load(env, s, i, tmp); } } else { /* store */ if (i == 15) { /* special case: r15 = PC + 8 */ val = (long)s->pc + 4; - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); } else if (user) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tmp2 = tcg_const_i32(i); gen_helper_get_user_reg(tmp, tmp2); tcg_temp_free_i32(tmp2); @@ -7239,7 +7842,7 @@ } store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } if (loaded_base) { store_reg(s, rn, loaded_var); @@ -7248,7 +7851,7 @@ /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); gen_set_cpsr(tmp, 0xffffffff); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); s->is_jmp = DISAS_UPDATE; } } @@ -7261,7 +7864,7 @@ /* branch (and link) */ val = (int32_t)s->pc; if (insn & (1 << 24)) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); store_reg(s, 14, tmp); } @@ -7323,8 +7926,7 @@ logic_cc = conds; break; case 3: /* orn */ - tcg_gen_not_i32(t1, t1); - tcg_gen_or_i32(t0, t0, t1); + tcg_gen_orc_i32(t0, t0, t1); logic_cc = conds; break; case 4: /* eor */ @@ -7394,13 +7996,14 @@ 16-bit instructions to get correct prefetch abort behavior. */ insn = insn_hw1; if ((insn & (1 << 12)) == 0) { + ARCH(5); /* Second half of blx. */ offset = ((insn & 0x7ff) << 1); tmp = load_reg(s, 14); tcg_gen_addi_i32(tmp, tmp, offset); tcg_gen_andi_i32(tmp, tmp, 0xfffffffc); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, s->pc | 1); store_reg(s, 14, tmp2); gen_bx(s, tmp); @@ -7412,7 +8015,7 @@ tmp = load_reg(s, 14); tcg_gen_addi_i32(tmp, tmp, offset); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, s->pc | 1); store_reg(s, 14, tmp2); gen_bx(s, tmp); @@ -7451,7 +8054,7 @@ if (insn & 0x01200000) { /* Load/store doubleword. */ if (rn == 15) { - addr = new_tmp(); + addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, s->pc & ~3); } else { addr = load_reg(s, rn); @@ -7485,7 +8088,7 @@ tcg_gen_addi_i32(addr, addr, offset - 4); store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } } else if ((insn & (1 << 23)) == 0) { /* Load/store exclusive word. */ @@ -7501,7 +8104,7 @@ } else if ((insn & (1 << 6)) == 0) { /* Table Branch. */ if (rn == 15) { - addr = new_tmp(); + addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, s->pc); } else { addr = load_reg(s, rn); @@ -7511,13 +8114,13 @@ if (insn & (1 << 4)) { /* tbh */ tcg_gen_add_i32(addr, addr, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tmp = gen_ld16u(addr, IS_USER(s)); } else { /* tbb */ - dead_tmp(tmp); + tcg_temp_free_i32(tmp); tmp = gen_ld8u(addr, IS_USER(s)); } - dead_tmp(addr); + tcg_temp_free_i32(addr); tcg_gen_shli_i32(tmp, tmp, 1); tcg_gen_addi_i32(tmp, tmp, s->pc); store_reg(s, 15, tmp); @@ -7561,13 +8164,13 @@ } store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } gen_rfe(s, tmp, tmp2); } else { /* srs */ op = (insn & 0x1f); - addr = new_tmp(); + addr = tcg_temp_new_i32(); tmp = tcg_const_i32(op); gen_helper_get_r13_banked(addr, cpu_env, tmp); tcg_temp_free_i32(tmp); @@ -7577,7 +8180,7 @@ tmp = load_reg(s, 14); gen_st32(tmp, addr, 0); tcg_gen_addi_i32(addr, addr, 4); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); gen_helper_cpsr_read(tmp); gen_st32(tmp, addr, 0); if (insn & (1 << 21)) { @@ -7590,11 +8193,12 @@ gen_helper_set_r13_banked(cpu_env, tmp, addr); tcg_temp_free_i32(tmp); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } } } else { - int i; + int i, loaded_base = 0; + TCGv loaded_var; /* Load/store multiple. */ addr = load_reg(s, rn); offset = 0; @@ -7606,6 +8210,7 @@ tcg_gen_addi_i32(addr, addr, -offset); } + TCGV_UNUSED(loaded_var); for (i = 0; i < 16; i++) { if ((insn & (1 << i)) == 0) continue; @@ -7614,6 +8219,9 @@ tmp = gen_ld32(addr, IS_USER(s)); if (i == 15) { gen_bx(s, tmp); + } else if (i == rn) { + loaded_var = tmp; + loaded_base = 1; } else { store_reg(s, i, tmp); } @@ -7624,6 +8232,9 @@ } tcg_gen_addi_i32(addr, addr, 4); } + if (loaded_base) { + store_reg(s, rn, loaded_var); + } if (insn & (1 << 21)) { /* Base register writeback. */ if (insn & (1 << 24)) { @@ -7634,7 +8245,7 @@ goto illegal_op; store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } } } @@ -7662,12 +8273,12 @@ tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); } tcg_gen_or_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else { /* Data processing register constant shift. */ if (rn == 15) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } else { tmp = load_reg(s, rn); @@ -7681,11 +8292,11 @@ 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); + tcg_temp_free_i32(tmp2); if (rd != 15) { store_reg(s, rd, tmp); } else { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } } break; @@ -7709,7 +8320,7 @@ case 1: /* Sign/zero extend. */ tmp = load_reg(s, rm); shift = (insn >> 4) & 3; - /* ??? In many cases it's not neccessary to do a + /* ??? In many cases it's not necessary to do a rotate, a shift is sufficient. */ if (shift != 0) tcg_gen_rotri_i32(tmp, tmp, shift * 8); @@ -7729,7 +8340,7 @@ gen_add16(tmp, tmp2); } else { tcg_gen_add_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } } store_reg(s, rd, tmp); @@ -7742,7 +8353,7 @@ tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); gen_thumb2_parallel_addsub(op, shift, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 3: /* Other data processing. */ @@ -7757,7 +8368,7 @@ gen_helper_sub_saturate(tmp, tmp2, tmp); else gen_helper_add_saturate(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } else { tmp = load_reg(s, rn); switch (op) { @@ -7775,11 +8386,11 @@ break; case 0x10: /* sel */ tmp2 = load_reg(s, rm); - tmp3 = new_tmp(); + tmp3 = tcg_temp_new_i32(); tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE)); gen_helper_sel_flags(tmp, tmp3, tmp, tmp2); - dead_tmp(tmp3); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp3); + tcg_temp_free_i32(tmp2); break; case 0x18: /* clz */ gen_helper_clz(tmp, tmp); @@ -7797,23 +8408,23 @@ switch ((insn >> 20) & 7) { case 0: /* 32 x 32 -> 32 */ tcg_gen_mul_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (rs != 15) { tmp2 = load_reg(s, rs); if (op) tcg_gen_sub_i32(tmp, tmp2, tmp); else tcg_gen_add_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } break; case 1: /* 16 x 16 -> 32 */ gen_mulxy(tmp, tmp2, op & 2, op & 1); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (rs != 15) { tmp2 = load_reg(s, rs); gen_helper_add_setq(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } break; case 2: /* Dual multiply add. */ @@ -7821,18 +8432,22 @@ if (op) gen_swap_half(tmp2); gen_smul_dual(tmp, tmp2); - /* This addition cannot overflow. */ if (insn & (1 << 22)) { + /* This subtraction cannot overflow. */ tcg_gen_sub_i32(tmp, tmp, tmp2); } else { - tcg_gen_add_i32(tmp, tmp, tmp2); + /* This addition cannot overflow 32 bits; + * however it may overflow considered as a signed + * operation, in which case we must set the Q flag. + */ + gen_helper_add_setq(tmp, tmp, tmp2); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (rs != 15) { tmp2 = load_reg(s, rs); gen_helper_add_setq(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } break; case 3: /* 32 * 16 -> 32msb */ @@ -7842,14 +8457,14 @@ gen_sxth(tmp2); tmp64 = gen_muls_i64_i32(tmp, tmp2); tcg_gen_shri_i64(tmp64, tmp64, 16); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_trunc_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); if (rs != 15) { tmp2 = load_reg(s, rs); gen_helper_add_setq(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } break; case 5: case 6: /* 32 * 32 -> 32msb (SMMUL, SMMLA, SMMLS) */ @@ -7866,17 +8481,17 @@ tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u); } tcg_gen_shri_i64(tmp64, tmp64, 32); - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); 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); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); if (rs != 15) { tmp2 = load_reg(s, rs); tcg_gen_add_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } break; } @@ -7894,7 +8509,7 @@ gen_helper_udiv(tmp, tmp, tmp2); else gen_helper_sdiv(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((op & 0xe) == 0xc) { /* Dual multiply accumulate long. */ @@ -7906,11 +8521,11 @@ } else { tcg_gen_add_i32(tmp, tmp, tmp2); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); /* BUGFIX */ tmp64 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_addq(s, tmp64, rs, rd); gen_storeq_reg(s, rs, rd, tmp64); tcg_temp_free_i64(tmp64); @@ -7922,10 +8537,10 @@ if (op & 8) { /* smlalxy */ gen_mulxy(tmp, tmp2, op & 2, op & 1); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); tmp64 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else { /* Signed 64-bit multiply */ tmp64 = gen_muls_i64_i32(tmp, tmp2); @@ -7949,7 +8564,7 @@ /* Coprocessor. */ if (((insn >> 24) & 3) == 3) { /* Translate into the equivalent ARM encoding. */ - insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4); + insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28); if (disas_neon_data_insn(env, s, insn)) goto illegal_op; } else { @@ -7986,6 +8601,7 @@ } else { /* blx */ offset &= ~(uint32_t)2; + /* thumb2 bx, no need to check */ gen_bx_im(s, offset); } } else if (((insn >> 23) & 7) == 7) { @@ -8005,7 +8621,7 @@ addr = tcg_const_i32(insn & 0xff); gen_helper_v7m_msr(cpu_env, addr, tmp); tcg_temp_free_i32(addr); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); gen_lookup_tb(s); break; } @@ -8079,7 +8695,7 @@ gen_exception_return(s, tmp); break; case 6: /* mrs cpsr. */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); if (IS_M(env)) { addr = tcg_const_i32(insn & 0xff); gen_helper_v7m_mrs(tmp, cpu_env, addr); @@ -8131,7 +8747,7 @@ imm = insn & 0x1f; shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c); if (rn == 15) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } else { tmp = load_reg(s, rn); @@ -8158,7 +8774,7 @@ if (imm != 32) { tmp2 = load_reg(s, rd); gen_bfi(tmp, tmp2, tmp, shift, (1u << imm) - 1); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } break; case 7: @@ -8201,7 +8817,7 @@ tcg_gen_ori_i32(tmp, tmp, imm << 16); } else { /* movw */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, imm); } } else { @@ -8212,7 +8828,7 @@ offset -= imm; else offset += imm; - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, offset); } else { tmp = load_reg(s, rn); @@ -8251,11 +8867,11 @@ shifter_out = 1; break; } - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, imm); rn = (insn >> 16) & 0xf; if (rn == 15) { - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } else { tmp = load_reg(s, rn); @@ -8264,12 +8880,12 @@ if (gen_thumb2_data_op(s, op, (insn & (1 << 20)) != 0, shifter_out, tmp, tmp2)) goto illegal_op; - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); rd = (insn >> 8) & 0xf; if (rd != 15) { store_reg(s, rd, tmp); } else { - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } } } @@ -8284,9 +8900,45 @@ goto illegal_op; break; } + op = ((insn >> 21) & 3) | ((insn >> 22) & 4); + if (rs == 15) { + if (!(insn & (1 << 20))) { + goto illegal_op; + } + if (op != 2) { + /* Byte or halfword load space with dest == r15 : memory hints. + * Catch them early so we don't emit pointless addressing code. + * This space is a mix of: + * PLD/PLDW/PLI, which we implement as NOPs (note that unlike + * the ARM encodings, PLDW space doesn't UNDEF for non-v7MP + * cores) + * unallocated hints, which must be treated as NOPs + * UNPREDICTABLE space, which we NOP or UNDEF depending on + * which is easiest for the decoding logic + * Some space which must UNDEF + */ + int op1 = (insn >> 23) & 3; + int op2 = (insn >> 6) & 0x3f; + if (op & 2) { + goto illegal_op; + } + if (rn == 15) { + /* UNPREDICTABLE or unallocated hint */ + return 0; + } + if (op1 & 1) { + return 0; /* PLD* or unallocated hint */ + } + if ((op2 == 0) || ((op2 & 0x3c) == 0x30)) { + return 0; /* PLD* or unallocated hint */ + } + /* UNDEF space, or an UNPREDICTABLE */ + return 1; + } + } user = IS_USER(s); if (rn == 15) { - addr = new_tmp(); + addr = tcg_temp_new_i32(); /* PC relative. */ /* s->pc has already been incremented by 4. */ imm = s->pc & 0xfffffffc; @@ -8302,77 +8954,74 @@ imm = insn & 0xfff; tcg_gen_addi_i32(addr, addr, imm); } else { - op = (insn >> 8) & 7; imm = insn & 0xff; - switch (op) { - case 0: case 8: /* Shifted Register. */ + switch ((insn >> 8) & 0xf) { + case 0x0: /* Shifted Register. */ shift = (insn >> 4) & 0xf; - if (shift > 3) + if (shift > 3) { + tcg_temp_free_i32(addr); goto illegal_op; + } tmp = load_reg(s, rm); if (shift) tcg_gen_shli_i32(tmp, tmp, shift); tcg_gen_add_i32(addr, addr, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); break; - case 4: /* Negative offset. */ + case 0xc: /* Negative offset. */ tcg_gen_addi_i32(addr, addr, -imm); break; - case 6: /* User privilege. */ + case 0xe: /* User privilege. */ tcg_gen_addi_i32(addr, addr, imm); user = 1; break; - case 1: /* Post-decrement. */ + case 0x9: /* Post-decrement. */ imm = -imm; /* Fall through. */ - case 3: /* Post-increment. */ + case 0xb: /* Post-increment. */ postinc = 1; writeback = 1; break; - case 5: /* Pre-decrement. */ + case 0xd: /* Pre-decrement. */ imm = -imm; /* Fall through. */ - case 7: /* Pre-increment. */ + case 0xf: /* Pre-increment. */ tcg_gen_addi_i32(addr, addr, imm); writeback = 1; break; default: + tcg_temp_free_i32(addr); goto illegal_op; } } } - op = ((insn >> 21) & 3) | ((insn >> 22) & 4); if (insn & (1 << 20)) { /* Load. */ - if (rs == 15 && op != 2) { - if (op & 2) - goto illegal_op; - /* Memory hint. Implemented as NOP. */ + switch (op) { + case 0: tmp = gen_ld8u(addr, user); break; + case 4: tmp = gen_ld8s(addr, user); break; + case 1: tmp = gen_ld16u(addr, user); break; + case 5: tmp = gen_ld16s(addr, user); break; + case 2: tmp = gen_ld32(addr, user); break; + default: + tcg_temp_free_i32(addr); + goto illegal_op; + } + if (rs == 15) { + gen_bx(s, tmp); } else { - switch (op) { - case 0: tmp = gen_ld8u(addr, user); break; - case 4: tmp = gen_ld8s(addr, user); break; - case 1: tmp = gen_ld16u(addr, user); break; - case 5: tmp = gen_ld16s(addr, user); break; - case 2: tmp = gen_ld32(addr, user); break; - default: goto illegal_op; - } - if (rs == 15) { - gen_bx(s, tmp); - } else { - store_reg(s, rs, tmp); - } + store_reg(s, rs, tmp); } } else { /* Store. */ - if (rs == 15) - goto illegal_op; tmp = load_reg(s, rs); switch (op) { case 0: gen_st8(tmp, addr, user); break; case 1: gen_st16(tmp, addr, user); break; case 2: gen_st32(tmp, addr, user); break; - default: goto illegal_op; + default: + tcg_temp_free_i32(addr); + goto illegal_op; } } if (postinc) @@ -8380,7 +9029,7 @@ if (writeback) { store_reg(s, rn, addr); } else { - dead_tmp(addr); + tcg_temp_free_i32(addr); } } break; @@ -8424,7 +9073,7 @@ tmp = load_reg(s, rn); if (insn & (1 << 10)) { /* immediate */ - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, (insn >> 6) & 7); } else { /* reg */ @@ -8442,7 +9091,7 @@ else gen_helper_add_cc(tmp, tmp, tmp2); } - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else { /* shift immediate */ @@ -8460,27 +9109,27 @@ op = (insn >> 11) & 3; rd = (insn >> 8) & 0x7; if (op == 0) { /* mov */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, insn & 0xff); if (!s->condexec_mask) gen_logic_CC(tmp); store_reg(s, rd, tmp); } else { tmp = load_reg(s, rd); - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, insn & 0xff); switch (op) { case 1: /* cmp */ gen_helper_sub_cc(tmp, tmp, tmp2); - dead_tmp(tmp); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); break; case 2: /* add */ if (s->condexec_mask) tcg_gen_add_i32(tmp, tmp, tmp2); else gen_helper_add_cc(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 3: /* sub */ @@ -8488,7 +9137,7 @@ tcg_gen_sub_i32(tmp, tmp, tmp2); else gen_helper_sub_cc(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; } @@ -8500,10 +9149,10 @@ /* load pc-relative. Bit 1 of PC is ignored. */ val = s->pc + 2 + ((insn & 0xff) * 4); val &= ~(uint32_t)2; - addr = new_tmp(); + addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, val); tmp = gen_ld32(addr, IS_USER(s)); - dead_tmp(addr); + tcg_temp_free_i32(addr); store_reg(s, rd, tmp); break; } @@ -8517,15 +9166,15 @@ tmp = load_reg(s, rd); tmp2 = load_reg(s, rm); tcg_gen_add_i32(tmp, tmp, tmp2); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 1: /* cmp */ tmp = load_reg(s, rd); tmp2 = load_reg(s, rm); gen_helper_sub_cc(tmp, tmp, tmp2); - dead_tmp(tmp2); - dead_tmp(tmp); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); break; case 2: /* mov/cpy */ tmp = load_reg(s, rm); @@ -8534,11 +9183,13 @@ case 3:/* branch [and link] exchange thumb register */ tmp = load_reg(s, rm); if (insn & (1 << 7)) { + ARCH(5); val = (uint32_t)s->pc | 1; - tmp2 = new_tmp(); + tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, val); store_reg(s, 14, tmp2); } + /* already thumb, no need to check */ gen_bx(s, tmp); break; } @@ -8560,7 +9211,7 @@ } if (op == 9) { /* neg */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } else if (op != 0xf) { /* mvn doesn't read its first operand */ tmp = load_reg(s, rd); @@ -8671,14 +9322,14 @@ if (val) { store_reg(s, rm, tmp2); if (op != 0xf) - dead_tmp(tmp); + tcg_temp_free_i32(tmp); } else { store_reg(s, rd, tmp); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } } else { - dead_tmp(tmp); - dead_tmp(tmp2); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); } break; @@ -8691,7 +9342,7 @@ addr = load_reg(s, rn); tmp = load_reg(s, rm); tcg_gen_add_i32(addr, addr, tmp); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); if (op < 3) /* store */ tmp = load_reg(s, rd); @@ -8724,7 +9375,7 @@ } if (op >= 3) /* load */ store_reg(s, rd, tmp); - dead_tmp(addr); + tcg_temp_free_i32(addr); break; case 6: @@ -8744,7 +9395,7 @@ tmp = load_reg(s, rd); gen_st32(tmp, addr, IS_USER(s)); } - dead_tmp(addr); + tcg_temp_free_i32(addr); break; case 7: @@ -8764,7 +9415,7 @@ tmp = load_reg(s, rd); gen_st8(tmp, addr, IS_USER(s)); } - dead_tmp(addr); + tcg_temp_free_i32(addr); break; case 8: @@ -8784,7 +9435,7 @@ tmp = load_reg(s, rd); gen_st16(tmp, addr, IS_USER(s)); } - dead_tmp(addr); + tcg_temp_free_i32(addr); break; case 9: @@ -8803,7 +9454,7 @@ tmp = load_reg(s, rd); gen_st32(tmp, addr, IS_USER(s)); } - dead_tmp(addr); + tcg_temp_free_i32(addr); break; case 10: @@ -8814,7 +9465,7 @@ tmp = load_reg(s, 13); } else { /* PC. bit 1 is ignored. */ - tmp = new_tmp(); + tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, (s->pc + 2) & ~(uint32_t)2); } val = (insn & 0xff) * 4; @@ -8898,8 +9549,9 @@ /* write back the new stack pointer */ store_reg(s, 13, addr); /* set the new PC value */ - if ((insn & 0x0900) == 0x0900) - gen_bx(s, tmp); + if ((insn & 0x0900) == 0x0900) { + store_reg_from_load(env, s, 15, tmp); + } break; case 1: case 3: case 9: case 11: /* czb */ @@ -8911,7 +9563,7 @@ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel); else tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, s->condlabel); - dead_tmp(tmp); + tcg_temp_free_i32(tmp); offset = ((insn & 0xf8) >> 2) | (insn & 0x200) >> 3; val = (uint32_t)s->pc + 2; val += offset; @@ -8930,6 +9582,7 @@ break; case 0xe: /* bkpt */ + ARCH(5); gen_exception_insn(s, 2, EXCP_BKPT); break; @@ -8982,7 +9635,10 @@ break; case 12: + { /* load/store multiple */ + TCGv loaded_var; + TCGV_UNUSED(loaded_var); rn = (insn >> 8) & 0x7; addr = load_reg(s, rn); for (i = 0; i < 8; i++) { @@ -8990,7 +9646,11 @@ if (insn & (1 << 11)) { /* load */ tmp = gen_ld32(addr, IS_USER(s)); - store_reg(s, i, tmp); + if (i == rn) { + loaded_var = tmp; + } else { + store_reg(s, i, tmp); + } } else { /* store */ tmp = load_reg(s, i); @@ -9000,14 +9660,18 @@ tcg_gen_addi_i32(addr, addr, 4); } } - /* Base register writeback. */ if ((insn & (1 << rn)) == 0) { + /* base reg not in list: base register writeback */ store_reg(s, rn, addr); } else { - dead_tmp(addr); + /* base reg in list: if load, complete it now */ + if (insn & (1 << 11)) { + store_reg(s, rn, loaded_var); + } + tcg_temp_free_i32(addr); } break; - + } case 13: /* conditional branch or swi */ cond = (insn >> 8) & 0xf; @@ -9076,8 +9740,6 @@ int max_insns; /* generate intermediate code */ - num_temps = 0; - pc_start = tb->pc; dc->tb = tb; @@ -9114,6 +9776,8 @@ gen_icount_start(); + tcg_clear_temp_count(); + /* A note on handling of the condexec (IT) bits: * * We want to avoid the overhead of having to write the updated condexec @@ -9136,8 +9800,8 @@ * 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. + * gen_opc_condexec_bits[]. restore_state_to_opc() 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 @@ -9149,7 +9813,7 @@ complications trying to do it at the end of the block. */ if (dc->condexec_mask || dc->condexec_cond) { - TCGv tmp = new_tmp(); + TCGv tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); store_cpu_field(tmp, condexec_bits); } @@ -9218,15 +9882,16 @@ } else { disas_arm_insn(env, dc); } - if (num_temps) { - fprintf(stderr, "Internal resource leak before %08x\n", dc->pc); - num_temps = 0; - } if (dc->condjmp && !dc->is_jmp) { gen_set_label(dc->condlabel); dc->condjmp = 0; } + + if (tcg_check_temp_count()) { + fprintf(stderr, "TCG temporary leak before %08x\n", dc->pc); + } + /* Translation stops when a conditional branch is encountered. * Otherwise the subsequent code could get translated several times. * Also stop translation when a page boundary is reached. This @@ -9401,8 +10066,7 @@ #endif } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->regs[15] = gen_opc_pc[pc_pos]; env->condexec_bits = gen_opc_condexec_bits[pc_pos]; diff -Nru qemu-kvm-0.14.1+noroms/target-cris/cpu.h qemu-kvm-0.15.0+noroms/target-cris/cpu.h --- qemu-kvm-0.14.1+noroms/target-cris/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-cris/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -36,6 +36,9 @@ #define EXCP_IRQ 4 #define EXCP_BREAK 5 +/* CRIS-specific interrupt pending bits. */ +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 + /* Register aliases. R0 - R15 */ #define R_FP 8 #define R_SP 14 @@ -101,7 +104,7 @@ /* P0 - P15 are referred to as special registers in the docs. */ uint32_t pregs[16]; - /* Pseudo register for the PC. Not directly accessable on CRIS. */ + /* Pseudo register for the PC. Not directly accessible on CRIS. */ uint32_t pc; /* Pseudo register for the kernel stack. */ @@ -265,4 +268,15 @@ #define cpu_list cris_cpu_list void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf); +static inline bool cpu_has_work(CPUState *env) +{ + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} #endif diff -Nru qemu-kvm-0.14.1+noroms/target-cris/exec.h qemu-kvm-0.15.0+noroms/target-cris/exec.h --- qemu-kvm-0.14.1+noroms/target-cris/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-cris/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -22,32 +22,7 @@ register struct CPUCRISState *env asm(AREG0); #include "cpu.h" -#include "exec-all.h" #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif - -static inline int cpu_has_work(CPUState *env) -{ - return (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)); -} - -static inline int cpu_halted(CPUState *env) { - if (!env->halted) - return 0; - - /* IRQ, NMI and GURU execeptions wakes us up. */ - if (env->interrupt_request - & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; -} - -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; -} - diff -Nru qemu-kvm-0.14.1+noroms/target-cris/helper.c qemu-kvm-0.15.0+noroms/target-cris/helper.c --- qemu-kvm-0.14.1+noroms/target-cris/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-cris/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include "config.h" #include "cpu.h" #include "mmu.h" -#include "exec-all.h" #include "host-utils.h" diff -Nru qemu-kvm-0.14.1+noroms/target-cris/mmu.c qemu-kvm-0.15.0+noroms/target-cris/mmu.c --- qemu-kvm-0.14.1+noroms/target-cris/mmu.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-cris/mmu.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ #include "config.h" #include "cpu.h" #include "mmu.h" -#include "exec-all.h" #ifdef DEBUG #define D(x) x diff -Nru qemu-kvm-0.14.1+noroms/target-cris/op_helper.c qemu-kvm-0.15.0+noroms/target-cris/op_helper.c --- qemu-kvm-0.14.1+noroms/target-cris/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-cris/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -77,13 +77,13 @@ 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); + cpu_restore_state(tb, env, pc); /* Evaluate flags after retranslation. */ helper_top_evaluate_flags(); } } - cpu_loop_exit(); + cpu_loop_exit(env); } env = saved_env; } @@ -93,7 +93,7 @@ void helper_raise_exception(uint32_t index) { env->exception_index = index; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_tlb_flush_pid(uint32_t pid) diff -Nru qemu-kvm-0.14.1+noroms/target-cris/translate.c qemu-kvm-0.15.0+noroms/target-cris/translate.c --- qemu-kvm-0.14.1+noroms/target-cris/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-cris/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -30,7 +30,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "helper.h" @@ -596,7 +595,7 @@ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(env_pc, dest); - tcg_gen_exit_tb((long)tb + n); + tcg_gen_exit_tb((tcg_target_long)tb + n); } else { tcg_gen_movi_tl(env_pc, dest); tcg_gen_exit_tb(0); @@ -3604,8 +3603,7 @@ #endif } -void gen_pc_load(CPUState *env, struct TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->pc = gen_opc_pc[pc_pos]; } diff -Nru qemu-kvm-0.14.1+noroms/target-cris/translate_v10.c qemu-kvm-0.15.0+noroms/target-cris/translate_v10.c --- qemu-kvm-0.14.1+noroms/target-cris/translate_v10.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-cris/translate_v10.c 2011-08-09 07:40:29.000000000 -0500 @@ -262,9 +262,6 @@ 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: @@ -956,7 +953,7 @@ return insn_len; } #endif - /* Now the rest of the modes are truely indirect. */ + /* Now the rest of the modes are truly 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); diff -Nru qemu-kvm-0.14.1+noroms/target-i386/cpu.h qemu-kvm-0.15.0+noroms/target-i386/cpu.h --- qemu-kvm-0.14.1+noroms/target-i386/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -438,9 +438,13 @@ #define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ #define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */ -#define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */ +#define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */ #define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ +#define CPUID_VENDOR_VIA_1 0x746e6543 /* "Cent" */ +#define CPUID_VENDOR_VIA_2 0x48727561 /* "aurH" */ +#define CPUID_VENDOR_VIA_3 0x736c7561 /* "auls" */ + #define CPUID_MWAIT_IBE (1 << 1) /* Interrupts can exit capability */ #define CPUID_MWAIT_EMX (1 << 0) /* enumeration supported */ @@ -466,6 +470,15 @@ #define EXCP_SYSCALL 0x100 /* only happens in user only emulation for syscall instruction */ +/* i386-specific interrupt pending bits. */ +#define CPU_INTERRUPT_SMI CPU_INTERRUPT_TGT_EXT_2 +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 +#define CPU_INTERRUPT_MCE CPU_INTERRUPT_TGT_EXT_4 +#define CPU_INTERRUPT_VIRQ CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_INIT CPU_INTERRUPT_TGT_INT_1 +#define CPU_INTERRUPT_SIPI CPU_INTERRUPT_TGT_INT_2 + + enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_EFLAGS, /* all cc are explicitly computed, CC_SRC = flags */ @@ -523,16 +536,6 @@ CC_OP_NB, }; -#ifdef FLOATX80 -#define USE_X86LDOUBLE -#endif - -#ifdef USE_X86LDOUBLE -typedef floatx80 CPU86_LDouble; -#else -typedef float64 CPU86_LDouble; -#endif - typedef struct SegmentCache { uint32_t selector; target_ulong base; @@ -585,11 +588,7 @@ #define MMX_Q(n) q typedef union { -#ifdef USE_X86LDOUBLE - CPU86_LDouble d __attribute__((aligned(16))); -#else - CPU86_LDouble d; -#endif + floatx80 d __attribute__((aligned(16))); MMXReg mmx; } FPReg; @@ -642,10 +641,14 @@ uint16_t fpuc; uint8_t fptags[8]; /* 0 = valid, 1 = empty */ FPReg fpregs[8]; + /* KVM-only so far */ + uint16_t fpop; + uint64_t fpip; + uint64_t fpdp; /* emulator internal variables */ float_status fp_status; - CPU86_LDouble ft0; + floatx80 ft0; float_status mmx_status; /* for 3DNow! float ops */ float_status sse_status; @@ -685,7 +688,7 @@ uint64_t tsc; - uint64_t pat; + uint64_t mcg_status; /* exception/interrupt handling */ int error_code; @@ -705,6 +708,8 @@ CPU_COMMON + uint64_t pat; + /* processor features (e.g. for CPUID insn) */ uint32_t cpuid_level; uint32_t cpuid_vendor1; @@ -719,6 +724,9 @@ uint32_t cpuid_ext3_features; uint32_t cpuid_apic_id; int cpuid_vendor_override; + /* Store the results of Centaur's CPUID instructions */ + uint32_t cpuid_xlevel2; + uint32_t cpuid_ext4_features; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -734,13 +742,13 @@ uint32_t sipi_vector; uint32_t cpuid_kvm_features; uint32_t cpuid_svm_features; + bool tsc_valid; /* in order to simplify APIC support, we leave this pointer to the user */ struct DeviceState *apic_state; uint64_t mcg_cap; - uint64_t mcg_status; uint64_t mcg_ctl; uint64_t mce_banks[MCE_BANKS_DEF*4]; @@ -856,8 +864,8 @@ /* op_helper.c */ /* used for debug or cpu save/restore */ -void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f); -CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper); +void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f); +floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper); /* cpu-exec.c */ /* the following helpers are only usable in user mode simulation as @@ -952,6 +960,36 @@ return (env->hflags & HF_CPL_MASK) == 3 ? 1 : 0; } +#undef EAX +#define EAX (env->regs[R_EAX]) +#undef ECX +#define ECX (env->regs[R_ECX]) +#undef EDX +#define EDX (env->regs[R_EDX]) +#undef EBX +#define EBX (env->regs[R_EBX]) +#undef ESP +#define ESP (env->regs[R_ESP]) +#undef EBP +#define EBP (env->regs[R_EBP]) +#undef ESI +#define ESI (env->regs[R_ESI]) +#undef EDI +#define EDI (env->regs[R_EDI]) +#undef EIP +#define EIP (env->eip) +#define DF (env->df) + +#define CC_SRC (env->cc_src) +#define CC_DST (env->cc_dst) +#define CC_OP (env->cc_op) + +/* float macros */ +#define FT0 (env->ft0) +#define ST0 (env->fpregs[env->fpstt].d) +#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7].d) +#define ST1 ST(1) + /* translate.c */ void optimize_flags_init(void); @@ -976,6 +1014,23 @@ #include "hw/apic.h" #endif +static inline bool cpu_has_work(CPUState *env) +{ + return ((env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) || + (env->interrupt_request & (CPU_INTERRUPT_NMI | + CPU_INTERRUPT_INIT | + CPU_INTERRUPT_SIPI | + CPU_INTERRUPT_MCE)); +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->eip = tb->pc - tb->cs_base; +} + static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -987,4 +1042,22 @@ void do_cpu_init(CPUState *env); void do_cpu_sipi(CPUState *env); + +#define MCE_INJECT_BROADCAST 1 +#define MCE_INJECT_UNCOND_AO 2 + +void cpu_x86_inject_mce(Monitor *mon, CPUState *cenv, int bank, + uint64_t status, uint64_t mcg_status, uint64_t addr, + uint64_t misc, int flags); + +/* op_helper.c */ +void do_interrupt(CPUState *env); +void do_interrupt_x86_hardirq(CPUState *env, int intno, int is_hw); + +void do_smm_enter(CPUState *env1); + +void svm_check_intercept(CPUState *env1, uint32_t type); + +uint32_t cpu_cc_compute_all(CPUState *env1, int op); + #endif /* CPU_I386_H */ diff -Nru qemu-kvm-0.14.1+noroms/target-i386/cpuid.c qemu-kvm-0.15.0+noroms/target-i386/cpuid.c --- qemu-kvm-0.14.1+noroms/target-i386/cpuid.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/cpuid.c 2011-08-09 07:40:29.000000000 -0500 @@ -73,7 +73,7 @@ }; static const char *kvm_feature_name[] = { - "kvmclock", "kvm_nopiodelay", "kvm_mmu", NULL, "kvm_asyncpf", NULL, NULL, NULL, + "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", "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, @@ -182,20 +182,22 @@ } /* search featureset for flag *[s..e), if found set corresponding bit in - * *pval and return success, otherwise return zero + * *pval and return true, otherwise return false */ -static int lookup_feature(uint32_t *pval, const char *s, const char *e, - const char **featureset) +static bool lookup_feature(uint32_t *pval, const char *s, const char *e, + const char **featureset) { uint32_t mask; const char **ppc; + bool found = false; - for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc) + for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc) { if (*ppc && !altcmp(s, e, *ppc)) { *pval |= mask; - break; + found = true; } - return (mask ? 1 : 0); + } + return found; } static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, @@ -228,6 +230,9 @@ char model_id[48]; int vendor_override; uint32_t flags; + /* Store the results of Centaur's CPUID instructions */ + uint32_t ext4_features; + uint32_t xlevel2; } x86_def_t; #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) @@ -520,6 +525,18 @@ cpu_x86_fill_model_id(x86_cpu_def->model_id); x86_cpu_def->vendor_override = 0; + /* Call Centaur's CPUID instruction. */ + if (x86_cpu_def->vendor1 == CPUID_VENDOR_VIA_1 && + x86_cpu_def->vendor2 == CPUID_VENDOR_VIA_2 && + x86_cpu_def->vendor3 == CPUID_VENDOR_VIA_3) { + host_cpuid(0xC0000000, 0, &eax, &ebx, &ecx, &edx); + if (eax >= 0xC0000001) { + /* Support VIA max extended level */ + x86_cpu_def->xlevel2 = eax; + host_cpuid(0xC0000001, 0, &eax, &ebx, &ecx, &edx); + x86_cpu_def->ext4_features = edx; + } + } /* * Every SVM feature requires emulation support in KVM - so we can't just @@ -847,13 +864,14 @@ 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; + env->cpuid_ext4_features = def->ext4_features; + env->cpuid_xlevel2 = def->xlevel2; if (!kvm_enabled()) { env->cpuid_features &= TCG_FEATURES; env->cpuid_ext_features &= TCG_EXT_FEATURES; @@ -1034,8 +1052,18 @@ { /* test if maximum index reached */ if (index & 0x80000000) { - if (index > env->cpuid_xlevel) - index = env->cpuid_level; + if (index > env->cpuid_xlevel) { + if (env->cpuid_xlevel2 > 0) { + /* Handle the Centaur's CPUID instruction. */ + if (index > env->cpuid_xlevel2) { + index = env->cpuid_xlevel2; + } else if (index < 0xC0000000) { + index = env->cpuid_xlevel; + } + } else { + index = env->cpuid_xlevel; + } + } } else { if (index > env->cpuid_level) index = env->cpuid_level; @@ -1114,6 +1142,21 @@ *ecx = 0; *edx = 0; break; + case 7: + if (kvm_enabled()) { + KVMState *s = env->kvm_state; + + *eax = kvm_arch_get_supported_cpuid(s, 0x7, count, R_EAX); + *ebx = kvm_arch_get_supported_cpuid(s, 0x7, count, R_EBX); + *ecx = kvm_arch_get_supported_cpuid(s, 0x7, count, R_ECX); + *edx = kvm_arch_get_supported_cpuid(s, 0x7, count, R_EDX); + } else { + *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 */ @@ -1138,10 +1181,12 @@ 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); + KVMState *s = env->kvm_state; + + *eax = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EAX); + *ebx = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EBX); + *ecx = kvm_arch_get_supported_cpuid(s, 0xd, count, R_ECX); + *edx = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EDX); } else { *eax = 0; *ebx = 0; @@ -1193,9 +1238,6 @@ /* disable CPU features that KVM cannot support */ - /* svm */ - if (!kvm_nested) - *ecx &= ~CPUID_EXT3_SVM; /* 3dnow */ *edx &= ~0xc0000000; } @@ -1255,6 +1297,28 @@ *edx = 0; } break; + case 0xC0000000: + *eax = env->cpuid_xlevel2; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + case 0xC0000001: + /* Support for VIA CPU's CPUID instruction */ + *eax = env->cpuid_version; + *ebx = 0; + *ecx = 0; + *edx = env->cpuid_ext4_features; + break; + case 0xC0000002: + case 0xC0000003: + case 0xC0000004: + /* Reserved for the future, and now filled with zero */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; default: /* reserved values: zero */ *eax = 0; diff -Nru qemu-kvm-0.14.1+noroms/target-i386/exec.h qemu-kvm-0.15.0+noroms/target-i386/exec.h --- qemu-kvm-0.14.1+noroms/target-i386/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -33,48 +33,12 @@ #include "qemu-common.h" #include "qemu-log.h" -#undef EAX -#define EAX (env->regs[R_EAX]) -#undef ECX -#define ECX (env->regs[R_ECX]) -#undef EDX -#define EDX (env->regs[R_EDX]) -#undef EBX -#define EBX (env->regs[R_EBX]) -#undef ESP -#define ESP (env->regs[R_ESP]) -#undef EBP -#define EBP (env->regs[R_EBP]) -#undef ESI -#define ESI (env->regs[R_ESI]) -#undef EDI -#define EDI (env->regs[R_EDI]) -#undef EIP -#define EIP (env->eip) -#define DF (env->df) - -#define CC_SRC (env->cc_src) -#define CC_DST (env->cc_dst) -#define CC_OP (env->cc_op) - -/* float macros */ -#define FT0 (env->ft0) -#define ST0 (env->fpregs[env->fpstt].d) -#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7].d) -#define ST1 ST(1) - #include "cpu.h" -#include "exec-all.h" /* op_helper.c */ -void do_interrupt(int intno, int is_int, int error_code, - target_ulong next_eip, int is_hw); -void do_interrupt_user(int intno, int is_int, int error_code, - 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 */ static inline target_long lshift(target_long x, int n) @@ -87,52 +51,12 @@ #include "helper.h" -static inline void svm_check_intercept(uint32_t type) -{ - helper_svm_check_intercept_param(type, 0); -} - #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -#ifdef USE_X86LDOUBLE -/* use long double functions */ -#define floatx_to_int32 floatx80_to_int32 -#define floatx_to_int64 floatx80_to_int64 -#define floatx_to_int32_round_to_zero floatx80_to_int32_round_to_zero -#define floatx_to_int64_round_to_zero floatx80_to_int64_round_to_zero -#define int32_to_floatx int32_to_floatx80 -#define int64_to_floatx int64_to_floatx80 -#define float32_to_floatx float32_to_floatx80 -#define float64_to_floatx float64_to_floatx80 -#define floatx_to_float32 floatx80_to_float32 -#define floatx_to_float64 floatx80_to_float64 -#define floatx_abs floatx80_abs -#define floatx_chs floatx80_chs -#define floatx_round_to_int floatx80_round_to_int -#define floatx_compare floatx80_compare -#define floatx_compare_quiet floatx80_compare_quiet -#else -#define floatx_to_int32 float64_to_int32 -#define floatx_to_int64 float64_to_int64 -#define floatx_to_int32_round_to_zero float64_to_int32_round_to_zero -#define floatx_to_int64_round_to_zero float64_to_int64_round_to_zero -#define int32_to_floatx int32_to_float64 -#define int64_to_floatx int64_to_float64 -#define float32_to_floatx float32_to_float64 -#define float64_to_floatx(x, e) (x) -#define floatx_to_float32 float64_to_float32 -#define floatx_to_float64(x, e) (x) -#define floatx_abs float64_abs -#define floatx_chs float64_chs -#define floatx_round_to_int float64_round_to_int -#define floatx_compare float64_compare -#define floatx_compare_quiet float64_compare_quiet -#endif - #define RC_MASK 0xc00 #define RC_NEAR 0x000 #define RC_DOWN 0x400 @@ -141,17 +65,6 @@ #define MAXTAN 9223372036854775808.0 -#ifdef USE_X86LDOUBLE - -/* only for x86 */ -typedef union { - long double d; - struct { - unsigned long long lower; - unsigned short upper; - } l; -} CPU86_LDoubleU; - /* the following deal with x86 long double-precision numbers */ #define MAXEXPD 0x7fff #define EXPBIAS 16383 @@ -160,40 +73,6 @@ #define MANTD(fp) (fp.l.lower) #define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS -#else - -/* NOTE: arm is horrible as double 32 bit words are stored in big endian ! */ -typedef union { - double d; -#if !defined(HOST_WORDS_BIGENDIAN) && !defined(__arm__) - struct { - uint32_t lower; - int32_t upper; - } l; -#else - struct { - int32_t upper; - uint32_t lower; - } l; -#endif -#ifndef __arm__ - int64_t ll; -#endif -} CPU86_LDoubleU; - -/* the following deal with IEEE double-precision numbers */ -#define MAXEXPD 0x7ff -#define EXPBIAS 1023 -#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) -#define SIGND(fp) ((fp.l.upper) & 0x80000000) -#ifdef __arm__ -#define MANTD(fp) (fp.l.lower | ((uint64_t)(fp.l.upper & ((1 << 20) - 1)) << 32)) -#else -#define MANTD(fp) (fp.ll & ((1LL << 52) - 1)) -#endif -#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7ff << 20)) | (EXPBIAS << 20) -#endif - static inline void fpush(void) { env->fpstt = (env->fpstt - 1) & 7; @@ -206,65 +85,24 @@ env->fpstt = (env->fpstt + 1) & 7; } -#ifndef USE_X86LDOUBLE -static inline CPU86_LDouble helper_fldt(target_ulong ptr) -{ - CPU86_LDoubleU temp; - int upper, e; - uint64_t ll; - - /* mantissa */ - upper = lduw(ptr + 8); - /* XXX: handle overflow ? */ - e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */ - e |= (upper >> 4) & 0x800; /* sign */ - ll = (ldq(ptr) >> 11) & ((1LL << 52) - 1); -#ifdef __arm__ - temp.l.upper = (e << 20) | (ll >> 32); - temp.l.lower = ll; -#else - temp.ll = ll | ((uint64_t)e << 52); -#endif - return temp.d; -} - -static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr) +static inline floatx80 helper_fldt(target_ulong ptr) { - CPU86_LDoubleU temp; - int e; - - temp.d = f; - /* mantissa */ - stq(ptr, (MANTD(temp) << 11) | (1LL << 63)); - /* exponent + sign */ - e = EXPD(temp) - EXPBIAS + 16383; - e |= SIGND(temp) >> 16; - stw(ptr + 8, e); -} -#else - -/* we use memory access macros */ - -static inline CPU86_LDouble helper_fldt(target_ulong ptr) -{ - CPU86_LDoubleU temp; + CPU_LDoubleU temp; temp.l.lower = ldq(ptr); temp.l.upper = lduw(ptr + 8); return temp.d; } -static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr) +static inline void helper_fstt(floatx80 f, target_ulong ptr) { - CPU86_LDoubleU temp; + CPU_LDoubleU temp; temp.d = f; stq(ptr, temp.l.lower); stw(ptr + 8, temp.l.upper); } -#endif /* USE_X86LDOUBLE */ - #define FPUS_IE (1 << 0) #define FPUS_DE (1 << 1) #define FPUS_ZE (1 << 2) @@ -291,31 +129,6 @@ (eflags & update_mask) | 0x2; } -static inline int cpu_has_work(CPUState *env) -{ - int work; - - work = (env->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK); - work |= env->interrupt_request & CPU_INTERRUPT_NMI; - work |= env->interrupt_request & CPU_INTERRUPT_INIT; - work |= env->interrupt_request & CPU_INTERRUPT_SIPI; - - return work; -} - -static inline int cpu_halted(CPUState *env) { - /* handle exit of HALTED state */ - if (!env->halted) - return 0; - /* disable halt condition */ - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; -} - /* load efer and update the corresponding hflags. XXX: do consistency checks with cpuid bits ? */ static inline void cpu_load_efer(CPUState *env, uint64_t val) @@ -327,9 +140,3 @@ 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.14.1+noroms/target-i386/helper.c qemu-kvm-0.15.0+noroms/target-i386/helper.c --- qemu-kvm-0.14.1+noroms/target-i386/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -21,15 +21,14 @@ #include #include #include -#include #include "cpu.h" -#include "exec-all.h" #include "qemu-common.h" #include "kvm.h" -#include "kvm_x86.h" - -#include "qemu-kvm.h" +#ifndef CONFIG_USER_ONLY +#include "sysemu.h" +#include "monitor.h" +#endif //#define DEBUG_MMU @@ -98,13 +97,13 @@ env->mxcsr = 0x1f80; + env->pat = 0x0007040600070406ULL; + memset(env->dr, 0, sizeof(env->dr)); env->dr[6] = DR6_FIXED_1; env->dr[7] = DR7_FIXED_1; cpu_breakpoint_remove_all(env, BP_CPU); cpu_watchpoint_remove_all(env, BP_CPU); - - env->mcg_status = 0; } void cpu_x86_close(CPUX86State *env) @@ -402,21 +401,10 @@ fptag, env->mxcsr); for(i=0;i<8;i++) { -#if defined(USE_X86LDOUBLE) - union { - long double d; - struct { - uint64_t lower; - uint16_t upper; - } l; - } tmp; - tmp.d = env->fpregs[i].d; + CPU_LDoubleU u; + u.d = env->fpregs[i].d; cpu_fprintf(f, "FPR%d=%016" PRIx64 " %04x", - i, tmp.l.lower, tmp.l.upper); -#else - cpu_fprintf(f, "FPR%d=%016" PRIx64, - i, env->fpregs[i].mmx.q); -#endif + i, u.l.lower, u.l.upper); if ((i & 1) == 1) cpu_fprintf(f, "\n"); else @@ -1067,91 +1055,138 @@ prev_debug_excp_handler(env); } -/* This should come from sysemu.h - if we could include it here... */ -void qemu_system_reset_request(void); +typedef struct MCEInjectionParams { + Monitor *mon; + CPUState *env; + int bank; + uint64_t status; + uint64_t mcg_status; + uint64_t addr; + uint64_t misc; + int flags; +} MCEInjectionParams; + +static void do_inject_x86_mce(void *data) +{ + MCEInjectionParams *params = data; + CPUState *cenv = params->env; + uint64_t *banks = cenv->mce_banks + 4 * params->bank; -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; - uint64_t *banks = cenv->mce_banks; + cpu_synchronize_state(cenv); /* - * if MSR_MCG_CTL is not all 1s, the uncorrected error - * reporting is disabled - */ - if ((status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) && - cenv->mcg_ctl != ~(uint64_t)0) - return; - banks += 4 * bank; - /* - * if MSR_MCi_CTL is not all 1s, the uncorrected error - * reporting is disabled for the bank + * If there is an MCE exception being processed, ignore this SRAO MCE + * unless unconditional injection was requested. */ - if ((status & MCI_STATUS_UC) && banks[0] != ~(uint64_t)0) + if (!(params->flags & MCE_INJECT_UNCOND_AO) + && !(params->status & MCI_STATUS_AR) + && (cenv->mcg_status & MCG_STATUS_MCIP)) { return; - if (status & MCI_STATUS_UC) { + } + + if (params->status & MCI_STATUS_UC) { + /* + * if MSR_MCG_CTL is not all 1s, the uncorrected error + * reporting is disabled + */ + if ((cenv->mcg_cap & MCG_CTL_P) && cenv->mcg_ctl != ~(uint64_t)0) { + monitor_printf(params->mon, + "CPU %d: Uncorrected error reporting disabled\n", + cenv->cpu_index); + return; + } + + /* + * if MSR_MCi_CTL is not all 1s, the uncorrected error + * reporting is disabled for the bank + */ + if (banks[0] != ~(uint64_t)0) { + monitor_printf(params->mon, + "CPU %d: Uncorrected error reporting disabled for" + " bank %d\n", + cenv->cpu_index, params->bank); + return; + } + if ((cenv->mcg_status & MCG_STATUS_MCIP) || !(cenv->cr[4] & CR4_MCE_MASK)) { - fprintf(stderr, "injects mce exception while previous " - "one is in progress!\n"); + monitor_printf(params->mon, + "CPU %d: Previous MCE still in progress, raising" + " triple fault\n", + cenv->cpu_index); qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); qemu_system_reset_request(); return; } - if (banks[1] & MCI_STATUS_VAL) - status |= MCI_STATUS_OVER; - banks[2] = addr; - banks[3] = misc; - cenv->mcg_status = mcg_status; - banks[1] = status; + if (banks[1] & MCI_STATUS_VAL) { + params->status |= MCI_STATUS_OVER; + } + banks[2] = params->addr; + banks[3] = params->misc; + cenv->mcg_status = params->mcg_status; + banks[1] = params->status; cpu_interrupt(cenv, CPU_INTERRUPT_MCE); } else if (!(banks[1] & MCI_STATUS_VAL) || !(banks[1] & MCI_STATUS_UC)) { - if (banks[1] & MCI_STATUS_VAL) - status |= MCI_STATUS_OVER; - banks[2] = addr; - banks[3] = misc; - banks[1] = status; - } else + if (banks[1] & MCI_STATUS_VAL) { + params->status |= MCI_STATUS_OVER; + } + banks[2] = params->addr; + banks[3] = params->misc; + banks[1] = params->status; + } 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) -{ +void cpu_x86_inject_mce(Monitor *mon, CPUState *cenv, int bank, + uint64_t status, uint64_t mcg_status, uint64_t addr, + uint64_t misc, int flags) +{ + MCEInjectionParams params = { + .mon = mon, + .env = cenv, + .bank = bank, + .status = status, + .mcg_status = mcg_status, + .addr = addr, + .misc = misc, + .flags = flags, + }; unsigned bank_num = cenv->mcg_cap & 0xff; CPUState *env; - int flag = 0; - if (bank >= bank_num || !(status & MCI_STATUS_VAL)) { + if (!cenv->mcg_cap) { + monitor_printf(mon, "MCE injection not supported\n"); return; } - - if (broadcast) { - if (!cpu_x86_support_mca_broadcast(cenv)) { - fprintf(stderr, "Current CPU does not support broadcast\n"); - return; - } + if (bank >= bank_num) { + monitor_printf(mon, "Invalid MCE bank number\n"); + return; + } + if (!(status & MCI_STATUS_VAL)) { + monitor_printf(mon, "Invalid MCE status code\n"); + return; + } + if ((flags & MCE_INJECT_BROADCAST) + && !cpu_x86_support_mca_broadcast(cenv)) { + monitor_printf(mon, "Guest CPU does not support MCA 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); + run_on_cpu(cenv, do_inject_x86_mce, ¶ms); + if (flags & MCE_INJECT_BROADCAST) { + params.bank = 1; + params.status = MCI_STATUS_VAL | MCI_STATUS_UC; + params.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV; + params.addr = 0; + params.misc = 0; + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (cenv == env) { + continue; } + params.env = env; + run_on_cpu(cenv, do_inject_x86_mce, ¶ms); } } } @@ -1159,15 +1194,16 @@ static void mce_init(CPUX86State *cenv) { - unsigned int bank, bank_num; + unsigned int bank; - if (((cenv->cpuid_version >> 8)&0xf) >= 6 - && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA)) { + if (((cenv->cpuid_version >> 8) & 0xf) >= 6 + && (cenv->cpuid_features & (CPUID_MCE | CPUID_MCA)) == + (CPUID_MCE | CPUID_MCA)) { cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF; cenv->mcg_ctl = ~(uint64_t)0; - bank_num = MCE_BANKS_DEF; - for (bank = 0; bank < bank_num; bank++) - cenv->mce_banks[bank*4] = ~(uint64_t)0; + for (bank = 0; bank < MCE_BANKS_DEF; bank++) { + cenv->mce_banks[bank * 4] = ~(uint64_t)0; + } } } @@ -1233,8 +1269,11 @@ void do_cpu_init(CPUState *env) { int sipi = env->interrupt_request & CPU_INTERRUPT_SIPI; + uint64_t pat = env->pat; + cpu_reset(env); env->interrupt_request = sipi; + env->pat = pat; apic_init_reset(env->apic_state); env->halted = !cpu_is_bsp(env); } diff -Nru qemu-kvm-0.14.1+noroms/target-i386/kvm.c qemu-kvm-0.15.0+noroms/target-i386/kvm.c --- qemu-kvm-0.14.1+noroms/target-i386/kvm.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/kvm.c 2011-08-09 07:40:29.000000000 -0500 @@ -18,6 +18,7 @@ #include #include +#include #include "qemu-common.h" #include "sysemu.h" @@ -28,12 +29,7 @@ #include "hw/pc.h" #include "hw/apic.h" #include "ioport.h" -#include "kvm_x86.h" -#ifdef CONFIG_KVM_PARA -#include -#endif -// //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -54,20 +50,16 @@ #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) @@ -95,22 +87,51 @@ return cpuid; } -uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, +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 }, + { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF }, + { -1, -1 } +}; + +static int get_para_features(KVMState *s) +{ + int i, features = 0; + + for (i = 0; i < ARRAY_SIZE(para_features) - 1; i++) { + if (kvm_check_extension(s, para_features[i].cap)) { + features |= (1 << para_features[i].feature); + } + } + + return features; +} + + +uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, uint32_t index, int reg) { struct kvm_cpuid2 *cpuid; int i, max; uint32_t ret = 0; uint32_t cpuid_1_edx; + int has_kvm_features = 0; max = 1; - while ((cpuid = try_get_cpuid(env->kvm_state, max)) == NULL) { + while ((cpuid = try_get_cpuid(s, max)) == NULL) { max *= 2; } for (i = 0; i < cpuid->nent; ++i) { if (cpuid->entries[i].function == function && cpuid->entries[i].index == index) { + if (cpuid->entries[i].function == KVM_CPUID_FEATURES) { + has_kvm_features = 1; + } switch (reg) { case R_EAX: ret = cpuid->entries[i].eax; @@ -132,7 +153,7 @@ /* 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, 0, R_EDX); + cpuid_1_edx = kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); ret |= cpuid_1_edx & 0x183f7ff; break; } @@ -143,40 +164,47 @@ qemu_free(cpuid); + /* fallback for older kernels */ + if (!has_kvm_features && (function == KVM_CPUID_FEATURES)) { + ret = get_para_features(s); + } + return ret; } -#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 } -}; +typedef struct HWPoisonPage { + ram_addr_t ram_addr; + QLIST_ENTRY(HWPoisonPage) list; +} HWPoisonPage; + +static QLIST_HEAD(, HWPoisonPage) hwpoison_page_list = + QLIST_HEAD_INITIALIZER(hwpoison_page_list); -static int get_para_features(CPUState *env) +static void kvm_unpoison_all(void *param) { - int i, features = 0; + HWPoisonPage *page, *next_page; - 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); + QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) { + QLIST_REMOVE(page, list); + qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE); + qemu_free(page); + } +} + +static void kvm_hwpoison_page_add(ram_addr_t ram_addr) +{ + HWPoisonPage *page; + + QLIST_FOREACH(page, &hwpoison_page_list, list) { + if (page->ram_addr == ram_addr) { + return; } } -#ifdef KVM_CAP_ASYNC_PF - has_msr_async_pf_en = features & (1 << KVM_FEATURE_ASYNC_PF); -#endif - return features; + page = qemu_malloc(sizeof(HWPoisonPage)); + page->ram_addr = ram_addr; + QLIST_INSERT_HEAD(&hwpoison_page_list, page, list); } -#endif -#ifdef KVM_CAP_MCE static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap, int *max_banks) { @@ -190,117 +218,129 @@ 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) +static void kvm_mce_inject(CPUState *env, target_phys_addr_t paddr, int code) { - return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m); + uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | + MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S; + uint64_t mcg_status = MCG_STATUS_MCIP; + + if (code == BUS_MCEERR_AR) { + status |= MCI_STATUS_AR | 0x134; + mcg_status |= MCG_STATUS_EIPV; + } else { + status |= 0xc0; + mcg_status |= MCG_STATUS_RIPV; + } + cpu_x86_inject_mce(NULL, env, 9, status, mcg_status, paddr, + (MCM_ADDR_PHYS << 6) | 0xc, + cpu_x86_support_mca_broadcast(env) ? + MCE_INJECT_BROADCAST : 0); } -static int kvm_get_msr(CPUState *env, struct kvm_msr_entry *msrs, int n) +static void hardware_memory_error(void) { - 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; + fprintf(stderr, "Hardware memory error!\n"); + exit(1); } -/* FIXME: kill this and kvm_get_msr, use env->mcg_status instead */ -static int kvm_mce_in_progress(CPUState *env) +int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr) { - struct kvm_msr_entry msr_mcg_status = { - .index = MSR_MCG_STATUS, - }; - int r; + ram_addr_t ram_addr; + target_phys_addr_t paddr; - r = kvm_get_msr(env, &msr_mcg_status, 1); - if (r == -1 || r == 0) { - fprintf(stderr, "Failed to get MCE status\n"); - return 0; + if ((env->mcg_cap & MCG_SER_P) && addr + && (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) { + if (qemu_ram_addr_from_host(addr, &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(); + } + } + kvm_hwpoison_page_add(ram_addr); + kvm_mce_inject(env, paddr, code); + } else { + if (code == BUS_MCEERR_AO) { + return 0; + } else if (code == BUS_MCEERR_AR) { + hardware_memory_error(); + } else { + return 1; + } } - return !!(msr_mcg_status.data & MCG_STATUS_MCIP); + return 0; } -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) +int kvm_arch_on_sigbus(int code, void *addr) { - struct kvm_x86_mce_data *data = _data; - int r; + if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) { + ram_addr_t ram_addr; + target_phys_addr_t paddr; - /* 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; + /* Hope we are lucky for AO MCE */ + if (qemu_ram_addr_from_host(addr, &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; } - } - - r = kvm_set_mce(data->env, data->mce); - if (r < 0) { - perror("kvm_set_mce FAILED"); - if (data->abort_on_error) { - abort(); + kvm_hwpoison_page_add(ram_addr); + kvm_mce_inject(first_cpu, paddr, code); + } else { + if (code == BUS_MCEERR_AO) { + return 0; + } else if (code == BUS_MCEERR_AR) { + hardware_memory_error(); + } else { + return 1; } } + return 0; } -static void kvm_inject_x86_mce_on(CPUState *env, struct kvm_x86_mce *mce, - int flag) +static int kvm_inject_mce_oldstyle(CPUState *env) { - 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; - } + if (!kvm_has_vcpu_events() && env->exception_injected == EXCP12_MCHK) { + unsigned int bank, bank_num = env->mcg_cap & 0xff; + struct kvm_x86_mce mce; - on_vcpu(env, kvm_do_inject_x86_mce, &data); -} + env->exception_injected = -1; -static void kvm_mce_broadcast_rest(CPUState *env); -#endif + /* + * There must be at least one bank in use if an MCE is pending. + * Find it and use its values for the event injection. + */ + for (bank = 0; bank < bank_num; bank++) { + if (env->mce_banks[bank * 4 + 1] & MCI_STATUS_VAL) { + break; + } + } + assert(bank < bank_num); -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, - }; + mce.bank = bank; + mce.status = env->mce_banks[bank * 4 + 1]; + mce.mcg_status = env->mcg_status; + mce.addr = env->mce_banks[bank * 4 + 2]; + mce.misc = env->mce_banks[bank * 4 + 3]; - if (flag & MCE_BROADCAST) { - kvm_mce_broadcast_rest(cenv); + return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, &mce); } + return 0; +} - kvm_inject_x86_mce_on(cenv, &mce, flag); -#else - if (flag & ABORT_ON_ERROR) { - abort(); +static void cpu_update_state(void *opaque, int running, int reason) +{ + CPUState *env = opaque; + + if (running) { + env->tsc_valid = false; } -#endif } static int _kvm_arch_init_vcpu(CPUState *env); @@ -312,35 +352,32 @@ struct kvm_cpuid2 cpuid; struct kvm_cpuid_entry2 entries[100]; } __attribute__((packed)) cpuid_data; + KVMState *s = env->kvm_state; uint32_t limit, i, j, cpuid_i; uint32_t unused; struct kvm_cpuid_entry2 *c; -#ifdef CONFIG_KVM_PARA uint32_t signature[3]; -#endif r = _kvm_arch_init_vcpu(env); if (r < 0) { return r; } - env->cpuid_features &= kvm_arch_get_supported_cpuid(env, 1, 0, R_EDX); + env->cpuid_features &= kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); i = env->cpuid_ext_features & CPUID_EXT_HYPERVISOR; - env->cpuid_ext_features &= kvm_arch_get_supported_cpuid(env, 1, 0, R_ECX); + env->cpuid_ext_features &= kvm_arch_get_supported_cpuid(s, 1, 0, R_ECX); env->cpuid_ext_features |= i; - env->cpuid_ext2_features &= kvm_arch_get_supported_cpuid(env, 0x80000001, + env->cpuid_ext2_features &= kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX); - env->cpuid_ext3_features &= kvm_arch_get_supported_cpuid(env, 0x80000001, + env->cpuid_ext3_features &= kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX); - env->cpuid_svm_features &= kvm_arch_get_supported_cpuid(env, 0x8000000A, + env->cpuid_svm_features &= kvm_arch_get_supported_cpuid(s, 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++]; @@ -354,8 +391,10 @@ 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 + c->eax = env->cpuid_kvm_features & + kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX); + + has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF); cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); @@ -385,6 +424,9 @@ case 0xb: case 0xd: for (j = 0; ; j++) { + if (i == 0xd && j == 64) { + break; + } c->function = i; c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; c->index = j; @@ -397,7 +439,7 @@ break; } if (i == 0xd && c->eax == 0) { - break; + continue; } c = &cpuid_data.entries[cpuid_i++]; } @@ -419,43 +461,62 @@ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); } + /* Call Centaur's CPUID instructions they are supported. */ + if (env->cpuid_xlevel2 > 0) { + env->cpuid_ext4_features &= + kvm_arch_get_supported_cpuid(s, 0xC0000001, 0, R_EDX); + cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); + + for (i = 0xC0000000; i <= limit; i++) { + c = &cpuid_data.entries[cpuid_i++]; + + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + } + 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; + int ret; - 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; - } + ret = kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks); + if (ret < 0) { + fprintf(stderr, "kvm_get_mce_cap_supported: %s", strerror(-ret)); + return ret; + } + + if (banks > MCE_BANKS_DEF) { + banks = MCE_BANKS_DEF; } + mcg_cap &= MCE_CAP_DEF; + mcg_cap |= banks; + ret = kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, &mcg_cap); + if (ret < 0) { + fprintf(stderr, "KVM_X86_SETUP_MCE: %s", strerror(-ret)); + return ret; + } + + env->mcg_cap = mcg_cap; } -#endif + + qemu_add_vm_change_state_handler(cpu_update_state, env); 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) @@ -472,7 +533,6 @@ } } - static int kvm_get_supported_msrs(KVMState *s) { static int kvm_supported_msrs; @@ -514,13 +574,13 @@ } } - free(kvm_msr_list); + qemu_free(kvm_msr_list); } return ret; } -#ifdef OBSOLETE_KVM_IMPL +static int kvm_create_pit(KVMState *s); int kvm_arch_init(KVMState *s) { @@ -547,7 +607,6 @@ * that case we need to stick with the default, i.e. a 256K maximum BIOS * size. */ -#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; @@ -557,7 +616,7 @@ 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) { @@ -570,12 +629,23 @@ fprintf(stderr, "e820_add_entry() table is full\n"); return ret; } + qemu_register_reset(kvm_unpoison_all, NULL); + + ret = kvm_create_pit(s); + if (ret < 0) { + return ret; + } + + if (kvm_shadow_memory) { + ret = kvm_vm_ioctl(s, KVM_SET_NR_MMU_PAGES, kvm_shadow_memory); + if (ret < 0) { + return ret; + } + } return 0; } -#endif - static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs) { lhs->selector = rhs->selector; @@ -684,6 +754,9 @@ fpu.fsw = env->fpus & ~(7 << 11); fpu.fsw |= (env->fpstt & 7) << 11; fpu.fcw = env->fpuc; + fpu.last_opcode = env->fpop; + fpu.last_ip = env->fpip; + fpu.last_dp = env->fpdp; for (i = 0; i < 8; ++i) { fpu.ftwx |= (!env->fptags[i]) << i; } @@ -694,7 +767,6 @@ 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 @@ -702,14 +774,12 @@ #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; + uint16_t cwd, swd, twd; if (!kvm_has_xsave()) { return kvm_put_fpu(env); @@ -717,7 +787,7 @@ xsave = qemu_memalign(4096, sizeof(struct kvm_xsave)); memset(xsave, 0, sizeof(struct kvm_xsave)); - cwd = swd = twd = fop = 0; + cwd = swd = twd = 0; swd = env->fpus & ~(7 << 11); swd |= (env->fpstt & 7) << 11; cwd = env->fpuc; @@ -725,7 +795,9 @@ twd |= (!env->fptags[i]) << i; } xsave->region[0] = (uint32_t)(swd << 16) + cwd; - xsave->region[1] = (uint32_t)(fop << 16) + twd; + xsave->region[1] = (uint32_t)(env->fpop << 16) + twd; + memcpy(&xsave->region[XSAVE_CWD_RIP], &env->fpip, sizeof(env->fpip)); + memcpy(&xsave->region[XSAVE_CWD_RDP], &env->fpdp, sizeof(env->fpdp)); memcpy(&xsave->region[XSAVE_ST_SPACE], env->fpregs, sizeof env->fpregs); memcpy(&xsave->region[XSAVE_XMM_SPACE], env->xmm_regs, @@ -737,14 +809,10 @@ 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()) { @@ -756,9 +824,6 @@ 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) @@ -827,6 +892,7 @@ 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); + kvm_msr_entry_set(&msrs[n++], MSR_PAT, env->pat); if (has_msr_star) { kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star); } @@ -861,28 +927,20 @@ 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]); - } + 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 msr_data.info.nmsrs = n; @@ -890,6 +948,7 @@ } + static int kvm_get_fpu(CPUState *env) { struct kvm_fpu fpu; @@ -903,6 +962,9 @@ env->fpstt = (fpu.fsw >> 11) & 7; env->fpus = fpu.fsw; env->fpuc = fpu.fcw; + env->fpop = fpu.last_opcode; + env->fpip = fpu.last_ip; + env->fpdp = fpu.last_dp; for (i = 0; i < 8; ++i) { env->fptags[i] = !((fpu.ftwx >> i) & 1); } @@ -915,10 +977,9 @@ static int kvm_get_xsave(CPUState *env) { -#ifdef KVM_CAP_XSAVE struct kvm_xsave* xsave; int ret, i; - uint16_t cwd, swd, twd, fop; + uint16_t cwd, swd, twd; if (!kvm_has_xsave()) { return kvm_get_fpu(env); @@ -934,13 +995,15 @@ 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->fpop = (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); } + memcpy(&env->fpip, &xsave->region[XSAVE_CWD_RIP], sizeof(env->fpip)); + memcpy(&env->fpdp, &xsave->region[XSAVE_CWD_RDP], sizeof(env->fpdp)); env->mxcsr = xsave->region[XSAVE_MXCSR]; memcpy(env->fpregs, &xsave->region[XSAVE_ST_SPACE], sizeof env->fpregs); @@ -951,14 +1014,10 @@ 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; @@ -979,9 +1038,6 @@ } } return 0; -#else - return 0; -#endif } static int kvm_get_sregs(CPUState *env) @@ -1082,13 +1138,19 @@ msrs[n++].index = MSR_IA32_SYSENTER_CS; msrs[n++].index = MSR_IA32_SYSENTER_ESP; msrs[n++].index = MSR_IA32_SYSENTER_EIP; + msrs[n++].index = MSR_PAT; 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; + + if (!env->tsc_valid) { + msrs[n++].index = MSR_IA32_TSC; + env->tsc_valid = !vm_running; + } + #ifdef TARGET_X86_64 if (lm_capable_kernel) { msrs[n++].index = MSR_CSTAR; @@ -1099,13 +1161,10 @@ #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; @@ -1113,7 +1172,6 @@ msrs[n++].index = MSR_MC0_CTL + i; } } -#endif msr_data.info.nmsrs = n; ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, &msr_data); @@ -1132,6 +1190,9 @@ case MSR_IA32_SYSENTER_EIP: env->sysenter_eip = msrs[i].data; break; + case MSR_PAT: + env->pat = msrs[i].data; + break; case MSR_STAR: env->star = msrs[i].data; break; @@ -1161,34 +1222,27 @@ case MSR_KVM_WALL_CLOCK: env->wall_clock_msr = msrs[i].data; break; -#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 }; @@ -1211,11 +1265,9 @@ } return 0; } -#endif static int kvm_put_vcpu_events(CPUState *env, int level) { -#ifdef KVM_CAP_VCPU_EVENTS struct kvm_vcpu_events events; if (!kvm_has_vcpu_events()) { @@ -1244,14 +1296,10 @@ } return kvm_vcpu_ioctl(env, KVM_SET_VCPU_EVENTS, &events); -#else - return 0; -#endif } static int kvm_get_vcpu_events(CPUState *env) { -#ifdef KVM_CAP_VCPU_EVENTS struct kvm_vcpu_events events; int ret; @@ -1281,7 +1329,6 @@ } env->sipi_vector = events.sipi_vector; -#endif return 0; } @@ -1289,7 +1336,6 @@ 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()) { @@ -1313,13 +1359,11 @@ (!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; @@ -1335,14 +1379,10 @@ 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; @@ -1359,17 +1399,15 @@ } 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)); + assert(cpu_is_stopped(env) || qemu_cpu_is_self(env)); ret = kvm_getput_regs(env, 1); if (ret < 0) { @@ -1387,6 +1425,11 @@ if (ret < 0) { return ret; } + /* must be before kvm_put_msrs */ + ret = kvm_inject_mce_oldstyle(env); + if (ret < 0) { + return ret; + } ret = kvm_put_msrs(env, level); if (ret < 0) { return ret; @@ -1396,6 +1439,8 @@ if (ret < 0) { return ret; } + + kvm_load_lapic(env); } ret = kvm_put_vcpu_events(env, level); if (ret < 0) { @@ -1405,6 +1450,11 @@ if (ret < 0) { return ret; } + if (level == KVM_PUT_FULL_STATE) { + if (env->kvm_vcpu_update_vapic) { + kvm_tpr_enable_vapic(env); + } + } /* must be last */ ret = kvm_guest_debug_workarounds(env); if (ret < 0) { @@ -1417,7 +1467,7 @@ { int ret; - assert(cpu_is_stopped(env) || qemu_cpu_self(env)); + assert(cpu_is_stopped(env) || qemu_cpu_is_self(env)); ret = kvm_getput_regs(env, 0); if (ret < 0) { @@ -1443,6 +1493,7 @@ if (ret < 0) { return ret; } + kvm_save_lapic(env); ret = kvm_get_vcpu_events(env); if (ret < 0) { return ret; @@ -1454,50 +1505,65 @@ return 0; } -int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) +void kvm_arch_pre_run(CPUState *env, struct kvm_run *run) { + int ret; + /* 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) && - (env->eflags & IF_MASK)) { - int irq; - - env->interrupt_request &= ~CPU_INTERRUPT_HARD; - irq = cpu_get_pic_interrupt(env); - if (irq >= 0) { - struct kvm_interrupt intr; - intr.irq = irq; - /* FIXME: errors */ - DPRINTF("injected interrupt %d\n", irq); - kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr); + ret = kvm_vcpu_ioctl(env, KVM_NMI); + if (ret < 0) { + fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n", + strerror(-ret)); } } - /* If we have an interrupt but the guest is not ready to receive an - * 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)) { - run->request_interrupt_window = 1; - } else { - run->request_interrupt_window = 0; - } + if (!kvm_irqchip_in_kernel()) { + /* Force the VCPU out of its inner loop to process the INIT request */ + if (env->interrupt_request & CPU_INTERRUPT_INIT) { + env->exit_request = 1; + } + + /* Try to inject an interrupt if the guest can accept it */ + if (run->ready_for_interrupt_injection && + (env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) { + int irq; + + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + irq = cpu_get_pic_interrupt(env); + if (irq >= 0) { + struct kvm_interrupt intr; + + intr.irq = irq; + DPRINTF("injected interrupt %d\n", irq); + ret = kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr); + if (ret < 0) { + fprintf(stderr, + "KVM: injection failed, interrupt lost (%s)\n", + strerror(-ret)); + } + } + } - DPRINTF("setting tpr\n"); - run->cr8 = cpu_get_apic_tpr(env->apic_state); + /* If we have an interrupt but the guest is not ready to receive an + * 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)) { + run->request_interrupt_window = 1; + } else { + run->request_interrupt_window = 0; + } - return 0; + DPRINTF("setting tpr\n"); + run->cr8 = cpu_get_apic_tpr(env->apic_state); + } } -#endif -int kvm_arch_post_run(CPUState *env, struct kvm_run *run) +void kvm_arch_post_run(CPUState *env, struct kvm_run *run) { if (run->if_flag) { env->eflags |= IF_MASK; @@ -1506,20 +1572,46 @@ } cpu_set_apic_tpr(env->apic_state, run->cr8); cpu_set_apic_base(env->apic_state, run->apic_base); - - return 0; } -#ifdef OBSOLETE_KVM_IMPL - -int kvm_arch_process_irqchip_events(CPUState *env) +int kvm_arch_process_async_events(CPUState *env) { + if (env->interrupt_request & CPU_INTERRUPT_MCE) { + /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ + assert(env->mcg_cap); + + env->interrupt_request &= ~CPU_INTERRUPT_MCE; + + kvm_cpu_synchronize_state(env); + + if (env->exception_injected == EXCP08_DBLE) { + /* this means triple fault */ + qemu_system_reset_request(); + env->exit_request = 1; + return 0; + } + env->exception_injected = EXCP12_MCHK; + env->has_error_code = 0; + + env->halted = 0; + if (kvm_irqchip_in_kernel() && env->mp_state == KVM_MP_STATE_HALTED) { + env->mp_state = KVM_MP_STATE_RUNNABLE; + } + } + + if (kvm_irqchip_in_kernel()) { + return 0; + } + + if (((env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) || + (env->interrupt_request & CPU_INTERRUPT_NMI)) { + env->halted = 0; + } 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); @@ -1534,69 +1626,12 @@ (env->eflags & IF_MASK)) && !(env->interrupt_request & CPU_INTERRUPT_NMI)) { env->halted = 1; - env->exception_index = EXCP_HLT; - return 0; + return EXCP_HLT; } - 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"); - 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; + return 0; } -#endif -#ifdef KVM_CAP_SET_GUEST_DEBUG int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) { static const uint8_t int3 = 0xcc; @@ -1703,31 +1738,31 @@ static CPUWatchpoint hw_watchpoint; -int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info) +static int kvm_handle_debug(struct kvm_debug_exit_arch *arch_info) { - int handle = 0; + int ret = 0; int n; if (arch_info->exception == 1) { if (arch_info->dr6 & (1 << 14)) { if (cpu_single_env->singlestep_enabled) { - handle = 1; + ret = EXCP_DEBUG; } } 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; + ret = EXCP_DEBUG; break; case 0x1: - handle = 1; + ret = EXCP_DEBUG; 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; + ret = EXCP_DEBUG; cpu_single_env->watchpoint_hit = &hw_watchpoint; hw_watchpoint.vaddr = hw_breakpoint[n].addr; hw_watchpoint.flags = BP_MEM_ACCESS; @@ -1737,17 +1772,18 @@ } } } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) { - handle = 1; + ret = EXCP_DEBUG; } - if (!handle) { + if (ret == 0) { cpu_synchronize_state(cpu_single_env); assert(cpu_single_env->exception_injected == -1); + /* pass to guest */ cpu_single_env->exception_injected = arch_info->exception; cpu_single_env->has_error_code = 0; } - return handle; + return ret; } void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) @@ -1776,182 +1812,72 @@ } } } -#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) +static bool host_supports_vmx(void) { - 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, - }; + uint32_t ecx, unused; - kvm_inject_x86_mce_on(env, &mce, ABORT_ON_ERROR); - kvm_mce_broadcast_rest(env); + host_cpuid(1, 0, &unused, &unused, &ecx, &unused); + return ecx & CPUID_EXT_VMX; } -#endif +#define VMX_INVALID_GUEST_STATE 0x80000021 -int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr) +int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run) { -#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(); - } - } + uint64_t code; + int ret; - 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; + switch (run->exit_reason) { + case KVM_EXIT_HLT: + DPRINTF("handle_hlt\n"); + ret = kvm_handle_halt(env); + break; + case KVM_EXIT_SET_TPR: + ret = 0; + 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; + case KVM_EXIT_DEBUG: + DPRINTF("kvm_exit_debug\n"); + ret = kvm_handle_debug(&run->debug.arch); + break; + case KVM_EXIT_TPR_ACCESS: + ret = kvm_handle_tpr_access(env); + break; + default: + fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); + ret = -1; + break; } - return 0; + + return ret; } -int kvm_on_sigbus(int code, void *addr) +bool kvm_arch_stop_on_emulation_error(CPUState *env) { -#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; + return !(env->cr[0] & CR0_PE_MASK) || + ((env->segs[R_CS].selector & 3) != 3); } #include "qemu-kvm-x86.c" diff -Nru qemu-kvm-0.14.1+noroms/target-i386/kvm_x86.h qemu-kvm-0.15.0+noroms/target-i386/kvm_x86.h --- qemu-kvm-0.14.1+noroms/target-i386/kvm_x86.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/kvm_x86.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,25 +0,0 @@ -/* - * 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.14.1+noroms/target-i386/libkvm.h qemu-kvm-0.15.0+noroms/target-i386/libkvm.h --- qemu-kvm-0.14.1+noroms/target-i386/libkvm.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/libkvm.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,28 +0,0 @@ -/* - * This header is for functions & variables that will ONLY be - * used inside libkvm for x86. - * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE - * WITHIN LIBKVM. - * - * derived from libkvm.c - * - * Copyright (C) 2006 Qumranet, Inc. - * - * Authors: - * Avi Kivity - * Yaniv Kamay - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#ifndef KVM_X86_H -#define KVM_X86_H - -#define PAGE_SIZE 4096ul -#define PAGE_MASK (~(PAGE_SIZE - 1)) - -int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr); - -#define smp_wmb() asm volatile("" ::: "memory") - -#endif diff -Nru qemu-kvm-0.14.1+noroms/target-i386/machine.c qemu-kvm-0.15.0+noroms/target-i386/machine.c --- qemu-kvm-0.14.1+noroms/target-i386/machine.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/machine.c 2011-08-09 07:40:29.000000000 -0500 @@ -3,7 +3,7 @@ #include "hw/pc.h" #include "hw/isa.h" -#include "exec-all.h" +#include "cpu.h" #include "kvm.h" static const VMStateDescription vmstate_segment = { @@ -84,7 +84,6 @@ exit(0); } -#ifdef USE_X86LDOUBLE /* XXX: add that in a FPU generic layer */ union x86_longdouble { uint64_t mant; @@ -202,102 +201,6 @@ VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_mmx, vmstate_fpreg_1_mmx, FPReg), \ VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_no_mmx, vmstate_fpreg_1_no_mmx, FPReg) -#else -static int get_fpreg(QEMUFile *f, void *opaque, size_t size) -{ - FPReg *fp_reg = opaque; - - qemu_get_be64s(f, &fp_reg->mmx.MMX_Q(0)); - return 0; -} - -static void put_fpreg(QEMUFile *f, void *opaque, size_t size) -{ - FPReg *fp_reg = opaque; - /* if we use doubles for float emulation, we save the doubles to - avoid losing information in case of MMX usage. It can give - problems if the image is restored on a CPU where long - doubles are used instead. */ - qemu_put_be64s(f, &fp_reg->mmx.MMX_Q(0)); -} - -const VMStateInfo vmstate_fpreg = { - .name = "fpreg", - .get = get_fpreg, - .put = put_fpreg, -}; - -static int get_fpreg_0_mmx(QEMUFile *f, void *opaque, size_t size) -{ - FPReg *fp_reg = opaque; - uint64_t mant; - uint16_t exp; - - qemu_get_be64s(f, &mant); - qemu_get_be16s(f, &exp); - fp_reg->mmx.MMX_Q(0) = mant; - return 0; -} - -const VMStateInfo vmstate_fpreg_0_mmx = { - .name = "fpreg_0_mmx", - .get = get_fpreg_0_mmx, - .put = put_fpreg_error, -}; - -static int get_fpreg_0_no_mmx(QEMUFile *f, void *opaque, size_t size) -{ - FPReg *fp_reg = opaque; - uint64_t mant; - uint16_t exp; - - qemu_get_be64s(f, &mant); - qemu_get_be16s(f, &exp); - - fp_reg->d = cpu_set_fp80(mant, exp); - return 0; -} - -const VMStateInfo vmstate_fpreg_0_no_mmx = { - .name = "fpreg_0_no_mmx", - .get = get_fpreg_0_no_mmx, - .put = put_fpreg_error, -}; - -static bool fpregs_is_1(void *opaque, int version_id) -{ - CPUState *env = opaque; - - return env->fpregs_format_vmstate == 1; -} - -static bool fpregs_is_0_mmx(void *opaque, int version_id) -{ - CPUState *env = opaque; - int guess_mmx; - - guess_mmx = ((env->fptag_vmstate == 0xff) && - (env->fpus_vmstate & 0x3800) == 0); - return guess_mmx && env->fpregs_format_vmstate == 0; -} - -static bool fpregs_is_0_no_mmx(void *opaque, int version_id) -{ - CPUState *env = opaque; - int guess_mmx; - - guess_mmx = ((env->fptag_vmstate == 0xff) && - (env->fpus_vmstate & 0x3800) == 0); - return !guess_mmx && env->fpregs_format_vmstate == 0; -} - -#define VMSTATE_FP_REGS(_field, _state, _n) \ - VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1, vmstate_fpreg, FPReg), \ - VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0_mmx, vmstate_fpreg_0_mmx, FPReg), \ - VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0_no_mmx, vmstate_fpreg_0_no_mmx, FPReg) - -#endif /* USE_X86LDOUBLE */ - static bool version_is_5(void *opaque, int version_id) { return version_id == 5; @@ -344,11 +247,7 @@ env->fptag_vmstate |= ((!env->fptags[i]) << i); } -#ifdef USE_X86LDOUBLE env->fpregs_format_vmstate = 0; -#else - env->fpregs_format_vmstate = 1; -#endif } static int cpu_post_load(void *opaque, int version_id) @@ -391,6 +290,26 @@ } }; +static bool fpop_ip_dp_needed(void *opaque) +{ + CPUState *env = opaque; + + return env->fpop != 0 || env->fpip != 0 || env->fpdp != 0; +} + +static const VMStateDescription vmstate_fpop_ip_dp = { + .name = "cpu/fpop_ip_dp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT16(fpop, CPUState), + VMSTATE_UINT64(fpip, CPUState), + VMSTATE_UINT64(fpdp, CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_cpu = { .name = "cpu", .version_id = CPU_SAVE_VERSION, @@ -487,7 +406,6 @@ /* 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), @@ -500,6 +418,9 @@ .vmsd = &vmstate_async_pf_msr, .needed = async_pf_msr_needed, } , { + .vmsd = &vmstate_fpop_ip_dp, + .needed = fpop_ip_dp_needed, + } , { /* empty */ } } diff -Nru qemu-kvm-0.14.1+noroms/target-i386/op_helper.c qemu-kvm-0.15.0+noroms/target-i386/op_helper.c --- qemu-kvm-0.14.1+noroms/target-i386/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -17,8 +17,8 @@ * License along with this library; if not, see . */ +#include #include "exec.h" -#include "exec-all.h" #include "host-utils.h" #include "ioport.h" @@ -94,16 +94,9 @@ 6, 7, 8, 0, 1, 2, 3, 4, }; -static const CPU86_LDouble f15rk[7] = -{ - 0.00000000000000000000L, - 1.00000000000000000000L, - 3.14159265358979323851L, /*pi*/ - 0.30102999566398119523L, /*lg2*/ - 0.69314718055994530943L, /*ln2*/ - 1.44269504088896340739L, /*l2e*/ - 3.32192809488736234781L, /*l2t*/ -}; +#define floatx80_lg2 make_floatx80( 0x3ffd, 0x9a209a84fbcff799LL ) +#define floatx80_l2e make_floatx80( 0x3fff, 0xb8aa3b295c17f0bcLL ) +#define floatx80_l2t make_floatx80( 0x4000, 0xd49a784bcd1b8afeLL ) /* broken thread support */ @@ -1006,7 +999,7 @@ { env->exception_index = EXCP_SYSCALL; env->exception_next_eip = env->eip + next_eip_addend; - cpu_loop_exit(); + cpu_loop_exit(env); } #else void helper_syscall(int next_eip_addend) @@ -1156,9 +1149,10 @@ env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK); } +#if defined(CONFIG_USER_ONLY) /* fake user mode interrupt */ -void do_interrupt_user(int intno, int is_int, int error_code, - target_ulong next_eip) +static void do_interrupt_user(int intno, int is_int, int error_code, + target_ulong next_eip) { SegmentCache *dt; target_ulong ptr; @@ -1187,7 +1181,8 @@ EIP = next_eip; } -#if !defined(CONFIG_USER_ONLY) +#else + static void handle_even_inj(int intno, int is_int, int error_code, int is_hw, int rm) { @@ -1213,8 +1208,8 @@ * the int instruction. next_eip is the EIP value AFTER the interrupt * instruction. It is only relevant if is_int is TRUE. */ -void do_interrupt(int intno, int is_int, int error_code, - target_ulong next_eip, int is_hw) +static void do_interrupt_all(int intno, int is_int, int error_code, + target_ulong next_eip, int is_hw) { if (qemu_loglevel_mask(CPU_LOG_INT)) { if ((env->cr[0] & CR0_PE_MASK)) { @@ -1276,6 +1271,46 @@ #endif } +void do_interrupt(CPUState *env1) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; +#if defined(CONFIG_USER_ONLY) + /* if user mode only, we simulate a fake exception + which will be handled outside the cpu execution + loop */ + do_interrupt_user(env->exception_index, + env->exception_is_int, + env->error_code, + env->exception_next_eip); + /* successfully delivered */ + env->old_exception = -1; +#else + /* simulate a real cpu exception. On i386, it can + trigger new exceptions, but we do not handle + double or triple faults yet. */ + do_interrupt_all(env->exception_index, + env->exception_is_int, + env->error_code, + env->exception_next_eip, 0); + /* successfully delivered */ + env->old_exception = -1; +#endif + env = saved_env; +} + +void do_interrupt_x86_hardirq(CPUState *env1, int intno, int is_hw) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + do_interrupt_all(intno, 0, 0, 0, is_hw); + env = saved_env; +} + /* This should come from sysemu.h - if we could include it here... */ void qemu_system_reset_request(void); @@ -1341,7 +1376,7 @@ env->error_code = error_code; env->exception_is_int = is_int; env->exception_next_eip = env->eip + next_eip_addend; - cpu_loop_exit(); + cpu_loop_exit(env); } /* shortcuts to generate exceptions */ @@ -1365,7 +1400,7 @@ #if defined(CONFIG_USER_ONLY) -void do_smm_enter(void) +void do_smm_enter(CPUState *env1) { } @@ -1381,11 +1416,15 @@ #define SMM_REVISION_ID 0x00020000 #endif -void do_smm_enter(void) +void do_smm_enter(CPUState *env1) { target_ulong sm_state; SegmentCache *dt; int i, offset; + CPUState *saved_env; + + saved_env = env; + env = env1; qemu_log_mask(CPU_LOG_INT, "SMM: enter\n"); log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP); @@ -1512,6 +1551,7 @@ cpu_x86_update_cr4(env, 0); env->dr[7] = 0x00000400; CC_OP = CC_OP_EFLAGS; + env = saved_env; } void helper_rsm(void) @@ -3431,6 +3471,28 @@ /* x87 FPU helpers */ +static inline double floatx80_to_double(floatx80 a) +{ + union { + float64 f64; + double d; + } u; + + u.f64 = floatx80_to_float64(a, &env->fp_status); + return u.d; +} + +static inline floatx80 double_to_floatx80(double a) +{ + union { + float64 f64; + double d; + } u; + + u.d = a; + return float64_to_floatx80(u.f64, &env->fp_status); +} + static void fpu_set_exception(int mask) { env->fpus |= mask; @@ -3438,11 +3500,12 @@ env->fpus |= FPUS_SE | FPUS_B; } -static inline CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b) +static inline floatx80 helper_fdiv(floatx80 a, floatx80 b) { - if (b == 0.0) + if (floatx80_is_zero(b)) { fpu_set_exception(FPUS_ZE); - return a / b; + } + return floatx80_div(a, b, &env->fp_status); } static void fpu_raise_exception(void) @@ -3464,7 +3527,7 @@ uint32_t i; } u; u.i = val; - FT0 = float32_to_floatx(u.f, &env->fp_status); + FT0 = float32_to_floatx80(u.f, &env->fp_status); } void helper_fldl_FT0(uint64_t val) @@ -3474,12 +3537,12 @@ uint64_t i; } u; u.i = val; - FT0 = float64_to_floatx(u.f, &env->fp_status); + FT0 = float64_to_floatx80(u.f, &env->fp_status); } void helper_fildl_FT0(int32_t val) { - FT0 = int32_to_floatx(val, &env->fp_status); + FT0 = int32_to_floatx80(val, &env->fp_status); } void helper_flds_ST0(uint32_t val) @@ -3491,7 +3554,7 @@ } u; new_fpstt = (env->fpstt - 1) & 7; u.i = val; - env->fpregs[new_fpstt].d = float32_to_floatx(u.f, &env->fp_status); + env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } @@ -3505,7 +3568,7 @@ } u; new_fpstt = (env->fpstt - 1) & 7; u.i = val; - env->fpregs[new_fpstt].d = float64_to_floatx(u.f, &env->fp_status); + env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } @@ -3514,7 +3577,7 @@ { int new_fpstt; new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = int32_to_floatx(val, &env->fp_status); + env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } @@ -3523,7 +3586,7 @@ { int new_fpstt; new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = int64_to_floatx(val, &env->fp_status); + env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } @@ -3534,7 +3597,7 @@ float32 f; uint32_t i; } u; - u.f = floatx_to_float32(ST0, &env->fp_status); + u.f = floatx80_to_float32(ST0, &env->fp_status); return u.i; } @@ -3544,14 +3607,14 @@ float64 f; uint64_t i; } u; - u.f = floatx_to_float64(ST0, &env->fp_status); + u.f = floatx80_to_float64(ST0, &env->fp_status); return u.i; } int32_t helper_fist_ST0(void) { int32_t val; - val = floatx_to_int32(ST0, &env->fp_status); + val = floatx80_to_int32(ST0, &env->fp_status); if (val != (int16_t)val) val = -32768; return val; @@ -3560,21 +3623,21 @@ int32_t helper_fistl_ST0(void) { int32_t val; - val = floatx_to_int32(ST0, &env->fp_status); + val = floatx80_to_int32(ST0, &env->fp_status); return val; } int64_t helper_fistll_ST0(void) { int64_t val; - val = floatx_to_int64(ST0, &env->fp_status); + val = floatx80_to_int64(ST0, &env->fp_status); return val; } int32_t helper_fistt_ST0(void) { int32_t val; - val = floatx_to_int32_round_to_zero(ST0, &env->fp_status); + val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); if (val != (int16_t)val) val = -32768; return val; @@ -3583,14 +3646,14 @@ int32_t helper_fisttl_ST0(void) { int32_t val; - val = floatx_to_int32_round_to_zero(ST0, &env->fp_status); + val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); return val; } int64_t helper_fisttll_ST0(void) { int64_t val; - val = floatx_to_int64_round_to_zero(ST0, &env->fp_status); + val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status); return val; } @@ -3659,7 +3722,7 @@ void helper_fxchg_ST0_STN(int st_index) { - CPU86_LDouble tmp; + floatx80 tmp; tmp = ST(st_index); ST(st_index) = ST0; ST0 = tmp; @@ -3673,7 +3736,7 @@ { int ret; - ret = floatx_compare(ST0, FT0, &env->fp_status); + ret = floatx80_compare(ST0, FT0, &env->fp_status); env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; } @@ -3681,7 +3744,7 @@ { int ret; - ret = floatx_compare_quiet(ST0, FT0, &env->fp_status); + ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1]; } @@ -3692,7 +3755,7 @@ int eflags; int ret; - ret = floatx_compare(ST0, FT0, &env->fp_status); + ret = floatx80_compare(ST0, FT0, &env->fp_status); eflags = helper_cc_compute_all(CC_OP); eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; CC_SRC = eflags; @@ -3703,7 +3766,7 @@ int eflags; int ret; - ret = floatx_compare_quiet(ST0, FT0, &env->fp_status); + ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); eflags = helper_cc_compute_all(CC_OP); eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; CC_SRC = eflags; @@ -3711,22 +3774,22 @@ void helper_fadd_ST0_FT0(void) { - ST0 += FT0; + ST0 = floatx80_add(ST0, FT0, &env->fp_status); } void helper_fmul_ST0_FT0(void) { - ST0 *= FT0; + ST0 = floatx80_mul(ST0, FT0, &env->fp_status); } void helper_fsub_ST0_FT0(void) { - ST0 -= FT0; + ST0 = floatx80_sub(ST0, FT0, &env->fp_status); } void helper_fsubr_ST0_FT0(void) { - ST0 = FT0 - ST0; + ST0 = floatx80_sub(FT0, ST0, &env->fp_status); } void helper_fdiv_ST0_FT0(void) @@ -3743,36 +3806,34 @@ void helper_fadd_STN_ST0(int st_index) { - ST(st_index) += ST0; + ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status); } void helper_fmul_STN_ST0(int st_index) { - ST(st_index) *= ST0; + ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status); } void helper_fsub_STN_ST0(int st_index) { - ST(st_index) -= ST0; + ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status); } void helper_fsubr_STN_ST0(int st_index) { - CPU86_LDouble *p; - p = &ST(st_index); - *p = ST0 - *p; + ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status); } void helper_fdiv_STN_ST0(int st_index) { - CPU86_LDouble *p; + floatx80 *p; p = &ST(st_index); *p = helper_fdiv(*p, ST0); } void helper_fdivr_STN_ST0(int st_index) { - CPU86_LDouble *p; + floatx80 *p; p = &ST(st_index); *p = helper_fdiv(ST0, *p); } @@ -3780,52 +3841,52 @@ /* misc FPU operations */ void helper_fchs_ST0(void) { - ST0 = floatx_chs(ST0); + ST0 = floatx80_chs(ST0); } void helper_fabs_ST0(void) { - ST0 = floatx_abs(ST0); + ST0 = floatx80_abs(ST0); } void helper_fld1_ST0(void) { - ST0 = f15rk[1]; + ST0 = floatx80_one; } void helper_fldl2t_ST0(void) { - ST0 = f15rk[6]; + ST0 = floatx80_l2t; } void helper_fldl2e_ST0(void) { - ST0 = f15rk[5]; + ST0 = floatx80_l2e; } void helper_fldpi_ST0(void) { - ST0 = f15rk[2]; + ST0 = floatx80_pi; } void helper_fldlg2_ST0(void) { - ST0 = f15rk[3]; + ST0 = floatx80_lg2; } void helper_fldln2_ST0(void) { - ST0 = f15rk[4]; + ST0 = floatx80_ln2; } void helper_fldz_ST0(void) { - ST0 = f15rk[0]; + ST0 = floatx80_zero; } void helper_fldz_FT0(void) { - FT0 = f15rk[0]; + FT0 = floatx80_zero; } uint32_t helper_fnstsw(void) @@ -3859,7 +3920,6 @@ break; } set_float_rounding_mode(rnd_type, &env->fp_status); -#ifdef FLOATX80 switch((env->fpuc >> 8) & 3) { case 0: rnd_type = 32; @@ -3873,7 +3933,6 @@ break; } set_floatx80_rounding_precision(rnd_type, &env->fp_status); -#endif } void helper_fldcw(uint32_t val) @@ -3912,7 +3971,7 @@ void helper_fbld_ST0(target_ulong ptr) { - CPU86_LDouble tmp; + floatx80 tmp; uint64_t val; unsigned int v; int i; @@ -3922,9 +3981,10 @@ v = ldub(ptr + i); val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); } - tmp = val; - if (ldub(ptr + 9) & 0x80) - tmp = -tmp; + tmp = int64_to_floatx80(val, &env->fp_status); + if (ldub(ptr + 9) & 0x80) { + floatx80_chs(tmp); + } fpush(); ST0 = tmp; } @@ -3935,7 +3995,7 @@ target_ulong mem_ref, mem_end; int64_t val; - val = floatx_to_int64(ST0, &env->fp_status); + val = floatx80_to_int64(ST0, &env->fp_status); mem_ref = ptr; mem_end = mem_ref + 9; if (val < 0) { @@ -3959,17 +4019,19 @@ void helper_f2xm1(void) { - ST0 = pow(2.0,ST0) - 1.0; + double val = floatx80_to_double(ST0); + val = pow(2.0, val) - 1.0; + ST0 = double_to_floatx80(val); } void helper_fyl2x(void) { - CPU86_LDouble fptemp; + double fptemp = floatx80_to_double(ST0); - fptemp = ST0; if (fptemp>0.0){ - fptemp = log(fptemp)/log(2.0); /* log2(ST) */ - ST1 *= fptemp; + fptemp = log(fptemp)/log(2.0); /* log2(ST) */ + fptemp *= floatx80_to_double(ST1); + ST1 = double_to_floatx80(fptemp); fpop(); } else { env->fpus &= (~0x4700); @@ -3979,15 +4041,15 @@ void helper_fptan(void) { - CPU86_LDouble fptemp; + double fptemp = floatx80_to_double(ST0); - fptemp = ST0; if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { env->fpus |= 0x400; } else { - ST0 = tan(fptemp); + fptemp = tan(fptemp); + ST0 = double_to_floatx80(fptemp); fpush(); - ST0 = 1.0; + ST0 = floatx80_one; env->fpus &= (~0x400); /* C2 <-- 0 */ /* the above code is for |arg| < 2**52 only */ } @@ -3995,45 +4057,57 @@ void helper_fpatan(void) { - CPU86_LDouble fptemp, fpsrcop; + double fptemp, fpsrcop; - fpsrcop = ST1; - fptemp = ST0; - ST1 = atan2(fpsrcop,fptemp); + fpsrcop = floatx80_to_double(ST1); + fptemp = floatx80_to_double(ST0); + ST1 = double_to_floatx80(atan2(fpsrcop, fptemp)); fpop(); } void helper_fxtract(void) { - CPU86_LDoubleU temp; - unsigned int expdif; + CPU_LDoubleU temp; temp.d = ST0; - expdif = EXPD(temp) - EXPBIAS; - /*DP exponent bias*/ - ST0 = expdif; - fpush(); - BIASEXPONENT(temp); - ST0 = temp.d; + + if (floatx80_is_zero(ST0)) { + /* Easy way to generate -inf and raising division by 0 exception */ + ST0 = floatx80_div(floatx80_chs(floatx80_one), floatx80_zero, &env->fp_status); + fpush(); + ST0 = temp.d; + } else { + int expdif; + + expdif = EXPD(temp) - EXPBIAS; + /*DP exponent bias*/ + ST0 = int32_to_floatx80(expdif, &env->fp_status); + fpush(); + BIASEXPONENT(temp); + ST0 = temp.d; + } } void helper_fprem1(void) { - CPU86_LDouble dblq, fpsrcop, fptemp; - CPU86_LDoubleU fpsrcop1, fptemp1; + double st0, st1, dblq, fpsrcop, fptemp; + CPU_LDoubleU fpsrcop1, fptemp1; int expdif; signed long long int q; - if (isinf(ST0) || isnan(ST0) || isnan(ST1) || (ST1 == 0.0)) { - ST0 = 0.0 / 0.0; /* NaN */ + st0 = floatx80_to_double(ST0); + st1 = floatx80_to_double(ST1); + + if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) { + ST0 = double_to_floatx80(0.0 / 0.0); /* NaN */ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ return; } - fpsrcop = ST0; - fptemp = ST1; - fpsrcop1.d = fpsrcop; - fptemp1.d = fptemp; + fpsrcop = st0; + fptemp = st1; + fpsrcop1.d = ST0; + fptemp1.d = ST1; expdif = EXPD(fpsrcop1) - EXPD(fptemp1); if (expdif < 0) { @@ -4047,7 +4121,7 @@ dblq = fpsrcop / fptemp; /* round dblq towards nearest integer */ dblq = rint(dblq); - ST0 = fpsrcop - fptemp * dblq; + st0 = fpsrcop - fptemp * dblq; /* convert dblq to q by truncating towards zero */ if (dblq < 0.0) @@ -4063,31 +4137,35 @@ } else { env->fpus |= 0x400; /* C2 <-- 1 */ fptemp = pow(2.0, expdif - 50); - fpsrcop = (ST0 / ST1) / fptemp; + fpsrcop = (st0 / st1) / fptemp; /* fpsrcop = integer obtained by chopping */ fpsrcop = (fpsrcop < 0.0) ? -(floor(fabs(fpsrcop))) : floor(fpsrcop); - ST0 -= (ST1 * fpsrcop * fptemp); + st0 -= (st1 * fpsrcop * fptemp); } + ST0 = double_to_floatx80(st0); } void helper_fprem(void) { - CPU86_LDouble dblq, fpsrcop, fptemp; - CPU86_LDoubleU fpsrcop1, fptemp1; + double st0, st1, dblq, fpsrcop, fptemp; + CPU_LDoubleU fpsrcop1, fptemp1; int expdif; signed long long int q; - if (isinf(ST0) || isnan(ST0) || isnan(ST1) || (ST1 == 0.0)) { - ST0 = 0.0 / 0.0; /* NaN */ + st0 = floatx80_to_double(ST0); + st1 = floatx80_to_double(ST1); + + if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) { + ST0 = double_to_floatx80(0.0 / 0.0); /* NaN */ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ return; } - fpsrcop = (CPU86_LDouble)ST0; - fptemp = (CPU86_LDouble)ST1; - fpsrcop1.d = fpsrcop; - fptemp1.d = fptemp; + fpsrcop = st0; + fptemp = st1; + fpsrcop1.d = ST0; + fptemp1.d = ST1; expdif = EXPD(fpsrcop1) - EXPD(fptemp1); if (expdif < 0) { @@ -4101,7 +4179,7 @@ dblq = fpsrcop/*ST0*/ / fptemp/*ST1*/; /* round dblq towards zero */ dblq = (dblq < 0.0) ? ceil(dblq) : floor(dblq); - ST0 = fpsrcop/*ST0*/ - fptemp * dblq; + st0 = fpsrcop/*ST0*/ - fptemp * dblq; /* convert dblq to q by truncating towards zero */ if (dblq < 0.0) @@ -4118,22 +4196,23 @@ int N = 32 + (expdif % 32); /* as per AMD docs */ env->fpus |= 0x400; /* C2 <-- 1 */ fptemp = pow(2.0, (double)(expdif - N)); - fpsrcop = (ST0 / ST1) / fptemp; + fpsrcop = (st0 / st1) / fptemp; /* fpsrcop = integer obtained by chopping */ fpsrcop = (fpsrcop < 0.0) ? -(floor(fabs(fpsrcop))) : floor(fpsrcop); - ST0 -= (ST1 * fpsrcop * fptemp); + st0 -= (st1 * fpsrcop * fptemp); } + ST0 = double_to_floatx80(st0); } void helper_fyl2xp1(void) { - CPU86_LDouble fptemp; + double fptemp = floatx80_to_double(ST0); - fptemp = ST0; if ((fptemp+1.0)>0.0) { fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */ - ST1 *= fptemp; + fptemp *= floatx80_to_double(ST1); + ST1 = double_to_floatx80(fptemp); fpop(); } else { env->fpus &= (~0x4700); @@ -4143,27 +4222,23 @@ void helper_fsqrt(void) { - CPU86_LDouble fptemp; - - fptemp = ST0; - if (fptemp<0.0) { + if (floatx80_is_neg(ST0)) { env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ env->fpus |= 0x400; } - ST0 = sqrt(fptemp); + ST0 = floatx80_sqrt(ST0, &env->fp_status); } void helper_fsincos(void) { - CPU86_LDouble fptemp; + double fptemp = floatx80_to_double(ST0); - fptemp = ST0; if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { env->fpus |= 0x400; } else { - ST0 = sin(fptemp); + ST0 = double_to_floatx80(sin(fptemp)); fpush(); - ST0 = cos(fptemp); + ST0 = double_to_floatx80(cos(fptemp)); env->fpus &= (~0x400); /* C2 <-- 0 */ /* the above code is for |arg| < 2**63 only */ } @@ -4171,23 +4246,27 @@ void helper_frndint(void) { - ST0 = floatx_round_to_int(ST0, &env->fp_status); + ST0 = floatx80_round_to_int(ST0, &env->fp_status); } void helper_fscale(void) { - ST0 = ldexp (ST0, (int)(ST1)); + if (floatx80_is_any_nan(ST1)) { + ST0 = ST1; + } else { + int n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status); + ST0 = floatx80_scalbn(ST0, n, &env->fp_status); + } } void helper_fsin(void) { - CPU86_LDouble fptemp; + double fptemp = floatx80_to_double(ST0); - fptemp = ST0; if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { env->fpus |= 0x400; } else { - ST0 = sin(fptemp); + ST0 = double_to_floatx80(sin(fptemp)); env->fpus &= (~0x400); /* C2 <-- 0 */ /* the above code is for |arg| < 2**53 only */ } @@ -4195,13 +4274,12 @@ void helper_fcos(void) { - CPU86_LDouble fptemp; + double fptemp = floatx80_to_double(ST0); - fptemp = ST0; if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { env->fpus |= 0x400; } else { - ST0 = cos(fptemp); + ST0 = double_to_floatx80(cos(fptemp)); env->fpus &= (~0x400); /* C2 <-- 0 */ /* the above code is for |arg5 < 2**63 only */ } @@ -4209,7 +4287,7 @@ void helper_fxam_ST0(void) { - CPU86_LDoubleU temp; + CPU_LDoubleU temp; int expdif; temp.d = ST0; @@ -4221,11 +4299,7 @@ /* XXX: test fptags too */ expdif = EXPD(temp); if (expdif == MAXEXPD) { -#ifdef USE_X86LDOUBLE if (MANTD(temp) == 0x8000000000000000ULL) -#else - if (MANTD(temp) == 0) -#endif env->fpus |= 0x500 /*Infinity*/; else env->fpus |= 0x100 /*NaN*/; @@ -4243,7 +4317,7 @@ { int fpus, fptag, exp, i; uint64_t mant; - CPU86_LDoubleU tmp; + CPU_LDoubleU tmp; fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; fptag = 0; @@ -4259,9 +4333,7 @@ /* zero */ fptag |= 1; } else if (exp == 0 || exp == MAXEXPD -#ifdef USE_X86LDOUBLE || (mant & (1LL << 63)) == 0 -#endif ) { /* NaNs, infinity, denormal */ fptag |= 2; @@ -4313,7 +4385,7 @@ void helper_fsave(target_ulong ptr, int data32) { - CPU86_LDouble tmp; + floatx80 tmp; int i; helper_fstenv(ptr, data32); @@ -4341,7 +4413,7 @@ void helper_frstor(target_ulong ptr, int data32) { - CPU86_LDouble tmp; + floatx80 tmp; int i; helper_fldenv(ptr, data32); @@ -4357,7 +4429,7 @@ void helper_fxsave(target_ulong ptr, int data64) { int fpus, fptag, i, nb_xmm_regs; - CPU86_LDouble tmp; + floatx80 tmp; target_ulong addr; /* The operand must be 16 byte aligned */ @@ -4418,7 +4490,7 @@ void helper_fxrstor(target_ulong ptr, int data64) { int i, fpus, fptag, nb_xmm_regs; - CPU86_LDouble tmp; + floatx80 tmp; target_ulong addr; /* The operand must be 16 byte aligned */ @@ -4465,61 +4537,23 @@ } } -#ifndef USE_X86LDOUBLE - -void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f) -{ - CPU86_LDoubleU temp; - int e; - - temp.d = f; - /* mantissa */ - *pmant = (MANTD(temp) << 11) | (1LL << 63); - /* exponent + sign */ - e = EXPD(temp) - EXPBIAS + 16383; - e |= SIGND(temp) >> 16; - *pexp = e; -} - -CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper) -{ - CPU86_LDoubleU temp; - int e; - uint64_t ll; - - /* XXX: handle overflow ? */ - e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */ - e |= (upper >> 4) & 0x800; /* sign */ - ll = (mant >> 11) & ((1LL << 52) - 1); -#ifdef __arm__ - temp.l.upper = (e << 20) | (ll >> 32); - temp.l.lower = ll; -#else - temp.ll = ll | ((uint64_t)e << 52); -#endif - return temp.d; -} - -#else - -void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f) +void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f) { - CPU86_LDoubleU temp; + CPU_LDoubleU temp; temp.d = f; *pmant = temp.l.lower; *pexp = temp.l.upper; } -CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper) +floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper) { - CPU86_LDoubleU temp; + CPU_LDoubleU temp; temp.l.upper = upper; temp.l.lower = mant; return temp.d; } -#endif #ifdef TARGET_X86_64 @@ -4670,7 +4704,7 @@ env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ env->halted = 1; env->exception_index = EXCP_HLT; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_hlt(int next_eip_addend) @@ -4708,7 +4742,7 @@ void helper_debug(void) { env->exception_index = EXCP_DEBUG; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_reset_rf(void) @@ -4783,16 +4817,6 @@ } } -static float approx_rsqrt(float a) -{ - return 1.0 / sqrt(a); -} - -static float approx_rcp(float a) -{ - return 1.0 / a; -} - #if !defined(CONFIG_USER_ONLY) #define MMUSUFFIX _mmu @@ -4837,7 +4861,7 @@ 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); + cpu_restore_state(tb, env, pc); } } raise_exception_err(env->exception_index, env->error_code); @@ -4881,6 +4905,10 @@ { } +void svm_check_intercept(CPUState *env1, uint32_t type) +{ +} + void helper_svm_check_io(uint32_t port, uint32_t param, uint32_t next_eip_addend) { @@ -5062,7 +5090,7 @@ env->exception_next_eip = -1; qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR"); /* XXX: is it always correct ? */ - do_interrupt(vector, 0, 0, 0, 1); + do_interrupt_all(vector, 0, 0, 0, 1); break; case SVM_EVTINJ_TYPE_NMI: env->exception_index = EXCP02_NMI; @@ -5070,7 +5098,7 @@ env->exception_is_int = 0; env->exception_next_eip = EIP; qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI"); - cpu_loop_exit(); + cpu_loop_exit(env); break; case SVM_EVTINJ_TYPE_EXEPT: env->exception_index = vector; @@ -5078,7 +5106,7 @@ env->exception_is_int = 0; env->exception_next_eip = -1; qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT"); - cpu_loop_exit(); + cpu_loop_exit(env); break; case SVM_EVTINJ_TYPE_SOFT: env->exception_index = vector; @@ -5086,7 +5114,7 @@ env->exception_is_int = 1; env->exception_next_eip = EIP; qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT"); - cpu_loop_exit(); + cpu_loop_exit(env); break; } qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", env->exception_index, env->error_code); @@ -5271,6 +5299,16 @@ } } +void svm_check_intercept(CPUState *env1, uint32_t type) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + helper_svm_check_intercept_param(type, 0); + env = saved_env; +} + void helper_svm_check_io(uint32_t port, uint32_t param, uint32_t next_eip_addend) { @@ -5422,7 +5460,7 @@ env->error_code = 0; env->old_exception = -1; - cpu_loop_exit(); + cpu_loop_exit(env); } #endif @@ -5597,6 +5635,18 @@ } } +uint32_t cpu_cc_compute_all(CPUState *env1, int op) +{ + CPUState *saved_env; + uint32_t ret; + + saved_env = env; + env = env1; + ret = helper_cc_compute_all(op); + env = saved_env; + return ret; +} + uint32_t helper_cc_compute_c(int op) { switch (op) { diff -Nru qemu-kvm-0.14.1+noroms/target-i386/ops_sse.h qemu-kvm-0.15.0+noroms/target-i386/ops_sse.h --- qemu-kvm-0.14.1+noroms/target-i386/ops_sse.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/ops_sse.h 2011-08-09 07:40:29.000000000 -0500 @@ -778,28 +778,38 @@ void helper_rsqrtps(XMMReg *d, XMMReg *s) { - d->XMM_S(0) = approx_rsqrt(s->XMM_S(0)); - d->XMM_S(1) = approx_rsqrt(s->XMM_S(1)); - d->XMM_S(2) = approx_rsqrt(s->XMM_S(2)); - d->XMM_S(3) = approx_rsqrt(s->XMM_S(3)); + d->XMM_S(0) = float32_div(float32_one, + float32_sqrt(s->XMM_S(0), &env->sse_status), + &env->sse_status); + d->XMM_S(1) = float32_div(float32_one, + float32_sqrt(s->XMM_S(1), &env->sse_status), + &env->sse_status); + d->XMM_S(2) = float32_div(float32_one, + float32_sqrt(s->XMM_S(2), &env->sse_status), + &env->sse_status); + d->XMM_S(3) = float32_div(float32_one, + float32_sqrt(s->XMM_S(3), &env->sse_status), + &env->sse_status); } void helper_rsqrtss(XMMReg *d, XMMReg *s) { - d->XMM_S(0) = approx_rsqrt(s->XMM_S(0)); + d->XMM_S(0) = float32_div(float32_one, + float32_sqrt(s->XMM_S(0), &env->sse_status), + &env->sse_status); } void helper_rcpps(XMMReg *d, XMMReg *s) { - d->XMM_S(0) = approx_rcp(s->XMM_S(0)); - d->XMM_S(1) = approx_rcp(s->XMM_S(1)); - d->XMM_S(2) = approx_rcp(s->XMM_S(2)); - d->XMM_S(3) = approx_rcp(s->XMM_S(3)); + d->XMM_S(0) = float32_div(float32_one, s->XMM_S(0), &env->sse_status); + d->XMM_S(1) = float32_div(float32_one, s->XMM_S(1), &env->sse_status); + d->XMM_S(2) = float32_div(float32_one, s->XMM_S(2), &env->sse_status); + d->XMM_S(3) = float32_div(float32_one, s->XMM_S(3), &env->sse_status); } void helper_rcpss(XMMReg *d, XMMReg *s) { - d->XMM_S(0) = approx_rcp(s->XMM_S(0)); + d->XMM_S(0) = float32_div(float32_one, s->XMM_S(0), &env->sse_status); } static inline uint64_t helper_extrq(uint64_t src, int shift, int len) @@ -921,14 +931,14 @@ d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\ } -#define FPU_CMPEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? -1 : 0 +#define FPU_CMPEQ(size, a, b) float ## size ## _eq_quiet(a, b, &env->sse_status) ? -1 : 0 #define FPU_CMPLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? -1 : 0 #define FPU_CMPLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? -1 : 0 -#define FPU_CMPUNORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? - 1 : 0 -#define FPU_CMPNEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? 0 : -1 +#define FPU_CMPUNORD(size, a, b) float ## size ## _unordered_quiet(a, b, &env->sse_status) ? - 1 : 0 +#define FPU_CMPNEQ(size, a, b) float ## size ## _eq_quiet(a, b, &env->sse_status) ? 0 : -1 #define FPU_CMPNLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? 0 : -1 #define FPU_CMPNLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? 0 : -1 -#define FPU_CMPORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? 0 : -1 +#define FPU_CMPORD(size, a, b) float ## size ## _unordered_quiet(a, b, &env->sse_status) ? 0 : -1 SSE_HELPER_CMP(cmpeq, FPU_CMPEQ) SSE_HELPER_CMP(cmplt, FPU_CMPLT) @@ -1216,8 +1226,8 @@ void helper_pfcmpeq(MMXReg *d, MMXReg *s) { - d->MMX_L(0) = float32_eq(d->MMX_S(0), s->MMX_S(0), &env->mmx_status) ? -1 : 0; - d->MMX_L(1) = float32_eq(d->MMX_S(1), s->MMX_S(1), &env->mmx_status) ? -1 : 0; + d->MMX_L(0) = float32_eq_quiet(d->MMX_S(0), s->MMX_S(0), &env->mmx_status) ? -1 : 0; + d->MMX_L(1) = float32_eq_quiet(d->MMX_S(1), s->MMX_S(1), &env->mmx_status) ? -1 : 0; } void helper_pfcmpge(MMXReg *d, MMXReg *s) @@ -1272,14 +1282,16 @@ void helper_pfrcp(MMXReg *d, MMXReg *s) { - d->MMX_S(0) = approx_rcp(s->MMX_S(0)); + d->MMX_S(0) = float32_div(float32_one, s->MMX_S(0), &env->mmx_status); d->MMX_S(1) = d->MMX_S(0); } void helper_pfrsqrt(MMXReg *d, MMXReg *s) { d->MMX_L(1) = s->MMX_L(0) & 0x7fffffff; - d->MMX_S(1) = approx_rsqrt(d->MMX_S(1)); + d->MMX_S(1) = float32_div(float32_one, + float32_sqrt(d->MMX_S(1), &env->mmx_status), + &env->mmx_status); d->MMX_L(1) |= s->MMX_L(0) & 0x80000000; d->MMX_L(0) = d->MMX_L(1); } diff -Nru qemu-kvm-0.14.1+noroms/target-i386/translate.c qemu-kvm-0.15.0+noroms/target-i386/translate.c --- qemu-kvm-0.14.1+noroms/target-i386/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-i386/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" @@ -2274,7 +2273,7 @@ /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); gen_jmp_im(eip); - tcg_gen_exit_tb((long)tb + tb_num); + tcg_gen_exit_tb((tcg_target_long)tb + tb_num); } else { /* jump to another page: currently not optimized */ gen_jmp_im(eip); @@ -7538,7 +7537,7 @@ break; case 5: /* lfence */ case 6: /* mfence */ - if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE)) + if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE2)) goto illegal_op; break; case 7: /* sfence / clflush */ @@ -7890,8 +7889,7 @@ gen_intermediate_code_internal(env, tb, 1); } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { int cc_op; #ifdef DEBUG_DISAS @@ -7903,8 +7901,8 @@ qemu_log("0x%04x: " TARGET_FMT_lx "\n", i, gen_opc_pc[i]); } } - qemu_log("spc=0x%08lx pc_pos=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n", - searched_pc, pc_pos, gen_opc_pc[pc_pos] - tb->cs_base, + qemu_log("pc_pos=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n", + pc_pos, gen_opc_pc[pc_pos] - tb->cs_base, (uint32_t)tb->cs_base); } #endif diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/cpu.h qemu-kvm-0.15.0+noroms/target-lm32/cpu.h --- qemu-kvm-0.14.1+noroms/target-lm32/cpu.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,257 @@ +/* + * LatticeMico32 virtual CPU header. + * + * Copyright (c) 2010 Michael Walle + * + * 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 CPU_LM32_H +#define CPU_LM32_H + +#define TARGET_LONG_BITS 32 + +#define CPUState struct CPULM32State + +#include "qemu-common.h" +#include "cpu-defs.h" +struct CPULM32State; + +#define TARGET_HAS_ICE 1 + +#define ELF_MACHINE EM_LATTICEMICO32 + +#define NB_MMU_MODES 1 +#define TARGET_PAGE_BITS 12 +static inline int cpu_mmu_index(CPUState *env) +{ + return 0; +} + +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +/* Exceptions indices */ +enum { + EXCP_RESET = 0, + EXCP_BREAKPOINT, + EXCP_INSN_BUS_ERROR, + EXCP_WATCHPOINT, + EXCP_DATA_BUS_ERROR, + EXCP_DIVIDE_BY_ZERO, + EXCP_IRQ, + EXCP_SYSTEMCALL +}; + +/* Registers */ +enum { + R_R0 = 0, R_R1, R_R2, R_R3, R_R4, R_R5, R_R6, R_R7, R_R8, R_R9, R_R10, + R_R11, R_R12, R_R13, R_R14, R_R15, R_R16, R_R17, R_R18, R_R19, R_R20, + R_R21, R_R22, R_R23, R_R24, R_R25, R_R26, R_R27, R_R28, R_R29, R_R30, + R_R31 +}; + +/* Register aliases */ +enum { + R_GP = R_R26, + R_FP = R_R27, + R_SP = R_R28, + R_RA = R_R29, + R_EA = R_R30, + R_BA = R_R31 +}; + +/* IE flags */ +enum { + IE_IE = (1<<0), + IE_EIE = (1<<1), + IE_BIE = (1<<2), +}; + +/* DC flags */ +enum { + DC_SS = (1<<0), + DC_RE = (1<<1), + DC_C0 = (1<<2), + DC_C1 = (1<<3), + DC_C2 = (1<<4), + DC_C3 = (1<<5), +}; + +/* CFG mask */ +enum { + CFG_M = (1<<0), + CFG_D = (1<<1), + CFG_S = (1<<2), + CFG_U = (1<<3), + CFG_X = (1<<4), + CFG_CC = (1<<5), + CFG_IC = (1<<6), + CFG_DC = (1<<7), + CFG_G = (1<<8), + CFG_H = (1<<9), + CFG_R = (1<<10), + CFG_J = (1<<11), + CFG_INT_SHIFT = 12, + CFG_BP_SHIFT = 18, + CFG_WP_SHIFT = 22, + CFG_REV_SHIFT = 26, +}; + +/* CSRs */ +enum { + CSR_IE = 0x00, + CSR_IM = 0x01, + CSR_IP = 0x02, + CSR_ICC = 0x03, + CSR_DCC = 0x04, + CSR_CC = 0x05, + CSR_CFG = 0x06, + CSR_EBA = 0x07, + CSR_DC = 0x08, + CSR_DEBA = 0x09, + CSR_JTX = 0x0e, + CSR_JRX = 0x0f, + CSR_BP0 = 0x10, + CSR_BP1 = 0x11, + CSR_BP2 = 0x12, + CSR_BP3 = 0x13, + CSR_WP0 = 0x18, + CSR_WP1 = 0x19, + CSR_WP2 = 0x1a, + CSR_WP3 = 0x1b, +}; + +enum { + LM32_FEATURE_MULTIPLY = 1, + LM32_FEATURE_DIVIDE = 2, + LM32_FEATURE_SHIFT = 4, + LM32_FEATURE_SIGN_EXTEND = 8, + LM32_FEATURE_I_CACHE = 16, + LM32_FEATURE_D_CACHE = 32, + LM32_FEATURE_CYCLE_COUNT = 64, +}; + +enum { + LM32_FLAG_IGNORE_MSB = 1, +}; + +typedef struct CPULM32State { + /* general registers */ + uint32_t regs[32]; + + /* special registers */ + uint32_t pc; /* program counter */ + uint32_t ie; /* interrupt enable */ + uint32_t icc; /* instruction cache control */ + uint32_t dcc; /* data cache control */ + uint32_t cc; /* cycle counter */ + uint32_t cfg; /* configuration */ + + /* debug registers */ + uint32_t dc; /* debug control */ + uint32_t bp[4]; /* breakpoint addresses */ + uint32_t wp[4]; /* watchpoint addresses */ + + CPU_COMMON + + uint32_t eba; /* exception base address */ + uint32_t deba; /* debug exception base address */ + + /* interrupt controller handle for callbacks */ + DeviceState *pic_state; + /* JTAG UART handle for callbacks */ + DeviceState *juart_state; + + /* processor core features */ + uint32_t features; + uint32_t flags; + uint8_t num_bps; + uint8_t num_wps; + +} CPULM32State; + + +CPUState *cpu_lm32_init(const char *cpu_model); +void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf); +int cpu_lm32_exec(CPUState *s); +void cpu_lm32_close(CPUState *s); +void do_interrupt(CPUState *env); +/* you can call this signal handler from your SIGBUS and SIGSEGV + signal handlers to inform the virtual CPU of exceptions. non zero + is returned if the signal was handled by the virtual CPU. */ +int cpu_lm32_signal_handler(int host_signum, void *pinfo, + void *puc); +void lm32_translate_init(void); +void cpu_lm32_set_phys_msb_ignore(CPUState *env, int value); + +#define cpu_list cpu_lm32_list +#define cpu_init cpu_lm32_init +#define cpu_exec cpu_lm32_exec +#define cpu_gen_code cpu_lm32_gen_code +#define cpu_signal_handler cpu_lm32_signal_handler + +#define CPU_SAVE_VERSION 1 + +int cpu_lm32_handle_mmu_fault(CPUState *env, target_ulong address, int rw, + int mmu_idx, int is_softmmu); +#define cpu_handle_mmu_fault cpu_lm32_handle_mmu_fault + +#if defined(CONFIG_USER_ONLY) +static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) +{ + if (newsp) { + env->regs[R_SP] = newsp; + } + env->regs[R_R1] = 0; +} +#endif + +static inline void cpu_set_tls(CPUState *env, target_ulong newtls) +{ +} + +static inline int cpu_interrupts_enabled(CPUState *env) +{ + return env->ie & IE_IE; +} + +#include "cpu-all.h" + +static inline target_ulong cpu_get_pc(CPUState *env) +{ + return env->pc; +} + +static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = 0; +} + +static inline bool cpu_has_work(CPUState *env) +{ + return env->interrupt_request & CPU_INTERRUPT_HARD; +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + +#endif diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/exec.h qemu-kvm-0.15.0+noroms/target-lm32/exec.h --- qemu-kvm-0.14.1+noroms/target-lm32/exec.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,38 @@ +/* + * LatticeMico32 execution defines. + * + * Copyright (c) 2010 Michael Walle + * + * 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 "dyngen-exec.h" + +register struct CPULM32State *env asm(AREG0); + +#include "cpu.h" + +static inline int cpu_halted(CPUState *env) +{ + if (!env->halted) { + return 0; + } + + /* IRQ execeptions wakes us up. */ + if (cpu_has_work(env)) { + env->halted = 0; + return 0; + } + return EXCP_HALTED; +} diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/helper.c qemu-kvm-0.15.0+noroms/target-lm32/helper.c --- qemu-kvm-0.14.1+noroms/target-lm32/helper.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,254 @@ +/* + * LatticeMico32 helper routines. + * + * Copyright (c) 2010 Michael Walle + * + * 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 "config.h" +#include "cpu.h" +#include "host-utils.h" + +int cpu_lm32_handle_mmu_fault(CPUState *env, target_ulong address, int rw, + int mmu_idx, int is_softmmu) +{ + int prot; + + address &= TARGET_PAGE_MASK; + prot = PAGE_BITS; + if (env->flags & LM32_FLAG_IGNORE_MSB) { + tlb_set_page(env, address, address & 0x7fffffff, prot, mmu_idx, + TARGET_PAGE_SIZE); + } else { + tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); + } + + return 0; +} + +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return addr & TARGET_PAGE_MASK; +} + +void do_interrupt(CPUState *env) +{ + qemu_log_mask(CPU_LOG_INT, + "exception at pc=%x type=%x\n", env->pc, env->exception_index); + + switch (env->exception_index) { + case EXCP_INSN_BUS_ERROR: + case EXCP_DATA_BUS_ERROR: + case EXCP_DIVIDE_BY_ZERO: + case EXCP_IRQ: + case EXCP_SYSTEMCALL: + /* non-debug exceptions */ + env->regs[R_EA] = env->pc; + env->ie |= (env->ie & IE_IE) ? IE_EIE : 0; + env->ie &= ~IE_IE; + if (env->dc & DC_RE) { + env->pc = env->deba + (env->exception_index * 32); + } else { + env->pc = env->eba + (env->exception_index * 32); + } + log_cpu_state_mask(CPU_LOG_INT, env, 0); + break; + case EXCP_BREAKPOINT: + case EXCP_WATCHPOINT: + /* debug exceptions */ + env->regs[R_BA] = env->pc; + env->ie |= (env->ie & IE_IE) ? IE_BIE : 0; + env->ie &= ~IE_IE; + env->pc = env->deba + (env->exception_index * 32); + log_cpu_state_mask(CPU_LOG_INT, env, 0); + break; + default: + cpu_abort(env, "unhandled exception type=%d\n", + env->exception_index); + break; + } +} + +typedef struct { + const char *name; + uint32_t revision; + uint8_t num_interrupts; + uint8_t num_breakpoints; + uint8_t num_watchpoints; + uint32_t features; +} LM32Def; + +static const LM32Def lm32_defs[] = { + { + .name = "lm32-basic", + .revision = 3, + .num_interrupts = 32, + .num_breakpoints = 4, + .num_watchpoints = 4, + .features = (LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_CYCLE_COUNT), + }, + { + .name = "lm32-standard", + .revision = 3, + .num_interrupts = 32, + .num_breakpoints = 4, + .num_watchpoints = 4, + .features = (LM32_FEATURE_MULTIPLY + | LM32_FEATURE_DIVIDE + | LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_I_CACHE + | LM32_FEATURE_CYCLE_COUNT), + }, + { + .name = "lm32-full", + .revision = 3, + .num_interrupts = 32, + .num_breakpoints = 4, + .num_watchpoints = 4, + .features = (LM32_FEATURE_MULTIPLY + | LM32_FEATURE_DIVIDE + | LM32_FEATURE_SHIFT + | LM32_FEATURE_SIGN_EXTEND + | LM32_FEATURE_I_CACHE + | LM32_FEATURE_D_CACHE + | LM32_FEATURE_CYCLE_COUNT), + } +}; + +void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf) +{ + int i; + + cpu_fprintf(f, "Available CPUs:\n"); + for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) { + cpu_fprintf(f, " %s\n", lm32_defs[i].name); + } +} + +static const LM32Def *cpu_lm32_find_by_name(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lm32_defs); i++) { + if (strcasecmp(name, lm32_defs[i].name) == 0) { + return &lm32_defs[i]; + } + } + + return NULL; +} + +static uint32_t cfg_by_def(const LM32Def *def) +{ + uint32_t cfg = 0; + + if (def->features & LM32_FEATURE_MULTIPLY) { + cfg |= CFG_M; + } + + if (def->features & LM32_FEATURE_DIVIDE) { + cfg |= CFG_D; + } + + if (def->features & LM32_FEATURE_SHIFT) { + cfg |= CFG_S; + } + + if (def->features & LM32_FEATURE_SIGN_EXTEND) { + cfg |= CFG_X; + } + + if (def->features & LM32_FEATURE_I_CACHE) { + cfg |= CFG_IC; + } + + if (def->features & LM32_FEATURE_D_CACHE) { + cfg |= CFG_DC; + } + + if (def->features & LM32_FEATURE_CYCLE_COUNT) { + cfg |= CFG_CC; + } + + cfg |= (def->num_interrupts << CFG_INT_SHIFT); + cfg |= (def->num_breakpoints << CFG_BP_SHIFT); + cfg |= (def->num_watchpoints << CFG_WP_SHIFT); + cfg |= (def->revision << CFG_REV_SHIFT); + + return cfg; +} + +CPUState *cpu_lm32_init(const char *cpu_model) +{ + CPUState *env; + const LM32Def *def; + static int tcg_initialized; + + def = cpu_lm32_find_by_name(cpu_model); + if (!def) { + return NULL; + } + + env = qemu_mallocz(sizeof(CPUState)); + + env->features = def->features; + env->num_bps = def->num_breakpoints; + env->num_wps = def->num_watchpoints; + env->cfg = cfg_by_def(def); + env->flags = 0; + + cpu_exec_init(env); + cpu_reset(env); + + if (!tcg_initialized) { + tcg_initialized = 1; + lm32_translate_init(); + } + + return env; +} + +/* Some soc ignores the MSB on the address bus. Thus creating a shadow memory + * area. As a general rule, 0x00000000-0x7fffffff is cached, whereas + * 0x80000000-0xffffffff is not cached and used to access IO devices. */ +void cpu_lm32_set_phys_msb_ignore(CPUState *env, int value) +{ + if (value) { + env->flags |= LM32_FLAG_IGNORE_MSB; + } else { + env->flags &= ~LM32_FLAG_IGNORE_MSB; + } +} + +void cpu_reset(CPUState *env) +{ + if (qemu_loglevel_mask(CPU_LOG_RESET)) { + qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + log_cpu_state(env, 0); + } + + tlb_flush(env, 1); + + /* reset cpu state */ + memset(env, 0, offsetof(CPULM32State, breakpoints)); +} + diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/helper.h qemu-kvm-0.15.0+noroms/target-lm32/helper.h --- qemu-kvm-0.14.1+noroms/target-lm32/helper.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,14 @@ +#include "def-helper.h" + +DEF_HELPER_1(raise_exception, void, i32) +DEF_HELPER_0(hlt, void) +DEF_HELPER_1(wcsr_im, void, i32) +DEF_HELPER_1(wcsr_ip, void, i32) +DEF_HELPER_1(wcsr_jtx, void, i32) +DEF_HELPER_1(wcsr_jrx, void, i32) +DEF_HELPER_0(rcsr_im, i32) +DEF_HELPER_0(rcsr_ip, i32) +DEF_HELPER_0(rcsr_jtx, i32) +DEF_HELPER_0(rcsr_jrx, i32) + +#include "def-helper.h" diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/machine.c qemu-kvm-0.15.0+noroms/target-lm32/machine.c --- qemu-kvm-0.14.1+noroms/target-lm32/machine.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/machine.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,33 @@ +#include "hw/hw.h" +#include "hw/boards.h" + +static const VMStateDescription vmstate_cpu = { + .name = "cpu", + .version_id = CPU_SAVE_VERSION, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, CPUState, 32), + VMSTATE_UINT32(pc, CPUState), + VMSTATE_UINT32(ie, CPUState), + VMSTATE_UINT32(icc, CPUState), + VMSTATE_UINT32(dcc, CPUState), + VMSTATE_UINT32(cc, CPUState), + VMSTATE_UINT32(eba, CPUState), + VMSTATE_UINT32(dc, CPUState), + VMSTATE_UINT32(deba, CPUState), + VMSTATE_UINT32_ARRAY(bp, CPUState, 4), + VMSTATE_UINT32_ARRAY(wp, CPUState, 4), + VMSTATE_END_OF_LIST() + } +}; + +void cpu_save(QEMUFile *f, void *opaque) +{ + vmstate_save_state(f, &vmstate_cpu, opaque); +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return vmstate_load_state(f, &vmstate_cpu, opaque, version_id); +} diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/op_helper.c qemu-kvm-0.15.0+noroms/target-lm32/op_helper.c --- qemu-kvm-0.14.1+noroms/target-lm32/op_helper.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,106 @@ +#include +#include "exec.h" +#include "helper.h" +#include "host-utils.h" + +#include "hw/lm32_pic.h" +#include "hw/lm32_juart.h" + +#if !defined(CONFIG_USER_ONLY) +#define MMUSUFFIX _mmu +#define SHIFT 0 +#include "softmmu_template.h" +#define SHIFT 1 +#include "softmmu_template.h" +#define SHIFT 2 +#include "softmmu_template.h" +#define SHIFT 3 +#include "softmmu_template.h" + +void helper_raise_exception(uint32_t index) +{ + env->exception_index = index; + cpu_loop_exit(env); +} + +void helper_hlt(void) +{ + env->halted = 1; + env->exception_index = EXCP_HLT; + cpu_loop_exit(env); +} + +void helper_wcsr_im(uint32_t im) +{ + lm32_pic_set_im(env->pic_state, im); +} + +void helper_wcsr_ip(uint32_t im) +{ + lm32_pic_set_ip(env->pic_state, im); +} + +void helper_wcsr_jtx(uint32_t jtx) +{ + lm32_juart_set_jtx(env->juart_state, jtx); +} + +void helper_wcsr_jrx(uint32_t jrx) +{ + lm32_juart_set_jrx(env->juart_state, jrx); +} + +uint32_t helper_rcsr_im(void) +{ + return lm32_pic_get_im(env->pic_state); +} + +uint32_t helper_rcsr_ip(void) +{ + return lm32_pic_get_ip(env->pic_state); +} + +uint32_t helper_rcsr_jtx(void) +{ + return lm32_juart_get_jtx(env->juart_state); +} + +uint32_t helper_rcsr_jrx(void) +{ + return lm32_juart_get_jrx(env->juart_state); +} + +/* Try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +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 + generated code */ + saved_env = env; + env = cpu_single_env; + + ret = cpu_lm32_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); + if (unlikely(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); + } + } + cpu_loop_exit(env); + } + env = saved_env; +} +#endif + diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/README qemu-kvm-0.15.0+noroms/target-lm32/README --- qemu-kvm-0.14.1+noroms/target-lm32/README 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/README 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,46 @@ +LatticeMico32 target +-------------------- + +General +------- +All opcodes including the JUART CSRs are supported. + + +JTAG UART +--------- +JTAG UART is routed to a serial console device. For the current boards it +is the second one. Ie to enable it in the qemu virtual console window use +the following command line parameters: + -serial vc -serial vc +This will make serial0 (the lm32_uart) and serial1 (the JTAG UART) +available as virtual consoles. + + +Programmatically terminate the emulator +---------------------------------------- +Originally neither the LatticeMico32 nor its peripherals support a +mechanism to shut down the machine. Emulation aware programs can write to a +to a special register within the system control block to shut down the +virtual machine. For more details see hw/lm32_sys.c. The lm32-evr is the +first BSP which instantiate this model. A (32 bit) write to 0xfff0000 +causes a vm shutdown. + + +Special instructions +-------------------- +The translation recognizes one special instruction to halt the cpu: + and r0, r0, r0 +On real hardware this instruction is a nop. It is not used by GCC and +should (hopefully) not be used within hand-crafted assembly. +Insert this instruction in your idle loop to reduce the cpu load on the +host. + + +Ignoring the MSB of the address bus +----------------------------------- +Some SoC ignores the MSB on the address bus. Thus creating a shadow memory +area. As a general rule, 0x00000000-0x7fffffff is cached, whereas +0x80000000-0xffffffff is not cached and used to access IO devices. This +behaviour can be enabled with: + cpu_lm32_set_phys_msb_ignore(env, 1); + diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/TODO qemu-kvm-0.15.0+noroms/target-lm32/TODO --- qemu-kvm-0.14.1+noroms/target-lm32/TODO 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/TODO 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,3 @@ +* disassembler (lm32-dis.c) +* linux-user emulation +* native bp/wp emulation (?) diff -Nru qemu-kvm-0.14.1+noroms/target-lm32/translate.c qemu-kvm-0.15.0+noroms/target-lm32/translate.c --- qemu-kvm-0.14.1+noroms/target-lm32/translate.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-lm32/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,1239 @@ +/* + * LatticeMico32 main translation routines. + * + * Copyright (c) 2010 Michael Walle + * + * 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 +#include + +#include "cpu.h" +#include "disas.h" +#include "helper.h" +#include "tcg-op.h" +#include "qemu-common.h" + +#include "hw/lm32_pic.h" + +#define GEN_HELPER 1 +#include "helper.h" + +#define DISAS_LM32 1 +#if DISAS_LM32 +# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) +#else +# define LOG_DIS(...) do { } while (0) +#endif + +#define EXTRACT_FIELD(src, start, end) \ + (((src) >> start) & ((1 << (end - start + 1)) - 1)) + +#define MEM_INDEX 0 + +static TCGv_ptr cpu_env; +static TCGv cpu_R[32]; +static TCGv cpu_pc; +static TCGv cpu_ie; +static TCGv cpu_icc; +static TCGv cpu_dcc; +static TCGv cpu_cc; +static TCGv cpu_cfg; +static TCGv cpu_eba; +static TCGv cpu_dc; +static TCGv cpu_deba; +static TCGv cpu_bp[4]; +static TCGv cpu_wp[4]; + +#include "gen-icount.h" + +enum { + OP_FMT_RI, + OP_FMT_RR, + OP_FMT_CR, + OP_FMT_I +}; + +/* This is the state at translation time. */ +typedef struct DisasContext { + CPUState *env; + target_ulong pc; + + /* Decoder. */ + int format; + uint32_t ir; + uint8_t opcode; + uint8_t r0, r1, r2, csr; + uint16_t imm5; + uint16_t imm16; + uint32_t imm26; + + unsigned int delayed_branch; + unsigned int tb_flags, synced_flags; /* tb dependent flags. */ + int is_jmp; + + int nr_nops; + struct TranslationBlock *tb; + int singlestep_enabled; +} DisasContext; + +static const char *regnames[] = { + "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/gp", "r27/fp", "r28/sp", "r29/ra", + "r30/ea", "r31/ba", "bp0", "bp1", "bp2", "bp3", "wp0", + "wp1", "wp2", "wp3" +}; + +static inline int zero_extend(unsigned int val, int width) +{ + return val & ((1 << width) - 1); +} + +static inline int sign_extend(unsigned int val, int width) +{ + int sval; + + /* LSL. */ + val <<= 32 - width; + sval = val; + /* ASR. */ + sval >>= 32 - width; + + return sval; +} + +static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) +{ + TCGv_i32 tmp = tcg_const_i32(index); + + gen_helper_raise_exception(tmp); + tcg_temp_free_i32(tmp); +} + +static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +{ + TranslationBlock *tb; + + tb = dc->tb; + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) && + likely(!dc->singlestep_enabled)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_tl(cpu_pc, dest); + tcg_gen_exit_tb((tcg_target_long)tb + n); + } else { + tcg_gen_movi_tl(cpu_pc, dest); + if (dc->singlestep_enabled) { + t_gen_raise_exception(dc, EXCP_DEBUG); + } + tcg_gen_exit_tb(0); + } +} + +static void dec_add(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + if (dc->r0 == R_R0) { + if (dc->r1 == R_R0 && dc->imm16 == 0) { + LOG_DIS("nop\n"); + } else { + LOG_DIS("mvi r%d, %d\n", dc->r1, sign_extend(dc->imm16, 16)); + } + } else { + LOG_DIS("addi r%d, r%d, %d\n", dc->r1, dc->r0, + sign_extend(dc->imm16, 16)); + } + } else { + LOG_DIS("add r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_addi_tl(cpu_R[dc->r1], cpu_R[dc->r0], + sign_extend(dc->imm16, 16)); + } else { + tcg_gen_add_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_and(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("andi r%d, r%d, %d\n", dc->r1, dc->r0, + zero_extend(dc->imm16, 16)); + } else { + LOG_DIS("and r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_andi_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + } else { + if (dc->r0 == 0 && dc->r1 == 0 && dc->r2 == 0) { + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + gen_helper_hlt(); + } else { + tcg_gen_and_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } + } +} + +static void dec_andhi(DisasContext *dc) +{ + LOG_DIS("andhi r%d, r%d, %d\n", dc->r2, dc->r0, dc->imm16); + + tcg_gen_andi_tl(cpu_R[dc->r1], cpu_R[dc->r0], (dc->imm16 << 16)); +} + +static void dec_b(DisasContext *dc) +{ + if (dc->r0 == R_RA) { + LOG_DIS("ret\n"); + } else if (dc->r0 == R_EA) { + LOG_DIS("eret\n"); + } else if (dc->r0 == R_BA) { + LOG_DIS("bret\n"); + } else { + LOG_DIS("b r%d\n", dc->r0); + } + + /* restore IE.IE in case of an eret */ + if (dc->r0 == R_EA) { + TCGv t0 = tcg_temp_new(); + int l1 = gen_new_label(); + tcg_gen_andi_tl(t0, cpu_ie, IE_EIE); + tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_IE); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_EIE, l1); + tcg_gen_andi_tl(cpu_ie, cpu_ie, ~IE_IE); + gen_set_label(l1); + tcg_temp_free(t0); + } else if (dc->r0 == R_BA) { + TCGv t0 = tcg_temp_new(); + int l1 = gen_new_label(); + tcg_gen_andi_tl(t0, cpu_ie, IE_BIE); + tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_IE); + tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_BIE, l1); + tcg_gen_andi_tl(cpu_ie, cpu_ie, ~IE_IE); + gen_set_label(l1); + tcg_temp_free(t0); + } + tcg_gen_mov_tl(cpu_pc, cpu_R[dc->r0]); + + dc->is_jmp = DISAS_JUMP; +} + +static void dec_bi(DisasContext *dc) +{ + LOG_DIS("bi %d\n", sign_extend(dc->imm26 << 2, 26)); + + gen_goto_tb(dc, 0, dc->pc + (sign_extend(dc->imm26 << 2, 26))); + + dc->is_jmp = DISAS_TB_JUMP; +} + +static inline void gen_cond_branch(DisasContext *dc, int cond) +{ + int l1; + + l1 = gen_new_label(); + tcg_gen_brcond_tl(cond, cpu_R[dc->r0], cpu_R[dc->r1], l1); + gen_goto_tb(dc, 0, dc->pc + 4); + gen_set_label(l1); + gen_goto_tb(dc, 1, dc->pc + (sign_extend(dc->imm16 << 2, 16))); + dc->is_jmp = DISAS_TB_JUMP; +} + +static void dec_be(DisasContext *dc) +{ + LOG_DIS("be r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_EQ); +} + +static void dec_bg(DisasContext *dc) +{ + LOG_DIS("bg r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16 * 4)); + + gen_cond_branch(dc, TCG_COND_GT); +} + +static void dec_bge(DisasContext *dc) +{ + LOG_DIS("bge r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_GE); +} + +static void dec_bgeu(DisasContext *dc) +{ + LOG_DIS("bgeu r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_GEU); +} + +static void dec_bgu(DisasContext *dc) +{ + LOG_DIS("bgu r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_GTU); +} + +static void dec_bne(DisasContext *dc) +{ + LOG_DIS("bne r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16) * 4); + + gen_cond_branch(dc, TCG_COND_NE); +} + +static void dec_call(DisasContext *dc) +{ + LOG_DIS("call r%d\n", dc->r0); + + tcg_gen_movi_tl(cpu_R[R_RA], dc->pc + 4); + tcg_gen_mov_tl(cpu_pc, cpu_R[dc->r0]); + + dc->is_jmp = DISAS_JUMP; +} + +static void dec_calli(DisasContext *dc) +{ + LOG_DIS("calli %d\n", sign_extend(dc->imm26, 26) * 4); + + tcg_gen_movi_tl(cpu_R[R_RA], dc->pc + 4); + gen_goto_tb(dc, 0, dc->pc + (sign_extend(dc->imm26 << 2, 26))); + + dc->is_jmp = DISAS_TB_JUMP; +} + +static inline void gen_compare(DisasContext *dc, int cond) +{ + int rX = (dc->format == OP_FMT_RR) ? dc->r2 : dc->r1; + int rY = (dc->format == OP_FMT_RR) ? dc->r0 : dc->r0; + int rZ = (dc->format == OP_FMT_RR) ? dc->r1 : -1; + + if (dc->format == OP_FMT_RI) { + tcg_gen_setcondi_tl(cond, cpu_R[rX], cpu_R[rY], + sign_extend(dc->imm16, 16)); + } else { + tcg_gen_setcond_tl(cond, cpu_R[rX], cpu_R[rY], cpu_R[rZ]); + } +} + +static void dec_cmpe(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpei r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpe r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_EQ); +} + +static void dec_cmpg(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgi r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpg r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GT); +} + +static void dec_cmpge(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgei r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpge r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GE); +} + +static void dec_cmpgeu(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgeui r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpgeu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GEU); +} + +static void dec_cmpgu(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpgui r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpgu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_GTU); +} + +static void dec_cmpne(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("cmpnei r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("cmpne r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + gen_compare(dc, TCG_COND_NE); +} + +static void dec_divu(DisasContext *dc) +{ + int l1; + + LOG_DIS("divu r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + + if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { + cpu_abort(dc->env, "hardware divider is not available\n"); + } + + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_R[dc->r1], 0, l1); + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_DIVIDE_BY_ZERO); + gen_set_label(l1); + tcg_gen_divu_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); +} + +static void dec_lb(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lb r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld8s(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lbu(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lbu r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld8u(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lh(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lh r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld16s(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lhu(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lhu r%d, (r%d+%d)\n", dc->r1, dc->r0, dc->imm16); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld16u(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_lw(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("lw r%d, (r%d+%d)\n", dc->r1, dc->r0, sign_extend(dc->imm16, 16)); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_ld32s(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_modu(DisasContext *dc) +{ + int l1; + + LOG_DIS("modu r%d, r%d, %d\n", dc->r2, dc->r0, dc->r1); + + if (!(dc->env->features & LM32_FEATURE_DIVIDE)) { + cpu_abort(dc->env, "hardware divider is not available\n"); + } + + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, cpu_R[dc->r1], 0, l1); + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_DIVIDE_BY_ZERO); + gen_set_label(l1); + tcg_gen_remu_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); +} + +static void dec_mul(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("muli r%d, r%d, %d\n", dc->r0, dc->r1, + sign_extend(dc->imm16, 16)); + } else { + LOG_DIS("mul r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_MULTIPLY)) { + cpu_abort(dc->env, "hardware multiplier is not available\n"); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_muli_tl(cpu_R[dc->r1], cpu_R[dc->r0], + sign_extend(dc->imm16, 16)); + } else { + tcg_gen_mul_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_nor(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("nori r%d, r%d, %d\n", dc->r0, dc->r1, + zero_extend(dc->imm16, 16)); + } else { + LOG_DIS("nor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + TCGv t0 = tcg_temp_new(); + tcg_gen_movi_tl(t0, zero_extend(dc->imm16, 16)); + tcg_gen_nor_tl(cpu_R[dc->r1], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } else { + tcg_gen_nor_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_or(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("ori r%d, r%d, %d\n", dc->r1, dc->r0, + zero_extend(dc->imm16, 16)); + } else { + if (dc->r1 == R_R0) { + LOG_DIS("mv r%d, r%d\n", dc->r2, dc->r0); + } else { + LOG_DIS("or r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_ori_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + } else { + tcg_gen_or_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_orhi(DisasContext *dc) +{ + if (dc->r0 == R_R0) { + LOG_DIS("mvhi r%d, %d\n", dc->r1, dc->imm16); + } else { + LOG_DIS("orhi r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm16); + } + + tcg_gen_ori_tl(cpu_R[dc->r1], cpu_R[dc->r0], (dc->imm16 << 16)); +} + +static void dec_scall(DisasContext *dc) +{ + if (dc->imm5 == 7) { + LOG_DIS("scall\n"); + } else if (dc->imm5 == 2) { + LOG_DIS("break\n"); + } else { + cpu_abort(dc->env, "invalid opcode\n"); + } + + if (dc->imm5 == 7) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_SYSTEMCALL); + } else { + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_BREAKPOINT); + } +} + +static void dec_rcsr(DisasContext *dc) +{ + LOG_DIS("rcsr r%d, %d\n", dc->r2, dc->csr); + + switch (dc->csr) { + case CSR_IE: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_ie); + break; + case CSR_IM: + gen_helper_rcsr_im(cpu_R[dc->r2]); + break; + case CSR_IP: + gen_helper_rcsr_ip(cpu_R[dc->r2]); + break; + case CSR_CC: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_cc); + break; + case CSR_CFG: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_cfg); + break; + case CSR_EBA: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_eba); + break; + case CSR_DC: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_dc); + break; + case CSR_DEBA: + tcg_gen_mov_tl(cpu_R[dc->r2], cpu_deba); + break; + case CSR_JTX: + gen_helper_rcsr_jtx(cpu_R[dc->r2]); + break; + case CSR_JRX: + gen_helper_rcsr_jrx(cpu_R[dc->r2]); + break; + case CSR_ICC: + case CSR_DCC: + case CSR_BP0: + case CSR_BP1: + case CSR_BP2: + case CSR_BP3: + case CSR_WP0: + case CSR_WP1: + case CSR_WP2: + case CSR_WP3: + cpu_abort(dc->env, "invalid read access csr=%x\n", dc->csr); + break; + default: + cpu_abort(dc->env, "read_csr: unknown csr=%x\n", dc->csr); + break; + } +} + +static void dec_sb(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("sb (r%d+%d), r%d\n", dc->r0, dc->imm16, dc->r1); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_st8(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_sextb(DisasContext *dc) +{ + LOG_DIS("sextb r%d, r%d\n", dc->r2, dc->r0); + + if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { + cpu_abort(dc->env, "hardware sign extender is not available\n"); + } + + tcg_gen_ext8s_tl(cpu_R[dc->r2], cpu_R[dc->r0]); +} + +static void dec_sexth(DisasContext *dc) +{ + LOG_DIS("sexth r%d, r%d\n", dc->r2, dc->r0); + + if (!(dc->env->features & LM32_FEATURE_SIGN_EXTEND)) { + cpu_abort(dc->env, "hardware sign extender is not available\n"); + } + + tcg_gen_ext16s_tl(cpu_R[dc->r2], cpu_R[dc->r0]); +} + +static void dec_sh(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("sh (r%d+%d), r%d\n", dc->r0, dc->imm16, dc->r1); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_st16(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_sl(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("sli r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm5); + } else { + LOG_DIS("sl r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + cpu_abort(dc->env, "hardware shifter is not available\n"); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_shli_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); + } else { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + tcg_gen_shl_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } +} + +static void dec_sr(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("sri r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm5); + } else { + LOG_DIS("sr r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + if (dc->format == OP_FMT_RI) { + /* TODO: check r1 == 1 during runtime */ + } else { + if (dc->imm5 != 1) { + cpu_abort(dc->env, "hardware shifter is not available\n"); + } + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_sari_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); + } else { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + tcg_gen_sar_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } +} + +static void dec_sru(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("srui r%d, r%d, %d\n", dc->r1, dc->r0, dc->imm5); + } else { + LOG_DIS("sru r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (!(dc->env->features & LM32_FEATURE_SHIFT)) { + if (dc->format == OP_FMT_RI) { + /* TODO: check r1 == 1 during runtime */ + } else { + if (dc->imm5 != 1) { + cpu_abort(dc->env, "hardware shifter is not available\n"); + } + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_shri_tl(cpu_R[dc->r1], cpu_R[dc->r0], dc->imm5); + } else { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_R[dc->r1], 0x1f); + tcg_gen_shr_tl(cpu_R[dc->r2], cpu_R[dc->r0], t0); + tcg_temp_free(t0); + } +} + +static void dec_sub(DisasContext *dc) +{ + LOG_DIS("sub r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + + tcg_gen_sub_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); +} + +static void dec_sw(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("sw (r%d+%d), r%d\n", dc->r0, sign_extend(dc->imm16, 16), dc->r1); + + t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[dc->r0], sign_extend(dc->imm16, 16)); + tcg_gen_qemu_st32(cpu_R[dc->r1], t0, MEM_INDEX); + tcg_temp_free(t0); +} + +static void dec_user(DisasContext *dc) +{ + LOG_DIS("user"); + + cpu_abort(dc->env, "user insn undefined\n"); +} + +static void dec_wcsr(DisasContext *dc) +{ + int no; + + LOG_DIS("wcsr r%d, %d\n", dc->r1, dc->csr); + + switch (dc->csr) { + case CSR_IE: + tcg_gen_mov_tl(cpu_ie, cpu_R[dc->r1]); + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + dc->is_jmp = DISAS_UPDATE; + break; + case CSR_IM: + /* mark as an io operation because it could cause an interrupt */ + if (use_icount) { + gen_io_start(); + } + gen_helper_wcsr_im(cpu_R[dc->r1]); + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + if (use_icount) { + gen_io_end(); + } + dc->is_jmp = DISAS_UPDATE; + break; + case CSR_IP: + /* mark as an io operation because it could cause an interrupt */ + if (use_icount) { + gen_io_start(); + } + gen_helper_wcsr_ip(cpu_R[dc->r1]); + tcg_gen_movi_tl(cpu_pc, dc->pc + 4); + if (use_icount) { + gen_io_end(); + } + dc->is_jmp = DISAS_UPDATE; + break; + case CSR_ICC: + /* TODO */ + break; + case CSR_DCC: + /* TODO */ + break; + case CSR_EBA: + tcg_gen_mov_tl(cpu_eba, cpu_R[dc->r1]); + break; + case CSR_DEBA: + tcg_gen_mov_tl(cpu_deba, cpu_R[dc->r1]); + break; + case CSR_JTX: + gen_helper_wcsr_jtx(cpu_R[dc->r1]); + break; + case CSR_JRX: + gen_helper_wcsr_jrx(cpu_R[dc->r1]); + break; + case CSR_DC: + tcg_gen_mov_tl(cpu_dc, cpu_R[dc->r1]); + break; + case CSR_BP0: + case CSR_BP1: + case CSR_BP2: + case CSR_BP3: + no = dc->csr - CSR_BP0; + if (dc->env->num_bps <= no) { + cpu_abort(dc->env, "breakpoint #%i is not available\n", no); + } + tcg_gen_mov_tl(cpu_bp[no], cpu_R[dc->r1]); + break; + case CSR_WP0: + case CSR_WP1: + case CSR_WP2: + case CSR_WP3: + no = dc->csr - CSR_WP0; + if (dc->env->num_wps <= no) { + cpu_abort(dc->env, "watchpoint #%i is not available\n", no); + } + tcg_gen_mov_tl(cpu_wp[no], cpu_R[dc->r1]); + break; + case CSR_CC: + case CSR_CFG: + cpu_abort(dc->env, "invalid write access csr=%x\n", dc->csr); + break; + default: + cpu_abort(dc->env, "write_csr unknown csr=%x\n", dc->csr); + break; + } +} + +static void dec_xnor(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("xnori r%d, r%d, %d\n", dc->r0, dc->r1, + zero_extend(dc->imm16, 16)); + } else { + if (dc->r1 == R_R0) { + LOG_DIS("not r%d, r%d\n", dc->r2, dc->r0); + } else { + LOG_DIS("xnor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_xori_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + tcg_gen_not_tl(cpu_R[dc->r1], cpu_R[dc->r1]); + } else { + tcg_gen_eqv_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_xor(DisasContext *dc) +{ + if (dc->format == OP_FMT_RI) { + LOG_DIS("xori r%d, r%d, %d\n", dc->r0, dc->r1, + zero_extend(dc->imm16, 16)); + } else { + LOG_DIS("xor r%d, r%d, r%d\n", dc->r2, dc->r0, dc->r1); + } + + if (dc->format == OP_FMT_RI) { + tcg_gen_xori_tl(cpu_R[dc->r1], cpu_R[dc->r0], + zero_extend(dc->imm16, 16)); + } else { + tcg_gen_xor_tl(cpu_R[dc->r2], cpu_R[dc->r0], cpu_R[dc->r1]); + } +} + +static void dec_ill(DisasContext *dc) +{ + cpu_abort(dc->env, "unknown opcode 0x%02x\n", dc->opcode); +} + +typedef void (*DecoderInfo)(DisasContext *dc); +static const DecoderInfo decinfo[] = { + dec_sru, dec_nor, dec_mul, dec_sh, dec_lb, dec_sr, dec_xor, dec_lh, + dec_and, dec_xnor, dec_lw, dec_lhu, dec_sb, dec_add, dec_or, dec_sl, + dec_lbu, dec_be, dec_bg, dec_bge, dec_bgeu, dec_bgu, dec_sw, dec_bne, + dec_andhi, dec_cmpe, dec_cmpg, dec_cmpge, dec_cmpgeu, dec_cmpgu, dec_orhi, + dec_cmpne, + dec_sru, dec_nor, dec_mul, dec_divu, dec_rcsr, dec_sr, dec_xor, dec_ill, + dec_and, dec_xnor, dec_ill, dec_scall, dec_sextb, dec_add, dec_or, dec_sl, + dec_b, dec_modu, dec_sub, dec_user, dec_wcsr, dec_ill, dec_call, dec_sexth, + dec_bi, dec_cmpe, dec_cmpg, dec_cmpge, dec_cmpgeu, dec_cmpgu, dec_calli, + dec_cmpne +}; + +static inline void decode(DisasContext *dc) +{ + uint32_t ir; + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(dc->pc); + } + + dc->ir = ir = ldl_code(dc->pc); + LOG_DIS("%8.8x\t", dc->ir); + + /* try guessing 'empty' instruction memory, although it may be a valid + * instruction sequence (eg. srui r0, r0, 0) */ + if (dc->ir) { + dc->nr_nops = 0; + } else { + LOG_DIS("nr_nops=%d\t", dc->nr_nops); + dc->nr_nops++; + if (dc->nr_nops > 4) { + cpu_abort(dc->env, "fetching nop sequence\n"); + } + } + + dc->opcode = EXTRACT_FIELD(ir, 26, 31); + + dc->imm5 = EXTRACT_FIELD(ir, 0, 4); + dc->imm16 = EXTRACT_FIELD(ir, 0, 15); + dc->imm26 = EXTRACT_FIELD(ir, 0, 25); + + dc->csr = EXTRACT_FIELD(ir, 21, 25); + dc->r0 = EXTRACT_FIELD(ir, 21, 25); + dc->r1 = EXTRACT_FIELD(ir, 16, 20); + dc->r2 = EXTRACT_FIELD(ir, 11, 15); + + /* bit 31 seems to indicate insn type. */ + if (ir & (1 << 31)) { + dc->format = OP_FMT_RR; + } else { + dc->format = OP_FMT_RI; + } + + assert(ARRAY_SIZE(decinfo) == 64); + assert(dc->opcode < 64); + + decinfo[dc->opcode](dc); +} + +static void check_breakpoint(CPUState *env, DisasContext *dc) +{ + CPUBreakpoint *bp; + + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc->pc) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + t_gen_raise_exception(dc, EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + } + } + } +} + +/* generate intermediate code for basic block 'tb'. */ +static void gen_intermediate_code_internal(CPUState *env, + TranslationBlock *tb, int search_pc) +{ + struct DisasContext ctx, *dc = &ctx; + uint16_t *gen_opc_end; + uint32_t pc_start; + int j, lj; + uint32_t next_page_start; + int num_insns; + int max_insns; + + qemu_log_try_set_file(stderr); + + pc_start = tb->pc; + dc->env = env; + dc->tb = tb; + + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + dc->is_jmp = DISAS_NEXT; + dc->pc = pc_start; + dc->singlestep_enabled = env->singlestep_enabled; + dc->nr_nops = 0; + + if (pc_start & 3) { + cpu_abort(env, "LM32: unaligned PC=%x\n", pc_start); + } + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("-----------------------------------------\n"); + log_cpu_state(env, 0); + } + + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + lj = -1; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + + gen_icount_start(); + do { + check_breakpoint(env, dc); + + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) { + gen_opc_instr_start[lj++] = 0; + } + } + gen_opc_pc[lj] = dc->pc; + gen_opc_instr_start[lj] = 1; + gen_opc_icount[lj] = num_insns; + } + + /* Pretty disas. */ + LOG_DIS("%8.8x:\t", dc->pc); + + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + decode(dc); + dc->pc += 4; + num_insns++; + + } while (!dc->is_jmp + && gen_opc_ptr < gen_opc_end + && !env->singlestep_enabled + && !singlestep + && (dc->pc < next_page_start) + && num_insns < max_insns); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + if (unlikely(env->singlestep_enabled)) { + if (dc->is_jmp == DISAS_NEXT) { + tcg_gen_movi_tl(cpu_pc, dc->pc); + } + t_gen_raise_exception(dc, EXCP_DEBUG); + } else { + switch (dc->is_jmp) { + case DISAS_NEXT: + gen_goto_tb(dc, 1, dc->pc); + break; + default: + case DISAS_JUMP: + case DISAS_UPDATE: + /* indicate that the hash table must be used + to find the next TB */ + tcg_gen_exit_tb(0); + break; + case DISAS_TB_JUMP: + /* nothing more to generate */ + break; + } + } + + gen_icount_end(tb, num_insns); + *gen_opc_ptr = INDEX_op_end; + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) { + gen_opc_instr_start[lj++] = 0; + } + } else { + tb->size = dc->pc - pc_start; + tb->icount = num_insns; + } + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("\n"); + log_target_disas(pc_start, dc->pc - pc_start, 0); + qemu_log("\nisize=%d osize=%td\n", + dc->pc - pc_start, gen_opc_ptr - gen_opc_buf); + } +#endif +} + +void gen_intermediate_code(CPUState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 0); +} + +void gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 1); +} + +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + int i; + + if (!env || !f) { + return; + } + + cpu_fprintf(f, "IN: PC=%x %s\n", + env->pc, lookup_symbol(env->pc)); + + cpu_fprintf(f, "ie=%8.8x (IE=%x EIE=%x BIE=%x) im=%8.8x ip=%8.8x\n", + env->ie, + (env->ie & IE_IE) ? 1 : 0, + (env->ie & IE_EIE) ? 1 : 0, + (env->ie & IE_BIE) ? 1 : 0, + lm32_pic_get_im(env->pic_state), + lm32_pic_get_ip(env->pic_state)); + cpu_fprintf(f, "eba=%8.8x deba=%8.8x\n", + env->eba, + env->deba); + + for (i = 0; i < 32; i++) { + cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); + if ((i + 1) % 4 == 0) { + cpu_fprintf(f, "\n"); + } + } + cpu_fprintf(f, "\n\n"); +} + +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) +{ + env->pc = gen_opc_pc[pc_pos]; +} + +void lm32_translate_init(void) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { + cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, regs[i]), + regnames[i]); + } + + for (i = 0; i < ARRAY_SIZE(cpu_bp); i++) { + cpu_bp[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, bp[i]), + regnames[32+i]); + } + + for (i = 0; i < ARRAY_SIZE(cpu_wp); i++) { + cpu_wp[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, wp[i]), + regnames[36+i]); + } + + cpu_pc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, pc), + "pc"); + cpu_ie = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, ie), + "ie"); + cpu_icc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, icc), + "icc"); + cpu_dcc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, dcc), + "dcc"); + cpu_cc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc), + "cc"); + cpu_cfg = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cfg), + "cfg"); + cpu_eba = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, eba), + "eba"); + cpu_dc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, dc), + "dc"); + cpu_deba = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, deba), + "deba"); +} + diff -Nru qemu-kvm-0.14.1+noroms/target-m68k/cpu.h qemu-kvm-0.15.0+noroms/target-m68k/cpu.h --- qemu-kvm-0.14.1+noroms/target-m68k/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-m68k/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -119,7 +119,8 @@ CPUM68KState *cpu_m68k_init(const char *cpu_model); int cpu_m68k_exec(CPUM68KState *s); void cpu_m68k_close(CPUM68KState *s); -void do_interrupt(int is_hw); +void do_interrupt(CPUState *env1); +void do_interrupt_m68k_hardirq(CPUState *env1); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ @@ -254,4 +255,16 @@ | ((env->macsr >> 4) & 0xf); /* Bits 0-3 */ } +static inline bool cpu_has_work(CPUState *env) +{ + return env->interrupt_request & CPU_INTERRUPT_HARD; +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + #endif diff -Nru qemu-kvm-0.14.1+noroms/target-m68k/exec.h qemu-kvm-0.15.0+noroms/target-m68k/exec.h --- qemu-kvm-0.14.1+noroms/target-m68k/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-m68k/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -22,29 +22,7 @@ register struct CPUM68KState *env asm(AREG0); #include "cpu.h" -#include "exec-all.h" #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif - -static inline int cpu_has_work(CPUState *env) -{ - return (env->interrupt_request & (CPU_INTERRUPT_HARD)); -} - -static inline int cpu_halted(CPUState *env) { - if (!env->halted) - return 0; - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; -} - -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; -} - diff -Nru qemu-kvm-0.14.1+noroms/target-m68k/helper.c qemu-kvm-0.15.0+noroms/target-m68k/helper.c --- qemu-kvm-0.14.1+noroms/target-m68k/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-m68k/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,7 +23,6 @@ #include "config.h" #include "cpu.h" -#include "exec-all.h" #include "qemu-common.h" #include "gdbstub.h" @@ -714,7 +713,7 @@ if (env->macsr & MACSR_V) { env->macsr |= MACSR_PAV0 << acc; if (env->macsr & MACSR_OMC) { - /* The result is saturated to 32 bits, despite overflow occuring + /* The result is saturated to 32 bits, despite overflow occurring at 48 bits. Seems weird, but that's what the hardware docs say. */ result = (result >> 63) ^ 0x7fffffff; diff -Nru qemu-kvm-0.14.1+noroms/target-m68k/op_helper.c qemu-kvm-0.15.0+noroms/target-m68k/op_helper.c --- qemu-kvm-0.14.1+noroms/target-m68k/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-m68k/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -21,9 +21,13 @@ #if defined(CONFIG_USER_ONLY) -void do_interrupt(int is_hw) +void do_interrupt(CPUState *env1) +{ + env1->exception_index = -1; +} + +void do_interrupt_m68k_hardirq(CPUState *env1) { - env->exception_index = -1; } #else @@ -68,10 +72,10 @@ 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); + cpu_restore_state(tb, env, pc); } } - cpu_loop_exit(); + cpu_loop_exit(env); } env = saved_env; } @@ -90,7 +94,7 @@ env->aregs[7] = sp + 8; } -void do_interrupt(int is_hw) +static void do_interrupt_all(int is_hw) { uint32_t sp; uint32_t fmt; @@ -118,7 +122,7 @@ } env->halted = 1; env->exception_index = EXCP_HLT; - cpu_loop_exit(); + cpu_loop_exit(env); return; } if (env->exception_index >= EXCP_TRAP0 @@ -155,12 +159,31 @@ env->pc = ldl_kernel(env->vbr + vector); } +void do_interrupt(CPUState *env1) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + do_interrupt_all(0); + env = saved_env; +} + +void do_interrupt_m68k_hardirq(CPUState *env1) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + do_interrupt_all(1); + env = saved_env; +} #endif static void raise_exception(int tt) { env->exception_index = tt; - cpu_loop_exit(); + cpu_loop_exit(env); } void HELPER(raise_exception)(uint32_t tt) diff -Nru qemu-kvm-0.14.1+noroms/target-m68k/translate.c qemu-kvm-0.15.0+noroms/target-m68k/translate.c --- qemu-kvm-0.14.1+noroms/target-m68k/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-m68k/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,7 +25,6 @@ #include "config.h" #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "qemu-log.h" @@ -171,9 +170,6 @@ static void disas_##name (DisasContext *s, uint16_t insn) #endif -/* FIXME: Remove this. */ -#define gen_im32(val) tcg_const_i32(val) - /* Generate a load from the specified address. Narrow values are sign extended to full register width. */ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign) @@ -339,7 +335,7 @@ if ((ext & 0x80) == 0) { /* base not suppressed */ if (IS_NULL_QREG(base)) { - base = gen_im32(offset + bd); + base = tcg_const_i32(offset + bd); bd = 0; } if (!IS_NULL_QREG(add)) { @@ -355,7 +351,7 @@ add = tmp; } } else { - add = gen_im32(bd); + add = tcg_const_i32(bd); } if ((ext & 3) != 0) { /* memory indirect */ @@ -536,15 +532,15 @@ case 0: /* Absolute short. */ offset = ldsw_code(s->pc); s->pc += 2; - return gen_im32(offset); + return tcg_const_i32(offset); case 1: /* Absolute long. */ offset = read_im32(s); - return gen_im32(offset); + return tcg_const_i32(offset); case 2: /* pc displacement */ offset = s->pc; offset += ldsw_code(s->pc); s->pc += 2; - return gen_im32(offset); + return tcg_const_i32(offset); case 3: /* pc index+displacement. */ return gen_lea_indexed(s, opsize, NULL_QREG); case 4: /* Immediate. */ @@ -861,7 +857,7 @@ (s->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(QREG_PC, dest); - tcg_gen_exit_tb((long)tb + n); + tcg_gen_exit_tb((tcg_target_long)tb + n); } else { gen_jmp_im(s, dest); tcg_gen_exit_tb(0); @@ -1209,16 +1205,16 @@ break; case 2: /* subi */ tcg_gen_mov_i32(dest, src1); - gen_helper_xflag_lt(QREG_CC_X, dest, gen_im32(im)); + gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im)); tcg_gen_subi_i32(dest, dest, im); - gen_update_cc_add(dest, gen_im32(im)); + gen_update_cc_add(dest, tcg_const_i32(im)); s->cc_op = CC_OP_SUB; break; case 3: /* addi */ tcg_gen_mov_i32(dest, src1); tcg_gen_addi_i32(dest, dest, im); - gen_update_cc_add(dest, gen_im32(im)); - gen_helper_xflag_lt(QREG_CC_X, dest, gen_im32(im)); + gen_update_cc_add(dest, tcg_const_i32(im)); + gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im)); s->cc_op = CC_OP_ADD; break; case 5: /* eori */ @@ -1228,7 +1224,7 @@ case 6: /* cmpi */ tcg_gen_mov_i32(dest, src1); tcg_gen_subi_i32(dest, dest, im); - gen_update_cc_add(dest, gen_im32(im)); + gen_update_cc_add(dest, tcg_const_i32(im)); s->cc_op = CC_OP_SUB; break; default: @@ -1324,8 +1320,8 @@ default: abort(); } - DEST_EA(insn, opsize, gen_im32(0), NULL); - gen_logic_cc(s, gen_im32(0)); + DEST_EA(insn, opsize, tcg_const_i32(0), NULL); + gen_logic_cc(s, tcg_const_i32(0)); } static TCGv gen_get_ccr(DisasContext *s) @@ -1589,7 +1585,7 @@ } if ((insn & 0x40) == 0) { /* jsr */ - gen_push(s, gen_im32(s->pc)); + gen_push(s, tcg_const_i32(s->pc)); } gen_jmp(s, tmp); } @@ -1617,7 +1613,7 @@ tcg_gen_addi_i32(dest, dest, val); } } else { - src2 = gen_im32(val); + src2 = tcg_const_i32(val); if (insn & 0x0100) { gen_helper_xflag_lt(QREG_CC_X, dest, src2); tcg_gen_subi_i32(dest, dest, val); @@ -1666,7 +1662,7 @@ } if (op == 1) { /* bsr */ - gen_push(s, gen_im32(s->pc)); + gen_push(s, tcg_const_i32(s->pc)); } gen_flush_cc_op(s); if (op > 1) { @@ -1757,7 +1753,7 @@ val = (insn >> 9) & 7; if (val == 0) val = -1; - src = gen_im32(val); + src = tcg_const_i32(val); gen_logic_cc(s, src); DEST_EA(insn, OS_LONG, src, NULL); } @@ -1883,7 +1879,7 @@ tmp = (insn >> 9) & 7; if (tmp == 0) tmp = 8; - shift = gen_im32(tmp); + shift = tcg_const_i32(tmp); /* No need to flush flags becuse we know we will set C flag. */ if (insn & 0x100) { gen_helper_shl_cc(reg, cpu_env, reg, shift); @@ -2191,7 +2187,7 @@ switch ((ext >> 10) & 7) { case 4: /* FPCR */ /* Not implemented. Always return zero. */ - tmp32 = gen_im32(0); + tmp32 = tcg_const_i32(0); break; case 1: /* FPIAR */ case 2: /* FPSR */ @@ -2592,7 +2588,7 @@ /* Skip the accumulate if the value is already saturated. */ l1 = gen_new_label(); tmp = tcg_temp_new(); - gen_op_and32(tmp, QREG_MACSR, gen_im32(MACSR_PAV0 << acc)); + gen_op_and32(tmp, QREG_MACSR, tcg_const_i32(MACSR_PAV0 << acc)); gen_op_jmp_nz32(tmp, l1); } #endif @@ -2626,7 +2622,7 @@ /* Skip the accumulate if the value is already saturated. */ l1 = gen_new_label(); tmp = tcg_temp_new(); - gen_op_and32(tmp, QREG_MACSR, gen_im32(MACSR_PAV0 << acc)); + gen_op_and32(tmp, QREG_MACSR, tcg_const_i32(MACSR_PAV0 << acc)); gen_op_jmp_nz32(tmp, l1); } #endif @@ -3113,8 +3109,7 @@ cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result); } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->pc = gen_opc_pc[pc_pos]; } diff -Nru qemu-kvm-0.14.1+noroms/target-microblaze/cpu.h qemu-kvm-0.15.0+noroms/target-microblaze/cpu.h --- qemu-kvm-0.14.1+noroms/target-microblaze/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -41,6 +41,9 @@ #define EXCP_HW_BREAK 5 #define EXCP_HW_EXCP 6 +/* MicroBlaze-specific interrupt pending bits. */ +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 + /* Register aliases. R0 - R15 */ #define R_SP 1 #define SR_PC 0 @@ -79,6 +82,8 @@ #define ESR_DIZ (1<<11) /* Zone Protection */ #define ESR_S (1<<10) /* Store instruction */ +#define ESR_ESS_FSL_OFFSET 5 + #define ESR_EC_FSL 0 #define ESR_EC_UNALIGNED_DATA 1 #define ESR_EC_ILLEGAL_OP 2 @@ -91,6 +96,7 @@ #define ESR_EC_INSN_STORAGE 9 #define ESR_EC_DATA_TLB 10 #define ESR_EC_INSN_TLB 11 +#define ESR_EC_MASK 31 /* Floating Point Status Register (FSR) Bits */ #define FSR_IO (1<<4) /* Invalid operation */ @@ -110,6 +116,9 @@ #define PVR0_USE_ICACHE_MASK 0x02000000 #define PVR0_USE_DCACHE_MASK 0x01000000 #define PVR0_USE_MMU 0x00800000 /* new */ +#define PVR0_USE_BTC 0x00400000 +#define PVR0_ENDI 0x00200000 +#define PVR0_FAULT 0x00100000 #define PVR0_VERSION_MASK 0x0000FF00 #define PVR0_USER1_MASK 0x000000FF @@ -169,6 +178,7 @@ #define PVR5_DCACHE_ALLOW_WR_MASK 0x01000000 #define PVR5_DCACHE_LINE_LEN_MASK 0x00E00000 #define PVR5_DCACHE_BYTE_SIZE_MASK 0x001F0000 +#define PVR5_DCACHE_WRITEBACK_MASK 0x00004000 /* ICache base address PVR mask */ #define PVR6_ICACHE_BASEADDR_MASK 0xFFFFFFFF @@ -190,7 +200,7 @@ #define PVR11_MMU_ITLB_SIZE 0x38000000 #define PVR11_MMU_DTLB_SIZE 0x07000000 #define PVR11_MMU_TLB_ACCESS 0x00C00000 -#define PVR11_MMU_ZONES 0x003C0000 +#define PVR11_MMU_ZONES 0x003E0000 /* MSR Reset value PVR mask */ #define PVR11_MSR_RESET_VALUE_MASK 0x000007FF @@ -207,6 +217,13 @@ #define CC_EQ 0 #define NB_MMU_MODES 3 + +#define STREAM_EXCEPTION (1 << 0) +#define STREAM_ATOMIC (1 << 1) +#define STREAM_TEST (1 << 2) +#define STREAM_CONTROL (1 << 3) +#define STREAM_NONBLOCK (1 << 4) + typedef struct CPUMBState { uint32_t debug; uint32_t btaken; @@ -330,7 +347,20 @@ } #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); +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int is_asi, int size); #endif + +static inline bool cpu_has_work(CPUState *env) +{ + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->sregs[SR_PC] = tb->pc; +} + #endif diff -Nru qemu-kvm-0.14.1+noroms/target-microblaze/exec.h qemu-kvm-0.15.0+noroms/target-microblaze/exec.h --- qemu-kvm-0.14.1+noroms/target-microblaze/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -21,32 +21,7 @@ register struct CPUMBState *env asm(AREG0); #include "cpu.h" -#include "exec-all.h" #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif - -static inline int cpu_has_work(CPUState *env) -{ - return (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)); -} - -static inline int cpu_halted(CPUState *env) { - if (!env->halted) - return 0; - - /* IRQ, NMI and GURU execeptions wakes us up. */ - if (env->interrupt_request - & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) { - env->halted = 0; - return 0; - } - 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.14.1+noroms/target-microblaze/helper.c qemu-kvm-0.15.0+noroms/target-microblaze/helper.c --- qemu-kvm-0.14.1+noroms/target-microblaze/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -23,7 +23,6 @@ #include "config.h" #include "cpu.h" -#include "exec-all.h" #include "host-utils.h" #define D(x) @@ -117,7 +116,7 @@ { uint32_t t; - /* IMM flag cannot propagate accross a branch and into the dslot. */ + /* IMM flag cannot propagate across a branch and into the dslot. */ assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); /* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */ diff -Nru qemu-kvm-0.14.1+noroms/target-microblaze/helper.h qemu-kvm-0.15.0+noroms/target-microblaze/helper.h --- qemu-kvm-0.14.1+noroms/target-microblaze/helper.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -33,4 +33,7 @@ DEF_HELPER_4(memalign, void, i32, i32, i32, i32) +DEF_HELPER_2(get, i32, i32, i32) +DEF_HELPER_3(put, void, i32, i32, i32) + #include "def-helper.h" diff -Nru qemu-kvm-0.14.1+noroms/target-microblaze/microblaze-decode.h qemu-kvm-0.15.0+noroms/target-microblaze/microblaze-decode.h --- qemu-kvm-0.14.1+noroms/target-microblaze/microblaze-decode.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/microblaze-decode.h 2011-08-09 07:40:29.000000000 -0500 @@ -50,3 +50,6 @@ #define DEC_BR {B8(00100110), B8(00110111)} #define DEC_BCC {B8(00100111), B8(00110111)} #define DEC_RTS {B8(00101101), B8(00111111)} + +#define DEC_STREAM {B8(00010011), B8(00110111)} + diff -Nru qemu-kvm-0.14.1+noroms/target-microblaze/mmu.c qemu-kvm-0.15.0+noroms/target-microblaze/mmu.c --- qemu-kvm-0.14.1+noroms/target-microblaze/mmu.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/mmu.c 2011-08-09 07:40:29.000000000 -0500 @@ -22,7 +22,6 @@ #include "config.h" #include "cpu.h" -#include "exec-all.h" #define D(x) diff -Nru qemu-kvm-0.14.1+noroms/target-microblaze/op_helper.c qemu-kvm-0.15.0+noroms/target-microblaze/op_helper.c --- qemu-kvm-0.14.1+noroms/target-microblaze/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -60,19 +60,54 @@ 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); + cpu_restore_state(tb, env, pc); } } - cpu_loop_exit(); + cpu_loop_exit(env); } env = saved_env; } #endif +void helper_put(uint32_t id, uint32_t ctrl, uint32_t data) +{ + int test = ctrl & STREAM_TEST; + int atomic = ctrl & STREAM_ATOMIC; + int control = ctrl & STREAM_CONTROL; + int nonblock = ctrl & STREAM_NONBLOCK; + int exception = ctrl & STREAM_EXCEPTION; + + qemu_log("Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n", + id, data, + test ? "t" : "", + nonblock ? "n" : "", + exception ? "e" : "", + control ? "c" : "", + atomic ? "a" : ""); +} + +uint32_t helper_get(uint32_t id, uint32_t ctrl) +{ + int test = ctrl & STREAM_TEST; + int atomic = ctrl & STREAM_ATOMIC; + int control = ctrl & STREAM_CONTROL; + int nonblock = ctrl & STREAM_NONBLOCK; + int exception = ctrl & STREAM_EXCEPTION; + + qemu_log("Unhandled stream get from stream-id=%d %s%s%s%s%s\n", + id, + test ? "t" : "", + nonblock ? "n" : "", + exception ? "e" : "", + control ? "c" : "", + atomic ? "a" : ""); + return 0xdead0000 | id; +} + void helper_raise_exception(uint32_t index) { env->exception_index = index; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_debug(void) @@ -303,7 +338,7 @@ set_float_exception_flags(0, &env->fp_status); fa.l = a; fb.l = b; - r = float32_eq(fa.f, fb.f, &env->fp_status); + r = float32_eq_quiet(fa.f, fb.f, &env->fp_status); flags = get_float_exception_flags(&env->fp_status); update_fpu_flags(flags & float_flag_invalid); @@ -349,7 +384,7 @@ fa.l = a; fb.l = b; set_float_exception_flags(0, &env->fp_status); - r = !float32_eq(fa.f, fb.f, &env->fp_status); + r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status); flags = get_float_exception_flags(&env->fp_status); update_fpu_flags(flags & float_flag_invalid); @@ -453,20 +488,14 @@ mmu_write(env, rn, v); } -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int is_asi, int size) +void cpu_unassigned_access(CPUState *env1, 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; - env = cpu_single_env; + env = env1; + qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n", addr, is_write, is_exec); if (!(env->sregs[SR_MSR] & MSR_EE)) { diff -Nru qemu-kvm-0.14.1+noroms/target-microblaze/translate.c qemu-kvm-0.15.0+noroms/target-microblaze/translate.c --- qemu-kvm-0.14.1+noroms/target-microblaze/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-microblaze/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,7 +25,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "helper.h" @@ -146,7 +145,7 @@ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_SR[SR_PC], dest); - tcg_gen_exit_tb((long)tb + n); + tcg_gen_exit_tb((tcg_target_long)tb + n); } else { tcg_gen_movi_tl(cpu_SR[SR_PC], dest); tcg_gen_exit_tb(0); @@ -923,7 +922,7 @@ /* * When doing reverse accesses we need to do two things. * - * 1. Reverse the address wrt endianess. + * 1. Reverse the address wrt endianness. * 2. Byteswap the data lanes on the way back into the CPU core. */ if (rev && size != 4) { @@ -1476,6 +1475,42 @@ dc->abort_at_next_insn = 1; } +/* Insns connected to FSL or AXI stream attached devices. */ +static void dec_stream(DisasContext *dc) +{ + int mem_index = cpu_mmu_index(dc->env); + TCGv_i32 t_id, t_ctrl; + int ctrl; + + LOG_DIS("%s%s imm=%x\n", dc->rd ? "get" : "put", + dc->type_b ? "" : "d", dc->imm); + + 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_id = tcg_temp_new(); + if (dc->type_b) { + tcg_gen_movi_tl(t_id, dc->imm & 0xf); + ctrl = dc->imm >> 10; + } else { + tcg_gen_andi_tl(t_id, cpu_R[dc->rb], 0xf); + ctrl = dc->imm >> 5; + } + + t_ctrl = tcg_const_tl(ctrl); + + if (dc->rd == 0) { + gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]); + } else { + gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl); + } + tcg_temp_free(t_id); + tcg_temp_free(t_ctrl); +} + static struct decoder_info { struct { uint32_t bits; @@ -1500,6 +1535,7 @@ {DEC_MUL, dec_mul}, {DEC_DIV, dec_div}, {DEC_MSR, dec_msr}, + {DEC_STREAM, dec_stream}, {{0, 0}, dec_null} }; @@ -1715,9 +1751,13 @@ t_sync_flags(dc); if (unlikely(env->singlestep_enabled)) { - t_gen_raise_exception(dc, EXCP_DEBUG); - if (dc->is_jmp == DISAS_NEXT) + TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); + + if (dc->is_jmp != DISAS_JUMP) { tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + } + gen_helper_raise_exception(tmp); + tcg_temp_free_i32(tmp); } else { switch(dc->is_jmp) { case DISAS_NEXT: @@ -1899,8 +1939,7 @@ #endif } -void gen_pc_load(CPUState *env, struct TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->sregs[SR_PC] = gen_opc_pc[pc_pos]; } diff -Nru qemu-kvm-0.14.1+noroms/target-mips/cpu.h qemu-kvm-0.15.0+noroms/target-mips/cpu.h --- qemu-kvm-0.14.1+noroms/target-mips/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-mips/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -63,7 +63,7 @@ uint32_t w[2]; /* binary single fixed-point */ }; /* define FP_ENDIAN_IDX to access the same location - * in the fpr_t union regardless of the host endianess + * in the fpr_t union regardless of the host endianness */ #if defined(HOST_WORDS_BIGENDIAN) # define FP_ENDIAN_IDX 1 @@ -493,8 +493,8 @@ void r4k_helper_tlbp (void); void r4k_helper_tlbr (void); -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int unused, int size); +void cpu_unassigned_access(CPUState *env, 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); @@ -656,4 +656,28 @@ env->tls_value = newtls; } +static inline int cpu_has_work(CPUState *env) +{ + int has_work = 0; + + /* 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; + } + + return has_work; +} + +#include "exec-all.h" + +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 (__MIPS_CPU_H__) */ diff -Nru qemu-kvm-0.14.1+noroms/target-mips/exec.h qemu-kvm-0.15.0+noroms/target-mips/exec.h --- qemu-kvm-0.14.1+noroms/target-mips/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-mips/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -11,42 +11,11 @@ register struct CPUMIPSState *env asm(AREG0); #include "cpu.h" -#include "exec-all.h" #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -static inline int cpu_has_work(CPUState *env) -{ - int has_work = 0; - - /* 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; - } - - if (env->interrupt_request & CPU_INTERRUPT_TIMER) { - has_work = 1; - } - - return has_work; -} - -static inline int cpu_halted(CPUState *env) -{ - if (!env->halted) - return 0; - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; -} - static inline void compute_hflags(CPUState *env) { env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | @@ -88,11 +57,4 @@ } } -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.14.1+noroms/target-mips/helper.c qemu-kvm-0.15.0+noroms/target-mips/helper.c --- qemu-kvm-0.14.1+noroms/target-mips/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-mips/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include #include "cpu.h" -#include "exec-all.h" enum { TLBRET_DIRTY = -4, @@ -272,8 +271,8 @@ #if !defined(CONFIG_USER_ONLY) target_phys_addr_t physical; int prot; -#endif int access_type; +#endif int ret = 0; #if 0 @@ -285,21 +284,19 @@ rw &= 1; /* data access */ +#if !defined(CONFIG_USER_ONLY) /* XXX: put correct access by using cpu_restore_state() correctly */ access_type = ACCESS_INT; -#if defined(CONFIG_USER_ONLY) - ret = TLBRET_NOMATCH; -#else ret = get_physical_address(env, &physical, &prot, address, rw, access_type); 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) { - tlb_set_page(env, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; + 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 { diff -Nru qemu-kvm-0.14.1+noroms/target-mips/machine.c qemu-kvm-0.15.0+noroms/target-mips/machine.c --- qemu-kvm-0.14.1+noroms/target-mips/machine.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-mips/machine.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,7 +1,7 @@ #include "hw/hw.h" #include "hw/boards.h" -#include "exec-all.h" +#include "cpu.h" static void save_tc(QEMUFile *f, TCState *tc) { diff -Nru qemu-kvm-0.14.1+noroms/target-mips/op_helper.c qemu-kvm-0.15.0+noroms/target-mips/op_helper.c --- qemu-kvm-0.14.1+noroms/target-mips/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-mips/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -38,7 +38,7 @@ #endif env->exception_index = exception; env->error_code = error_code; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_raise_exception (uint32_t exception) @@ -54,7 +54,7 @@ tb = tb_find_pc (pc); if (tb) { - cpu_restore_state (tb, env, pc, NULL); + cpu_restore_state(tb, env, pc); } } #endif @@ -277,7 +277,7 @@ lladdr = cpu_mips_translate_address(env, address, rw); if (lladdr == -1LL) { - cpu_loop_exit(); + cpu_loop_exit(env); } else { return lladdr; } @@ -1972,7 +1972,7 @@ 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); + cpu_restore_state(tb, env, pc); } } helper_raise_exception_err(env->exception_index, env->error_code); @@ -1980,9 +1980,11 @@ env = saved_env; } -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int unused, int size) +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int unused, int size) { + env = env1; + if (is_exec) helper_raise_exception(EXCP_IBE); else @@ -2077,22 +2079,27 @@ helper_raise_exception(EXCP_FPE); } -static inline char ieee_ex_to_mips(char xcpt) +static inline int ieee_ex_to_mips(int xcpt) { - return (xcpt & float_flag_inexact) >> 5 | - (xcpt & float_flag_underflow) >> 3 | - (xcpt & float_flag_overflow) >> 1 | - (xcpt & float_flag_divbyzero) << 1 | - (xcpt & float_flag_invalid) << 4; -} - -static inline char mips_ex_to_ieee(char xcpt) -{ - return (xcpt & FP_INEXACT) << 5 | - (xcpt & FP_UNDERFLOW) << 3 | - (xcpt & FP_OVERFLOW) << 1 | - (xcpt & FP_DIV0) >> 1 | - (xcpt & FP_INVALID) >> 4; + int ret = 0; + if (xcpt) { + if (xcpt & float_flag_invalid) { + ret |= FP_INVALID; + } + if (xcpt & float_flag_overflow) { + ret |= FP_OVERFLOW; + } + if (xcpt & float_flag_underflow) { + ret |= FP_UNDERFLOW; + } + if (xcpt & float_flag_divbyzero) { + ret |= FP_DIV0; + } + if (xcpt & float_flag_inexact) { + ret |= FP_INEXACT; + } + } + return ret; } static inline void update_fcr31(void) @@ -2282,6 +2289,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2295,6 +2303,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2308,6 +2317,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2321,6 +2331,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2334,6 +2345,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); dt2 = float64_to_int64_round_to_zero(fdt0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2345,6 +2357,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2356,6 +2369,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2367,6 +2381,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status); update_fcr31(); if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) @@ -2378,6 +2393,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2391,6 +2407,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2404,6 +2421,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2417,6 +2435,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2430,6 +2449,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2443,6 +2463,7 @@ { uint64_t dt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2456,6 +2477,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2469,6 +2491,7 @@ { uint32_t wt2; + set_float_exception_flags(0, &env->active_fpu.fp_status); set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); RESTORE_ROUNDING_MODE; @@ -2853,7 +2876,9 @@ #define FOP_COND_D(op, cond) \ void helper_cmp_d_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \ { \ - int c = cond; \ + int c; \ + set_float_exception_flags(0, &env->active_fpu.fp_status); \ + c = cond; \ update_fcr31(); \ if (c) \ SET_FP_COND(cc, env->active_fpu); \ @@ -2863,6 +2888,7 @@ void helper_cmpabs_d_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \ { \ int c; \ + set_float_exception_flags(0, &env->active_fpu.fp_status); \ fdt0 = float64_abs(fdt0); \ fdt1 = float64_abs(fdt1); \ c = cond; \ @@ -2873,45 +2899,33 @@ CLEAR_FP_COND(cc, env->active_fpu); \ } -static int float64_is_unordered(int sig, float64 a, float64 b STATUS_PARAM) -{ - if (float64_is_signaling_nan(a) || - float64_is_signaling_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_quiet_nan(a) || float64_is_quiet_nan(b)) { - return 1; - } else { - return 0; - } -} - /* NOTE: the comma operator will make "cond" to eval to false, - * but float*_is_unordered() is still called. */ -FOP_COND_D(f, (float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status), 0)) -FOP_COND_D(un, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status)) -FOP_COND_D(eq, !float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) && float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(ueq, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(olt, !float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) && float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(ult, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(ole, !float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) && float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(ule, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) || float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) + * but float64_unordered_quiet() is still called. */ +FOP_COND_D(f, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_COND_D(un, float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)) +FOP_COND_D(eq, float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ueq, float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) || float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(olt, float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ult, float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ole, float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ule, float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) /* NOTE: the comma operator will make "cond" to eval to false, - * but float*_is_unordered() is still called. */ -FOP_COND_D(sf, (float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status), 0)) -FOP_COND_D(ngle,float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status)) -FOP_COND_D(seq, !float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) && float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(ngl, float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(lt, !float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) && float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(nge, float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(le, !float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) && float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) -FOP_COND_D(ngt, float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) || float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) + * but float64_unordered() is still called. */ +FOP_COND_D(sf, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_COND_D(ngle,float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)) +FOP_COND_D(seq, float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ngl, float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(lt, float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(nge, float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(le, float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ngt, float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) || float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) #define FOP_COND_S(op, cond) \ void helper_cmp_s_ ## op (uint32_t fst0, uint32_t fst1, int cc) \ { \ - int c = cond; \ + int c; \ + set_float_exception_flags(0, &env->active_fpu.fp_status); \ + c = cond; \ update_fcr31(); \ if (c) \ SET_FP_COND(cc, env->active_fpu); \ @@ -2921,6 +2935,7 @@ void helper_cmpabs_s_ ## op (uint32_t fst0, uint32_t fst1, int cc) \ { \ int c; \ + set_float_exception_flags(0, &env->active_fpu.fp_status); \ fst0 = float32_abs(fst0); \ fst1 = float32_abs(fst1); \ c = cond; \ @@ -2931,51 +2946,39 @@ CLEAR_FP_COND(cc, env->active_fpu); \ } -static flag float32_is_unordered(int sig, float32 a, float32 b STATUS_PARAM) -{ - if (float32_is_signaling_nan(a) || - float32_is_signaling_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_quiet_nan(a) || float32_is_quiet_nan(b)) { - return 1; - } else { - return 0; - } -} - /* NOTE: the comma operator will make "cond" to eval to false, - * but float*_is_unordered() is still called. */ -FOP_COND_S(f, (float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status), 0)) -FOP_COND_S(un, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status)) -FOP_COND_S(eq, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(ueq, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(olt, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(ult, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(ole, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(ule, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status)) + * but float32_unordered_quiet() is still called. */ +FOP_COND_S(f, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_COND_S(un, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)) +FOP_COND_S(eq, float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ueq, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) || float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(olt, float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ult, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ole, float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ule, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status)) /* NOTE: the comma operator will make "cond" to eval to false, - * but float*_is_unordered() is still called. */ -FOP_COND_S(sf, (float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status), 0)) -FOP_COND_S(ngle,float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status)) -FOP_COND_S(seq, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(ngl, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(lt, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(nge, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(le, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status)) -FOP_COND_S(ngt, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status)) + * but float32_unordered() is still called. */ +FOP_COND_S(sf, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_COND_S(ngle,float32_unordered(fst1, fst0, &env->active_fpu.fp_status)) +FOP_COND_S(seq, float32_eq(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ngl, float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(lt, float32_lt(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(nge, float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(le, float32_le(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ngt, float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status)) #define FOP_COND_PS(op, condl, condh) \ void helper_cmp_ps_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \ { \ - uint32_t fst0 = float32_abs(fdt0 & 0XFFFFFFFF); \ - uint32_t fsth0 = float32_abs(fdt0 >> 32); \ - uint32_t fst1 = float32_abs(fdt1 & 0XFFFFFFFF); \ - uint32_t fsth1 = float32_abs(fdt1 >> 32); \ - int cl = condl; \ - int ch = condh; \ - \ + uint32_t fst0, fsth0, fst1, fsth1; \ + int ch, cl; \ + set_float_exception_flags(0, &env->active_fpu.fp_status); \ + fst0 = fdt0 & 0XFFFFFFFF; \ + fsth0 = fdt0 >> 32; \ + fst1 = fdt1 & 0XFFFFFFFF; \ + fsth1 = fdt1 >> 32; \ + cl = condl; \ + ch = condh; \ update_fcr31(); \ if (cl) \ SET_FP_COND(cc, env->active_fpu); \ @@ -2988,13 +2991,14 @@ } \ void helper_cmpabs_ps_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \ { \ - uint32_t fst0 = float32_abs(fdt0 & 0XFFFFFFFF); \ - uint32_t fsth0 = float32_abs(fdt0 >> 32); \ - uint32_t fst1 = float32_abs(fdt1 & 0XFFFFFFFF); \ - uint32_t fsth1 = float32_abs(fdt1 >> 32); \ - int cl = condl; \ - int ch = condh; \ - \ + uint32_t fst0, fsth0, fst1, fsth1; \ + int ch, cl; \ + fst0 = float32_abs(fdt0 & 0XFFFFFFFF); \ + fsth0 = float32_abs(fdt0 >> 32); \ + fst1 = float32_abs(fdt1 & 0XFFFFFFFF); \ + fsth1 = float32_abs(fdt1 >> 32); \ + cl = condl; \ + ch = condh; \ update_fcr31(); \ if (cl) \ SET_FP_COND(cc, env->active_fpu); \ @@ -3007,38 +3011,38 @@ } /* NOTE: the comma operator will make "cond" to eval to false, - * but float*_is_unordered() is still called. */ -FOP_COND_PS(f, (float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status), 0), - (float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status), 0)) -FOP_COND_PS(un, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status), - float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status)) -FOP_COND_PS(eq, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status), - !float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) && float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(ueq, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status), - float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) || float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(olt, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status), - !float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) && float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(ult, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status), - float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) || float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(ole, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status), - !float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) && float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(ule, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status), - float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) || float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) + * but float32_unordered_quiet() is still called. */ +FOP_COND_PS(f, (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), 0), + (float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status), 0)) +FOP_COND_PS(un, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status)) +FOP_COND_PS(eq, float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status), + float32_eq_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ueq, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) || float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status) || float32_eq_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(olt, float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status), + float32_lt_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ult, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status) || float32_lt_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ole, float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status), + float32_le_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ule, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status) || float32_le_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) /* NOTE: the comma operator will make "cond" to eval to false, - * but float*_is_unordered() is still called. */ -FOP_COND_PS(sf, (float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status), 0), - (float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status), 0)) -FOP_COND_PS(ngle,float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status), - float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status)) -FOP_COND_PS(seq, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status), - !float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) && float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(ngl, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status), - float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) || float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(lt, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status), - !float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) && float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(nge, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status), - float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) || float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(le, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status), - !float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) && float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) -FOP_COND_PS(ngt, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status), - float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) || float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) + * but float32_unordered() is still called. */ +FOP_COND_PS(sf, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status), 0), + (float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status), 0)) +FOP_COND_PS(ngle,float32_unordered(fst1, fst0, &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status)) +FOP_COND_PS(seq, float32_eq(fst0, fst1, &env->active_fpu.fp_status), + float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ngl, float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status) || float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(lt, float32_lt(fst0, fst1, &env->active_fpu.fp_status), + float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(nge, float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status) || float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(le, float32_le(fst0, fst1, &env->active_fpu.fp_status), + float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ngt, float32_unordered(fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status) || float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) diff -Nru qemu-kvm-0.14.1+noroms/target-mips/translate.c qemu-kvm-0.15.0+noroms/target-mips/translate.c --- qemu-kvm-0.14.1+noroms/target-mips/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-mips/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "qemu-common.h" @@ -2686,7 +2685,7 @@ likely(!ctx->singlestep_enabled)) { tcg_gen_goto_tb(n); gen_save_pc(dest); - tcg_gen_exit_tb((long)tb + n); + tcg_gen_exit_tb((tcg_target_long)tb + n); } else { gen_save_pc(dest); if (ctx->singlestep_enabled) { @@ -12737,8 +12736,7 @@ env->exception_index = EXCP_NONE; } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->active_tc.PC = gen_opc_pc[pc_pos]; env->hflags &= ~MIPS_HFLAG_BMASK; diff -Nru qemu-kvm-0.14.1+noroms/target-mips/translate_init.c qemu-kvm-0.15.0+noroms/target-mips/translate_init.c --- qemu-kvm-0.14.1+noroms/target-mips/translate_init.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-mips/translate_init.c 2011-08-09 07:40:29.000000000 -0500 @@ -38,7 +38,7 @@ ((1 << CP0C2_M)) /* No config4, no DSP ASE, no large physaddr (PABITS), - no external interrupt controller, no vectored interupts, + no external interrupt controller, no vectored interrupts, no 1kb pages, no SmartMIPS ASE, no trace logic */ #define MIPS_CONFIG3 \ ((0 << CP0C3_M) | (0 << CP0C3_DSPP) | (0 << CP0C3_LPA) | \ @@ -477,7 +477,7 @@ .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), .SYNCI_Step = 16, .CCRes = 2, - .CP0_Status_rw_bitmask = 0xF5D0FF1F, /*bit5:7 not writeable*/ + .CP0_Status_rw_bitmask = 0xF5D0FF1F, /*bit5:7 not writable*/ .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV), .SEGBITS = 40, .PABITS = 40, diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/cpu.h qemu-kvm-0.15.0+noroms/target-ppc/cpu.h --- qemu-kvm-0.14.1+noroms/target-ppc/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -43,6 +43,8 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 64 #endif +#define TARGET_PAGE_BITS_16M 24 + #else /* defined (TARGET_PPC64) */ /* PowerPC 32 definitions */ #define TARGET_LONG_BITS 32 @@ -50,10 +52,9 @@ #if defined(TARGET_PPCEMB) /* Specific definitions for PowerPC embedded */ /* BookE have 36 bits physical address space */ -#if defined(CONFIG_USER_ONLY) || defined(USE_KVM) +#if defined(CONFIG_USER_ONLY) /* It looks like a lot of Linux programs assume page size * is 4kB long. This is evil, but we have to deal with it... - * Also kvm for embedded powerpc needs (atm) 4kB aligned pages */ #define TARGET_PAGE_BITS 12 #else /* defined(CONFIG_USER_ONLY) */ @@ -74,8 +75,6 @@ #include "cpu-defs.h" -#include - #include "softfloat.h" #define TARGET_HAS_ICE 1 @@ -107,16 +106,19 @@ POWERPC_MMU_MPC8xx = 0x00000007, /* BookE MMU model */ POWERPC_MMU_BOOKE = 0x00000008, - /* BookE FSL MMU model */ - POWERPC_MMU_BOOKE_FSL = 0x00000009, + /* BookE 2.06 MMU model */ + POWERPC_MMU_BOOKE206 = 0x00000009, /* PowerPC 601 MMU model (specific BATs format) */ POWERPC_MMU_601 = 0x0000000A, #if defined(TARGET_PPC64) #define POWERPC_MMU_64 0x00010000 +#define POWERPC_MMU_1TSEG 0x00020000 /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, /* 620 variant (no segment exceptions) */ POWERPC_MMU_620 = POWERPC_MMU_64 | 0x00000002, + /* Architecture 2.06 variant */ + POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003, #endif /* defined(TARGET_PPC64) */ }; @@ -152,6 +154,8 @@ #if defined(TARGET_PPC64) /* PowerPC 970 exception model */ POWERPC_EXCP_970, + /* POWER7 exception model */ + POWERPC_EXCP_POWER7, #endif /* defined(TARGET_PPC64) */ }; @@ -219,7 +223,7 @@ /* 970FX specific exceptions */ POWERPC_EXCP_SOFTP = 88, /* Soft patch exception */ POWERPC_EXCP_MAINT = 89, /* Maintenance exception */ - /* Freescale embeded cores specific exceptions */ + /* Freescale embedded cores specific exceptions */ POWERPC_EXCP_MEXTBR = 90, /* Maskable external breakpoint */ POWERPC_EXCP_NMEXTBR = 91, /* Non maskable external breakpoint */ POWERPC_EXCP_ITLBE = 92, /* Instruction TLB error */ @@ -287,6 +291,8 @@ PPC_FLAGS_INPUT_405, /* PowerPC 970 bus */ PPC_FLAGS_INPUT_970, + /* PowerPC POWER7 bus */ + PPC_FLAGS_INPUT_POWER7, /* PowerPC 401 bus */ PPC_FLAGS_INPUT_401, /* Freescale RCPU bus */ @@ -352,18 +358,71 @@ uint32_t attr; /* Storage attributes */ }; +typedef struct ppcmas_tlb_t { + uint32_t mas8; + uint32_t mas1; + uint64_t mas2; + uint64_t mas7_3; +} ppcmas_tlb_t; + union ppc_tlb_t { - ppc6xx_tlb_t tlb6; - ppcemb_tlb_t tlbe; + ppc6xx_tlb_t *tlb6; + ppcemb_tlb_t *tlbe; + ppcmas_tlb_t *tlbm; }; + +/* possible TLB variants */ +#define TLB_NONE 0 +#define TLB_6XX 1 +#define TLB_EMB 2 +#define TLB_MAS 3 #endif +#define SDR_32_HTABORG 0xFFFF0000UL +#define SDR_32_HTABMASK 0x000001FFUL + +#if defined(TARGET_PPC64) +#define SDR_64_HTABORG 0xFFFFFFFFFFFC0000ULL +#define SDR_64_HTABSIZE 0x000000000000001FULL +#endif /* defined(TARGET_PPC64 */ + +#define HASH_PTE_SIZE_32 8 +#define HASH_PTE_SIZE_64 16 + typedef struct ppc_slb_t ppc_slb_t; struct ppc_slb_t { - uint64_t tmp64; - uint32_t tmp; + uint64_t esid; + uint64_t vsid; }; +/* Bits in the SLB ESID word */ +#define SLB_ESID_ESID 0xFFFFFFFFF0000000ULL +#define SLB_ESID_V 0x0000000008000000ULL /* valid */ + +/* Bits in the SLB VSID word */ +#define SLB_VSID_SHIFT 12 +#define SLB_VSID_SHIFT_1T 24 +#define SLB_VSID_SSIZE_SHIFT 62 +#define SLB_VSID_B 0xc000000000000000ULL +#define SLB_VSID_B_256M 0x0000000000000000ULL +#define SLB_VSID_B_1T 0x4000000000000000ULL +#define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL +#define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID) +#define SLB_VSID_KS 0x0000000000000800ULL +#define SLB_VSID_KP 0x0000000000000400ULL +#define SLB_VSID_N 0x0000000000000200ULL /* no-execute */ +#define SLB_VSID_L 0x0000000000000100ULL +#define SLB_VSID_C 0x0000000000000080ULL /* class */ +#define SLB_VSID_LP 0x0000000000000030ULL +#define SLB_VSID_ATTR 0x0000000000000FFFULL + +#define SEGMENT_SHIFT_256M 28 +#define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1)) + +#define SEGMENT_SHIFT_1T 40 +#define SEGMENT_MASK_1T (~((1ULL << SEGMENT_SHIFT_1T) - 1)) + + /*****************************************************************************/ /* Machine state register bits definition */ #define MSR_SF 63 /* Sixty-four-bit mode hflags */ @@ -373,6 +432,7 @@ #define MSR_CM 31 /* Computation mode for BookE hflags */ #define MSR_ICM 30 /* Interrupt computation mode for BookE */ #define MSR_THV 29 /* hypervisor state for 32 bits PowerPC hflags */ +#define MSR_GS 28 /* guest state for BookE */ #define MSR_UCLE 26 /* User-mode cache lock enable for BookE */ #define MSR_VR 25 /* altivec available x hflags */ #define MSR_SPE 25 /* SPE enable for BookE x hflags */ @@ -410,6 +470,7 @@ #define msr_cm ((env->msr >> MSR_CM) & 1) #define msr_icm ((env->msr >> MSR_ICM) & 1) #define msr_thv ((env->msr >> MSR_THV) & 1) +#define msr_gs ((env->msr >> MSR_GS) & 1) #define msr_ucle ((env->msr >> MSR_UCLE) & 1) #define msr_vr ((env->msr >> MSR_VR) & 1) #define msr_spe ((env->msr >> MSR_SPE) & 1) @@ -559,6 +620,224 @@ #define vscr_sat (((env->vscr) >> VSCR_SAT) & 0x1) /*****************************************************************************/ +/* BookE e500 MMU registers */ + +#define MAS0_NV_SHIFT 0 +#define MAS0_NV_MASK (0xfff << MAS0_NV_SHIFT) + +#define MAS0_WQ_SHIFT 12 +#define MAS0_WQ_MASK (3 << MAS0_WQ_SHIFT) +/* Write TLB entry regardless of reservation */ +#define MAS0_WQ_ALWAYS (0 << MAS0_WQ_SHIFT) +/* Write TLB entry only already in use */ +#define MAS0_WQ_COND (1 << MAS0_WQ_SHIFT) +/* Clear TLB entry */ +#define MAS0_WQ_CLR_RSRV (2 << MAS0_WQ_SHIFT) + +#define MAS0_HES_SHIFT 14 +#define MAS0_HES (1 << MAS0_HES_SHIFT) + +#define MAS0_ESEL_SHIFT 16 +#define MAS0_ESEL_MASK (0xfff << MAS0_ESEL_SHIFT) + +#define MAS0_TLBSEL_SHIFT 28 +#define MAS0_TLBSEL_MASK (3 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB0 (0 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB1 (1 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB2 (2 << MAS0_TLBSEL_SHIFT) +#define MAS0_TLBSEL_TLB3 (3 << MAS0_TLBSEL_SHIFT) + +#define MAS0_ATSEL_SHIFT 31 +#define MAS0_ATSEL (1 << MAS0_ATSEL_SHIFT) +#define MAS0_ATSEL_TLB 0 +#define MAS0_ATSEL_LRAT MAS0_ATSEL + +#define MAS1_TSIZE_SHIFT 8 +#define MAS1_TSIZE_MASK (0xf << MAS1_TSIZE_SHIFT) + +#define MAS1_TS_SHIFT 12 +#define MAS1_TS (1 << MAS1_TS_SHIFT) + +#define MAS1_IND_SHIFT 13 +#define MAS1_IND (1 << MAS1_IND_SHIFT) + +#define MAS1_TID_SHIFT 16 +#define MAS1_TID_MASK (0x3fff << MAS1_TID_SHIFT) + +#define MAS1_IPROT_SHIFT 30 +#define MAS1_IPROT (1 << MAS1_IPROT_SHIFT) + +#define MAS1_VALID_SHIFT 31 +#define MAS1_VALID 0x80000000 + +#define MAS2_EPN_SHIFT 12 +#define MAS2_EPN_MASK (0xfffff << MAS2_EPN_SHIFT) + +#define MAS2_ACM_SHIFT 6 +#define MAS2_ACM (1 << MAS2_ACM_SHIFT) + +#define MAS2_VLE_SHIFT 5 +#define MAS2_VLE (1 << MAS2_VLE_SHIFT) + +#define MAS2_W_SHIFT 4 +#define MAS2_W (1 << MAS2_W_SHIFT) + +#define MAS2_I_SHIFT 3 +#define MAS2_I (1 << MAS2_I_SHIFT) + +#define MAS2_M_SHIFT 2 +#define MAS2_M (1 << MAS2_M_SHIFT) + +#define MAS2_G_SHIFT 1 +#define MAS2_G (1 << MAS2_G_SHIFT) + +#define MAS2_E_SHIFT 0 +#define MAS2_E (1 << MAS2_E_SHIFT) + +#define MAS3_RPN_SHIFT 12 +#define MAS3_RPN_MASK (0xfffff << MAS3_RPN_SHIFT) + +#define MAS3_U0 0x00000200 +#define MAS3_U1 0x00000100 +#define MAS3_U2 0x00000080 +#define MAS3_U3 0x00000040 +#define MAS3_UX 0x00000020 +#define MAS3_SX 0x00000010 +#define MAS3_UW 0x00000008 +#define MAS3_SW 0x00000004 +#define MAS3_UR 0x00000002 +#define MAS3_SR 0x00000001 +#define MAS3_SPSIZE_SHIFT 1 +#define MAS3_SPSIZE_MASK (0x3e << MAS3_SPSIZE_SHIFT) + +#define MAS4_TLBSELD_SHIFT MAS0_TLBSEL_SHIFT +#define MAS4_TLBSELD_MASK MAS0_TLBSEL_MASK +#define MAS4_TIDSELD_MASK 0x00030000 +#define MAS4_TIDSELD_PID0 0x00000000 +#define MAS4_TIDSELD_PID1 0x00010000 +#define MAS4_TIDSELD_PID2 0x00020000 +#define MAS4_TIDSELD_PIDZ 0x00030000 +#define MAS4_INDD 0x00008000 /* Default IND */ +#define MAS4_TSIZED_SHIFT MAS1_TSIZE_SHIFT +#define MAS4_TSIZED_MASK MAS1_TSIZE_MASK +#define MAS4_ACMD 0x00000040 +#define MAS4_VLED 0x00000020 +#define MAS4_WD 0x00000010 +#define MAS4_ID 0x00000008 +#define MAS4_MD 0x00000004 +#define MAS4_GD 0x00000002 +#define MAS4_ED 0x00000001 +#define MAS4_WIMGED_MASK 0x0000001f /* Default WIMGE */ +#define MAS4_WIMGED_SHIFT 0 + +#define MAS5_SGS 0x80000000 +#define MAS5_SLPID_MASK 0x00000fff + +#define MAS6_SPID0 0x3fff0000 +#define MAS6_SPID1 0x00007ffe +#define MAS6_ISIZE(x) MAS1_TSIZE(x) +#define MAS6_SAS 0x00000001 +#define MAS6_SPID MAS6_SPID0 +#define MAS6_SIND 0x00000002 /* Indirect page */ +#define MAS6_SIND_SHIFT 1 +#define MAS6_SPID_MASK 0x3fff0000 +#define MAS6_SPID_SHIFT 16 +#define MAS6_ISIZE_MASK 0x00000f80 +#define MAS6_ISIZE_SHIFT 7 + +#define MAS7_RPN 0xffffffff + +#define MAS8_TGS 0x80000000 +#define MAS8_VF 0x40000000 +#define MAS8_TLBPID 0x00000fff + +/* Bit definitions for MMUCFG */ +#define MMUCFG_MAVN 0x00000003 /* MMU Architecture Version Number */ +#define MMUCFG_MAVN_V1 0x00000000 /* v1.0 */ +#define MMUCFG_MAVN_V2 0x00000001 /* v2.0 */ +#define MMUCFG_NTLBS 0x0000000c /* Number of TLBs */ +#define MMUCFG_PIDSIZE 0x000007c0 /* PID Reg Size */ +#define MMUCFG_TWC 0x00008000 /* TLB Write Conditional (v2.0) */ +#define MMUCFG_LRAT 0x00010000 /* LRAT Supported (v2.0) */ +#define MMUCFG_RASIZE 0x00fe0000 /* Real Addr Size */ +#define MMUCFG_LPIDSIZE 0x0f000000 /* LPID Reg Size */ + +/* Bit definitions for MMUCSR0 */ +#define MMUCSR0_TLB1FI 0x00000002 /* TLB1 Flash invalidate */ +#define MMUCSR0_TLB0FI 0x00000004 /* TLB0 Flash invalidate */ +#define MMUCSR0_TLB2FI 0x00000040 /* TLB2 Flash invalidate */ +#define MMUCSR0_TLB3FI 0x00000020 /* TLB3 Flash invalidate */ +#define MMUCSR0_TLBFI (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \ + MMUCSR0_TLB2FI | MMUCSR0_TLB3FI) +#define MMUCSR0_TLB0PS 0x00000780 /* TLB0 Page Size */ +#define MMUCSR0_TLB1PS 0x00007800 /* TLB1 Page Size */ +#define MMUCSR0_TLB2PS 0x00078000 /* TLB2 Page Size */ +#define MMUCSR0_TLB3PS 0x00780000 /* TLB3 Page Size */ + +/* TLBnCFG encoding */ +#define TLBnCFG_N_ENTRY 0x00000fff /* number of entries */ +#define TLBnCFG_HES 0x00002000 /* HW select supported */ +#define TLBnCFG_AVAIL 0x00004000 /* variable page size */ +#define TLBnCFG_IPROT 0x00008000 /* IPROT supported */ +#define TLBnCFG_GTWE 0x00010000 /* Guest can write */ +#define TLBnCFG_IND 0x00020000 /* IND entries supported */ +#define TLBnCFG_PT 0x00040000 /* Can load from page table */ +#define TLBnCFG_MINSIZE 0x00f00000 /* Minimum Page Size (v1.0) */ +#define TLBnCFG_MINSIZE_SHIFT 20 +#define TLBnCFG_MAXSIZE 0x000f0000 /* Maximum Page Size (v1.0) */ +#define TLBnCFG_MAXSIZE_SHIFT 16 +#define TLBnCFG_ASSOC 0xff000000 /* Associativity */ +#define TLBnCFG_ASSOC_SHIFT 24 + +/* TLBnPS encoding */ +#define TLBnPS_4K 0x00000004 +#define TLBnPS_8K 0x00000008 +#define TLBnPS_16K 0x00000010 +#define TLBnPS_32K 0x00000020 +#define TLBnPS_64K 0x00000040 +#define TLBnPS_128K 0x00000080 +#define TLBnPS_256K 0x00000100 +#define TLBnPS_512K 0x00000200 +#define TLBnPS_1M 0x00000400 +#define TLBnPS_2M 0x00000800 +#define TLBnPS_4M 0x00001000 +#define TLBnPS_8M 0x00002000 +#define TLBnPS_16M 0x00004000 +#define TLBnPS_32M 0x00008000 +#define TLBnPS_64M 0x00010000 +#define TLBnPS_128M 0x00020000 +#define TLBnPS_256M 0x00040000 +#define TLBnPS_512M 0x00080000 +#define TLBnPS_1G 0x00100000 +#define TLBnPS_2G 0x00200000 +#define TLBnPS_4G 0x00400000 +#define TLBnPS_8G 0x00800000 +#define TLBnPS_16G 0x01000000 +#define TLBnPS_32G 0x02000000 +#define TLBnPS_64G 0x04000000 +#define TLBnPS_128G 0x08000000 +#define TLBnPS_256G 0x10000000 + +/* tlbilx action encoding */ +#define TLBILX_T_ALL 0 +#define TLBILX_T_TID 1 +#define TLBILX_T_FULLMATCH 3 +#define TLBILX_T_CLASS0 4 +#define TLBILX_T_CLASS1 5 +#define TLBILX_T_CLASS2 6 +#define TLBILX_T_CLASS3 7 + +/* BookE 2.06 helper defines */ + +#define BOOKE206_FLUSH_TLB0 (1 << 0) +#define BOOKE206_FLUSH_TLB1 (1 << 1) +#define BOOKE206_FLUSH_TLB2 (1 << 2) +#define BOOKE206_FLUSH_TLB3 (1 << 3) + +/* number of possible TLBs */ +#define BOOKE206_MAX_TLBN 4 + +/*****************************************************************************/ /* The whole PowerPC CPU context */ #define NB_MMU_MODES 3 @@ -620,20 +899,24 @@ int slb_nr; #endif /* segment registers */ - target_ulong sdr1; + target_phys_addr_t htab_base; + target_phys_addr_t htab_mask; target_ulong sr[32]; + /* externally stored hash table */ + uint8_t *external_htab; /* BATs */ int nb_BATs; target_ulong DBAT[2][8]; target_ulong IBAT[2][8]; - /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */ + /* PowerPC TLB registers (for 4xx, e500 and 60x software driven TLBs) */ int nb_tlb; /* Total number of TLB */ int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */ int nb_ways; /* Number of ways in the TLB set */ int last_way; /* Last used way used to allocate TLB in a LRU way */ int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */ int nb_pids; /* Number of available PID registers */ - ppc_tlb_t *tlb; /* TLB is optional. Allocate them only if needed */ + int tlb_type; /* Type of TLB we're dealing with */ + ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ /* 403 dedicated access protection registers */ target_ulong pb[4]; #endif @@ -670,6 +953,14 @@ int bfd_mach; uint32_t flags; uint64_t insns_flags; + uint64_t insns_flags2; + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + target_phys_addr_t vpa; + target_phys_addr_t slb_shadow; + target_phys_addr_t dispatch_trace_log; + uint32_t dtl_size; +#endif /* TARGET_PPC64 */ int error_code; uint32_t pending_interrupts; @@ -713,7 +1004,7 @@ target_phys_addr_t raddr; /* Real address */ target_phys_addr_t eaddr; /* Effective address */ int prot; /* Protection bits */ - target_phys_addr_t pg_addr[2]; /* PTE tables base addresses */ + target_phys_addr_t hash[2]; /* Pagetable hash values */ target_ulong ptem; /* Virtual segment ID | API */ int key; /* Access key */ int nx; /* Non-execute area */ @@ -756,7 +1047,9 @@ void ppc_store_asr (CPUPPCState *env, target_ulong value); target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr); target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr); -void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); +int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); +int ppc_load_slb_esid (CPUPPCState *env, target_ulong rb, target_ulong *rt); +int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt); #endif /* defined(TARGET_PPC64) */ void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value); #endif /* !defined(CONFIG_USER_ONLY) */ @@ -794,6 +1087,14 @@ void store_40x_sler (CPUPPCState *env, uint32_t val); void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); +void booke206_flush_tlb(CPUState *env, int flags, const int check_iprot); +target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb); +int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb, + target_phys_addr_t *raddrp, target_ulong address, + uint32_t pid, int ext, int i); +int ppcmas_tlb_check(CPUState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddrp, target_ulong address, + uint32_t pid); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); #if defined(TARGET_PPC64) @@ -957,6 +1258,8 @@ #define SPR_HSPRG1 (0x131) #define SPR_HDSISR (0x132) #define SPR_HDAR (0x133) +#define SPR_BOOKE_EPCR (0x133) +#define SPR_SPURR (0x134) #define SPR_BOOKE_DBCR0 (0x134) #define SPR_IBCR (0x135) #define SPR_PURR (0x135) @@ -1481,6 +1784,13 @@ PPC_DCRX = 0x2000000000000000ULL, /* user-mode DCR access, implemented in PowerPC 460 */ PPC_DCRUX = 0x4000000000000000ULL, + /* popcntw and popcntd instructions */ + PPC_POPCNTWD = 0x8000000000000000ULL, + + /* extended type values */ + + /* BookE 2.06 PowerPC specification */ + PPC2_BOOKE206 = 0x0000000000000001ULL, }; /*****************************************************************************/ @@ -1579,6 +1889,15 @@ PPC970_INPUT_THINT = 6, PPC970_INPUT_NB, }; + +enum { + /* POWER7 input pins */ + POWER7_INPUT_INT = 0, + /* POWER7 probably has other inputs, but we don't care about them + * for any existing machine. We can wire these up when we need + * them */ + POWER7_INPUT_NB, +}; #endif /* Hardware exceptions definitions */ @@ -1624,11 +1943,89 @@ #endif } -/* hidden flags (hflags) - used internally by qemu to represent additional - * cpu states. - */ -#define HF_HALTED_SHIFT 1 +#if !defined(CONFIG_USER_ONLY) +static inline int booke206_tlbm_id(CPUState *env, ppcmas_tlb_t *tlbm) +{ + uintptr_t tlbml = (uintptr_t)tlbm; + uintptr_t tlbl = (uintptr_t)env->tlb.tlbm; + + return (tlbml - tlbl) / sizeof(env->tlb.tlbm[0]); +} -#define HF_HALTED_MASK 1<spr[SPR_BOOKE_TLB0CFG + tlbn]; + int r = tlbncfg & TLBnCFG_N_ENTRY; + return r; +} + +static inline int booke206_tlb_ways(CPUState *env, int tlbn) +{ + uint32_t tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + int r = tlbncfg >> TLBnCFG_ASSOC_SHIFT; + return r; +} + +static inline int booke206_tlbm_to_tlbn(CPUState *env, ppcmas_tlb_t *tlbm) +{ + int id = booke206_tlbm_id(env, tlbm); + int end = 0; + int i; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + end += booke206_tlb_size(env, i); + if (id < end) { + return i; + } + } + + cpu_abort(env, "Unknown TLBe: %d\n", id); + return 0; +} + +static inline int booke206_tlbm_to_way(CPUState *env, ppcmas_tlb_t *tlb) +{ + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + int tlbid = booke206_tlbm_id(env, tlb); + return tlbid & (booke206_tlb_ways(env, tlbn) - 1); +} + +static inline ppcmas_tlb_t *booke206_get_tlbm(CPUState *env, const int tlbn, + target_ulong ea, int way) +{ + int r; + uint32_t ways = booke206_tlb_ways(env, tlbn); + int ways_bits = ffs(ways) - 1; + int tlb_bits = ffs(booke206_tlb_size(env, tlbn)) - 1; + int i; + + way &= ways - 1; + ea >>= MAS2_EPN_SHIFT; + ea &= (1 << (tlb_bits - ways_bits)) - 1; + r = (ea << ways_bits) | way; + + /* bump up to tlbn index */ + for (i = 0; i < tlbn; i++) { + r += booke206_tlb_size(env, i); + } + + return &env->tlb.tlbm[r]; +} + +#endif + +extern void (*cpu_ppc_hypercall)(CPUState *); + +static inline bool cpu_has_work(CPUState *env) +{ + return msr_ee && (env->interrupt_request & CPU_INTERRUPT_HARD); +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->nip = tb->pc; +} #endif /* !defined (__CPU_PPC_H__) */ diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/exec.h qemu-kvm-0.15.0+noroms/target-ppc/exec.h --- qemu-kvm-0.14.1+noroms/target-ppc/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include "dyngen-exec.h" #include "cpu.h" -#include "exec-all.h" register struct CPUPPCState *env asm(AREG0); @@ -32,26 +31,4 @@ #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -static inline int cpu_has_work(CPUState *env) -{ - return (msr_ee && (env->interrupt_request & CPU_INTERRUPT_HARD)); -} - - -static inline int cpu_halted(CPUState *env) -{ - if (!env->halted) - return 0; - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - 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.14.1+noroms/target-ppc/fake-exec.c qemu-kvm-0.15.0+noroms/target-ppc/fake-exec.c --- qemu-kvm-0.14.1+noroms/target-ppc/fake-exec.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/fake-exec.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,104 +0,0 @@ -/* - * fake-exec.c - * - * This is a file for stub functions so that compilation is possible - * when TCG CPU emulation is disabled during compilation. - * - * Copyright 2007 IBM Corporation. - * Added by & Authors: - * Jerone Young - * This work is licensed under the GNU GPL licence version 2 or later. - * - */ - -#include -#include -#include -#include -#include - -#include "cpu.h" -#include "exec-all.h" - - -struct ppc_def_t { - const unsigned char *name; - uint32_t pvr; - uint32_t svr; - uint64_t insns_flags; - uint64_t msr_mask; - powerpc_mmu_t mmu_model; - powerpc_excp_t excp_model; - powerpc_input_t bus_model; - uint32_t flags; - int bfd_mach; - void (*init_proc)(CPUPPCState *env); - int (*check_pow)(CPUPPCState *env); -}; - -int code_copy_enabled = 0; - -void cpu_dump_state (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), - int flags) -{ -} - -void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) -{ -} - -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) -{ -} - -int cpu_restore_state(TranslationBlock *tb, - CPUState *env, unsigned long searched_pc, - void *puc) - -{ - return 0; -} - -int cpu_ppc_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr) -{ - return 0; -} - -void init_proc_ppc440ep_kvm(CPUPPCState *env) -{ - ppc40x_irq_init(env); -} - -static ppc_def_t ppc440ep_kvm = { - .name = "440EP KVM", - .mmu_model = POWERPC_MMU_SOFT_4xx, /*XXX needed for GDB stub */ - .init_proc = init_proc_ppc440ep_kvm, -}; - -const ppc_def_t *cpu_ppc_find_by_name (const unsigned char *name) -{ - return &ppc440ep_kvm; -} - -int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def) -{ - env->mmu_model = def->mmu_model; - (*def->init_proc)(env); - return 0; -} - -void flush_icache_range(unsigned long start, unsigned long stop) -{ -} diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/helper.c qemu-kvm-0.15.0+noroms/target-ppc/helper.c --- qemu-kvm-0.14.1+noroms/target-ppc/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -21,14 +21,11 @@ #include #include #include -#include #include "cpu.h" -#include "exec-all.h" #include "helper_regs.h" #include "qemu-common.h" #include "kvm.h" -#include "qemu-kvm.h" //#define DEBUG_MMU //#define DEBUG_BATS @@ -71,6 +68,10 @@ # define LOG_EXCP(...) do { } while (0) #endif +/*****************************************************************************/ +/* PowerPC Hypercall emulation */ + +void (*cpu_ppc_hypercall)(CPUState *); /*****************************************************************************/ /* PowerPC MMU emulation */ @@ -321,7 +322,7 @@ if (env->id_tlbs == 1) max *= 2; for (nr = 0; nr < max; nr++) { - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; pte_invalidate(&tlb->pte0); } tlb_flush(env, 1); @@ -338,7 +339,7 @@ /* Invalidate ITLB + DTLB, all ways */ for (way = 0; way < env->nb_ways; way++) { nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) { LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr, env->nb_tlb, eaddr); @@ -365,7 +366,7 @@ int nr; nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx " PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1); /* Invalidate any pending reference in Qemu for this virtual address */ @@ -389,7 +390,7 @@ for (way = 0; way < env->nb_ways; way++) { nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == ACCESS_CODE ? 1 : 0); - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; /* This test "emulates" the PTE index match for hardware TLBs */ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx @@ -432,7 +433,7 @@ LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); /* Update page flags */ - pte_update_flags(ctx, &env->tlb[best].tlb6.pte1, ret, rw); + pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, rw); } return ret; @@ -564,21 +565,35 @@ return ret; } +static inline target_phys_addr_t get_pteg_offset(CPUState *env, + target_phys_addr_t hash, + int pte_size) +{ + return (hash * pte_size * 8) & env->htab_mask; +} + /* PTE table lookup */ -static inline int _find_pte(mmu_ctx_t *ctx, int is_64b, int h, int rw, - int type, int target_page_bits) +static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h, + int rw, int type, int target_page_bits) { - target_ulong base, pte0, pte1; + target_phys_addr_t pteg_off; + target_ulong pte0, pte1; int i, good = -1; int ret, r; ret = -1; /* No entry found */ - base = ctx->pg_addr[h]; + pteg_off = get_pteg_offset(env, ctx->hash[h], + is_64b ? HASH_PTE_SIZE_64 : HASH_PTE_SIZE_32); for (i = 0; i < 8; i++) { #if defined(TARGET_PPC64) if (is_64b) { - pte0 = ldq_phys(base + (i * 16)); - pte1 = ldq_phys(base + (i * 16) + 8); + if (env->external_htab) { + pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); + pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8); + } else { + pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16)); + pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8); + } /* We have a TLB that saves 4K pages, so let's * split a huge page to 4k chunks */ @@ -589,17 +604,22 @@ r = pte64_check(ctx, pte0, pte1, h, rw, type); LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - base + (i * 16), pte0, pte1, (int)(pte0 & 1), h, + pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, (int)((pte0 >> 1) & 1), ctx->ptem); } else #endif { - pte0 = ldl_phys(base + (i * 8)); - pte1 = ldl_phys(base + (i * 8) + 4); + if (env->external_htab) { + pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); + pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); + } else { + pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); + pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); + } r = pte32_check(ctx, pte0, pte1, h, rw, type); LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - base + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, + pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem); } switch (r) { @@ -635,11 +655,23 @@ if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { #if defined(TARGET_PPC64) if (is_64b) { - stq_phys_notdirty(base + (good * 16) + 8, pte1); + if (env->external_htab) { + stq_p(env->external_htab + pteg_off + (good * 16) + 8, + pte1); + } else { + stq_phys_notdirty(env->htab_base + pteg_off + + (good * 16) + 8, pte1); + } } else #endif { - stl_phys_notdirty(base + (good * 8) + 4, pte1); + if (env->external_htab) { + stl_p(env->external_htab + pteg_off + (good * 8) + 4, + pte1); + } else { + stl_phys_notdirty(env->htab_base + pteg_off + + (good * 8) + 4, pte1); + } } } } @@ -647,111 +679,45 @@ return ret; } -static inline int find_pte32(mmu_ctx_t *ctx, int h, int rw, int type, - int target_page_bits) -{ - return _find_pte(ctx, 0, h, rw, type, target_page_bits); -} - -#if defined(TARGET_PPC64) -static inline int find_pte64(mmu_ctx_t *ctx, int h, int rw, int type, - int target_page_bits) -{ - return _find_pte(ctx, 1, h, rw, type, target_page_bits); -} -#endif - static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw, int type, int target_page_bits) { #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) - return find_pte64(ctx, h, rw, type, target_page_bits); + return _find_pte(env, ctx, 1, h, rw, type, target_page_bits); #endif - return find_pte32(ctx, h, rw, type, target_page_bits); + return _find_pte(env, ctx, 0, h, rw, type, target_page_bits); } #if defined(TARGET_PPC64) -static ppc_slb_t *slb_get_entry(CPUPPCState *env, int nr) -{ - ppc_slb_t *retval = &env->slb[nr]; - -#if 0 // XXX implement bridge mode? - if (env->spr[SPR_ASR] & 1) { - target_phys_addr_t sr_base; - - sr_base = env->spr[SPR_ASR] & 0xfffffffffffff000; - sr_base += (12 * nr); - - retval->tmp64 = ldq_phys(sr_base); - retval->tmp = ldl_phys(sr_base + 8); - } -#endif - - return retval; -} - -static void slb_set_entry(CPUPPCState *env, int nr, ppc_slb_t *slb) -{ - ppc_slb_t *entry = &env->slb[nr]; - - if (slb == entry) - return; - - entry->tmp64 = slb->tmp64; - entry->tmp = slb->tmp; -} - -static inline int slb_is_valid(ppc_slb_t *slb) +static inline ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) { - return (int)(slb->tmp64 & 0x0000000008000000ULL); -} + uint64_t esid_256M, esid_1T; + int n; -static inline void slb_invalidate(ppc_slb_t *slb) -{ - slb->tmp64 &= ~0x0000000008000000ULL; -} + LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); -static inline int slb_lookup(CPUPPCState *env, target_ulong eaddr, - target_ulong *vsid, target_ulong *page_mask, - int *attr, int *target_page_bits) -{ - target_ulong mask; - int n, ret; + esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; + esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; - ret = -5; - LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); - mask = 0x0000000000000000ULL; /* Avoid gcc warning */ for (n = 0; n < env->slb_nr; n++) { - ppc_slb_t *slb = slb_get_entry(env, n); + ppc_slb_t *slb = &env->slb[n]; - LOG_SLB("%s: seg %d %016" PRIx64 " %08" - PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp); - if (slb_is_valid(slb)) { - /* SLB entry is valid */ - mask = 0xFFFFFFFFF0000000ULL; - if (slb->tmp & 0x8) { - /* 16 MB PTEs */ - if (target_page_bits) - *target_page_bits = 24; - } else { - /* 4 KB PTEs */ - if (target_page_bits) - *target_page_bits = TARGET_PAGE_BITS; - } - if ((eaddr & mask) == (slb->tmp64 & mask)) { - /* SLB match */ - *vsid = ((slb->tmp64 << 24) | (slb->tmp >> 8)) & 0x0003FFFFFFFFFFFFULL; - *page_mask = ~mask; - *attr = slb->tmp & 0xFF; - ret = n; - break; - } + LOG_SLB("%s: slot %d %016" PRIx64 " %016" + PRIx64 "\n", __func__, n, slb->esid, slb->vsid); + /* We check for 1T matches on all MMUs here - if the MMU + * doesn't have 1T segment support, we will have prevented 1T + * entries from being inserted in the slbmte code. */ + if (((slb->esid == esid_256M) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) + || ((slb->esid == esid_1T) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { + return slb; } } - return ret; + return NULL; } void ppc_slb_invalidate_all (CPUPPCState *env) @@ -761,11 +727,10 @@ do_invalidate = 0; /* XXX: Warning: slbia never invalidates the first segment */ for (n = 1; n < env->slb_nr; n++) { - ppc_slb_t *slb = slb_get_entry(env, n); + ppc_slb_t *slb = &env->slb[n]; - if (slb_is_valid(slb)) { - slb_invalidate(slb); - slb_set_entry(env, n, slb); + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; /* XXX: given the fact that segment size is 256 MB or 1TB, * and we still don't have a tlb_flush_mask(env, n, mask) * in Qemu, we just invalidate all TLBs @@ -779,131 +744,145 @@ void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0) { - target_ulong vsid, page_mask; - int attr; - int n; + ppc_slb_t *slb; - n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL); - if (n >= 0) { - ppc_slb_t *slb = slb_get_entry(env, n); - - if (slb_is_valid(slb)) { - slb_invalidate(slb); - slb_set_entry(env, n, slb); - /* XXX: given the fact that segment size is 256 MB or 1TB, - * and we still don't have a tlb_flush_mask(env, n, mask) - * in Qemu, we just invalidate all TLBs - */ - tlb_flush(env, 1); - } + slb = slb_lookup(env, T0); + if (!slb) { + return; + } + + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; + + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in Qemu, we just invalidate all TLBs + */ + tlb_flush(env, 1); } } -target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr) +int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs) { - target_ulong rt; - ppc_slb_t *slb = slb_get_entry(env, slb_nr); + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; - if (slb_is_valid(slb)) { - /* SLB entry is valid */ - /* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */ - rt = slb->tmp >> 8; /* 65:88 => 40:63 */ - rt |= (slb->tmp64 & 0x7) << 24; /* 62:64 => 37:39 */ - /* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */ - rt |= ((slb->tmp >> 4) & 0xF) << 27; - } else { - rt = 0; + if (rb & (0x1000 - env->slb_nr)) { + return -1; /* Reserved bits set or slot too high */ } - LOG_SLB("%s: %016" PRIx64 " %08" PRIx32 " => %d " - TARGET_FMT_lx "\n", __func__, slb->tmp64, slb->tmp, slb_nr, rt); + if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { + return -1; /* Bad segment size */ + } + if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { + return -1; /* 1T segment on MMU that doesn't support it */ + } + + /* Mask out the slot number as we store the entry */ + slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); + slb->vsid = rs; + + LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 + " %016" PRIx64 "\n", __func__, slot, rb, rs, + slb->esid, slb->vsid); - return rt; + return 0; } -void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs) +int ppc_load_slb_esid (CPUPPCState *env, target_ulong rb, target_ulong *rt) { - ppc_slb_t *slb; - - uint64_t vsid; - uint64_t esid; - int flags, valid, slb_nr; - - vsid = rs >> 12; - flags = ((rs >> 8) & 0xf); - - esid = rb >> 28; - valid = (rb & (1 << 27)); - slb_nr = rb & 0xfff; - - slb = slb_get_entry(env, slb_nr); - slb->tmp64 = (esid << 28) | valid | (vsid >> 24); - slb->tmp = (vsid << 8) | (flags << 3); + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; - LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 - " %08" PRIx32 "\n", __func__, slb_nr, rb, rs, slb->tmp64, - slb->tmp); + if (slot >= env->slb_nr) { + return -1; + } - slb_set_entry(env, slb_nr, slb); + *rt = slb->esid; + return 0; } -#endif /* defined(TARGET_PPC64) */ -/* Perform segment based translation */ -static inline target_phys_addr_t get_pgaddr(target_phys_addr_t sdr1, - int sdr_sh, - target_phys_addr_t hash, - target_phys_addr_t mask) +int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt) { - return (sdr1 & ((target_phys_addr_t)(-1ULL) << sdr_sh)) | (hash & mask); + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (slot >= env->slb_nr) { + return -1; + } + + *rt = slb->vsid; + return 0; } +#endif /* defined(TARGET_PPC64) */ +/* Perform segment based translation */ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int type) { - target_phys_addr_t sdr, hash, mask, sdr_mask, htab_mask; - target_ulong sr, vsid, vsid_mask, pgidx, page_mask; -#if defined(TARGET_PPC64) - int attr; -#endif - int ds, vsid_sh, sdr_sh, pr, target_page_bits; + target_phys_addr_t hash; + target_ulong vsid; + int ds, pr, target_page_bits; int ret, ret2; pr = msr_pr; + ctx->eaddr = eaddr; #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) { + ppc_slb_t *slb; + target_ulong pageaddr; + int segment_bits; + LOG_MMU("Check SLBs\n"); - ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr, - &target_page_bits); - if (ret < 0) - return ret; - ctx->key = ((attr & 0x40) && (pr != 0)) || - ((attr & 0x80) && (pr == 0)) ? 1 : 0; + slb = slb_lookup(env, eaddr); + if (!slb) { + return -5; + } + + if (slb->vsid & SLB_VSID_B) { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; + segment_bits = 40; + } else { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; + segment_bits = 28; + } + + target_page_bits = (slb->vsid & SLB_VSID_L) + ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP) + : (slb->vsid & SLB_VSID_KS)); ds = 0; - ctx->nx = attr & 0x10 ? 1 : 0; - ctx->eaddr = eaddr; - vsid_mask = 0x00003FFFFFFFFF80ULL; - vsid_sh = 7; - sdr_sh = 18; - sdr_mask = 0x3FF80; + ctx->nx = !!(slb->vsid & SLB_VSID_N); + + pageaddr = eaddr & ((1ULL << segment_bits) + - (1ULL << target_page_bits)); + if (slb->vsid & SLB_VSID_B) { + hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); + } else { + hash = vsid ^ (pageaddr >> target_page_bits); + } + /* Only 5 bits of the page index are used in the AVPN */ + ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | + ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); } else #endif /* defined(TARGET_PPC64) */ { + target_ulong sr, pgidx; + sr = env->sr[eaddr >> 28]; - page_mask = 0x0FFFFFFF; ctx->key = (((sr & 0x20000000) && (pr != 0)) || ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; ds = sr & 0x80000000 ? 1 : 0; ctx->nx = sr & 0x10000000 ? 1 : 0; vsid = sr & 0x00FFFFFF; - vsid_mask = 0x01FFFFC0; - vsid_sh = 6; - sdr_sh = 16; - sdr_mask = 0xFFC0; target_page_bits = TARGET_PAGE_BITS; LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx " ir=%d dr=%d pr=%d %d t=%d\n", eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, (int)msr_dr, pr != 0 ? 1 : 0, rw, type); + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + hash = vsid ^ pgidx; + ctx->ptem = (vsid << 7) | (pgidx >> 10); } LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); @@ -912,44 +891,12 @@ /* Check if instruction fetch is allowed, if needed */ if (type != ACCESS_CODE || ctx->nx == 0) { /* Page address translation */ - /* Primary table address */ - sdr = env->sdr1; - pgidx = (eaddr & page_mask) >> target_page_bits; -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - htab_mask = 0x0FFFFFFF >> (28 - (sdr & 0x1F)); - /* XXX: this is false for 1 TB segments */ - hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask; - } else -#endif - { - htab_mask = sdr & 0x000001FF; - hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask; - } - mask = (htab_mask << sdr_sh) | sdr_mask; - LOG_MMU("sdr " TARGET_FMT_plx " sh %d hash " TARGET_FMT_plx - " mask " TARGET_FMT_plx " " TARGET_FMT_lx "\n", - sdr, sdr_sh, hash, mask, page_mask); - ctx->pg_addr[0] = get_pgaddr(sdr, sdr_sh, hash, mask); - /* Secondary table address */ - hash = (~hash) & vsid_mask; - LOG_MMU("sdr " TARGET_FMT_plx " sh %d hash " TARGET_FMT_plx - " mask " TARGET_FMT_plx "\n", sdr, sdr_sh, hash, mask); - ctx->pg_addr[1] = get_pgaddr(sdr, sdr_sh, hash, mask); -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - /* Only 5 bits of the page index are used in the AVPN */ - if (target_page_bits > 23) { - ctx->ptem = (vsid << 12) | - ((pgidx << (target_page_bits - 16)) & 0xF80); - } else { - ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80); - } - } else -#endif - { - ctx->ptem = (vsid << 7) | (pgidx >> 10); - } + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; + /* Initialize real address with an invalid value */ ctx->raddr = (target_phys_addr_t)-1ULL; if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx || @@ -957,19 +904,20 @@ /* Software TLB search */ ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); } else { - LOG_MMU("0 sdr1=" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " " - "api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx - " pg_addr=" TARGET_FMT_plx "\n", - sdr, vsid, pgidx, hash, ctx->pg_addr[0]); + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ctx->ptem, + ctx->hash[0]); /* Primary table lookup */ ret = find_pte(env, ctx, 0, rw, type, target_page_bits); if (ret < 0) { /* Secondary table lookup */ if (eaddr != 0xEFFFFFFF) - LOG_MMU("1 sdr1=" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " " - "api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx - " pg_addr=" TARGET_FMT_plx "\n", sdr, vsid, - pgidx, hash, ctx->pg_addr[1]); + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); ret2 = find_pte(env, ctx, 1, rw, type, target_page_bits); if (ret2 != -1) @@ -1000,8 +948,24 @@ ret = -3; } } else { + target_ulong sr; LOG_MMU("direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ + + /* Direct-store implies a 32-bit MMU. + * Check the Segment Register's bus unit ID (BUID). + */ + sr = env->sr[eaddr >> 28]; + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* Memory-forced I/O controller interface access */ + /* If T=1 and BUID=x'07F', the 601 performs a memory access + * to SR[28-31] LA[4-31], bypassing all protection mechanisms. + */ + ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + switch (type) { case ACCESS_INT: /* Integer load/store : only access allowed */ @@ -1042,10 +1006,10 @@ } /* Generic TLB check function for embedded PowerPC implementations */ -static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb, - target_phys_addr_t *raddrp, - target_ulong address, uint32_t pid, int ext, - int i) +int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb, + target_phys_addr_t *raddrp, + target_ulong address, uint32_t pid, int ext, + int i) { target_ulong mask; @@ -1055,8 +1019,8 @@ } mask = ~(tlb->size - 1); LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx - " " TARGET_FMT_lx " %u\n", __func__, i, address, pid, tlb->EPN, - mask, (uint32_t)tlb->PID); + " " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN, + mask, (uint32_t)tlb->PID, tlb->prot); /* Check PID */ if (tlb->PID != 0 && tlb->PID != pid) return -1; @@ -1084,7 +1048,7 @@ /* Default return value is no match */ ret = -1; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { ret = i; break; @@ -1101,7 +1065,7 @@ int i; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; tlb->prot &= ~PAGE_VALID; } tlb_flush(env, 1); @@ -1117,7 +1081,7 @@ int i; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) { end = tlb->EPN + tlb->size; for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) @@ -1142,7 +1106,7 @@ raddr = (target_phys_addr_t)-1ULL; pr = msr_pr; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, address, env->spr[SPR_40x_PID], 0, i) < 0) continue; @@ -1202,48 +1166,301 @@ env->spr[SPR_405_SLER] = val; } +static inline int mmubooke_check_tlb (CPUState *env, ppcemb_tlb_t *tlb, + target_phys_addr_t *raddr, int *prot, + target_ulong address, int rw, + int access_type, int i) +{ + int ret, _prot; + + if (ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID], + !env->nb_pids, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1], 0, i) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2], 0, i) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + _prot = tlb->prot & 0xF; + } else { + _prot = (tlb->prot >> 4) & 0xF; + } + + /* Check the address space */ + if (access_type == ACCESS_CODE) { + if (msr_ir != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = _prot; + if (_prot & PAGE_EXEC) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, _prot); + ret = -3; + } else { + if (msr_dr != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = _prot; + if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) { + LOG_SWTLB("%s: found TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, _prot); + ret = -2; + } + + return ret; +} + static int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong address, int rw, int access_type) { ppcemb_tlb_t *tlb; target_phys_addr_t raddr; - int i, prot, ret; + int i, ret; ret = -1; raddr = (target_phys_addr_t)-1ULL; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; - if (ppcemb_tlb_check(env, tlb, &raddr, address, - env->spr[SPR_BOOKE_PID], 1, i) < 0) - continue; - if (msr_pr != 0) - prot = tlb->prot & 0xF; - else - prot = (tlb->prot >> 4) & 0xF; - /* Check the address space */ - if (access_type == ACCESS_CODE) { - if (msr_ir != (tlb->attr & 1)) - continue; - ctx->prot = prot; - if (prot & PAGE_EXEC) { - ret = 0; - break; + tlb = &env->tlb.tlbe[i]; + ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw, + access_type, i); + if (!ret) { + break; + } + } + + if (ret >= 0) { + ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } + + return ret; +} + +void booke206_flush_tlb(CPUState *env, int flags, const int check_iprot) +{ + int tlb_size; + int i, j; + ppcmas_tlb_t *tlb = env->tlb.tlbm; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + if (flags & (1 << i)) { + tlb_size = booke206_tlb_size(env, i); + for (j = 0; j < tlb_size; j++) { + if (!check_iprot || !(tlb[j].mas1 & MAS1_IPROT)) { + tlb[j].mas1 &= ~MAS1_VALID; + } } - ret = -3; - } else { - if (msr_dr != (tlb->attr & 1)) - continue; - ctx->prot = prot; - if ((!rw && prot & PAGE_READ) || (rw && (prot & PAGE_WRITE))) { - ret = 0; - break; + } + tlb += booke206_tlb_size(env, i); + } + + tlb_flush(env, 1); +} + +target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb) +{ + uint32_t tlbncfg; + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + target_phys_addr_t tlbm_size; + + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + + if (tlbncfg & TLBnCFG_AVAIL) { + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + } else { + tlbm_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT; + } + + return (1 << (tlbm_size << 1)) << 10; +} + +/* TLB check function for MAS based SoftTLBs */ +int ppcmas_tlb_check(CPUState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddrp, + target_ulong address, uint32_t pid) +{ + target_ulong mask; + uint32_t tlb_pid; + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%" + PRIx64 " mask=0x" TARGET_FMT_lx " MAS7_3=0x%" PRIx64 " MAS8=%x\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, tlb->mas7_3, + tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + + return 0; +} + +static int mmubooke206_check_tlb(CPUState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddr, int *prot, + target_ulong address, int rw, + int access_type) +{ + int ret; + int _prot = 0; + + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + if (tlb->mas7_3 & MAS3_UR) { + _prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + _prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + _prot |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + _prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + _prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + _prot |= PAGE_EXEC; + } + } + + /* Check the address space and permissions */ + if (access_type == ACCESS_CODE) { + if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = _prot; + if (_prot & PAGE_EXEC) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, _prot); + ret = -3; + } else { + if (msr_dr != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = _prot; + if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) { + LOG_SWTLB("%s: found TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, _prot); + ret = -2; + } + + return ret; +} + +static int mmubooke206_get_physical_address(CPUState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcmas_tlb_t *tlb; + target_phys_addr_t raddr; + int i, j, ret; + + ret = -1; + raddr = (target_phys_addr_t)-1ULL; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, + rw, access_type); + if (ret != -1) { + goto found_tlb; } - ret = -2; } } - if (ret >= 0) + +found_tlb: + + if (ret >= 0) { ctx->raddr = raddr; + LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, ctx->raddr, ctx->prot, + ret); + } else { + LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx + " %d %d\n", __func__, address, raddr, ctx->prot, ret); + } return ret; } @@ -1269,6 +1486,7 @@ #if defined(TARGET_PPC64) case POWERPC_MMU_620: case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: /* Real address are 60 bits long */ ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL; ctx->prot |= PAGE_WRITE; @@ -1302,9 +1520,8 @@ /* XXX: TODO */ cpu_abort(env, "MPC8xx MMU model is not implemented\n"); break; - case POWERPC_MMU_BOOKE_FSL: - /* XXX: TODO */ - cpu_abort(env, "BookE FSL MMU model not implemented\n"); + case POWERPC_MMU_BOOKE206: + cpu_abort(env, "BookE 2.06 MMU doesn't have physical real mode\n"); break; default: cpu_abort(env, "Unknown or invalid MMU model\n"); @@ -1329,6 +1546,9 @@ IS and DS bits only affect the address space. */ ret = mmubooke_get_physical_address(env, ctx, eaddr, rw, access_type); + } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { + ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, + access_type); } else { /* No address translation. */ ret = check_physical(env, ctx, eaddr, rw); @@ -1346,6 +1566,7 @@ #if defined(TARGET_PPC64) case POWERPC_MMU_620: case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: #endif if (ret < 0) { /* We didn't match any BAT entry or don't have BATs */ @@ -1361,14 +1582,14 @@ ret = mmubooke_get_physical_address(env, ctx, eaddr, rw, access_type); break; + case POWERPC_MMU_BOOKE206: + ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, + access_type); + break; case POWERPC_MMU_MPC8xx: /* XXX: TODO */ cpu_abort(env, "MPC8xx MMU model is not implemented\n"); break; - case POWERPC_MMU_BOOKE_FSL: - /* XXX: TODO */ - cpu_abort(env, "BookE FSL MMU model not implemented\n"); - return -1; case POWERPC_MMU_REAL: cpu_abort(env, "PowerPC in real mode do not do any translation\n"); return -1; @@ -1395,6 +1616,46 @@ return ctx.raddr & TARGET_PAGE_MASK; } +static void booke206_update_mas_tlb_miss(CPUState *env, target_ulong address, + int rw) +{ + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS6] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + /* AS */ + if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; + } + + env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; + env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT; + break; + case MAS4_TIDSELD_PID1: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT; + break; + case MAS4_TIDSELD_PID2: + env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT; + break; + } + + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + /* Perform address translation */ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu) @@ -1445,19 +1706,19 @@ #if defined(TARGET_PPC64) case POWERPC_MMU_620: case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: #endif env->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x40000000; break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, address, rw); + /* fall through */ case POWERPC_MMU_BOOKE: 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 */ - cpu_abort(env, "BookE FSL MMU model is not implemented\n"); - return -1; case POWERPC_MMU_MPC8xx: /* XXX: TODO */ cpu_abort(env, "MPC8xx MMU model is not implemented\n"); @@ -1478,7 +1739,8 @@ break; case -3: /* No execute protection violation */ - if (env->mmu_model == POWERPC_MMU_BOOKE) { + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { env->spr[SPR_BOOKE_ESR] = 0x00000000; } env->exception_index = POWERPC_EXCP_ISI; @@ -1521,8 +1783,10 @@ env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; tlb_miss: env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = ctx.pg_addr[0]; - env->spr[SPR_HASH2] = ctx.pg_addr[1]; + env->spr[SPR_HASH1] = env->htab_base + + get_pteg_offset(env, ctx.hash[0], HASH_PTE_SIZE_32); + env->spr[SPR_HASH2] = env->htab_base + + get_pteg_offset(env, ctx.hash[1], HASH_PTE_SIZE_32); break; case POWERPC_MMU_SOFT_74xx: if (rw == 1) { @@ -1552,6 +1816,7 @@ #if defined(TARGET_PPC64) case POWERPC_MMU_620: case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: #endif env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; @@ -1565,16 +1830,15 @@ /* XXX: TODO */ cpu_abort(env, "MPC8xx MMU model is not implemented\n"); break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, address, rw); + /* fall through */ case POWERPC_MMU_BOOKE: 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 */ - cpu_abort(env, "BookE FSL MMU model is not implemented\n"); - return -1; case POWERPC_MMU_REAL: cpu_abort(env, "PowerPC in real mode should never raise " "any MMU exceptions\n"); @@ -1594,7 +1858,8 @@ if (rw) { env->spr[SPR_40x_ESR] |= 0x00800000; } - } else if (env->mmu_model == POWERPC_MMU_BOOKE) { + } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { env->spr[SPR_BOOKE_DEAR] = address; env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0; } else { @@ -1865,16 +2130,15 @@ case POWERPC_MMU_BOOKE: tlb_flush(env, 1); break; - case POWERPC_MMU_BOOKE_FSL: - /* XXX: TODO */ - if (!kvm_enabled()) - cpu_abort(env, "BookE MMU model is not implemented\n"); + case POWERPC_MMU_BOOKE206: + booke206_flush_tlb(env, -1, 0); break; case POWERPC_MMU_32B: case POWERPC_MMU_601: #if defined(TARGET_PPC64) case POWERPC_MMU_620: case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: #endif /* defined(TARGET_PPC64) */ tlb_flush(env, 1); break; @@ -1911,9 +2175,9 @@ /* XXX: TODO */ cpu_abort(env, "BookE MMU model is not implemented\n"); break; - case POWERPC_MMU_BOOKE_FSL: + case POWERPC_MMU_BOOKE206: /* XXX: TODO */ - cpu_abort(env, "BookE FSL MMU model is not implemented\n"); + cpu_abort(env, "BookE 2.06 MMU model is not implemented\n"); break; case POWERPC_MMU_32B: case POWERPC_MMU_601: @@ -1942,6 +2206,7 @@ #if defined(TARGET_PPC64) case POWERPC_MMU_620: case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: /* tlbie invalidate TLBs for all segments */ /* XXX: given the fact that there are too many segments to invalidate, * and we still don't have a tlb_flush_mask(env, n, mask) in Qemu, @@ -1975,11 +2240,26 @@ void ppc_store_sdr1 (CPUPPCState *env, target_ulong value) { LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value); - if (env->sdr1 != value) { - /* XXX: for PowerPC 64, should check that the HTABSIZE value - * is <= 28 - */ - env->sdr1 = value; + if (env->spr[SPR_SDR1] != value) { + env->spr[SPR_SDR1] = value; +#if defined(TARGET_PPC64) + if (env->mmu_model & POWERPC_MMU_64) { + target_ulong htabsize = value & SDR_64_HTABSIZE; + + if (htabsize > 28) { + fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx + " stored in SDR1\n", htabsize); + htabsize = 28; + } + env->htab_mask = (1ULL << (htabsize + 18)) - 1; + env->htab_base = value & SDR_64_HTABORG; + } else +#endif /* defined(TARGET_PPC64) */ + { + /* FIXME: Should check for valid HTABMASK values */ + env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; + env->htab_base = value & SDR_32_HTABORG; + } tlb_flush(env, 1); } } @@ -2010,7 +2290,7 @@ /* VSID = VSID */ rs |= (value & 0xfffffff) << 12; /* flags = flags */ - rs |= ((value >> 27) & 0xf) << 9; + rs |= ((value >> 27) & 0xf) << 8; ppc_store_slb(env, rb, rs); } else @@ -2229,6 +2509,10 @@ case POWERPC_EXCP_SYSCALL: /* System call exception */ dump_syscall(env); lev = env->error_code; + if ((lev == 1) && cpu_ppc_hypercall) { + cpu_ppc_hypercall(env); + return; + } if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) new_msr |= (target_ulong)MSR_HVB; goto store_next; @@ -2611,7 +2895,8 @@ env->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_BOOKE) { + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { /* 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. */ diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/helper.h qemu-kvm-0.15.0+noroms/target-ppc/helper.h --- qemu-kvm-0.14.1+noroms/target-ppc/helper.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -38,10 +38,11 @@ DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) +DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) DEF_HELPER_2(sraw, tl, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) -DEF_HELPER_FLAGS_1(popcntb_64, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) +DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl) DEF_HELPER_2(srad, tl, tl, tl) #endif @@ -50,9 +51,7 @@ DEF_HELPER_FLAGS_2(brinc, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl, tl) DEF_HELPER_0(float_check_status, void) -#ifdef CONFIG_SOFTFLOAT DEF_HELPER_0(reset_fpstatus, void) -#endif DEF_HELPER_2(compute_fprf, i32, i64, i32) DEF_HELPER_2(store_fpscr, void, i64, i32) DEF_HELPER_1(fpscr_clrbit, void, i32) @@ -333,6 +332,12 @@ DEF_HELPER_2(440_tlbre, tl, i32, tl) DEF_HELPER_3(440_tlbwe, void, i32, tl, tl) DEF_HELPER_1(440_tlbsx, tl, tl) +DEF_HELPER_0(booke206_tlbre, void) +DEF_HELPER_0(booke206_tlbwe, void) +DEF_HELPER_1(booke206_tlbsx, void, tl) +DEF_HELPER_1(booke206_tlbivax, void, tl) +DEF_HELPER_1(booke206_tlbflush, void, i32) +DEF_HELPER_2(booke_setpid, void, i32, tl) DEF_HELPER_1(6xx_tlbd, void, tl) DEF_HELPER_1(6xx_tlbi, void, tl) DEF_HELPER_1(74xx_tlbd, void, tl) @@ -340,8 +345,9 @@ DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void) DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_1(load_slb, TCG_CALL_CONST, tl, tl) DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl) +DEF_HELPER_1(load_slb_esid, tl, tl) +DEF_HELPER_1(load_slb_vsid, tl, tl) DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void) DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl) #endif @@ -375,6 +381,7 @@ #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) DEF_HELPER_1(store_asr, void, tl) +DEF_HELPER_0(load_purr, tl) #endif DEF_HELPER_1(store_sdr1, void, tl) DEF_HELPER_1(store_tbl, void, tl) diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/kvm.c qemu-kvm-0.15.0+noroms/target-ppc/kvm.c --- qemu-kvm-0.14.1+noroms/target-ppc/kvm.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/kvm.c 2011-08-09 07:40:29.000000000 -0500 @@ -2,6 +2,7 @@ * PowerPC implementation of KVM hooks * * Copyright IBM Corp. 2007 + * Copyright (C) 2011 Freescale Semiconductor, Inc. * * Authors: * Jerone Young @@ -43,6 +44,8 @@ static int cap_interrupt_unset = false; static int cap_interrupt_level = false; +static int cap_segstate; +static int cap_booke_sregs; /* 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 @@ -62,12 +65,10 @@ 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 + cap_segstate = kvm_check_extension(s, KVM_CAP_PPC_SEGSTATE); + cap_booke_sregs = kvm_check_extension(s, KVM_CAP_PPC_BOOKE_SREGS); if (!cap_interrupt_level) { fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " @@ -77,15 +78,43 @@ return 0; } -int kvm_arch_init_vcpu(CPUState *cenv) +static int kvm_arch_sync_sregs(CPUState *cenv) { - int ret = 0; struct kvm_sregs sregs; + int ret; + + if (cenv->excp_model == POWERPC_EXCP_BOOKE) { + /* What we're really trying to say is "if we're on BookE, we use + the native PVR for now". This is the only sane way to check + it though, so we potentially confuse users that they can run + BookE guests on BookS. Let's hope nobody dares enough :) */ + return 0; + } else { + if (!cap_segstate) { + fprintf(stderr, "kvm error: missing PVR setting capability\n"); + return -ENOSYS; + } + } + + ret = kvm_vcpu_ioctl(cenv, KVM_GET_SREGS, &sregs); + if (ret) { + return ret; + } sregs.pvr = cenv->spr[SPR_PVR]; - ret = kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs); + return kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs); +} - idle_timer = qemu_new_timer(vm_clock, kvm_kick_env, cenv); +int kvm_arch_init_vcpu(CPUState *cenv) +{ + int ret; + + ret = kvm_arch_sync_sregs(cenv); + if (ret) { + return ret; + } + + idle_timer = qemu_new_timer_ns(vm_clock, kvm_kick_env, cenv); return ret; } @@ -122,6 +151,8 @@ regs.sprg6 = env->spr[SPR_SPRG6]; regs.sprg7 = env->spr[SPR_SPRG7]; + regs.pid = env->spr[SPR_BOOKE_PID]; + for (i = 0;i < 32; i++) regs.gpr[i] = env->gpr[i]; @@ -136,15 +167,18 @@ { struct kvm_regs regs; struct kvm_sregs sregs; + uint32_t cr; int i, ret; ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); if (ret < 0) return ret; - ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); - if (ret < 0) - return ret; + cr = regs.cr; + for (i = 7; i >= 0; i--) { + env->crf[i] = cr & 15; + cr >>= 4; + } env->ctr = regs.ctr; env->lr = regs.lr; @@ -164,12 +198,122 @@ env->spr[SPR_SPRG6] = regs.sprg6; env->spr[SPR_SPRG7] = regs.sprg7; + env->spr[SPR_BOOKE_PID] = regs.pid; + for (i = 0;i < 32; i++) env->gpr[i] = regs.gpr[i]; -#ifdef KVM_CAP_PPC_SEGSTATE - if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_SEGSTATE)) { - env->sdr1 = sregs.u.s.sdr1; + if (cap_booke_sregs) { + ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (ret < 0) { + return ret; + } + + if (sregs.u.e.features & KVM_SREGS_E_BASE) { + env->spr[SPR_BOOKE_CSRR0] = sregs.u.e.csrr0; + env->spr[SPR_BOOKE_CSRR1] = sregs.u.e.csrr1; + env->spr[SPR_BOOKE_ESR] = sregs.u.e.esr; + env->spr[SPR_BOOKE_DEAR] = sregs.u.e.dear; + env->spr[SPR_BOOKE_MCSR] = sregs.u.e.mcsr; + env->spr[SPR_BOOKE_TSR] = sregs.u.e.tsr; + env->spr[SPR_BOOKE_TCR] = sregs.u.e.tcr; + env->spr[SPR_DECR] = sregs.u.e.dec; + env->spr[SPR_TBL] = sregs.u.e.tb & 0xffffffff; + env->spr[SPR_TBU] = sregs.u.e.tb >> 32; + env->spr[SPR_VRSAVE] = sregs.u.e.vrsave; + } + + if (sregs.u.e.features & KVM_SREGS_E_ARCH206) { + env->spr[SPR_BOOKE_PIR] = sregs.u.e.pir; + env->spr[SPR_BOOKE_MCSRR0] = sregs.u.e.mcsrr0; + env->spr[SPR_BOOKE_MCSRR1] = sregs.u.e.mcsrr1; + env->spr[SPR_BOOKE_DECAR] = sregs.u.e.decar; + env->spr[SPR_BOOKE_IVPR] = sregs.u.e.ivpr; + } + + if (sregs.u.e.features & KVM_SREGS_E_64) { + env->spr[SPR_BOOKE_EPCR] = sregs.u.e.epcr; + } + + if (sregs.u.e.features & KVM_SREGS_E_SPRG8) { + env->spr[SPR_BOOKE_SPRG8] = sregs.u.e.sprg8; + } + + if (sregs.u.e.features & KVM_SREGS_E_IVOR) { + env->spr[SPR_BOOKE_IVOR0] = sregs.u.e.ivor_low[0]; + env->spr[SPR_BOOKE_IVOR1] = sregs.u.e.ivor_low[1]; + env->spr[SPR_BOOKE_IVOR2] = sregs.u.e.ivor_low[2]; + env->spr[SPR_BOOKE_IVOR3] = sregs.u.e.ivor_low[3]; + env->spr[SPR_BOOKE_IVOR4] = sregs.u.e.ivor_low[4]; + env->spr[SPR_BOOKE_IVOR5] = sregs.u.e.ivor_low[5]; + env->spr[SPR_BOOKE_IVOR6] = sregs.u.e.ivor_low[6]; + env->spr[SPR_BOOKE_IVOR7] = sregs.u.e.ivor_low[7]; + env->spr[SPR_BOOKE_IVOR8] = sregs.u.e.ivor_low[8]; + env->spr[SPR_BOOKE_IVOR9] = sregs.u.e.ivor_low[9]; + env->spr[SPR_BOOKE_IVOR10] = sregs.u.e.ivor_low[10]; + env->spr[SPR_BOOKE_IVOR11] = sregs.u.e.ivor_low[11]; + env->spr[SPR_BOOKE_IVOR12] = sregs.u.e.ivor_low[12]; + env->spr[SPR_BOOKE_IVOR13] = sregs.u.e.ivor_low[13]; + env->spr[SPR_BOOKE_IVOR14] = sregs.u.e.ivor_low[14]; + env->spr[SPR_BOOKE_IVOR15] = sregs.u.e.ivor_low[15]; + + if (sregs.u.e.features & KVM_SREGS_E_SPE) { + env->spr[SPR_BOOKE_IVOR32] = sregs.u.e.ivor_high[0]; + env->spr[SPR_BOOKE_IVOR33] = sregs.u.e.ivor_high[1]; + env->spr[SPR_BOOKE_IVOR34] = sregs.u.e.ivor_high[2]; + } + + if (sregs.u.e.features & KVM_SREGS_E_PM) { + env->spr[SPR_BOOKE_IVOR35] = sregs.u.e.ivor_high[3]; + } + + if (sregs.u.e.features & KVM_SREGS_E_PC) { + env->spr[SPR_BOOKE_IVOR36] = sregs.u.e.ivor_high[4]; + env->spr[SPR_BOOKE_IVOR37] = sregs.u.e.ivor_high[5]; + } + } + + if (sregs.u.e.features & KVM_SREGS_E_ARCH206_MMU) { + env->spr[SPR_BOOKE_MAS0] = sregs.u.e.mas0; + env->spr[SPR_BOOKE_MAS1] = sregs.u.e.mas1; + env->spr[SPR_BOOKE_MAS2] = sregs.u.e.mas2; + env->spr[SPR_BOOKE_MAS3] = sregs.u.e.mas7_3 & 0xffffffff; + env->spr[SPR_BOOKE_MAS4] = sregs.u.e.mas4; + env->spr[SPR_BOOKE_MAS6] = sregs.u.e.mas6; + env->spr[SPR_BOOKE_MAS7] = sregs.u.e.mas7_3 >> 32; + env->spr[SPR_MMUCFG] = sregs.u.e.mmucfg; + env->spr[SPR_BOOKE_TLB0CFG] = sregs.u.e.tlbcfg[0]; + env->spr[SPR_BOOKE_TLB1CFG] = sregs.u.e.tlbcfg[1]; + } + + if (sregs.u.e.features & KVM_SREGS_EXP) { + env->spr[SPR_BOOKE_EPR] = sregs.u.e.epr; + } + + if (sregs.u.e.features & KVM_SREGS_E_PD) { + env->spr[SPR_BOOKE_EPLC] = sregs.u.e.eplc; + env->spr[SPR_BOOKE_EPSC] = sregs.u.e.epsc; + } + + if (sregs.u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { + env->spr[SPR_E500_SVR] = sregs.u.e.impl.fsl.svr; + env->spr[SPR_Exxx_MCAR] = sregs.u.e.impl.fsl.mcar; + env->spr[SPR_HID0] = sregs.u.e.impl.fsl.hid0; + + if (sregs.u.e.impl.fsl.features & KVM_SREGS_E_FSL_PIDn) { + env->spr[SPR_BOOKE_PID1] = sregs.u.e.impl.fsl.pid1; + env->spr[SPR_BOOKE_PID2] = sregs.u.e.impl.fsl.pid2; + } + } + } + + if (cap_segstate) { + ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (ret < 0) { + return ret; + } + + ppc_store_sdr1(env, sregs.u.s.sdr1); /* Sync SLB */ #ifdef TARGET_PPC64 @@ -192,7 +336,6 @@ env->IBAT[1][i] = sregs.u.s.ppc32.ibat[i] >> 32; } } -#endif return 0; } @@ -222,7 +365,7 @@ #define PPC_INPUT_INT PPC6xx_INPUT_INT #endif -int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) +void kvm_arch_pre_run(CPUState *env, struct kvm_run *run) { int r; unsigned irq; @@ -246,22 +389,20 @@ printf("cpu %d fail inject %x\n", env->cpu_index, irq); /* Always wake up soon in case the interrupt was level based */ - qemu_mod_timer(idle_timer, qemu_get_clock(vm_clock) + + qemu_mod_timer(idle_timer, qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50)); } /* We don't know if there are more interrupts pending after this. However, * the guest will return to userspace in the course of handling this one * anyways, so we will get a chance to deliver the rest. */ - return 0; } -int kvm_arch_post_run(CPUState *env, struct kvm_run *run) +void kvm_arch_post_run(CPUState *env, struct kvm_run *run) { - return 0; } -int kvm_arch_process_irqchip_events(CPUState *env) +int kvm_arch_process_async_events(CPUState *env) { return 0; } @@ -273,7 +414,7 @@ env->exception_index = EXCP_HLT; } - return 1; + return 0; } /* map dcr access to existing qemu dcr emulation */ @@ -282,7 +423,7 @@ if (ppc_dcr_read(env->dcr_env, dcrn, data) < 0) fprintf(stderr, "Read to unhandled DCR (0x%x)\n", dcrn); - return 1; + return 0; } static int kvmppc_handle_dcr_write(CPUState *env, uint32_t dcrn, uint32_t data) @@ -290,12 +431,12 @@ if (ppc_dcr_write(env->dcr_env, dcrn, data) < 0) fprintf(stderr, "Write to unhandled DCR (0x%x)\n", dcrn); - return 1; + return 0; } int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run) { - int ret = 0; + int ret; switch (run->exit_reason) { case KVM_EXIT_DCR: @@ -372,7 +513,6 @@ { 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) && @@ -381,7 +521,6 @@ return 0; } -#endif /* * Fallback to always fail hypercalls: @@ -404,3 +543,13 @@ { return true; } + +int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr) +{ + return 1; +} + +int kvm_arch_on_sigbus(int code, void *addr) +{ + return 1; +} diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/kvm_ppc.c qemu-kvm-0.15.0+noroms/target-ppc/kvm_ppc.c --- qemu-kvm-0.14.1+noroms/target-ppc/kvm_ppc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/kvm_ppc.c 2011-08-09 07:40:29.000000000 -0500 @@ -89,7 +89,7 @@ static void kvmppc_timer_hack(void *opaque) { qemu_service_io(); - qemu_mod_timer(kvmppc_timer, qemu_get_clock(vm_clock) + kvmppc_timer_rate); + qemu_mod_timer(kvmppc_timer, qemu_get_clock_ns(vm_clock) + kvmppc_timer_rate); } void kvmppc_init(void) @@ -99,7 +99,7 @@ * run. So, until Qemu gains IO threads, we create this timer to ensure * that the device model gets a chance to run. */ kvmppc_timer_rate = get_ticks_per_sec() / 10; - kvmppc_timer = qemu_new_timer(vm_clock, &kvmppc_timer_hack, NULL); - qemu_mod_timer(kvmppc_timer, qemu_get_clock(vm_clock) + kvmppc_timer_rate); + kvmppc_timer = qemu_new_timer_ns(vm_clock, &kvmppc_timer_hack, NULL); + qemu_mod_timer(kvmppc_timer, qemu_get_clock_ns(vm_clock) + kvmppc_timer_rate); } diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/kvm_ppc.h qemu-kvm-0.15.0+noroms/target-ppc/kvm_ppc.h --- qemu-kvm-0.14.1+noroms/target-ppc/kvm_ppc.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/kvm_ppc.h 2011-08-09 07:40:29.000000000 -0500 @@ -11,13 +11,33 @@ void kvmppc_init(void); void kvmppc_fdt_update(void *fdt); +#ifndef CONFIG_KVM +static inline int kvmppc_read_host_property(const char *node_path, const char *prop, + void *val, size_t len) +{ + assert(0); + return -ENOSYS; +} +#else int kvmppc_read_host_property(const char *node_path, const char *prop, void *val, size_t len); +#endif 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 CONFIG_KVM +#define kvmppc_eieio() do { } while (0) +#else +#define kvmppc_eieio() \ + do { \ + if (kvm_enabled()) { \ + asm volatile("eieio" : : : "memory"); \ + } \ + } while (0) +#endif + #ifndef KVM_INTERRUPT_SET #define KVM_INTERRUPT_SET -1 #endif diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/libkvm.c qemu-kvm-0.15.0+noroms/target-ppc/libkvm.c --- qemu-kvm-0.14.1+noroms/target-ppc/libkvm.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/libkvm.c 1969-12-31 18:00:00.000000000 -0600 @@ -1,102 +0,0 @@ -/* - * This file contains the powerpc specific implementation for the - * architecture dependent functions defined in kvm-common.h and - * libkvm.h - * - * Copyright (C) 2006 Qumranet, Inc. - * - * Authors: - * Avi Kivity - * Yaniv Kamay - * - * Copyright IBM Corp. 2007,2008 - * Authors: - * Jerone Young - * Christian Ehrhardt - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#include "libkvm-all.h" -#include "libkvm.h" -#include -#include -#include - -int handle_dcr(kvm_vcpu_context_t vcpu) -{ - int ret = 0; - struct kvm_run *run = vcpu->run; - kvm_context_t kvm = vcpu->kvm; - - if (run->dcr.is_write) - ret = kvm->callbacks->powerpc_dcr_write(vcpu, - run->dcr.dcrn, - run->dcr.data); - else - ret = kvm->callbacks->powerpc_dcr_read(vcpu, - run->dcr.dcrn, - &(run->dcr.data)); - - return ret; -} - -void kvm_show_code(kvm_vcpu_context_t vcpu) -{ - fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__); -} - -void kvm_show_regs(kvm_vcpu_context_t vcpu) -{ - struct kvm_regs regs; - int i; - - if (kvm_get_regs(vcpu, ®s)) - return; - - fprintf(stderr,"guest vcpu #%d\n", vcpu); - fprintf(stderr,"pc: %016"PRIx64" msr: %016"PRIx64"\n", - regs.pc, regs.msr); - fprintf(stderr,"lr: %016"PRIx64" ctr: %016"PRIx64"\n", - regs.lr, regs.ctr); - fprintf(stderr,"srr0: %016"PRIx64" srr1: %016"PRIx64"\n", - regs.srr0, regs.srr1); - for (i=0; i<32; i+=4) - { - fprintf(stderr, "gpr%02d: %016"PRIx64" %016"PRIx64" %016"PRIx64 - " %016"PRIx64"\n", i, - regs.gpr[i], - regs.gpr[i+1], - regs.gpr[i+2], - regs.gpr[i+3]); - } - - fflush(stdout); -} - -int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, - void **vm_mem) -{ - int r; - - r = kvm_init_coalesced_mmio(kvm); - if (r < 0) - return r; - - return 0; -} - -int kvm_arch_run(kvm_vcpu_context_t vcpu) -{ - int ret = 0; - - switch (vcpu->run->exit_reason){ - case KVM_EXIT_DCR: - ret = handle_dcr(vcpu); - break; - default: - ret = 1; - break; - } - return ret; -} diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/libkvm.h qemu-kvm-0.15.0+noroms/target-ppc/libkvm.h --- qemu-kvm-0.14.1+noroms/target-ppc/libkvm.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/libkvm.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,36 +0,0 @@ -/* - * This header is for functions & variables that will ONLY be - * used inside libkvm for powerpc. - * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE - * WITHIN LIBKVM. - * - * Copyright (C) 2006 Qumranet, Inc. - * - * Authors: - * Avi Kivity - * Yaniv Kamay - * - * Copyright 2007 IBM Corporation. - * Added by: Jerone Young - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#ifndef KVM_POWERPC_H -#define KVM_POWERPC_H - -#include "libkvm-all.h" - -extern int kvm_page_size; - -#define PAGE_SIZE kvm_page_size -#define PAGE_MASK (~(PAGE_SIZE - 1)) - -static inline void eieio(void) -{ - asm volatile("eieio" : : : "memory"); -} - -#define smp_wmb() eieio() - -#endif diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/machine.c qemu-kvm-0.15.0+noroms/target-ppc/machine.c --- qemu-kvm-0.14.1+noroms/target-ppc/machine.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/machine.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,7 +1,6 @@ #include "hw/hw.h" #include "hw/boards.h" #include "kvm.h" -#include "qemu-kvm.h" void cpu_save(QEMUFile *f, void *opaque) { @@ -38,7 +37,7 @@ qemu_put_betls(f, &env->asr); qemu_put_sbe32s(f, &env->slb_nr); #endif - qemu_put_betls(f, &env->sdr1); + qemu_put_betls(f, &env->spr[SPR_SDR1]); for (i = 0; i < 32; i++) qemu_put_betls(f, &env->sr[i]); for (i = 0; i < 2; i++) @@ -53,12 +52,12 @@ qemu_put_sbe32s(f, &env->last_way); qemu_put_sbe32s(f, &env->id_tlbs); qemu_put_sbe32s(f, &env->nb_pids); - if (env->tlb) { + if (env->tlb.tlb6) { // XXX assumes 6xx for (i = 0; i < env->nb_tlb; i++) { - qemu_put_betls(f, &env->tlb[i].tlb6.pte0); - qemu_put_betls(f, &env->tlb[i].tlb6.pte1); - qemu_put_betls(f, &env->tlb[i].tlb6.EPN); + qemu_put_betls(f, &env->tlb.tlb6[i].pte0); + qemu_put_betls(f, &env->tlb.tlb6[i].pte1); + qemu_put_betls(f, &env->tlb.tlb6[i].EPN); } } for (i = 0; i < 4; i++) @@ -94,6 +93,7 @@ { CPUState *env = (CPUState *)opaque; unsigned int i, j; + target_ulong sdr1; for (i = 0; i < 32; i++) qemu_get_betls(f, &env->gpr[i]); @@ -125,7 +125,7 @@ qemu_get_betls(f, &env->asr); qemu_get_sbe32s(f, &env->slb_nr); #endif - qemu_get_betls(f, &env->sdr1); + qemu_get_betls(f, &sdr1); for (i = 0; i < 32; i++) qemu_get_betls(f, &env->sr[i]); for (i = 0; i < 2; i++) @@ -140,12 +140,12 @@ qemu_get_sbe32s(f, &env->last_way); qemu_get_sbe32s(f, &env->id_tlbs); qemu_get_sbe32s(f, &env->nb_pids); - if (env->tlb) { + if (env->tlb.tlb6) { // XXX assumes 6xx for (i = 0; i < env->nb_tlb; i++) { - qemu_get_betls(f, &env->tlb[i].tlb6.pte0); - qemu_get_betls(f, &env->tlb[i].tlb6.pte1); - qemu_get_betls(f, &env->tlb[i].tlb6.EPN); + qemu_get_betls(f, &env->tlb.tlb6[i].pte0); + qemu_get_betls(f, &env->tlb.tlb6[i].pte1); + qemu_get_betls(f, &env->tlb.tlb6[i].EPN); } } for (i = 0; i < 4; i++) @@ -153,6 +153,7 @@ #endif for (i = 0; i < 1024; i++) qemu_get_betls(f, &env->spr[i]); + ppc_store_sdr1(env, sdr1); qemu_get_be32s(f, &env->vscr); qemu_get_be64s(f, &env->spe_acc); qemu_get_be32s(f, &env->spe_fscr); diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/op_helper.c qemu-kvm-0.15.0+noroms/target-ppc/op_helper.c --- qemu-kvm-0.14.1+noroms/target-ppc/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -44,7 +44,7 @@ #endif env->exception_index = exception; env->error_code = error_code; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_raise_exception (uint32_t exception) @@ -86,6 +86,13 @@ return cpu_ppc_load_atbu(env); } +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +target_ulong helper_load_purr (void) +{ + return (target_ulong)cpu_ppc_load_purr(env); +} +#endif + target_ulong helper_load_601_rtcl (void) { return cpu_ppc601_load_rtcl(env); @@ -355,7 +362,6 @@ * do the load "by hand". */ ldl(addr); - tb_invalidate_page_range(addr, addr + env->icache_line_size); } // XXX: to be tested @@ -492,6 +498,38 @@ } #endif +#if defined(TARGET_PPC64) +target_ulong helper_popcntb (target_ulong val) +{ + val = (val & 0x5555555555555555ULL) + ((val >> 1) & + 0x5555555555555555ULL); + val = (val & 0x3333333333333333ULL) + ((val >> 2) & + 0x3333333333333333ULL); + val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & + 0x0f0f0f0f0f0f0f0fULL); + return val; +} + +target_ulong helper_popcntw (target_ulong val) +{ + val = (val & 0x5555555555555555ULL) + ((val >> 1) & + 0x5555555555555555ULL); + val = (val & 0x3333333333333333ULL) + ((val >> 2) & + 0x3333333333333333ULL); + val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & + 0x0f0f0f0f0f0f0f0fULL); + val = (val & 0x00ff00ff00ff00ffULL) + ((val >> 8) & + 0x00ff00ff00ff00ffULL); + val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) & + 0x0000ffff0000ffffULL); + return val; +} + +target_ulong helper_popcntd (target_ulong val) +{ + return ctpop64(val); +} +#else target_ulong helper_popcntb (target_ulong val) { val = (val & 0x55555555) + ((val >> 1) & 0x55555555); @@ -500,12 +538,13 @@ return val; } -#if defined(TARGET_PPC64) -target_ulong helper_popcntb_64 (target_ulong val) +target_ulong helper_popcntw (target_ulong val) { - val = (val & 0x5555555555555555ULL) + ((val >> 1) & 0x5555555555555555ULL); - val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL); - val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & 0x0f0f0f0f0f0f0f0fULL); + val = (val & 0x55555555) + ((val >> 1) & 0x55555555); + val = (val & 0x33333333) + ((val >> 2) & 0x33333333); + val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f); + val = (val & 0x00ff00ff) + ((val >> 8) & 0x00ff00ff); + val = (val & 0x0000ffff) + ((val >> 16) & 0x0000ffff); return val; } #endif @@ -932,7 +971,6 @@ void helper_float_check_status (void) { -#ifdef CONFIG_SOFTFLOAT if (env->exception_index == POWERPC_EXCP_PROGRAM && (env->error_code & POWERPC_EXCP_FP)) { /* Differred floating-point exception after target FPR update */ @@ -950,22 +988,12 @@ float_inexact_excp(); } } -#else - if (env->exception_index == POWERPC_EXCP_PROGRAM && - (env->error_code & POWERPC_EXCP_FP)) { - /* Differred floating-point exception after target FPR update */ - if (msr_fe0 != 0 || msr_fe1 != 0) - helper_raise_exception_err(env->exception_index, env->error_code); - } -#endif } -#ifdef CONFIG_SOFTFLOAT void helper_reset_fpstatus (void) { set_float_exception_flags(0, &env->fp_status); } -#endif /* fadd - fadd. */ uint64_t helper_fadd (uint64_t arg1, uint64_t arg2) @@ -1247,7 +1275,6 @@ /* 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; @@ -1263,10 +1290,6 @@ ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } -#else - /* This is OK on x86 hosts */ - farg1.d = (farg1.d * farg2.d) + farg3.d; -#endif } return farg1.ll; @@ -1292,7 +1315,6 @@ /* 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; @@ -1308,10 +1330,6 @@ ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } -#else - /* This is OK on x86 hosts */ - farg1.d = (farg1.d * farg2.d) - farg3.d; -#endif } return farg1.ll; } @@ -1336,7 +1354,6 @@ /* 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; @@ -1352,10 +1369,6 @@ ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } -#else - /* This is OK on x86 hosts */ - farg1.d = (farg1.d * farg2.d) + farg3.d; -#endif if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); } @@ -1383,7 +1396,6 @@ /* 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; @@ -1399,10 +1411,6 @@ ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } -#else - /* This is OK on x86 hosts */ - farg1.d = (farg1.d * farg2.d) - farg3.d; -#endif if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); } @@ -3323,7 +3331,7 @@ HELPER_SPE_VECTOR_ARITH(fsdiv); /* Single-precision floating-point comparisons */ -static inline uint32_t efststlt(uint32_t op1, uint32_t op2) +static inline uint32_t efscmplt(uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; @@ -3331,7 +3339,7 @@ return float32_lt(u1.f, u2.f, &env->vec_status) ? 4 : 0; } -static inline uint32_t efststgt(uint32_t op1, uint32_t op2) +static inline uint32_t efscmpgt(uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; @@ -3339,7 +3347,7 @@ return float32_le(u1.f, u2.f, &env->vec_status) ? 0 : 4; } -static inline uint32_t efststeq(uint32_t op1, uint32_t op2) +static inline uint32_t efscmpeq(uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; @@ -3347,22 +3355,22 @@ return float32_eq(u1.f, u2.f, &env->vec_status) ? 4 : 0; } -static inline uint32_t efscmplt(uint32_t op1, uint32_t op2) +static inline uint32_t efststlt(uint32_t op1, uint32_t op2) { - /* XXX: TODO: test special values (NaN, infinites, ...) */ - return efststlt(op1, op2); + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmplt(op1, op2); } -static inline uint32_t efscmpgt(uint32_t op1, uint32_t op2) +static inline uint32_t efststgt(uint32_t op1, uint32_t op2) { - /* XXX: TODO: test special values (NaN, infinites, ...) */ - return efststgt(op1, op2); + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmpgt(op1, op2); } -static inline uint32_t efscmpeq(uint32_t op1, uint32_t op2) +static inline uint32_t efststeq(uint32_t op1, uint32_t op2) { - /* XXX: TODO: test special values (NaN, infinites, ...) */ - return efststeq(op1, op2); + /* XXX: TODO: ignore special values (NaN, infinites, ...) */ + return efscmpeq(op1, op2); } #define HELPER_SINGLE_SPE_CMP(name) \ @@ -3658,7 +3666,7 @@ CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; - return float64_eq(u1.d, u2.d, &env->vec_status) ? 4 : 0; + return float64_eq_quiet(u1.d, u2.d, &env->vec_status) ? 4 : 0; } uint32_t helper_efdcmplt (uint64_t op1, uint64_t op2) @@ -3721,7 +3729,7 @@ if (likely(tb)) { /* the PC is inside the translated code. It means that we have a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); + cpu_restore_state(tb, env, pc); } } helper_raise_exception_err(env->exception_index, env->error_code); @@ -3746,14 +3754,31 @@ /* SLB management */ #if defined(TARGET_PPC64) -target_ulong helper_load_slb (target_ulong slb_nr) +void helper_store_slb (target_ulong rb, target_ulong rs) { - return ppc_load_slb(env, slb_nr); + if (ppc_store_slb(env, rb, rs) < 0) { + helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); + } } -void helper_store_slb (target_ulong rb, target_ulong rs) +target_ulong helper_load_slb_esid (target_ulong rb) { - ppc_store_slb(env, rb, rs); + target_ulong rt; + + if (ppc_load_slb_esid(env, rb, &rt) < 0) { + helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); + } + return rt; +} + +target_ulong helper_load_slb_vsid (target_ulong rb) +{ + target_ulong rt; + + if (ppc_load_slb_vsid(env, rb, &rt) < 0) { + helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); + } + return rt; } void helper_slbia (void) @@ -3934,7 +3959,7 @@ int size; entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; ret = tlb->EPN; if (tlb->prot & PAGE_VALID) { ret |= PPC4XX_TLBHI_V; @@ -3954,7 +3979,7 @@ target_ulong ret; entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; ret = tlb->RPN; if (tlb->prot & PAGE_EXEC) { ret |= PPC4XX_TLBLO_EX; @@ -3973,7 +3998,7 @@ LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry, val); entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; /* Invalidate previous TLB (if it's valid) */ if (tlb->prot & PAGE_VALID) { end = tlb->EPN + tlb->size; @@ -4031,7 +4056,7 @@ LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry, val); entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK; tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK; tlb->prot = PAGE_READ; @@ -4066,7 +4091,7 @@ __func__, word, (int)entry, value); do_flush_tlbs = 0; entry &= 0x3F; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; switch (word) { default: /* Just here to please gcc */ @@ -4125,7 +4150,7 @@ int size; entry &= 0x3F; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; switch (word) { default: /* Just here to please gcc */ @@ -4169,4 +4194,216 @@ return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF); } +/* PowerPC BookE 2.06 TLB management */ + +static ppcmas_tlb_t *booke206_cur_tlb(CPUState *env) +{ + uint32_t tlbncfg = 0; + int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT; + int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK); + int tlb; + + tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb]; + + if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) { + cpu_abort(env, "we don't support HES yet\n"); + } + + return booke206_get_tlbm(env, tlb, ea, esel); +} + +void helper_booke_setpid(uint32_t pidn, target_ulong pid) +{ + env->spr[pidn] = pid; + /* changing PIDs mean we're in a different address space now */ + tlb_flush(env, 1); +} + +void helper_booke206_tlbwe(void) +{ + uint32_t tlbncfg, tlbn; + ppcmas_tlb_t *tlb; + + switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) { + case MAS0_WQ_ALWAYS: + /* good to go, write that entry */ + break; + case MAS0_WQ_COND: + /* XXX check if reserved */ + if (0) { + return; + } + break; + case MAS0_WQ_CLR_RSRV: + /* XXX clear entry */ + return; + default: + /* no idea what to do */ + return; + } + + if (((env->spr[SPR_BOOKE_MAS0] & MAS0_ATSEL) == MAS0_ATSEL_LRAT) && + !msr_gs) { + /* XXX we don't support direct LRAT setting yet */ + fprintf(stderr, "cpu: don't support LRAT setting yet\n"); + return; + } + + tlbn = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT; + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + + tlb = booke206_cur_tlb(env); + + if (msr_gs) { + cpu_abort(env, "missing HV implementation\n"); + } + tlb->mas7_3 = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) | + env->spr[SPR_BOOKE_MAS3]; + tlb->mas1 = env->spr[SPR_BOOKE_MAS1]; + /* XXX needs to change when supporting 64-bit e500 */ + tlb->mas2 = env->spr[SPR_BOOKE_MAS2] & 0xffffffff; + + if (!(tlbncfg & TLBnCFG_IPROT)) { + /* no IPROT supported by TLB */ + tlb->mas1 &= ~MAS1_IPROT; + } + + if (booke206_tlb_to_page_size(env, tlb) == TARGET_PAGE_SIZE) { + tlb_flush_page(env, tlb->mas2 & MAS2_EPN_MASK); + } else { + tlb_flush(env, 1); + } +} + +static inline void booke206_tlb_to_mas(CPUState *env, ppcmas_tlb_t *tlb) +{ + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + int way = booke206_tlbm_to_way(env, tlb); + + env->spr[SPR_BOOKE_MAS0] = tlbn << MAS0_TLBSEL_SHIFT; + env->spr[SPR_BOOKE_MAS0] |= way << MAS0_ESEL_SHIFT; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; + + env->spr[SPR_BOOKE_MAS1] = tlb->mas1; + env->spr[SPR_BOOKE_MAS2] = tlb->mas2; + env->spr[SPR_BOOKE_MAS3] = tlb->mas7_3; + env->spr[SPR_BOOKE_MAS7] = tlb->mas7_3 >> 32; +} + +void helper_booke206_tlbre(void) +{ + ppcmas_tlb_t *tlb = NULL; + + tlb = booke206_cur_tlb(env); + booke206_tlb_to_mas(env, tlb); +} + +void helper_booke206_tlbsx(target_ulong address) +{ + ppcmas_tlb_t *tlb = NULL; + int i, j; + target_phys_addr_t raddr; + uint32_t spid, sas; + + spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT; + sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + + if (ppcmas_tlb_check(env, tlb, &raddr, address, spid)) { + continue; + } + + if (sas != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + continue; + } + + booke206_tlb_to_mas(env, tlb); + return; + } + } + + /* no entry found, fill with defaults */ + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + } + + env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16) + << MAS1_TID_SHIFT; + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +static inline void booke206_invalidate_ea_tlb(CPUState *env, int tlbn, + uint32_t ea) +{ + int i; + int ways = booke206_tlb_ways(env, tlbn); + target_ulong mask; + + for (i = 0; i < ways; i++) { + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, tlbn, ea, i); + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + if (((tlb->mas2 & MAS2_EPN_MASK) == (ea & mask)) && + !(tlb->mas1 & MAS1_IPROT)) { + tlb->mas1 &= ~MAS1_VALID; + } + } +} + +void helper_booke206_tlbivax(target_ulong address) +{ + if (address & 0x4) { + /* flush all entries */ + if (address & 0x8) { + /* flush all of TLB1 */ + booke206_flush_tlb(env, BOOKE206_FLUSH_TLB1, 1); + } else { + /* flush all of TLB0 */ + booke206_flush_tlb(env, BOOKE206_FLUSH_TLB0, 0); + } + return; + } + + if (address & 0x8) { + /* flush TLB1 entries */ + booke206_invalidate_ea_tlb(env, 1, address); + tlb_flush(env, 1); + } else { + /* flush TLB0 entries */ + booke206_invalidate_ea_tlb(env, 0, address); + tlb_flush_page(env, address & MAS2_EPN_MASK); + } +} + +void helper_booke206_tlbflush(uint32_t type) +{ + int flags = 0; + + if (type & 2) { + flags |= BOOKE206_FLUSH_TLB1; + } + + if (type & 4) { + flags |= BOOKE206_FLUSH_TLB0; + } + + booke206_flush_tlb(env, flags, 1); +} + #endif /* !CONFIG_USER_ONLY */ diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/STATUS qemu-kvm-0.15.0+noroms/target-ppc/STATUS --- qemu-kvm-0.14.1+noroms/target-ppc/STATUS 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/STATUS 2011-08-09 07:40:29.000000000 -0500 @@ -11,7 +11,7 @@ SPR: special purpose registers set OK => all SPR registered (but some may be fake) KO => some SPR are missing or should be removed - ? => uncheked + ? => unchecked MSR: MSR bits definitions OK => all MSR bits properly defined KO => MSR definition is incorrect diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/translate.c qemu-kvm-0.15.0+noroms/target-ppc/translate.c --- qemu-kvm-0.14.1+noroms/target-ppc/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -2,6 +2,7 @@ * PowerPC emulation for qemu: main translation routines. * * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (C) 2011 Freescale Semiconductor, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,7 +24,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "qemu-common.h" @@ -200,6 +200,8 @@ uint32_t inval; /* instruction type */ uint64_t type; + /* extended instruction type */ + uint64_t type2; /* handler */ void (*handler)(DisasContext *ctx); #if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU) @@ -212,9 +214,7 @@ static inline void gen_reset_fpstatus(void) { -#ifdef CONFIG_SOFTFLOAT gen_helper_reset_fpstatus(); -#endif } static inline void gen_compute_fprf(TCGv_i64 arg, int set_fprf, int set_rc) @@ -313,10 +313,16 @@ } #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ -GEN_OPCODE(name, opc1, opc2, opc3, inval, type) +GEN_OPCODE(name, opc1, opc2, opc3, inval, type, PPC_NONE) + +#define GEN_HANDLER_E(name, opc1, opc2, opc3, inval, type, type2) \ +GEN_OPCODE(name, opc1, opc2, opc3, inval, type, type2) #define GEN_HANDLER2(name, onam, opc1, opc2, opc3, inval, type) \ -GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type) +GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type, PPC_NONE) + +#define GEN_HANDLER2_E(name, onam, opc1, opc2, opc3, inval, type, type2) \ +GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type, type2) typedef struct opcode_t { unsigned char opc1, opc2, opc3; @@ -456,7 +462,7 @@ /* PowerPC instructions table */ #if defined(DO_PPC_STATISTICS) -#define GEN_OPCODE(name, op1, op2, op3, invl, _typ) \ +#define GEN_OPCODE(name, op1, op2, op3, invl, _typ, _typ2) \ { \ .opc1 = op1, \ .opc2 = op2, \ @@ -465,12 +471,13 @@ .handler = { \ .inval = invl, \ .type = _typ, \ + .type2 = _typ2, \ .handler = &gen_##name, \ .oname = stringify(name), \ }, \ .oname = stringify(name), \ } -#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ) \ +#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ, _typ2) \ { \ .opc1 = op1, \ .opc2 = op2, \ @@ -479,13 +486,14 @@ .handler = { \ .inval = invl, \ .type = _typ, \ + .type2 = _typ2, \ .handler = &gen_##name, \ .oname = onam, \ }, \ .oname = onam, \ } #else -#define GEN_OPCODE(name, op1, op2, op3, invl, _typ) \ +#define GEN_OPCODE(name, op1, op2, op3, invl, _typ, _typ2) \ { \ .opc1 = op1, \ .opc2 = op2, \ @@ -494,11 +502,12 @@ .handler = { \ .inval = invl, \ .type = _typ, \ + .type2 = _typ2, \ .handler = &gen_##name, \ }, \ .oname = stringify(name), \ } -#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ) \ +#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ, _typ2) \ { \ .opc1 = op1, \ .opc2 = op2, \ @@ -507,6 +516,7 @@ .handler = { \ .inval = invl, \ .type = _typ, \ + .type2 = _typ2, \ .handler = &gen_##name, \ }, \ .oname = onam, \ @@ -533,6 +543,7 @@ static opc_handler_t invalid_handler = { .inval = 0xFFFFFFFF, .type = PPC_NONE, + .type2 = PPC_NONE, .handler = gen_invalid, }; @@ -1483,13 +1494,21 @@ /* popcntb : PowerPC 2.03 specification */ static void gen_popcntb(DisasContext *ctx) { + gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); +} + +static void gen_popcntw(DisasContext *ctx) +{ + gen_helper_popcntw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); +} + #if defined(TARGET_PPC64) - if (ctx->sf_mode) - gen_helper_popcntb_64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); - else -#endif - gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); +/* popcntd: PowerPC 2.06 specification */ +static void gen_popcntd(DisasContext *ctx) +{ + gen_helper_popcntd(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); } +#endif #if defined(TARGET_PPC64) /* extsw & extsw. */ @@ -3339,7 +3358,7 @@ likely(!ctx->singlestep_enabled)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_nip, dest & ~3); - tcg_gen_exit_tb((long)tb + n); + tcg_gen_exit_tb((tcg_target_long)tb + n); } else { tcg_gen_movi_tl(cpu_nip, dest & ~3); if (unlikely(ctx->singlestep_enabled)) { @@ -3858,24 +3877,19 @@ tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0); } else { + TCGv msr = tcg_temp_new(); + /* XXX: we need to update nip before the store * if we enter power saving mode, we will exit the loop * directly from ppc_store_msr */ gen_update_nip(ctx, ctx->nip); #if defined(TARGET_PPC64) - if (!ctx->sf_mode) { - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_msr, 0xFFFFFFFF00000000ULL); - tcg_gen_ext32u_tl(t1, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - gen_helper_store_msr(t0); - tcg_temp_free(t0); - } else + tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); +#else + tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); #endif - gen_helper_store_msr(cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_msr(msr); /* Must stop the translation as machine state (may have) changed */ /* Note that mtmsr is not always defined as context-synchronizing */ gen_stop_exception(ctx); @@ -4227,6 +4241,33 @@ #endif } +static void gen_slbmfee(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); +#else + if (unlikely(!ctx->mem_idx)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + return; + } + gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); +#endif +} + +static void gen_slbmfev(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); +#else + if (unlikely(!ctx->mem_idx)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + return; + } + gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], + cpu_gpr[rB(ctx->opcode)]); +#endif +} #endif /* defined(TARGET_PPC64) */ /*** Lookaside buffer management ***/ @@ -5939,6 +5980,80 @@ #endif } +/* TLB management - PowerPC BookE 2.06 implementation */ + +/* tlbre */ +static void gen_tlbre_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +#else + if (unlikely(!ctx->mem_idx)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return; + } + + gen_helper_booke206_tlbre(); +#endif +} + +/* tlbsx - tlbsx. */ +static void gen_tlbsx_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +#else + TCGv t0; + if (unlikely(!ctx->mem_idx)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return; + } + + if (rA(ctx->opcode)) { + t0 = tcg_temp_new(); + tcg_gen_mov_tl(t0, cpu_gpr[rD(ctx->opcode)]); + } else { + t0 = tcg_const_tl(0); + } + + tcg_gen_add_tl(t0, t0, cpu_gpr[rB(ctx->opcode)]); + gen_helper_booke206_tlbsx(t0); +#endif +} + +/* tlbwe */ +static void gen_tlbwe_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +#else + if (unlikely(!ctx->mem_idx)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return; + } + gen_helper_booke206_tlbwe(); +#endif +} + +static void gen_tlbivax_booke206(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +#else + TCGv t0; + if (unlikely(!ctx->mem_idx)) { + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + return; + } + + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + + gen_helper_booke206_tlbivax(t0); +#endif +} + + /* wrtee */ static void gen_wrtee(DisasContext *ctx) { @@ -6502,6 +6617,39 @@ /*** SPE extension ***/ /* Register moves */ + +static inline void gen_evmra(DisasContext *ctx) +{ + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_APU); + return; + } + +#if defined(TARGET_PPC64) + /* rD := rA */ + tcg_gen_mov_i64(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + + /* spe_acc := rA */ + tcg_gen_st_i64(cpu_gpr[rA(ctx->opcode)], + cpu_env, + offsetof(CPUState, spe_acc)); +#else + TCGv_i64 tmp = tcg_temp_new_i64(); + + /* tmp := rA_lo + rA_hi << 32 */ + tcg_gen_concat_i32_i64(tmp, cpu_gpr[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); + + /* spe_acc := tmp */ + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUState, spe_acc)); + tcg_temp_free_i64(tmp); + + /* rD := rA */ + tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_mov_i32(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); +#endif +} + static inline void gen_load_gpr64(TCGv_i64 t, int reg) { #if defined(TARGET_PPC64) @@ -6942,7 +7090,7 @@ #if defined(TARGET_PPC64) TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x00000000FFFFFFFFLL); + tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); tcg_gen_shli_tl(t1, cpu_gpr[rA(ctx->opcode)], 32); tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], t0, t1); tcg_temp_free(t0); @@ -6961,7 +7109,7 @@ #if defined(TARGET_PPC64) TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x00000000FFFFFFFFLL); + tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); tcg_gen_andi_tl(t1, cpu_gpr[rA(ctx->opcode)], 0xFFFFFFFF0000000ULL); tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], t0, t1); tcg_temp_free(t0); @@ -7050,14 +7198,14 @@ tcg_gen_andi_i32(t0, cpu_crf[ctx->opcode & 0x07], 1 << 2); tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l3); #if defined(TARGET_PPC64) - tcg_gen_andi_tl(t2, cpu_gpr[rA(ctx->opcode)], 0x00000000FFFFFFFFULL); + tcg_gen_ext32u_tl(t2, cpu_gpr[rA(ctx->opcode)]); #else tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); #endif tcg_gen_br(l4); gen_set_label(l3); #if defined(TARGET_PPC64) - tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x00000000FFFFFFFFULL); + tcg_gen_ext32u_tl(t2, cpu_gpr[rB(ctx->opcode)]); #else tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); #endif @@ -7090,6 +7238,163 @@ gen_evsel(ctx); } +/* Multiply */ + +static inline void gen_evmwumi(DisasContext *ctx) +{ + TCGv_i64 t0, t1; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_APU); + return; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + /* t0 := rA; t1 := rB */ +#if defined(TARGET_PPC64) + tcg_gen_ext32u_tl(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32u_tl(t1, cpu_gpr[rB(ctx->opcode)]); +#else + tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); +#endif + + tcg_gen_mul_i64(t0, t0, t1); /* t0 := rA * rB */ + + gen_store_gpr64(rD(ctx->opcode), t0); /* rD := t0 */ + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +static inline void gen_evmwumia(DisasContext *ctx) +{ + TCGv_i64 tmp; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_APU); + return; + } + + gen_evmwumi(ctx); /* rD := rA * rB */ + + tmp = tcg_temp_new_i64(); + + /* acc := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUState, spe_acc)); + tcg_temp_free_i64(tmp); +} + +static inline void gen_evmwumiaa(DisasContext *ctx) +{ + TCGv_i64 acc; + TCGv_i64 tmp; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_APU); + return; + } + + gen_evmwumi(ctx); /* rD := rA * rB */ + + acc = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); + + /* tmp := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + + /* Load acc */ + tcg_gen_ld_i64(acc, cpu_env, offsetof(CPUState, spe_acc)); + + /* acc := tmp + acc */ + tcg_gen_add_i64(acc, acc, tmp); + + /* Store acc */ + tcg_gen_st_i64(acc, cpu_env, offsetof(CPUState, spe_acc)); + + /* rD := acc */ + gen_store_gpr64(rD(ctx->opcode), acc); + + tcg_temp_free_i64(acc); + tcg_temp_free_i64(tmp); +} + +static inline void gen_evmwsmi(DisasContext *ctx) +{ + TCGv_i64 t0, t1; + + if (unlikely(!ctx->spe_enabled)) { + gen_exception(ctx, POWERPC_EXCP_APU); + return; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + + /* t0 := rA; t1 := rB */ +#if defined(TARGET_PPC64) + tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]); +#else + tcg_gen_ext_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); +#endif + + tcg_gen_mul_i64(t0, t0, t1); /* t0 := rA * rB */ + + gen_store_gpr64(rD(ctx->opcode), t0); /* rD := t0 */ + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +static inline void gen_evmwsmia(DisasContext *ctx) +{ + TCGv_i64 tmp; + + gen_evmwsmi(ctx); /* rD := rA * rB */ + + tmp = tcg_temp_new_i64(); + + /* acc := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUState, spe_acc)); + + tcg_temp_free_i64(tmp); +} + +static inline void gen_evmwsmiaa(DisasContext *ctx) +{ + TCGv_i64 acc = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + + gen_evmwsmi(ctx); /* rD := rA * rB */ + + acc = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); + + /* tmp := rD */ + gen_load_gpr64(tmp, rD(ctx->opcode)); + + /* Load acc */ + tcg_gen_ld_i64(acc, cpu_env, offsetof(CPUState, spe_acc)); + + /* acc := tmp + acc */ + tcg_gen_add_i64(acc, acc, tmp); + + /* Store acc */ + tcg_gen_st_i64(acc, cpu_env, offsetof(CPUState, spe_acc)); + + /* rD := acc */ + gen_store_gpr64(rD(ctx->opcode), acc); + + tcg_temp_free_i64(acc); + tcg_temp_free_i64(tmp); +} + GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, PPC_SPE); //// GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, PPC_SPE); GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, PPC_SPE); //// @@ -7098,10 +7403,14 @@ GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, PPC_SPE); //// GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, PPC_SPE); //// GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x00000000, PPC_SPE); // +GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, PPC_SPE); GEN_SPE(speundef, evand, 0x08, 0x08, 0x00000000, PPC_SPE); //// GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, PPC_SPE); //// GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, PPC_SPE); //// GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, PPC_SPE); //// +GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, PPC_SPE); +GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, PPC_SPE); +GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, PPC_SPE); GEN_SPE(speundef, evorc, 0x0D, 0x08, 0x00000000, PPC_SPE); //// GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, PPC_SPE); //// GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, PPC_SPE); //// @@ -7491,14 +7800,12 @@ GEN_SPE(evmwhumi, evmwhsmi, 0x06, 0x11, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwhsmf, 0x07, 0x11, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwssf, 0x09, 0x11, 0x00000000, PPC_SPE); -GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwsmf, 0x0D, 0x11, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwhssfa, 0x13, 0x11, 0x00000000, PPC_SPE); GEN_SPE(evmwlumia, speundef, 0x14, 0x11, 0x00000000, PPC_SPE); GEN_SPE(evmwhumia, evmwhsmia, 0x16, 0x11, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwhsmfa, 0x17, 0x11, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwssfa, 0x19, 0x11, 0x00000000, PPC_SPE); -GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwsmfa, 0x1D, 0x11, 0x00000000, PPC_SPE); GEN_SPE(evadduiaaw, evaddsiaaw, 0x00, 0x13, 0x0000F800, PPC_SPE); @@ -7506,7 +7813,6 @@ GEN_SPE(evaddumiaaw, evaddsmiaaw, 0x04, 0x13, 0x0000F800, PPC_SPE); GEN_SPE(evsubfumiaaw, evsubfsmiaaw, 0x05, 0x13, 0x0000F800, PPC_SPE); GEN_SPE(evdivws, evdivwu, 0x06, 0x13, 0x00000000, PPC_SPE); -GEN_SPE(evmra, speundef, 0x07, 0x13, 0x0000F800, PPC_SPE); GEN_SPE(evmheusiaaw, evmhessiaaw, 0x00, 0x14, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmhessfaaw, 0x01, 0x14, 0x00000000, PPC_SPE); @@ -7524,7 +7830,6 @@ GEN_SPE(evmwlusiaaw, evmwlssiaaw, 0x00, 0x15, 0x00000000, PPC_SPE); GEN_SPE(evmwlumiaaw, evmwlsmiaaw, 0x04, 0x15, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwssfaa, 0x09, 0x15, 0x00000000, PPC_SPE); -GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, PPC_SPE); GEN_SPE(speundef, evmwsmfaa, 0x0D, 0x15, 0x00000000, PPC_SPE); GEN_SPE(evmheusianw, evmhessianw, 0x00, 0x16, 0x00000000, PPC_SPE); @@ -8009,7 +8314,9 @@ GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(popcntb, 0x1F, 0x03, 0x03, 0x0000F801, PPC_POPCNTB), +GEN_HANDLER(popcntw, 0x1F, 0x1A, 0x0b, 0x0000F801, PPC_POPCNTWD), #if defined(TARGET_PPC64) +GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), #endif GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), @@ -8110,7 +8417,9 @@ GEN_HANDLER2(mtsr_64b, "mtsr", 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT_64B), GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT_64B), -GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x00000000, PPC_SEGMENT_64B), +GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x001F0001, PPC_SEGMENT_64B), +GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B), +GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B), #endif GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA), GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x03FF0001, PPC_MEM_TLBIE), @@ -8191,7 +8500,7 @@ GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON), GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON), GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP), -GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE), +GEN_HANDLER_E(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER(rfdi, 0x13, 0x07, 0x01, 0x03FF8001, PPC_RFDI), GEN_HANDLER(rfmci, 0x13, 0x06, 0x01, 0x03FF8001, PPC_RFMCI), GEN_HANDLER2(tlbre_40x, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_TLB), @@ -8200,12 +8509,23 @@ GEN_HANDLER2(tlbre_440, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_BOOKE), GEN_HANDLER2(tlbsx_440, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, PPC_BOOKE), GEN_HANDLER2(tlbwe_440, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_BOOKE), +GEN_HANDLER2_E(tlbre_booke206, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(tlbsx_booke206, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(tlbwe_booke206, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, + PPC_NONE, PPC2_BOOKE206), +GEN_HANDLER2_E(tlbivax_booke206, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001, + PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE), GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE), GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC), -GEN_HANDLER(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, PPC_BOOKE), -GEN_HANDLER(msync, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE), -GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE), +GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, + PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER_E(msync, 0x1F, 0x16, 0x12, 0x03FFF801, + PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, + PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), @@ -8739,10 +9059,14 @@ GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, PPC_SPE), GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, PPC_SPE), GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x00000000, PPC_SPE), +GEN_SPE(evmra, speundef, 0x02, 0x13, 0x0000F800, PPC_SPE), GEN_SPE(speundef, evand, 0x08, 0x08, 0x00000000, PPC_SPE), GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, PPC_SPE), GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, PPC_SPE), GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, PPC_SPE), +GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, PPC_SPE), +GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, PPC_SPE), +GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, PPC_SPE), GEN_SPE(speundef, evorc, 0x0D, 0x08, 0x00000000, PPC_SPE), GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, PPC_SPE), GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, PPC_SPE), @@ -8891,9 +9215,84 @@ } cpu_fprintf(f, "FPSCR %08x\n", env->fpscr); #if !defined(CONFIG_USER_ONLY) - cpu_fprintf(f, "SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx " SDR1 " - TARGET_FMT_lx "\n", env->spr[SPR_SRR0], env->spr[SPR_SRR1], - env->sdr1); + cpu_fprintf(f, " SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx + " PVR " TARGET_FMT_lx " VRSAVE " TARGET_FMT_lx "\n", + env->spr[SPR_SRR0], env->spr[SPR_SRR1], + env->spr[SPR_PVR], env->spr[SPR_VRSAVE]); + + cpu_fprintf(f, "SPRG0 " TARGET_FMT_lx " SPRG1 " TARGET_FMT_lx + " SPRG2 " TARGET_FMT_lx " SPRG3 " TARGET_FMT_lx "\n", + env->spr[SPR_SPRG0], env->spr[SPR_SPRG1], + env->spr[SPR_SPRG2], env->spr[SPR_SPRG3]); + + cpu_fprintf(f, "SPRG4 " TARGET_FMT_lx " SPRG5 " TARGET_FMT_lx + " SPRG6 " TARGET_FMT_lx " SPRG7 " TARGET_FMT_lx "\n", + env->spr[SPR_SPRG4], env->spr[SPR_SPRG5], + env->spr[SPR_SPRG6], env->spr[SPR_SPRG7]); + + if (env->excp_model == POWERPC_EXCP_BOOKE) { + cpu_fprintf(f, "CSRR0 " TARGET_FMT_lx " CSRR1 " TARGET_FMT_lx + " MCSRR0 " TARGET_FMT_lx " MCSRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1], + env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); + + cpu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx + " ESR " TARGET_FMT_lx " DEAR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_TCR], env->spr[SPR_BOOKE_TSR], + env->spr[SPR_BOOKE_ESR], env->spr[SPR_BOOKE_DEAR]); + + cpu_fprintf(f, " PIR " TARGET_FMT_lx " DECAR " TARGET_FMT_lx + " IVPR " TARGET_FMT_lx " EPCR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_PIR], env->spr[SPR_BOOKE_DECAR], + env->spr[SPR_BOOKE_IVPR], env->spr[SPR_BOOKE_EPCR]); + + cpu_fprintf(f, " MCSR " TARGET_FMT_lx " SPRG8 " TARGET_FMT_lx + " EPR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MCSR], env->spr[SPR_BOOKE_SPRG8], + env->spr[SPR_BOOKE_EPR]); + + /* FSL-specific */ + cpu_fprintf(f, " MCAR " TARGET_FMT_lx " PID1 " TARGET_FMT_lx + " PID2 " TARGET_FMT_lx " SVR " TARGET_FMT_lx "\n", + env->spr[SPR_Exxx_MCAR], env->spr[SPR_BOOKE_PID1], + env->spr[SPR_BOOKE_PID2], env->spr[SPR_E500_SVR]); + + /* + * IVORs are left out as they are large and do not change often -- + * they can be read with "p $ivor0", "p $ivor1", etc. + */ + } + + switch (env->mmu_model) { + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: +#if defined(TARGET_PPC64) + case POWERPC_MMU_620: + case POWERPC_MMU_64B: +#endif + cpu_fprintf(f, " SDR1 " TARGET_FMT_lx "\n", env->spr[SPR_SDR1]); + break; + case POWERPC_MMU_BOOKE206: + cpu_fprintf(f, " MAS0 " TARGET_FMT_lx " MAS1 " TARGET_FMT_lx + " MAS2 " TARGET_FMT_lx " MAS3 " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MAS0], env->spr[SPR_BOOKE_MAS1], + env->spr[SPR_BOOKE_MAS2], env->spr[SPR_BOOKE_MAS3]); + + cpu_fprintf(f, " MAS4 " TARGET_FMT_lx " MAS6 " TARGET_FMT_lx + " MAS7 " TARGET_FMT_lx " PID " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MAS4], env->spr[SPR_BOOKE_MAS6], + env->spr[SPR_BOOKE_MAS7], env->spr[SPR_BOOKE_PID]); + + cpu_fprintf(f, "MMUCFG " TARGET_FMT_lx " TLB0CFG " TARGET_FMT_lx + " TLB1CFG " TARGET_FMT_lx "\n", + env->spr[SPR_MMUCFG], env->spr[SPR_BOOKE_TLB0CFG], + env->spr[SPR_BOOKE_TLB1CFG]); + break; + default: + break; + } #endif #undef RGPL @@ -9134,8 +9533,7 @@ gen_intermediate_code_internal(env, tb, 1); } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->nip = gen_opc_pc[pc_pos]; } diff -Nru qemu-kvm-0.14.1+noroms/target-ppc/translate_init.c qemu-kvm-0.15.0+noroms/target-ppc/translate_init.c --- qemu-kvm-0.14.1+noroms/target-ppc/translate_init.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-ppc/translate_init.c 2011-08-09 07:40:29.000000000 -0500 @@ -37,6 +37,7 @@ uint32_t pvr; uint32_t svr; uint64_t insns_flags; + uint64_t insns_flags2; uint64_t msr_mask; powerpc_mmu_t mmu_model; powerpc_excp_t excp_model; @@ -61,6 +62,7 @@ PPC_IRQ_INIT_FN(40x); PPC_IRQ_INIT_FN(6xx); PPC_IRQ_INIT_FN(970); +PPC_IRQ_INIT_FN(POWER7); PPC_IRQ_INIT_FN(e500); /* Generic callbacks: @@ -71,7 +73,7 @@ gen_load_spr(cpu_gpr[gprn], sprn); #ifdef PPC_DUMP_SPR_ACCESSES { - TCGv t0 = tcg_const_i32(sprn); + TCGv_i32 t0 = tcg_const_i32(sprn); gen_helper_load_dump_spr(t0); tcg_temp_free_i32(t0); } @@ -83,7 +85,7 @@ gen_store_spr(sprn, cpu_gpr[gprn]); #ifdef PPC_DUMP_SPR_ACCESSES { - TCGv t0 = tcg_const_i32(sprn); + TCGv_i32 t0 = tcg_const_i32(sprn); gen_helper_store_dump_spr(t0); tcg_temp_free_i32(t0); } @@ -154,12 +156,26 @@ #if !defined(CONFIG_USER_ONLY) static void spr_read_decr (void *opaque, int gprn, int sprn) { + if (use_icount) { + gen_io_start(); + } gen_helper_load_decr(cpu_gpr[gprn]); + if (use_icount) { + gen_io_end(); + gen_stop_exception(opaque); + } } static void spr_write_decr (void *opaque, int sprn, int gprn) { + if (use_icount) { + gen_io_start(); + } gen_helper_store_decr(cpu_gpr[gprn]); + if (use_icount) { + gen_io_end(); + gen_stop_exception(opaque); + } } #endif @@ -167,12 +183,26 @@ /* Time base */ static void spr_read_tbl (void *opaque, int gprn, int sprn) { + if (use_icount) { + gen_io_start(); + } gen_helper_load_tbl(cpu_gpr[gprn]); + if (use_icount) { + gen_io_end(); + gen_stop_exception(opaque); + } } static void spr_read_tbu (void *opaque, int gprn, int sprn) { + if (use_icount) { + gen_io_start(); + } gen_helper_load_tbu(cpu_gpr[gprn]); + if (use_icount) { + gen_io_end(); + gen_stop_exception(opaque); + } } __attribute__ (( unused )) @@ -190,12 +220,26 @@ #if !defined(CONFIG_USER_ONLY) static void spr_write_tbl (void *opaque, int sprn, int gprn) { + if (use_icount) { + gen_io_start(); + } gen_helper_store_tbl(cpu_gpr[gprn]); + if (use_icount) { + gen_io_end(); + gen_stop_exception(opaque); + } } static void spr_write_tbu (void *opaque, int sprn, int gprn) { + if (use_icount) { + gen_io_start(); + } gen_helper_store_tbu(cpu_gpr[gprn]); + if (use_icount) { + gen_io_end(); + gen_stop_exception(opaque); + } } __attribute__ (( unused )) @@ -209,6 +253,14 @@ { gen_helper_store_atbu(cpu_gpr[gprn]); } + +#if defined(TARGET_PPC64) +__attribute__ (( unused )) +static void spr_read_purr (void *opaque, int gprn, int sprn) +{ + gen_helper_load_purr(cpu_gpr[gprn]); +} +#endif #endif #if !defined(CONFIG_USER_ONLY) @@ -293,11 +345,6 @@ } /* SDR1 */ -static void spr_read_sdr1 (void *opaque, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, sdr1)); -} - static void spr_write_sdr1 (void *opaque, int sprn, int gprn) { gen_helper_store_sdr1(cpu_gpr[gprn]); @@ -621,7 +668,7 @@ /* Memory management */ spr_register(env, SPR_SDR1, "SDR1", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_sdr1, &spr_write_sdr1, + &spr_read_generic, &spr_write_sdr1, 0x00000000); } @@ -797,6 +844,7 @@ env->nb_tlb = nb_tlbs; env->nb_ways = nb_ways; env->id_tlbs = 1; + env->tlb_type = TLB_6XX; spr_register(env, SPR_DMISS, "DMISS", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, @@ -1290,6 +1338,7 @@ env->nb_tlb = nb_tlbs; env->nb_ways = nb_ways; env->id_tlbs = 1; + env->tlb_type = TLB_6XX; /* XXX : not implemented */ spr_register(env, SPR_PTEHI, "PTEHI", SPR_NOACCESS, SPR_NOACCESS, @@ -1308,6 +1357,31 @@ #endif } +#if !defined(CONFIG_USER_ONLY) +static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, cpu_gpr[gprn], ~256); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +static void spr_write_booke206_mmucsr0 (void *opaque, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_booke206_tlbflush(t0); + tcg_temp_free_i32(t0); +} + +static void spr_write_booke_pid (void *opaque, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_booke_setpid(t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} +#endif + static void gen_spr_usprgh (CPUPPCState *env) { spr_register(env, SPR_USPRG4, "USPRG4", @@ -1447,7 +1521,7 @@ } spr_register(env, SPR_BOOKE_PID, "PID", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_booke_pid, 0x00000000); spr_register(env, SPR_BOOKE_TCR, "TCR", SPR_NOACCESS, SPR_NOACCESS, @@ -1489,8 +1563,19 @@ 0x00000000); } -/* FSL storage control registers */ -static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask) +static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize, + uint32_t maxsize, uint32_t flags, + uint32_t nentries) +{ + return (assoc << TLBnCFG_ASSOC_SHIFT) | + (minsize << TLBnCFG_MINSIZE_SHIFT) | + (maxsize << TLBnCFG_MAXSIZE_SHIFT) | + flags | nentries; +} + +/* BookE 2.06 storage control registers */ +static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, + uint32_t *tlbncfg) { #if !defined(CONFIG_USER_ONLY) const char *mas_names[8] = { @@ -1516,14 +1601,14 @@ /* XXX : not implemented */ spr_register(env, SPR_BOOKE_PID1, "PID1", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_booke_pid, 0x00000000); } if (env->nb_pids > 2) { /* XXX : not implemented */ spr_register(env, SPR_BOOKE_PID2, "PID2", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_booke_pid, 0x00000000); } /* XXX : not implemented */ @@ -1531,45 +1616,38 @@ SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, 0x00000000); /* TOFIX */ - /* XXX : not implemented */ - spr_register(env, SPR_MMUCSR0, "MMUCSR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* TOFIX */ switch (env->nb_ways) { case 4: - /* XXX : not implemented */ spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ + tlbncfg[3]); /* Fallthru */ case 3: - /* XXX : not implemented */ spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ + tlbncfg[2]); /* Fallthru */ case 2: - /* XXX : not implemented */ spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ + tlbncfg[1]); /* Fallthru */ case 1: - /* XXX : not implemented */ spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, - 0x00000000); /* TOFIX */ + tlbncfg[0]); /* Fallthru */ case 0: default: break; } #endif + + gen_spr_usprgh(env); } /* SPR specific to PowerPC 440 implementation */ @@ -3086,6 +3164,35 @@ env->hreset_vector = 0x0000000000000100ULL; #endif } + +static void init_excp_POWER7 (CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; + env->excp_vectors[POWERPC_EXCP_DSEG] = 0x00000380; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; + env->excp_vectors[POWERPC_EXCP_ISEG] = 0x00000480; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; + env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; + env->excp_vectors[POWERPC_EXCP_HDECR] = 0x00000980; + env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; + env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; + env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; + env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20; + env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; + env->excp_vectors[POWERPC_EXCP_MAINT] = 0x00001600; + env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001700; + env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001800; + env->hreset_excp_prefix = 0; + /* Hardware reset vector */ + env->hreset_vector = 0x0000000000000100ULL; +#endif +} #endif /*****************************************************************************/ @@ -3126,6 +3233,7 @@ PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_4xx_COMMON | PPC_40x_EXCP) +#define POWERPC_INSNS2_401 (PPC_NONE) #define POWERPC_MSRM_401 (0x00000000000FD201ULL) #define POWERPC_MMU_401 (POWERPC_MMU_REAL) #define POWERPC_EXCP_401 (POWERPC_EXCP_40x) @@ -3155,6 +3263,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ PPC_4xx_COMMON | PPC_40x_EXCP) +#define POWERPC_INSNS2_401x2 (PPC_NONE) #define POWERPC_MSRM_401x2 (0x00000000001FD231ULL) #define POWERPC_MMU_401x2 (POWERPC_MMU_SOFT_4xx_Z) #define POWERPC_EXCP_401x2 (POWERPC_EXCP_40x) @@ -3175,6 +3284,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3191,6 +3301,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ PPC_4xx_COMMON | PPC_40x_EXCP) +#define POWERPC_INSNS2_401x3 (PPC_NONE) #define POWERPC_MSRM_401x3 (0x00000000001FD631ULL) #define POWERPC_MMU_401x3 (POWERPC_MMU_SOFT_4xx_Z) #define POWERPC_EXCP_401x3 (POWERPC_EXCP_40x) @@ -3223,6 +3334,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ PPC_4xx_COMMON | PPC_40x_EXCP) +#define POWERPC_INSNS2_IOP480 (PPC_NONE) #define POWERPC_MSRM_IOP480 (0x00000000001FD231ULL) #define POWERPC_MMU_IOP480 (POWERPC_MMU_SOFT_4xx_Z) #define POWERPC_EXCP_IOP480 (POWERPC_EXCP_40x) @@ -3243,6 +3355,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3258,6 +3371,7 @@ PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_4xx_COMMON | PPC_40x_EXCP) +#define POWERPC_INSNS2_403 (PPC_NONE) #define POWERPC_MSRM_403 (0x000000000007D00DULL) #define POWERPC_MMU_403 (POWERPC_MMU_REAL) #define POWERPC_EXCP_403 (POWERPC_EXCP_40x) @@ -3288,6 +3402,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ PPC_4xx_COMMON | PPC_40x_EXCP) +#define POWERPC_INSNS2_403GCX (PPC_NONE) #define POWERPC_MSRM_403GCX (0x000000000007D00DULL) #define POWERPC_MMU_403GCX (POWERPC_MMU_SOFT_4xx_Z) #define POWERPC_EXCP_403GCX (POWERPC_EXCP_40x) @@ -3320,6 +3435,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3336,6 +3452,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ PPC_4xx_COMMON | PPC_405_MAC | PPC_40x_EXCP) +#define POWERPC_INSNS2_405 (PPC_NONE) #define POWERPC_MSRM_405 (0x000000000006E630ULL) #define POWERPC_MMU_405 (POWERPC_MMU_SOFT_4xx) #define POWERPC_EXCP_405 (POWERPC_EXCP_40x) @@ -3367,6 +3484,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3383,6 +3501,7 @@ PPC_MEM_TLBSYNC | PPC_MFTB | \ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ PPC_440_SPEC) +#define POWERPC_INSNS2_440EP (PPC_NONE) #define POWERPC_MSRM_440EP (0x000000000006D630ULL) #define POWERPC_MMU_440EP (POWERPC_MMU_BOOKE) #define POWERPC_EXCP_440EP (POWERPC_EXCP_BOOKE) @@ -3448,6 +3567,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3463,6 +3583,7 @@ PPC_MEM_TLBSYNC | PPC_TLBIVA | PPC_MFTB | \ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ PPC_440_SPEC) +#define POWERPC_INSNS2_440GP (PPC_NONE) #define POWERPC_MSRM_440GP (0x000000000006FF30ULL) #define POWERPC_MMU_440GP (POWERPC_MMU_BOOKE) #define POWERPC_EXCP_440GP (POWERPC_EXCP_BOOKE) @@ -3510,6 +3631,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3525,6 +3647,7 @@ PPC_MEM_TLBSYNC | PPC_MFTB | \ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ PPC_440_SPEC) +#define POWERPC_INSNS2_440x4 (PPC_NONE) #define POWERPC_MSRM_440x4 (0x000000000006FF30ULL) #define POWERPC_MMU_440x4 (POWERPC_MMU_BOOKE) #define POWERPC_EXCP_440x4 (POWERPC_EXCP_BOOKE) @@ -3572,6 +3695,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3587,6 +3711,7 @@ PPC_MEM_TLBSYNC | PPC_MFTB | \ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ PPC_440_SPEC) +#define POWERPC_INSNS2_440x5 (PPC_NONE) #define POWERPC_MSRM_440x5 (0x000000000006FF30ULL) #define POWERPC_MMU_440x5 (POWERPC_MMU_BOOKE) #define POWERPC_EXCP_440x5 (POWERPC_EXCP_BOOKE) @@ -3651,6 +3776,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3667,6 +3793,7 @@ PPC_MEM_TLBSYNC | PPC_TLBIVA | \ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ PPC_440_SPEC) +#define POWERPC_INSNS2_460 (PPC_NONE) #define POWERPC_MSRM_460 (0x000000000006FF30ULL) #define POWERPC_MMU_460 (POWERPC_MMU_BOOKE) #define POWERPC_EXCP_460 (POWERPC_EXCP_BOOKE) @@ -3737,6 +3864,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3756,6 +3884,7 @@ PPC_MEM_TLBSYNC | PPC_TLBIVA | \ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ PPC_440_SPEC) +#define POWERPC_INSNS2_460F (PPC_NONE) #define POWERPC_MSRM_460 (0x000000000006FF30ULL) #define POWERPC_MMU_460F (POWERPC_MMU_BOOKE) #define POWERPC_EXCP_460F (POWERPC_EXCP_BOOKE) @@ -3826,6 +3955,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3838,6 +3968,7 @@ PPC_MEM_EIEIO | PPC_MEM_SYNC | \ PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | \ PPC_MFTB) +#define POWERPC_INSNS2_MPC5xx (PPC_NONE) #define POWERPC_MSRM_MPC5xx (0x000000000001FF43ULL) #define POWERPC_MMU_MPC5xx (POWERPC_MMU_REAL) #define POWERPC_EXCP_MPC5xx (POWERPC_EXCP_603) @@ -3864,6 +3995,7 @@ #define POWERPC_INSNS_MPC8xx (PPC_INSNS_BASE | PPC_STRING | \ PPC_MEM_EIEIO | PPC_MEM_SYNC | \ PPC_CACHE_ICBI | PPC_MFTB) +#define POWERPC_INSNS2_MPC8xx (PPC_NONE) #define POWERPC_MSRM_MPC8xx (0x000000000001F673ULL) #define POWERPC_MMU_MPC8xx (POWERPC_MMU_MPC8xx) #define POWERPC_EXCP_MPC8xx (POWERPC_EXCP_603) @@ -3895,6 +4027,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_G2 (PPC_NONE) #define POWERPC_MSRM_G2 (0x000000000006FFF2ULL) #define POWERPC_MMU_G2 (POWERPC_MMU_SOFT_6xx) //#define POWERPC_EXCP_G2 (POWERPC_EXCP_G2) @@ -3952,6 +4085,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_G2LE (PPC_NONE) #define POWERPC_MSRM_G2LE (0x000000000007FFF3ULL) #define POWERPC_MMU_G2LE (POWERPC_MMU_SOFT_6xx) #define POWERPC_EXCP_G2LE (POWERPC_EXCP_G2) @@ -4018,8 +4152,9 @@ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ PPC_MEM_TLBSYNC | PPC_TLBIVAX | \ PPC_BOOKE) +#define POWERPC_INSNS2_e200 (PPC_NONE) #define POWERPC_MSRM_e200 (0x000000000606FF30ULL) -#define POWERPC_MMU_e200 (POWERPC_MMU_BOOKE_FSL) +#define POWERPC_MMU_e200 (POWERPC_MMU_BOOKE206) #define POWERPC_EXCP_e200 (POWERPC_EXCP_BOOKE) #define POWERPC_INPUT_e200 (PPC_FLAGS_INPUT_BookE) #define POWERPC_BFDM_e200 (bfd_mach_ppc_860) @@ -4040,7 +4175,7 @@ &spr_read_spefscr, &spr_write_spefscr, 0x00000000); /* Memory management */ - gen_spr_BookE_FSL(env, 0x0000005D); + gen_spr_BookE206(env, 0x0000005D, NULL); /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", SPR_NOACCESS, SPR_NOACCESS, @@ -4111,6 +4246,11 @@ SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); + /* XXX : not implemented */ + spr_register(env, SPR_MMUCSR0, "MMUCSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); /* TOFIX */ spr_register(env, SPR_BOOKE_DSRR0, "DSRR0", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -4123,6 +4263,7 @@ env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_e200(env); env->dcache_line_size = 32; @@ -4138,6 +4279,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_e300 (PPC_NONE) #define POWERPC_MSRM_e300 (0x000000000007FFF3ULL) #define POWERPC_MMU_e300 (POWERPC_MMU_SOFT_6xx) #define POWERPC_EXCP_e300 (POWERPC_EXCP_603) @@ -4187,10 +4329,10 @@ PPC_WRTEE | PPC_RFDI | \ PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVAX | \ - PPC_BOOKE) + PPC_MEM_TLBSYNC | PPC_TLBIVAX) +#define POWERPC_INSNS2_e500v1 (PPC2_BOOKE206) #define POWERPC_MSRM_e500v1 (0x000000000606FF30ULL) -#define POWERPC_MMU_e500v1 (POWERPC_MMU_BOOKE_FSL) +#define POWERPC_MMU_e500v1 (POWERPC_MMU_BOOKE206) #define POWERPC_EXCP_e500v1 (POWERPC_EXCP_BOOKE) #define POWERPC_INPUT_e500v1 (PPC_FLAGS_INPUT_BookE) #define POWERPC_BFDM_e500v1 (bfd_mach_ppc_860) @@ -4198,7 +4340,7 @@ POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \ POWERPC_FLAG_BUS_CLK) #define check_pow_e500v1 check_pow_hid0 -#define init_proc_e500v1 init_proc_e500 +#define init_proc_e500v1 init_proc_e500v1 /* e500v2 core */ #define POWERPC_INSNS_e500v2 (PPC_INSNS_BASE | PPC_ISEL | \ @@ -4206,10 +4348,10 @@ PPC_WRTEE | PPC_RFDI | \ PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVAX | \ - PPC_BOOKE) + PPC_MEM_TLBSYNC | PPC_TLBIVAX) +#define POWERPC_INSNS2_e500v2 (PPC2_BOOKE206) #define POWERPC_MSRM_e500v2 (0x000000000606FF30ULL) -#define POWERPC_MMU_e500v2 (POWERPC_MMU_BOOKE_FSL) +#define POWERPC_MMU_e500v2 (POWERPC_MMU_BOOKE206) #define POWERPC_EXCP_e500v2 (POWERPC_EXCP_BOOKE) #define POWERPC_INPUT_e500v2 (PPC_FLAGS_INPUT_BookE) #define POWERPC_BFDM_e500v2 (bfd_mach_ppc_860) @@ -4217,13 +4359,23 @@ POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \ POWERPC_FLAG_BUS_CLK) #define check_pow_e500v2 check_pow_hid0 -#define init_proc_e500v2 init_proc_e500 +#define init_proc_e500v2 init_proc_e500v2 -static void init_proc_e500 (CPUPPCState *env) +static void init_proc_e500 (CPUPPCState *env, int version) { + uint32_t tlbncfg[2]; +#if !defined(CONFIG_USER_ONLY) + int i; +#endif + /* Time base */ gen_tbl(env); - gen_spr_BookE(env, 0x0000000F0000FD7FULL); + /* + * XXX The e500 doesn't implement IVOR7 and IVOR9, but doesn't + * complain when accessing them. + * gen_spr_BookE(env, 0x0000000F0000FD7FULL); + */ + gen_spr_BookE(env, 0x0000000F0000FFFFULL); /* Processor identification */ spr_register(env, SPR_BOOKE_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, @@ -4237,8 +4389,24 @@ /* Memory management */ #if !defined(CONFIG_USER_ONLY) env->nb_pids = 3; + env->nb_ways = 2; + env->id_tlbs = 0; + switch (version) { + case 1: + /* e500v1 */ + tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, 256); + tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); + break; + case 2: + /* e500v2 */ + tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512); + tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); + break; + default: + cpu_abort(env, "Unknown CPU: " TARGET_FMT_lx "\n", env->spr[SPR_PVR]); + } #endif - gen_spr_BookE_FSL(env, 0x0000005F); + gen_spr_BookE206(env, 0x000000DF, tlbncfg); /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", SPR_NOACCESS, SPR_NOACCESS, @@ -4287,23 +4455,13 @@ /* XXX : not implemented */ spr_register(env, SPR_Exxx_L1CSR0, "L1CSR0", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_e500_l1csr0, 0x00000000); /* XXX : not implemented */ spr_register(env, SPR_Exxx_L1CSR1, "L1CSR1", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -4312,11 +4470,19 @@ SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); + spr_register(env, SPR_MMUCSR0, "MMUCSR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_booke206_mmucsr0, + 0x00000000); + #if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->id_tlbs = 0; + env->nb_tlb = 0; + env->tlb_type = TLB_MAS; + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + env->nb_tlb += booke206_tlb_size(env, i); + } #endif + init_excp_e200(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -4324,6 +4490,16 @@ ppce500_irq_init(env); } +static void init_proc_e500v1(CPUPPCState *env) +{ + init_proc_e500(env, 1); +} + +static void init_proc_e500v2(CPUPPCState *env) +{ + init_proc_e500(env, 2); +} + /* Non-embedded PowerPC */ /* POWER : same as 601, without mfmsr, mfsr */ @@ -4339,6 +4515,7 @@ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_601 (PPC_NONE) #define POWERPC_MSRM_601 (0x000000000000FD70ULL) #define POWERPC_MSRR_601 (0x0000000000001040ULL) //#define POWERPC_MMU_601 (POWERPC_MMU_601) @@ -4391,6 +4568,7 @@ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_601v (PPC_NONE) #define POWERPC_MSRM_601v (0x000000000000FD70ULL) #define POWERPC_MSRR_601v (0x0000000000001040ULL) #define POWERPC_MMU_601v (POWERPC_MMU_601) @@ -4418,6 +4596,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_6xx_TLB | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_602_SPEC) +#define POWERPC_INSNS2_602 (PPC_NONE) #define POWERPC_MSRM_602 (0x0000000000C7FF73ULL) /* XXX: 602 MMU is quite specific. Should add a special case */ #define POWERPC_MMU_602 (POWERPC_MMU_SOFT_6xx) @@ -4463,6 +4642,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_603 (PPC_NONE) #define POWERPC_MSRM_603 (0x000000000007FF73ULL) #define POWERPC_MMU_603 (POWERPC_MMU_SOFT_6xx) //#define POWERPC_EXCP_603 (POWERPC_EXCP_603) @@ -4507,6 +4687,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_603E (PPC_NONE) #define POWERPC_MSRM_603E (0x000000000007FF73ULL) #define POWERPC_MMU_603E (POWERPC_MMU_SOFT_6xx) //#define POWERPC_EXCP_603E (POWERPC_EXCP_603E) @@ -4556,6 +4737,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_604 (PPC_NONE) #define POWERPC_MSRM_604 (0x000000000005FF77ULL) #define POWERPC_MMU_604 (POWERPC_MMU_32B) //#define POWERPC_EXCP_604 (POWERPC_EXCP_604) @@ -4594,6 +4776,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_604E (PPC_NONE) #define POWERPC_MSRM_604E (0x000000000005FF77ULL) #define POWERPC_MMU_604E (POWERPC_MMU_32B) #define POWERPC_EXCP_604E (POWERPC_EXCP_604) @@ -4652,6 +4835,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_740 (PPC_NONE) #define POWERPC_MSRM_740 (0x000000000005FF77ULL) #define POWERPC_MMU_740 (POWERPC_MMU_32B) #define POWERPC_EXCP_740 (POWERPC_EXCP_7x0) @@ -4697,6 +4881,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_750 (PPC_NONE) #define POWERPC_MSRM_750 (0x000000000005FF77ULL) #define POWERPC_MMU_750 (POWERPC_MMU_32B) #define POWERPC_EXCP_750 (POWERPC_EXCP_7x0) @@ -4788,6 +4973,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_750cl (PPC_NONE) #define POWERPC_MSRM_750cl (0x000000000005FF77ULL) #define POWERPC_MMU_750cl (POWERPC_MMU_32B) #define POWERPC_EXCP_750cl (POWERPC_EXCP_7x0) @@ -4926,6 +5112,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_750cx (PPC_NONE) #define POWERPC_MSRM_750cx (0x000000000005FF77ULL) #define POWERPC_MMU_750cx (POWERPC_MMU_32B) #define POWERPC_EXCP_750cx (POWERPC_EXCP_7x0) @@ -4983,6 +5170,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_750fx (PPC_NONE) #define POWERPC_MSRM_750fx (0x000000000005FF77ULL) #define POWERPC_MMU_750fx (POWERPC_MMU_32B) #define POWERPC_EXCP_750fx (POWERPC_EXCP_7x0) @@ -5045,6 +5233,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_750gx (PPC_NONE) #define POWERPC_MSRM_750gx (0x000000000005FF77ULL) #define POWERPC_MMU_750gx (POWERPC_MMU_32B) #define POWERPC_EXCP_750gx (POWERPC_EXCP_7x0) @@ -5107,6 +5296,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_745 (PPC_NONE) #define POWERPC_MSRM_745 (0x000000000005FF77ULL) #define POWERPC_MMU_745 (POWERPC_MMU_SOFT_6xx) #define POWERPC_EXCP_745 (POWERPC_EXCP_7x5) @@ -5160,6 +5350,7 @@ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ PPC_SEGMENT | PPC_EXTERN) +#define POWERPC_INSNS2_755 (PPC_NONE) #define POWERPC_MSRM_755 (0x000000000005FF77ULL) #define POWERPC_MMU_755 (POWERPC_MMU_SOFT_6xx) #define POWERPC_EXCP_755 (POWERPC_EXCP_7x5) @@ -5228,6 +5419,7 @@ PPC_MEM_TLBIA | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_ALTIVEC) +#define POWERPC_INSNS2_7400 (PPC_NONE) #define POWERPC_MSRM_7400 (0x000000000205FF77ULL) #define POWERPC_MMU_7400 (POWERPC_MMU_32B) #define POWERPC_EXCP_7400 (POWERPC_EXCP_74xx) @@ -5280,6 +5472,7 @@ PPC_MEM_TLBIA | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_ALTIVEC) +#define POWERPC_INSNS2_7410 (PPC_NONE) #define POWERPC_MSRM_7410 (0x000000000205FF77ULL) #define POWERPC_MMU_7410 (POWERPC_MMU_32B) #define POWERPC_EXCP_7410 (POWERPC_EXCP_74xx) @@ -5338,6 +5531,7 @@ PPC_MEM_TLBIA | PPC_74xx_TLB | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_ALTIVEC) +#define POWERPC_INSNS2_7440 (PPC_NONE) #define POWERPC_MSRM_7440 (0x000000000205FF77ULL) #define POWERPC_MMU_7440 (POWERPC_MMU_SOFT_74xx) #define POWERPC_EXCP_7440 (POWERPC_EXCP_74xx) @@ -5423,6 +5617,7 @@ PPC_MEM_TLBIA | PPC_74xx_TLB | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_ALTIVEC) +#define POWERPC_INSNS2_7450 (PPC_NONE) #define POWERPC_MSRM_7450 (0x000000000205FF77ULL) #define POWERPC_MMU_7450 (POWERPC_MMU_SOFT_74xx) #define POWERPC_EXCP_7450 (POWERPC_EXCP_74xx) @@ -5534,6 +5729,7 @@ PPC_MEM_TLBIA | PPC_74xx_TLB | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_ALTIVEC) +#define POWERPC_INSNS2_7445 (PPC_NONE) #define POWERPC_MSRM_7445 (0x000000000205FF77ULL) #define POWERPC_MMU_7445 (POWERPC_MMU_SOFT_74xx) #define POWERPC_EXCP_7445 (POWERPC_EXCP_74xx) @@ -5648,6 +5844,7 @@ PPC_MEM_TLBIA | PPC_74xx_TLB | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_ALTIVEC) +#define POWERPC_INSNS2_7455 (PPC_NONE) #define POWERPC_MSRM_7455 (0x000000000205FF77ULL) #define POWERPC_MMU_7455 (POWERPC_MMU_SOFT_74xx) #define POWERPC_EXCP_7455 (POWERPC_EXCP_74xx) @@ -5764,6 +5961,7 @@ PPC_MEM_TLBIA | PPC_74xx_TLB | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_ALTIVEC) +#define POWERPC_INSNS2_7457 (PPC_NONE) #define POWERPC_MSRM_7457 (0x000000000205FF77ULL) #define POWERPC_MMU_7457 (POWERPC_MMU_SOFT_74xx) #define POWERPC_EXCP_7457 (POWERPC_EXCP_74xx) @@ -5903,6 +6101,7 @@ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ PPC_SEGMENT_64B | PPC_SLBI) +#define POWERPC_INSNS2_970 (PPC_NONE) #define POWERPC_MSRM_970 (0x900000000204FF36ULL) #define POWERPC_MMU_970 (POWERPC_MMU_64B) //#define POWERPC_EXCP_970 (POWERPC_EXCP_970) @@ -5998,6 +6197,7 @@ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ PPC_SEGMENT_64B | PPC_SLBI) +#define POWERPC_INSNS2_970FX (PPC_NONE) #define POWERPC_MSRM_970FX (0x800000000204FF36ULL) #define POWERPC_MMU_970FX (POWERPC_MMU_64B) #define POWERPC_EXCP_970FX (POWERPC_EXCP_970) @@ -6099,6 +6299,7 @@ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ PPC_SEGMENT_64B | PPC_SLBI) +#define POWERPC_INSNS2_970GX (PPC_NONE) #define POWERPC_MSRM_970GX (0x800000000204FF36ULL) #define POWERPC_MMU_970GX (POWERPC_MMU_64B) #define POWERPC_EXCP_970GX (POWERPC_EXCP_970) @@ -6188,6 +6389,7 @@ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ PPC_SEGMENT_64B | PPC_SLBI) +#define POWERPC_INSNS2_970MP (PPC_NONE) #define POWERPC_MSRM_970MP (0x900000000204FF36ULL) #define POWERPC_MMU_970MP (POWERPC_MMU_64B) #define POWERPC_EXCP_970MP (POWERPC_EXCP_970) @@ -6267,6 +6469,79 @@ vscr_init(env, 0x00010000); } +#if defined(TARGET_PPC64) +/* POWER7 */ +#define POWERPC_INSNS_POWER7 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ + PPC_FLOAT_STFIWX | \ + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ + PPC_MEM_SYNC | PPC_MEM_EIEIO | \ + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ + PPC_64B | PPC_ALTIVEC | \ + PPC_SEGMENT_64B | PPC_SLBI | \ + PPC_POPCNTB | PPC_POPCNTWD) +#define POWERPC_INSNS2_POWER7 (PPC_NONE) +#define POWERPC_MSRM_POWER7 (0x800000000204FF36ULL) +#define POWERPC_MMU_POWER7 (POWERPC_MMU_2_06) +#define POWERPC_EXCP_POWER7 (POWERPC_EXCP_POWER7) +#define POWERPC_INPUT_POWER7 (PPC_FLAGS_INPUT_POWER7) +#define POWERPC_BFDM_POWER7 (bfd_mach_ppc64) +#define POWERPC_FLAG_POWER7 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ + POWERPC_FLAG_BUS_CLK) +#define check_pow_POWER7 check_pow_nocheck + +static void init_proc_POWER7 (CPUPPCState *env) +{ + gen_spr_ne_601(env); + gen_spr_7xx(env); + /* Time base */ + gen_tbl(env); +#if !defined(CONFIG_USER_ONLY) + /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ + spr_register(env, SPR_PURR, "PURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + 0x00000000); + spr_register(env, SPR_SPURR, "SPURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + 0x00000000); +#endif /* !CONFIG_USER_ONLY */ + /* Memory management */ + /* XXX : not implemented */ + spr_register(env, SPR_MMUCFG, "MMUCFG", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + 0x00000000); /* TOFIX */ + /* XXX : not implemented */ + spr_register(env, SPR_CTRL, "SPR_CTRLT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x80800000); + spr_register(env, SPR_UCTRL, "SPR_CTRLF", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x80800000); + spr_register(env, SPR_VRSAVE, "SPR_VRSAVE", + &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_generic, + 0x00000000); +#if !defined(CONFIG_USER_ONLY) + env->slb_nr = 32; +#endif + init_excp_POWER7(env); + env->dcache_line_size = 128; + env->icache_line_size = 128; + /* Allocate hardware IRQ controller */ + ppcPOWER7_irq_init(env); + /* Can't find information on what this should be on reset. This + * value is the one used by 74xx processors. */ + vscr_init(env, 0x00010000); +} +#endif /* TARGET_PPC64 */ + /* PowerPC 620 */ #define POWERPC_INSNS_620 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ @@ -6277,6 +6552,7 @@ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_SEGMENT | PPC_EXTERN | \ PPC_64B | PPC_SLBI) +#define POWERPC_INSNS2_620 (PPC_NONE) #define POWERPC_MSRM_620 (0x800000000005FF77ULL) //#define POWERPC_MMU_620 (POWERPC_MMU_620) #define POWERPC_EXCP_620 (POWERPC_EXCP_970) @@ -6312,6 +6588,7 @@ /* Default 32 bits PowerPC target will be 604 */ #define CPU_POWERPC_PPC32 CPU_POWERPC_604 #define POWERPC_INSNS_PPC32 POWERPC_INSNS_604 +#define POWERPC_INSNS2_PPC32 POWERPC_INSNS2_604 #define POWERPC_MSRM_PPC32 POWERPC_MSRM_604 #define POWERPC_MMU_PPC32 POWERPC_MMU_604 #define POWERPC_EXCP_PPC32 POWERPC_EXCP_604 @@ -6324,6 +6601,7 @@ /* Default 64 bits PowerPC target will be 970 FX */ #define CPU_POWERPC_PPC64 CPU_POWERPC_970FX #define POWERPC_INSNS_PPC64 POWERPC_INSNS_970FX +#define POWERPC_INSNS2_PPC64 POWERPC_INSNS2_970FX #define POWERPC_MSRM_PPC64 POWERPC_MSRM_970FX #define POWERPC_MMU_PPC64 POWERPC_MMU_970FX #define POWERPC_EXCP_PPC64 POWERPC_EXCP_970FX @@ -6335,27 +6613,29 @@ /* Default PowerPC target will be PowerPC 32 */ #if defined (TARGET_PPC64) && 0 // XXX: TODO -#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC64 -#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC64 -#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC64 -#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC64 -#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC64 -#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC64 -#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC64 -#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC64 -#define check_pow_DEFAULT check_pow_PPC64 -#define init_proc_DEFAULT init_proc_PPC64 +#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC64 +#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC64 +#define POWERPC_INSNS2_DEFAULT POWERPC_INSNS_PPC64 +#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC64 +#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC64 +#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC64 +#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC64 +#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC64 +#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC64 +#define check_pow_DEFAULT check_pow_PPC64 +#define init_proc_DEFAULT init_proc_PPC64 #else -#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC32 -#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC32 -#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC32 -#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC32 -#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC32 -#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC32 -#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC32 -#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC32 -#define check_pow_DEFAULT check_pow_PPC32 -#define init_proc_DEFAULT init_proc_PPC32 +#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC32 +#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC32 +#define POWERPC_INSNS2_DEFAULT POWERPC_INSNS_PPC32 +#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC32 +#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC32 +#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC32 +#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC32 +#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC32 +#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC32 +#define check_pow_DEFAULT check_pow_PPC32 +#define init_proc_DEFAULT init_proc_PPC32 #endif /*****************************************************************************/ @@ -6989,6 +7269,8 @@ CPU_POWERPC_POWER6 = 0x003E0000, CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */ CPU_POWERPC_POWER6A = 0x0F000002, +#define CPU_POWERPC_POWER7 CPU_POWERPC_POWER7_v20 + CPU_POWERPC_POWER7_v20 = 0x003F0200, CPU_POWERPC_970 = 0x00390202, #define CPU_POWERPC_970FX CPU_POWERPC_970FX_v31 CPU_POWERPC_970FX_v10 = 0x00391100, @@ -7202,18 +7484,19 @@ /* PowerPC CPU definitions */ #define POWERPC_DEF_SVR(_name, _pvr, _svr, _type) \ { \ - .name = _name, \ - .pvr = _pvr, \ - .svr = _svr, \ - .insns_flags = glue(POWERPC_INSNS_,_type), \ - .msr_mask = glue(POWERPC_MSRM_,_type), \ - .mmu_model = glue(POWERPC_MMU_,_type), \ - .excp_model = glue(POWERPC_EXCP_,_type), \ - .bus_model = glue(POWERPC_INPUT_,_type), \ - .bfd_mach = glue(POWERPC_BFDM_,_type), \ - .flags = glue(POWERPC_FLAG_,_type), \ - .init_proc = &glue(init_proc_,_type), \ - .check_pow = &glue(check_pow_,_type), \ + .name = _name, \ + .pvr = _pvr, \ + .svr = _svr, \ + .insns_flags = glue(POWERPC_INSNS_,_type), \ + .insns_flags2 = glue(POWERPC_INSNS2_,_type), \ + .msr_mask = glue(POWERPC_MSRM_,_type), \ + .mmu_model = glue(POWERPC_MMU_,_type), \ + .excp_model = glue(POWERPC_EXCP_,_type), \ + .bus_model = glue(POWERPC_INPUT_,_type), \ + .bfd_mach = glue(POWERPC_BFDM_,_type), \ + .flags = glue(POWERPC_FLAG_,_type), \ + .init_proc = &glue(init_proc_,_type), \ + .check_pow = &glue(check_pow_,_type), \ } #define POWERPC_DEF(_name, _pvr, _type) \ POWERPC_DEF_SVR(_name, _pvr, POWERPC_SVR_NONE, _type) @@ -8791,6 +9074,9 @@ /* POWER6A */ POWERPC_DEF("POWER6A", CPU_POWERPC_POWER6A, POWER6), #endif + /* POWER7 */ + POWERPC_DEF("POWER7", CPU_POWERPC_POWER7, POWER7), + POWERPC_DEF("POWER7_v2.0", CPU_POWERPC_POWER7_v20, POWER7), /* PowerPC 970 */ POWERPC_DEF("970", CPU_POWERPC_970, 970), /* PowerPC 970FX (G5) */ @@ -8897,7 +9183,7 @@ }; /*****************************************************************************/ -/* Generic CPU instanciation routine */ +/* Generic CPU instantiation routine */ static void init_ppc_proc (CPUPPCState *env, const ppc_def_t *def) { #if !defined(CONFIG_USER_ONLY) @@ -8914,6 +9200,7 @@ env->nb_BATs = 0; env->nb_tlb = 0; env->nb_ways = 0; + env->tlb_type = TLB_NONE; #endif /* Register SPR common to all PowerPC implementations */ gen_spr_generic(env); @@ -9038,7 +9325,17 @@ int nb_tlb = env->nb_tlb; if (env->id_tlbs != 0) nb_tlb *= 2; - env->tlb = qemu_mallocz(nb_tlb * sizeof(ppc_tlb_t)); + switch (env->tlb_type) { + case TLB_6XX: + env->tlb.tlb6 = qemu_mallocz(nb_tlb * sizeof(ppc6xx_tlb_t)); + break; + case TLB_EMB: + env->tlb.tlbe = qemu_mallocz(nb_tlb * sizeof(ppcemb_tlb_t)); + break; + case TLB_MAS: + env->tlb.tlbm = qemu_mallocz(nb_tlb * sizeof(ppcmas_tlb_t)); + break; + } /* Pre-compute some useful values */ env->tlb_per_way = env->nb_tlb / env->nb_ways; } @@ -9285,7 +9582,8 @@ fill_new_table(env->opcodes, 0x40); for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) { - if ((opc->handler.type & def->insns_flags) != 0) { + if (((opc->handler.type & def->insns_flags) != 0) || + ((opc->handler.type2 & def->insns_flags2) != 0)) { if (register_insn(env->opcodes, opc) < 0) { printf("*** ERROR initializing PowerPC instruction " "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2, @@ -9498,6 +9796,7 @@ env->excp_model = def->excp_model; env->bus_model = def->bus_model; env->insns_flags = def->insns_flags; + env->insns_flags2 = def->insns_flags2; env->flags = def->flags; env->bfd_mach = def->bfd_mach; env->check_pow = def->check_pow; @@ -9547,8 +9846,8 @@ case POWERPC_MMU_BOOKE: mmu_model = "PowerPC BookE"; break; - case POWERPC_MMU_BOOKE_FSL: - mmu_model = "PowerPC BookE FSL"; + case POWERPC_MMU_BOOKE206: + mmu_model = "PowerPC BookE 2.06"; break; case POWERPC_MMU_601: mmu_model = "PowerPC 601"; diff -Nru qemu-kvm-0.14.1+noroms/target-s390x/cpu.h qemu-kvm-0.15.0+noroms/target-s390x/cpu.h --- qemu-kvm-0.14.1+noroms/target-s390x/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-s390x/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -26,24 +26,35 @@ #define CPUState struct CPUS390XState #include "cpu-defs.h" +#define TARGET_PAGE_BITS 12 + +#define TARGET_PHYS_ADDR_SPACE_BITS 64 +#define TARGET_VIRT_ADDR_SPACE_BITS 64 + +#include "cpu-all.h" #include "softfloat.h" -#define NB_MMU_MODES 2 +#define NB_MMU_MODES 3 -typedef union FPReg { - struct { -#ifdef WORDS_BIGENDIAN - float32 e; - int32_t __pad; -#else - int32_t __pad; - float32 e; -#endif - }; - float64 d; - uint64_t i; -} FPReg; +#define MMU_MODE0_SUFFIX _primary +#define MMU_MODE1_SUFFIX _secondary +#define MMU_MODE2_SUFFIX _home + +#define MMU_USER_IDX 1 + +#define MAX_EXT_QUEUE 16 + +typedef struct PSW { + uint64_t mask; + uint64_t addr; +} PSW; + +typedef struct ExtQueue { + uint32_t code; + uint32_t param; + uint32_t param64; +} ExtQueue; typedef struct CPUS390XState { uint64_t regs[16]; /* GP registers */ @@ -51,42 +62,217 @@ uint32_t aregs[16]; /* access registers */ uint32_t fpc; /* floating-point control register */ - FPReg fregs[16]; /* FP registers */ + CPU_DoubleU fregs[16]; /* FP registers */ float_status fpu_status; /* passed to softfloat lib */ - struct { - uint64_t mask; - uint64_t addr; - } psw; + PSW psw; - int cc; /* condition code (0-3) */ + uint32_t cc_op; + uint64_t cc_src; + uint64_t cc_dst; + uint64_t cc_vr; uint64_t __excp_addr; + uint64_t psa; + + uint32_t int_pgm_code; + uint32_t int_pgm_ilc; + + uint32_t int_svc_code; + uint32_t int_svc_ilc; + + uint64_t cregs[16]; /* control registers */ + + int pending_int; + ExtQueue ext_queue[MAX_EXT_QUEUE]; + + int ext_index; CPU_COMMON + + /* reset does memset(0) up to here */ + + int cpu_num; + uint8_t *storage_keys; + + uint64_t tod_offset; + uint64_t tod_basetime; + QEMUTimer *tod_timer; + + QEMUTimer *cpu_timer; } CPUS390XState; #if defined(CONFIG_USER_ONLY) static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) { - if (newsp) + if (newsp) { env->regs[15] = newsp; + } env->regs[0] = 0; } #endif -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _user -#define MMU_USER_IDX 1 +/* Interrupt Codes */ +/* Program Interrupts */ +#define PGM_OPERATION 0x0001 +#define PGM_PRIVILEGED 0x0002 +#define PGM_EXECUTE 0x0003 +#define PGM_PROTECTION 0x0004 +#define PGM_ADDRESSING 0x0005 +#define PGM_SPECIFICATION 0x0006 +#define PGM_DATA 0x0007 +#define PGM_FIXPT_OVERFLOW 0x0008 +#define PGM_FIXPT_DIVIDE 0x0009 +#define PGM_DEC_OVERFLOW 0x000a +#define PGM_DEC_DIVIDE 0x000b +#define PGM_HFP_EXP_OVERFLOW 0x000c +#define PGM_HFP_EXP_UNDERFLOW 0x000d +#define PGM_HFP_SIGNIFICANCE 0x000e +#define PGM_HFP_DIVIDE 0x000f +#define PGM_SEGMENT_TRANS 0x0010 +#define PGM_PAGE_TRANS 0x0011 +#define PGM_TRANS_SPEC 0x0012 +#define PGM_SPECIAL_OP 0x0013 +#define PGM_OPERAND 0x0015 +#define PGM_TRACE_TABLE 0x0016 +#define PGM_SPACE_SWITCH 0x001c +#define PGM_HFP_SQRT 0x001d +#define PGM_PC_TRANS_SPEC 0x001f +#define PGM_AFX_TRANS 0x0020 +#define PGM_ASX_TRANS 0x0021 +#define PGM_LX_TRANS 0x0022 +#define PGM_EX_TRANS 0x0023 +#define PGM_PRIM_AUTH 0x0024 +#define PGM_SEC_AUTH 0x0025 +#define PGM_ALET_SPEC 0x0028 +#define PGM_ALEN_SPEC 0x0029 +#define PGM_ALE_SEQ 0x002a +#define PGM_ASTE_VALID 0x002b +#define PGM_ASTE_SEQ 0x002c +#define PGM_EXT_AUTH 0x002d +#define PGM_STACK_FULL 0x0030 +#define PGM_STACK_EMPTY 0x0031 +#define PGM_STACK_SPEC 0x0032 +#define PGM_STACK_TYPE 0x0033 +#define PGM_STACK_OP 0x0034 +#define PGM_ASCE_TYPE 0x0038 +#define PGM_REG_FIRST_TRANS 0x0039 +#define PGM_REG_SEC_TRANS 0x003a +#define PGM_REG_THIRD_TRANS 0x003b +#define PGM_MONITOR 0x0040 +#define PGM_PER 0x0080 +#define PGM_CRYPTO 0x0119 + +/* External Interrupts */ +#define EXT_INTERRUPT_KEY 0x0040 +#define EXT_CLOCK_COMP 0x1004 +#define EXT_CPU_TIMER 0x1005 +#define EXT_MALFUNCTION 0x1200 +#define EXT_EMERGENCY 0x1201 +#define EXT_EXTERNAL_CALL 0x1202 +#define EXT_ETR 0x1406 +#define EXT_SERVICE 0x2401 +#define EXT_VIRTIO 0x2603 + +/* PSW defines */ +#undef PSW_MASK_PER +#undef PSW_MASK_DAT +#undef PSW_MASK_IO +#undef PSW_MASK_EXT +#undef PSW_MASK_KEY +#undef PSW_SHIFT_KEY +#undef PSW_MASK_MCHECK +#undef PSW_MASK_WAIT +#undef PSW_MASK_PSTATE +#undef PSW_MASK_ASC +#undef PSW_MASK_CC +#undef PSW_MASK_PM +#undef PSW_MASK_64 + +#define PSW_MASK_PER 0x4000000000000000ULL +#define PSW_MASK_DAT 0x0400000000000000ULL +#define PSW_MASK_IO 0x0200000000000000ULL +#define PSW_MASK_EXT 0x0100000000000000ULL +#define PSW_MASK_KEY 0x00F0000000000000ULL +#define PSW_SHIFT_KEY 56 +#define PSW_MASK_MCHECK 0x0004000000000000ULL +#define PSW_MASK_WAIT 0x0002000000000000ULL +#define PSW_MASK_PSTATE 0x0001000000000000ULL +#define PSW_MASK_ASC 0x0000C00000000000ULL +#define PSW_MASK_CC 0x0000300000000000ULL +#define PSW_MASK_PM 0x00000F0000000000ULL +#define PSW_MASK_64 0x0000000100000000ULL +#define PSW_MASK_32 0x0000000080000000ULL + +#undef PSW_ASC_PRIMARY +#undef PSW_ASC_ACCREG +#undef PSW_ASC_SECONDARY +#undef PSW_ASC_HOME + +#define PSW_ASC_PRIMARY 0x0000000000000000ULL +#define PSW_ASC_ACCREG 0x0000400000000000ULL +#define PSW_ASC_SECONDARY 0x0000800000000000ULL +#define PSW_ASC_HOME 0x0000C00000000000ULL + +/* tb flags */ + +#define FLAG_MASK_PER (PSW_MASK_PER >> 32) +#define FLAG_MASK_DAT (PSW_MASK_DAT >> 32) +#define FLAG_MASK_IO (PSW_MASK_IO >> 32) +#define FLAG_MASK_EXT (PSW_MASK_EXT >> 32) +#define FLAG_MASK_KEY (PSW_MASK_KEY >> 32) +#define FLAG_MASK_MCHECK (PSW_MASK_MCHECK >> 32) +#define FLAG_MASK_WAIT (PSW_MASK_WAIT >> 32) +#define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> 32) +#define FLAG_MASK_ASC (PSW_MASK_ASC >> 32) +#define FLAG_MASK_CC (PSW_MASK_CC >> 32) +#define FLAG_MASK_PM (PSW_MASK_PM >> 32) +#define FLAG_MASK_64 (PSW_MASK_64 >> 32) +#define FLAG_MASK_32 0x00001000 + static inline int cpu_mmu_index (CPUState *env) { - /* XXX: Currently we don't implement virtual memory */ + if (env->psw.mask & PSW_MASK_PSTATE) { + return 1; + } + return 0; } +static inline void cpu_get_tb_cpu_state(CPUState* env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = env->psw.addr; + *cs_base = 0; + *flags = ((env->psw.mask >> 32) & ~FLAG_MASK_CC) | + ((env->psw.mask & PSW_MASK_32) ? FLAG_MASK_32 : 0); +} + +static inline int get_ilc(uint8_t opc) +{ + switch (opc >> 6) { + case 0: + return 1; + case 1: + case 2: + return 2; + case 3: + return 3; + } + + return 0; +} + +#define ILC_LATER 0x20 +#define ILC_LATER_INC 0x21 +#define ILC_LATER_INC_2 0x22 + + CPUS390XState *cpu_s390x_init(const char *cpu_model); +void s390x_translate_init(void); int cpu_s390x_exec(CPUS390XState *s); void cpu_s390x_close(CPUS390XState *s); +void do_interrupt (CPUState *env); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero @@ -97,41 +283,71 @@ int mmu_idx, int is_softmuu); #define cpu_handle_mmu_fault cpu_s390x_handle_mmu_fault -#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 -int s390_virtio_hypercall(CPUState *env); +int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall); + +#ifdef CONFIG_KVM +void kvm_s390_interrupt(CPUState *env, int type, uint32_t code); void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token); +void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm, + uint64_t parm64, int vm); +#else +static inline void kvm_s390_interrupt(CPUState *env, int type, uint32_t code) +{ +} + +static inline void kvm_s390_virtio_irq(CPUState *env, int config_change, + uint64_t token) +{ +} + +static inline void kvm_s390_interrupt_internal(CPUState *env, int type, + uint32_t parm, uint64_t parm64, + int vm) +{ +} +#endif CPUState *s390_cpu_addr2state(uint16_t cpu_addr); + +/* from s390-virtio-bus */ +extern const target_phys_addr_t virtio_size; + #endif +void cpu_lock(void); +void cpu_unlock(void); +static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) +{ + env->aregs[0] = newtls >> 32; + env->aregs[1] = newtls & 0xffffffffULL; +} #define cpu_init cpu_s390x_init #define cpu_exec cpu_s390x_exec #define cpu_gen_code cpu_s390x_gen_code +#define cpu_signal_handler cpu_s390x_signal_handler -#include "cpu-all.h" +#include "exec-all.h" + +#ifdef CONFIG_USER_ONLY #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 */ +#define EXCP_SPEC 6 /* specification exception */ -static inline void cpu_get_tb_cpu_state(CPUState* env, target_ulong *pc, - target_ulong *cs_base, int *flags) -{ - *pc = env->psw.addr; - /* XXX this is correct for user-mode emulation, but needs - * the asce register information as well when softmmu - * is implemented in the future */ - *cs_base = 0; - *flags = env->psw.mask; -} +#else + +#define EXCP_EXT 1 /* external interrupt */ +#define EXCP_SVC 2 /* supervisor call (syscall) */ +#define EXCP_PGM 3 /* program interruption */ + +#endif /* CONFIG_USER_ONLY */ + +#define INTERRUPT_EXT (1 << 0) +#define INTERRUPT_TOD (1 << 1) +#define INTERRUPT_CPUTIMER (1 << 2) /* Program Status Word. */ #define S390_PSWM_REGNUM 0 @@ -265,5 +481,496 @@ #define S390_NUM_PSEUDO_REGS 2 #define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2) +/* CC optimization */ + +enum cc_op { + CC_OP_CONST0 = 0, /* CC is 0 */ + CC_OP_CONST1, /* CC is 1 */ + CC_OP_CONST2, /* CC is 2 */ + CC_OP_CONST3, /* CC is 3 */ + + CC_OP_DYNAMIC, /* CC calculation defined by env->cc_op */ + CC_OP_STATIC, /* CC value is env->cc_op */ + + CC_OP_NZ, /* env->cc_dst != 0 */ + CC_OP_LTGT_32, /* signed less/greater than (32bit) */ + CC_OP_LTGT_64, /* signed less/greater than (64bit) */ + CC_OP_LTUGTU_32, /* unsigned less/greater than (32bit) */ + CC_OP_LTUGTU_64, /* unsigned less/greater than (64bit) */ + CC_OP_LTGT0_32, /* signed less/greater than 0 (32bit) */ + CC_OP_LTGT0_64, /* signed less/greater than 0 (64bit) */ + + CC_OP_ADD_64, /* overflow on add (64bit) */ + CC_OP_ADDU_64, /* overflow on unsigned add (64bit) */ + CC_OP_SUB_64, /* overflow on substraction (64bit) */ + CC_OP_SUBU_64, /* overflow on unsigned substraction (64bit) */ + CC_OP_ABS_64, /* sign eval on abs (64bit) */ + CC_OP_NABS_64, /* sign eval on nabs (64bit) */ + + CC_OP_ADD_32, /* overflow on add (32bit) */ + CC_OP_ADDU_32, /* overflow on unsigned add (32bit) */ + CC_OP_SUB_32, /* overflow on substraction (32bit) */ + CC_OP_SUBU_32, /* overflow on unsigned substraction (32bit) */ + CC_OP_ABS_32, /* sign eval on abs (64bit) */ + CC_OP_NABS_32, /* sign eval on nabs (64bit) */ + + CC_OP_COMP_32, /* complement */ + CC_OP_COMP_64, /* complement */ + + CC_OP_TM_32, /* test under mask (32bit) */ + CC_OP_TM_64, /* test under mask (64bit) */ + + CC_OP_LTGT_F32, /* FP compare (32bit) */ + CC_OP_LTGT_F64, /* FP compare (64bit) */ + + CC_OP_NZ_F32, /* FP dst != 0 (32bit) */ + CC_OP_NZ_F64, /* FP dst != 0 (64bit) */ + + CC_OP_ICM, /* insert characters under mask */ + CC_OP_SLAG, /* Calculate shift left signed */ + CC_OP_MAX +}; + +static const char *cc_names[] = { + [CC_OP_CONST0] = "CC_OP_CONST0", + [CC_OP_CONST1] = "CC_OP_CONST1", + [CC_OP_CONST2] = "CC_OP_CONST2", + [CC_OP_CONST3] = "CC_OP_CONST3", + [CC_OP_DYNAMIC] = "CC_OP_DYNAMIC", + [CC_OP_STATIC] = "CC_OP_STATIC", + [CC_OP_NZ] = "CC_OP_NZ", + [CC_OP_LTGT_32] = "CC_OP_LTGT_32", + [CC_OP_LTGT_64] = "CC_OP_LTGT_64", + [CC_OP_LTUGTU_32] = "CC_OP_LTUGTU_32", + [CC_OP_LTUGTU_64] = "CC_OP_LTUGTU_64", + [CC_OP_LTGT0_32] = "CC_OP_LTGT0_32", + [CC_OP_LTGT0_64] = "CC_OP_LTGT0_64", + [CC_OP_ADD_64] = "CC_OP_ADD_64", + [CC_OP_ADDU_64] = "CC_OP_ADDU_64", + [CC_OP_SUB_64] = "CC_OP_SUB_64", + [CC_OP_SUBU_64] = "CC_OP_SUBU_64", + [CC_OP_ABS_64] = "CC_OP_ABS_64", + [CC_OP_NABS_64] = "CC_OP_NABS_64", + [CC_OP_ADD_32] = "CC_OP_ADD_32", + [CC_OP_ADDU_32] = "CC_OP_ADDU_32", + [CC_OP_SUB_32] = "CC_OP_SUB_32", + [CC_OP_SUBU_32] = "CC_OP_SUBU_32", + [CC_OP_ABS_32] = "CC_OP_ABS_32", + [CC_OP_NABS_32] = "CC_OP_NABS_32", + [CC_OP_COMP_32] = "CC_OP_COMP_32", + [CC_OP_COMP_64] = "CC_OP_COMP_64", + [CC_OP_TM_32] = "CC_OP_TM_32", + [CC_OP_TM_64] = "CC_OP_TM_64", + [CC_OP_LTGT_F32] = "CC_OP_LTGT_F32", + [CC_OP_LTGT_F64] = "CC_OP_LTGT_F64", + [CC_OP_NZ_F32] = "CC_OP_NZ_F32", + [CC_OP_NZ_F64] = "CC_OP_NZ_F64", + [CC_OP_ICM] = "CC_OP_ICM", + [CC_OP_SLAG] = "CC_OP_SLAG", +}; + +static inline const char *cc_name(int cc_op) +{ + return cc_names[cc_op]; +} + +/* SCLP PV interface defines */ +#define SCLP_CMDW_READ_SCP_INFO 0x00020001 +#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 + +#define SCP_LENGTH 0x00 +#define SCP_FUNCTION_CODE 0x02 +#define SCP_CONTROL_MASK 0x03 +#define SCP_RESPONSE_CODE 0x06 +#define SCP_MEM_CODE 0x08 +#define SCP_INCREMENT 0x0a + +typedef struct LowCore +{ + /* prefix area: defined by architecture */ + uint32_t ccw1[2]; /* 0x000 */ + uint32_t ccw2[4]; /* 0x008 */ + uint8_t pad1[0x80-0x18]; /* 0x018 */ + uint32_t ext_params; /* 0x080 */ + uint16_t cpu_addr; /* 0x084 */ + uint16_t ext_int_code; /* 0x086 */ + uint16_t svc_ilc; /* 0x088 */ + uint16_t svc_code; /* 0x08a */ + uint16_t pgm_ilc; /* 0x08c */ + uint16_t pgm_code; /* 0x08e */ + uint32_t data_exc_code; /* 0x090 */ + uint16_t mon_class_num; /* 0x094 */ + uint16_t per_perc_atmid; /* 0x096 */ + uint64_t per_address; /* 0x098 */ + uint8_t exc_access_id; /* 0x0a0 */ + uint8_t per_access_id; /* 0x0a1 */ + uint8_t op_access_id; /* 0x0a2 */ + uint8_t ar_access_id; /* 0x0a3 */ + uint8_t pad2[0xA8-0xA4]; /* 0x0a4 */ + uint64_t trans_exc_code; /* 0x0a8 */ + uint64_t monitor_code; /* 0x0b0 */ + uint16_t subchannel_id; /* 0x0b8 */ + uint16_t subchannel_nr; /* 0x0ba */ + uint32_t io_int_parm; /* 0x0bc */ + uint32_t io_int_word; /* 0x0c0 */ + uint8_t pad3[0xc8-0xc4]; /* 0x0c4 */ + uint32_t stfl_fac_list; /* 0x0c8 */ + uint8_t pad4[0xe8-0xcc]; /* 0x0cc */ + uint32_t mcck_interruption_code[2]; /* 0x0e8 */ + uint8_t pad5[0xf4-0xf0]; /* 0x0f0 */ + uint32_t external_damage_code; /* 0x0f4 */ + uint64_t failing_storage_address; /* 0x0f8 */ + uint8_t pad6[0x120-0x100]; /* 0x100 */ + PSW restart_old_psw; /* 0x120 */ + PSW external_old_psw; /* 0x130 */ + PSW svc_old_psw; /* 0x140 */ + PSW program_old_psw; /* 0x150 */ + PSW mcck_old_psw; /* 0x160 */ + PSW io_old_psw; /* 0x170 */ + uint8_t pad7[0x1a0-0x180]; /* 0x180 */ + PSW restart_psw; /* 0x1a0 */ + PSW external_new_psw; /* 0x1b0 */ + PSW svc_new_psw; /* 0x1c0 */ + PSW program_new_psw; /* 0x1d0 */ + PSW mcck_new_psw; /* 0x1e0 */ + PSW io_new_psw; /* 0x1f0 */ + PSW return_psw; /* 0x200 */ + uint8_t irb[64]; /* 0x210 */ + uint64_t sync_enter_timer; /* 0x250 */ + uint64_t async_enter_timer; /* 0x258 */ + uint64_t exit_timer; /* 0x260 */ + uint64_t last_update_timer; /* 0x268 */ + uint64_t user_timer; /* 0x270 */ + uint64_t system_timer; /* 0x278 */ + uint64_t last_update_clock; /* 0x280 */ + uint64_t steal_clock; /* 0x288 */ + PSW return_mcck_psw; /* 0x290 */ + uint8_t pad8[0xc00-0x2a0]; /* 0x2a0 */ + /* System info area */ + uint64_t save_area[16]; /* 0xc00 */ + uint8_t pad9[0xd40-0xc80]; /* 0xc80 */ + uint64_t kernel_stack; /* 0xd40 */ + uint64_t thread_info; /* 0xd48 */ + uint64_t async_stack; /* 0xd50 */ + uint64_t kernel_asce; /* 0xd58 */ + uint64_t user_asce; /* 0xd60 */ + uint64_t panic_stack; /* 0xd68 */ + uint64_t user_exec_asce; /* 0xd70 */ + uint8_t pad10[0xdc0-0xd78]; /* 0xd78 */ + + /* SMP info area: defined by DJB */ + uint64_t clock_comparator; /* 0xdc0 */ + uint64_t ext_call_fast; /* 0xdc8 */ + uint64_t percpu_offset; /* 0xdd0 */ + uint64_t current_task; /* 0xdd8 */ + uint32_t softirq_pending; /* 0xde0 */ + uint32_t pad_0x0de4; /* 0xde4 */ + uint64_t int_clock; /* 0xde8 */ + uint8_t pad12[0xe00-0xdf0]; /* 0xdf0 */ + + /* 0xe00 is used as indicator for dump tools */ + /* whether the kernel died with panic() or not */ + uint32_t panic_magic; /* 0xe00 */ + + uint8_t pad13[0x11b8-0xe04]; /* 0xe04 */ + + /* 64 bit extparam used for pfault, diag 250 etc */ + uint64_t ext_params2; /* 0x11B8 */ + + uint8_t pad14[0x1200-0x11C0]; /* 0x11C0 */ + + /* System info area */ + + uint64_t floating_pt_save_area[16]; /* 0x1200 */ + uint64_t gpregs_save_area[16]; /* 0x1280 */ + uint32_t st_status_fixed_logout[4]; /* 0x1300 */ + uint8_t pad15[0x1318-0x1310]; /* 0x1310 */ + uint32_t prefixreg_save_area; /* 0x1318 */ + uint32_t fpt_creg_save_area; /* 0x131c */ + uint8_t pad16[0x1324-0x1320]; /* 0x1320 */ + uint32_t tod_progreg_save_area; /* 0x1324 */ + uint32_t cpu_timer_save_area[2]; /* 0x1328 */ + uint32_t clock_comp_save_area[2]; /* 0x1330 */ + uint8_t pad17[0x1340-0x1338]; /* 0x1338 */ + uint32_t access_regs_save_area[16]; /* 0x1340 */ + uint64_t cregs_save_area[16]; /* 0x1380 */ + + /* align to the top of the prefix area */ + + uint8_t pad18[0x2000-0x1400]; /* 0x1400 */ +} __attribute__((packed)) LowCore; + +/* STSI */ +#define STSI_LEVEL_MASK 0x00000000f0000000ULL +#define STSI_LEVEL_CURRENT 0x0000000000000000ULL +#define STSI_LEVEL_1 0x0000000010000000ULL +#define STSI_LEVEL_2 0x0000000020000000ULL +#define STSI_LEVEL_3 0x0000000030000000ULL +#define STSI_R0_RESERVED_MASK 0x000000000fffff00ULL +#define STSI_R0_SEL1_MASK 0x00000000000000ffULL +#define STSI_R1_RESERVED_MASK 0x00000000ffff0000ULL +#define STSI_R1_SEL2_MASK 0x000000000000ffffULL + +/* Basic Machine Configuration */ +struct sysib_111 { + uint32_t res1[8]; + uint8_t manuf[16]; + uint8_t type[4]; + uint8_t res2[12]; + uint8_t model[16]; + uint8_t sequence[16]; + uint8_t plant[4]; + uint8_t res3[156]; +}; + +/* Basic Machine CPU */ +struct sysib_121 { + uint32_t res1[80]; + uint8_t sequence[16]; + uint8_t plant[4]; + uint8_t res2[2]; + uint16_t cpu_addr; + uint8_t res3[152]; +}; + +/* Basic Machine CPUs */ +struct sysib_122 { + uint8_t res1[32]; + uint32_t capability; + uint16_t total_cpus; + uint16_t active_cpus; + uint16_t standby_cpus; + uint16_t reserved_cpus; + uint16_t adjustments[2026]; +}; + +/* LPAR CPU */ +struct sysib_221 { + uint32_t res1[80]; + uint8_t sequence[16]; + uint8_t plant[4]; + uint16_t cpu_id; + uint16_t cpu_addr; + uint8_t res3[152]; +}; + +/* LPAR CPUs */ +struct sysib_222 { + uint32_t res1[32]; + uint16_t lpar_num; + uint8_t res2; + uint8_t lcpuc; + uint16_t total_cpus; + uint16_t conf_cpus; + uint16_t standby_cpus; + uint16_t reserved_cpus; + uint8_t name[8]; + uint32_t caf; + uint8_t res3[16]; + uint16_t dedicated_cpus; + uint16_t shared_cpus; + uint8_t res4[180]; +}; + +/* VM CPUs */ +struct sysib_322 { + uint8_t res1[31]; + uint8_t count; + struct { + uint8_t res2[4]; + uint16_t total_cpus; + uint16_t conf_cpus; + uint16_t standby_cpus; + uint16_t reserved_cpus; + uint8_t name[8]; + uint32_t caf; + uint8_t cpi[16]; + uint8_t res3[24]; + } vm[8]; + uint8_t res4[3552]; +}; + +/* MMU defines */ +#define _ASCE_ORIGIN ~0xfffULL /* segment table origin */ +#define _ASCE_SUBSPACE 0x200 /* subspace group control */ +#define _ASCE_PRIVATE_SPACE 0x100 /* private space control */ +#define _ASCE_ALT_EVENT 0x80 /* storage alteration event control */ +#define _ASCE_SPACE_SWITCH 0x40 /* space switch event */ +#define _ASCE_REAL_SPACE 0x20 /* real space control */ +#define _ASCE_TYPE_MASK 0x0c /* asce table type mask */ +#define _ASCE_TYPE_REGION1 0x0c /* region first table type */ +#define _ASCE_TYPE_REGION2 0x08 /* region second table type */ +#define _ASCE_TYPE_REGION3 0x04 /* region third table type */ +#define _ASCE_TYPE_SEGMENT 0x00 /* segment table type */ +#define _ASCE_TABLE_LENGTH 0x03 /* region table length */ + +#define _REGION_ENTRY_ORIGIN ~0xfffULL /* region/segment table origin */ +#define _REGION_ENTRY_INV 0x20 /* invalid region table entry */ +#define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */ +#define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */ +#define _REGION_ENTRY_TYPE_R2 0x08 /* region second table type */ +#define _REGION_ENTRY_TYPE_R3 0x04 /* region third table type */ +#define _REGION_ENTRY_LENGTH 0x03 /* region third length */ + +#define _SEGMENT_ENTRY_ORIGIN ~0x7ffULL /* segment table origin */ +#define _SEGMENT_ENTRY_RO 0x200 /* page protection bit */ +#define _SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */ + +#define _PAGE_RO 0x200 /* HW read-only bit */ +#define _PAGE_INVALID 0x400 /* HW invalid bit */ + + + +/* EBCDIC handling */ +static const uint8_t ebcdic2ascii[] = { + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07, +}; + +static const uint8_t ascii2ebcdic [] = { + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +static inline void ebcdic_put(uint8_t *p, const char *ascii, int len) +{ + int i; + + for (i = 0; i < len; i++) { + p[i] = ascii2ebcdic[(int)ascii[i]]; + } +} + +#define SIGP_SENSE 0x01 +#define SIGP_EXTERNAL_CALL 0x02 +#define SIGP_EMERGENCY 0x03 +#define SIGP_START 0x04 +#define SIGP_STOP 0x05 +#define SIGP_RESTART 0x06 +#define SIGP_STOP_STORE_STATUS 0x09 +#define SIGP_INITIAL_CPU_RESET 0x0b +#define SIGP_CPU_RESET 0x0c +#define SIGP_SET_PREFIX 0x0d +#define SIGP_STORE_STATUS_ADDR 0x0e +#define SIGP_SET_ARCH 0x12 + +/* cpu status bits */ +#define SIGP_STAT_EQUIPMENT_CHECK 0x80000000UL +#define SIGP_STAT_INCORRECT_STATE 0x00000200UL +#define SIGP_STAT_INVALID_PARAMETER 0x00000100UL +#define SIGP_STAT_EXT_CALL_PENDING 0x00000080UL +#define SIGP_STAT_STOPPED 0x00000040UL +#define SIGP_STAT_OPERATOR_INTERV 0x00000020UL +#define SIGP_STAT_CHECK_STOP 0x00000010UL +#define SIGP_STAT_INOPERATIVE 0x00000004UL +#define SIGP_STAT_INVALID_ORDER 0x00000002UL +#define SIGP_STAT_RECEIVER_CHECK 0x00000001UL + +void load_psw(CPUState *env, uint64_t mask, uint64_t addr); +int mmu_translate(CPUState *env, target_ulong vaddr, int rw, uint64_t asc, + target_ulong *raddr, int *flags); +int sclp_service_call(CPUState *env, uint32_t sccb, uint64_t code); +uint32_t calc_cc(CPUState *env, uint32_t cc_op, uint64_t src, uint64_t dst, + uint64_t vr); + +#define TARGET_HAS_ICE 1 + +/* The value of the TOD clock for 1.1.1970. */ +#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL + +/* Converts ns to s390's clock format */ +static inline uint64_t time2tod(uint64_t ns) { + return (ns << 9) / 125; +} + +static inline void cpu_inject_ext(CPUState *env, uint32_t code, uint32_t param, + uint64_t param64) +{ + if (env->ext_index == MAX_EXT_QUEUE - 1) { + /* ugh - can't queue anymore. Let's drop. */ + return; + } + + env->ext_index++; + assert(env->ext_index < MAX_EXT_QUEUE); + + env->ext_queue[env->ext_index].code = code; + env->ext_queue[env->ext_index].param = param; + env->ext_queue[env->ext_index].param64 = param64; + + env->pending_int |= INTERRUPT_EXT; + cpu_interrupt(env, CPU_INTERRUPT_HARD); +} + +static inline bool cpu_has_work(CPUState *env) +{ + return (env->interrupt_request & CPU_INTERRUPT_HARD) && + (env->psw.mask & PSW_MASK_EXT); +} + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock* tb) +{ + env->psw.addr = tb->pc; +} #endif diff -Nru qemu-kvm-0.14.1+noroms/target-s390x/exec.h qemu-kvm-0.15.0+noroms/target-s390x/exec.h --- qemu-kvm-0.14.1+noroms/target-s390x/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-s390x/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -23,31 +23,15 @@ #include "config.h" #include "cpu.h" -#include "exec-all.h" #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -static inline int cpu_has_work(CPUState *env) +static inline void regs_to_env(void) { - return env->interrupt_request & CPU_INTERRUPT_HARD; // guess } -static inline int cpu_halted(CPUState *env) +static inline void env_to_regs(void) { - if (!env->halted) { - return 0; - } - if (cpu_has_work(env)) { - env->halted = 0; - return 0; - } - return EXCP_HALTED; } - -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock* tb) -{ - env->psw.addr = tb->pc; -} - diff -Nru qemu-kvm-0.14.1+noroms/target-s390x/helper.c qemu-kvm-0.15.0+noroms/target-s390x/helper.c --- qemu-kvm-0.14.1+noroms/target-s390x/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-s390x/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -2,6 +2,7 @@ * S/390 helpers * * Copyright (c) 2009 Ulrich Hecht + * Copyright (c) 2011 Alexander Graf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,30 +23,104 @@ #include #include "cpu.h" -#include "exec-all.h" #include "gdbstub.h" #include "qemu-common.h" +#include "qemu-timer.h" -#include -#include "kvm.h" +//#define DEBUG_S390 +//#define DEBUG_S390_PTE +//#define DEBUG_S390_STDOUT + +#ifdef DEBUG_S390 +#ifdef DEBUG_S390_STDOUT +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); \ + qemu_log(fmt, ##__VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { qemu_log(fmt, ## __VA_ARGS__); } while (0) +#endif +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +#ifdef DEBUG_S390_PTE +#define PTE_DPRINTF DPRINTF +#else +#define PTE_DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +#ifndef CONFIG_USER_ONLY +static void s390x_tod_timer(void *opaque) +{ + CPUState *env = opaque; + + env->pending_int |= INTERRUPT_TOD; + cpu_interrupt(env, CPU_INTERRUPT_HARD); +} + +static void s390x_cpu_timer(void *opaque) +{ + CPUState *env = opaque; + + env->pending_int |= INTERRUPT_CPUTIMER; + cpu_interrupt(env, CPU_INTERRUPT_HARD); +} +#endif CPUS390XState *cpu_s390x_init(const char *cpu_model) { CPUS390XState *env; +#if !defined (CONFIG_USER_ONLY) + struct tm tm; +#endif static int inited = 0; + static int cpu_num = 0; env = qemu_mallocz(sizeof(CPUS390XState)); cpu_exec_init(env); if (!inited) { inited = 1; + s390x_translate_init(); } +#if !defined(CONFIG_USER_ONLY) + qemu_get_timedate(&tm, 0); + env->tod_offset = TOD_UNIX_EPOCH + + (time2tod(mktimegm(&tm)) * 1000000000ULL); + env->tod_basetime = 0; + env->tod_timer = qemu_new_timer_ns(vm_clock, s390x_tod_timer, env); + env->cpu_timer = qemu_new_timer_ns(vm_clock, s390x_cpu_timer, env); +#endif env->cpu_model_str = cpu_model; + env->cpu_num = cpu_num++; + env->ext_index = -1; cpu_reset(env); qemu_init_vcpu(env); return env; } +#if defined(CONFIG_USER_ONLY) + +void do_interrupt (CPUState *env) +{ + env->exception_index = -1; +} + +int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + int mmu_idx, int is_softmmu) +{ + /* fprintf(stderr,"%s: address 0x%lx rw %d mmu_idx %d is_softmmu %d\n", + __FUNCTION__, address, rw, mmu_idx, is_softmmu); */ + env->exception_index = EXCP_ADDR; + env->__excp_addr = address; /* FIXME: find out how this works on a real machine */ + return 1; +} + +#endif /* CONFIG_USER_ONLY */ + void cpu_reset(CPUS390XState *env) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { @@ -58,27 +133,495 @@ tlb_flush(env, 1); } -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +#ifndef CONFIG_USER_ONLY + +/* Ensure to exit the TB after this call! */ +static void trigger_pgm_exception(CPUState *env, uint32_t code, uint32_t ilc) +{ + env->exception_index = EXCP_PGM; + env->int_pgm_code = code; + env->int_pgm_ilc = ilc; +} + +static int trans_bits(CPUState *env, uint64_t mode) +{ + int bits = 0; + + switch (mode) { + case PSW_ASC_PRIMARY: + bits = 1; + break; + case PSW_ASC_SECONDARY: + bits = 2; + break; + case PSW_ASC_HOME: + bits = 3; + break; + default: + cpu_abort(env, "unknown asc mode\n"); + break; + } + + return bits; +} + +static void trigger_prot_fault(CPUState *env, target_ulong vaddr, uint64_t mode) { + int ilc = ILC_LATER_INC_2; + int bits = trans_bits(env, mode) | 4; + + DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __FUNCTION__, vaddr, bits); + + stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); + trigger_pgm_exception(env, PGM_PROTECTION, ilc); +} + +static void trigger_page_fault(CPUState *env, target_ulong vaddr, uint32_t type, + uint64_t asc, int rw) +{ + int ilc = ILC_LATER; + int bits = trans_bits(env, asc); + + if (rw == 2) { + /* code has is undefined ilc */ + ilc = 2; + } + + DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __FUNCTION__, vaddr, bits); + + stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); + trigger_pgm_exception(env, type, ilc); +} + +static int mmu_translate_asce(CPUState *env, target_ulong vaddr, uint64_t asc, + uint64_t asce, int level, target_ulong *raddr, + int *flags, int rw) +{ + uint64_t offs = 0; + uint64_t origin; + uint64_t new_asce; + + PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __FUNCTION__, asce); + + if (((level != _ASCE_TYPE_SEGMENT) && (asce & _REGION_ENTRY_INV)) || + ((level == _ASCE_TYPE_SEGMENT) && (asce & _SEGMENT_ENTRY_INV))) { + /* XXX different regions have different faults */ + DPRINTF("%s: invalid region\n", __FUNCTION__); + trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw); + return -1; + } + + if ((level <= _ASCE_TYPE_MASK) && ((asce & _ASCE_TYPE_MASK) != level)) { + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + return -1; + } + + if (asce & _ASCE_REAL_SPACE) { + /* direct mapping */ + + *raddr = vaddr; + return 0; + } + + origin = asce & _ASCE_ORIGIN; + + switch (level) { + case _ASCE_TYPE_REGION1 + 4: + offs = (vaddr >> 50) & 0x3ff8; + break; + case _ASCE_TYPE_REGION1: + offs = (vaddr >> 39) & 0x3ff8; + break; + case _ASCE_TYPE_REGION2: + offs = (vaddr >> 28) & 0x3ff8; + break; + case _ASCE_TYPE_REGION3: + offs = (vaddr >> 17) & 0x3ff8; + break; + case _ASCE_TYPE_SEGMENT: + offs = (vaddr >> 9) & 0x07f8; + origin = asce & _SEGMENT_ENTRY_ORIGIN; + break; + } + + /* XXX region protection flags */ + /* *flags &= ~PAGE_WRITE */ + + new_asce = ldq_phys(origin + offs); + PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", + __FUNCTION__, origin, offs, new_asce); + + if (level != _ASCE_TYPE_SEGMENT) { + /* yet another region */ + return mmu_translate_asce(env, vaddr, asc, new_asce, level - 4, raddr, + flags, rw); + } + + /* PTE */ + if (new_asce & _PAGE_INVALID) { + DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __FUNCTION__, new_asce); + trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw); + return -1; + } + + if (new_asce & _PAGE_RO) { + *flags &= ~PAGE_WRITE; + } + + *raddr = new_asce & _ASCE_ORIGIN; + + PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __FUNCTION__, new_asce); + return 0; } -#ifndef CONFIG_USER_ONLY +static int mmu_translate_asc(CPUState *env, target_ulong vaddr, uint64_t asc, + target_ulong *raddr, int *flags, int rw) +{ + uint64_t asce = 0; + int level, new_level; + int r; + + switch (asc) { + case PSW_ASC_PRIMARY: + PTE_DPRINTF("%s: asc=primary\n", __FUNCTION__); + asce = env->cregs[1]; + break; + case PSW_ASC_SECONDARY: + PTE_DPRINTF("%s: asc=secondary\n", __FUNCTION__); + asce = env->cregs[7]; + break; + case PSW_ASC_HOME: + PTE_DPRINTF("%s: asc=home\n", __FUNCTION__); + asce = env->cregs[13]; + break; + } -int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw, + switch (asce & _ASCE_TYPE_MASK) { + case _ASCE_TYPE_REGION1: + break; + case _ASCE_TYPE_REGION2: + if (vaddr & 0xffe0000000000000ULL) { + DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 + " 0xffe0000000000000ULL\n", __FUNCTION__, + vaddr); + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + return -1; + } + break; + case _ASCE_TYPE_REGION3: + if (vaddr & 0xfffffc0000000000ULL) { + DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 + " 0xfffffc0000000000ULL\n", __FUNCTION__, + vaddr); + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + return -1; + } + break; + case _ASCE_TYPE_SEGMENT: + if (vaddr & 0xffffffff80000000ULL) { + DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64 + " 0xffffffff80000000ULL\n", __FUNCTION__, + vaddr); + trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw); + return -1; + } + break; + } + + /* fake level above current */ + level = asce & _ASCE_TYPE_MASK; + new_level = level + 4; + asce = (asce & ~_ASCE_TYPE_MASK) | (new_level & _ASCE_TYPE_MASK); + + r = mmu_translate_asce(env, vaddr, asc, asce, new_level, raddr, flags, rw); + + if ((rw == 1) && !(*flags & PAGE_WRITE)) { + trigger_prot_fault(env, vaddr, asc); + return -1; + } + + return r; +} + +int mmu_translate(CPUState *env, target_ulong vaddr, int rw, uint64_t asc, + target_ulong *raddr, int *flags) +{ + int r = -1; + + *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + vaddr &= TARGET_PAGE_MASK; + + if (!(env->psw.mask & PSW_MASK_DAT)) { + *raddr = vaddr; + r = 0; + goto out; + } + + switch (asc) { + case PSW_ASC_PRIMARY: + case PSW_ASC_HOME: + r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw); + break; + case PSW_ASC_SECONDARY: + /* + * Instruction: Primary + * Data: Secondary + */ + if (rw == 2) { + r = mmu_translate_asc(env, vaddr, PSW_ASC_PRIMARY, raddr, flags, + rw); + *flags &= ~(PAGE_READ | PAGE_WRITE); + } else { + r = mmu_translate_asc(env, vaddr, PSW_ASC_SECONDARY, raddr, flags, + rw); + *flags &= ~(PAGE_EXEC); + } + break; + case PSW_ASC_ACCREG: + default: + hw_error("guest switched to unknown asc mode\n"); + break; + } + +out: + /* Convert real address -> absolute address */ + if (*raddr < 0x2000) { + *raddr = *raddr + env->psa; + } + + return r; +} + +int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong _vaddr, int rw, int mmu_idx, int is_softmmu) { - target_ulong phys; + uint64_t asc = env->psw.mask & PSW_MASK_ASC; + target_ulong vaddr, raddr; int prot; - /* XXX: implement mmu */ + DPRINTF("%s: address 0x%" PRIx64 " rw %d mmu_idx %d is_softmmu %d\n", + __FUNCTION__, _vaddr, rw, mmu_idx, is_softmmu); - phys = address; - prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + _vaddr &= TARGET_PAGE_MASK; + vaddr = _vaddr; - tlb_set_page(env, address & TARGET_PAGE_MASK, - phys & TARGET_PAGE_MASK, prot, + /* 31-Bit mode */ + if (!(env->psw.mask & PSW_MASK_64)) { + vaddr &= 0x7fffffff; + } + + if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) { + /* Translation ended in exception */ + return 1; + } + + /* check out of RAM access */ + if (raddr > (ram_size + virtio_size)) { + DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __FUNCTION__, + (uint64_t)aaddr, (uint64_t)ram_size); + trigger_pgm_exception(env, PGM_ADDRESSING, ILC_LATER); + return 1; + } + + DPRINTF("%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", __FUNCTION__, + (uint64_t)vaddr, (uint64_t)raddr, prot); + + tlb_set_page(env, _vaddr, raddr, prot, mmu_idx, TARGET_PAGE_SIZE); + return 0; } + +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong vaddr) +{ + target_ulong raddr; + int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + int old_exc = env->exception_index; + uint64_t asc = env->psw.mask & PSW_MASK_ASC; + + /* 31-Bit mode */ + if (!(env->psw.mask & PSW_MASK_64)) { + vaddr &= 0x7fffffff; + } + + mmu_translate(env, vaddr, 2, asc, &raddr, &prot); + env->exception_index = old_exc; + + return raddr; +} + +void load_psw(CPUState *env, uint64_t mask, uint64_t addr) +{ + if (mask & PSW_MASK_WAIT) { + env->halted = 1; + env->exception_index = EXCP_HLT; + if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) { + /* XXX disabled wait state - CPU is dead */ + } + } + + env->psw.addr = addr; + env->psw.mask = mask; + env->cc_op = (mask >> 13) & 3; +} + +static uint64_t get_psw_mask(CPUState *env) +{ + uint64_t r = env->psw.mask; + + env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr); + + r &= ~(3ULL << 13); + assert(!(env->cc_op & ~3)); + r |= env->cc_op << 13; + + return r; +} + +static void do_svc_interrupt(CPUState *env) +{ + uint64_t mask, addr; + LowCore *lowcore; + target_phys_addr_t len = TARGET_PAGE_SIZE; + + lowcore = cpu_physical_memory_map(env->psa, &len, 1); + + lowcore->svc_code = cpu_to_be16(env->int_svc_code); + lowcore->svc_ilc = cpu_to_be16(env->int_svc_ilc); + lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + (env->int_svc_ilc)); + mask = be64_to_cpu(lowcore->svc_new_psw.mask); + addr = be64_to_cpu(lowcore->svc_new_psw.addr); + + cpu_physical_memory_unmap(lowcore, len, 1, len); + + load_psw(env, mask, addr); +} + +static void do_program_interrupt(CPUState *env) +{ + uint64_t mask, addr; + LowCore *lowcore; + target_phys_addr_t len = TARGET_PAGE_SIZE; + int ilc = env->int_pgm_ilc; + + switch (ilc) { + case ILC_LATER: + ilc = get_ilc(ldub_code(env->psw.addr)); + break; + case ILC_LATER_INC: + ilc = get_ilc(ldub_code(env->psw.addr)); + env->psw.addr += ilc * 2; + break; + case ILC_LATER_INC_2: + ilc = get_ilc(ldub_code(env->psw.addr)) * 2; + env->psw.addr += ilc; + break; + } + + qemu_log("%s: code=0x%x ilc=%d\n", __FUNCTION__, env->int_pgm_code, ilc); + + lowcore = cpu_physical_memory_map(env->psa, &len, 1); + + lowcore->pgm_ilc = cpu_to_be16(ilc); + lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); + lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->program_new_psw.mask); + addr = be64_to_cpu(lowcore->program_new_psw.addr); + + cpu_physical_memory_unmap(lowcore, len, 1, len); + + DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __FUNCTION__, + env->int_pgm_code, ilc, env->psw.mask, + env->psw.addr); + + load_psw(env, mask, addr); +} + +#define VIRTIO_SUBCODE_64 0x0D00 + +static void do_ext_interrupt(CPUState *env) +{ + uint64_t mask, addr; + LowCore *lowcore; + target_phys_addr_t len = TARGET_PAGE_SIZE; + ExtQueue *q; + + if (!(env->psw.mask & PSW_MASK_EXT)) { + cpu_abort(env, "Ext int w/o ext mask\n"); + } + + if (env->ext_index < 0 || env->ext_index > MAX_EXT_QUEUE) { + cpu_abort(env, "Ext queue overrun: %d\n", env->ext_index); + } + + q = &env->ext_queue[env->ext_index]; + lowcore = cpu_physical_memory_map(env->psa, &len, 1); + + lowcore->ext_int_code = cpu_to_be16(q->code); + lowcore->ext_params = cpu_to_be32(q->param); + lowcore->ext_params2 = cpu_to_be64(q->param64); + lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr); + lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64); + mask = be64_to_cpu(lowcore->external_new_psw.mask); + addr = be64_to_cpu(lowcore->external_new_psw.addr); + + cpu_physical_memory_unmap(lowcore, len, 1, len); + + env->ext_index--; + if (env->ext_index == -1) { + env->pending_int &= ~INTERRUPT_EXT; + } + + DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __FUNCTION__, + env->psw.mask, env->psw.addr); + + load_psw(env, mask, addr); +} + +void do_interrupt (CPUState *env) +{ + qemu_log("%s: %d at pc=%" PRIx64 "\n", __FUNCTION__, env->exception_index, + env->psw.addr); + + /* handle external interrupts */ + if ((env->psw.mask & PSW_MASK_EXT) && + env->exception_index == -1) { + if (env->pending_int & INTERRUPT_EXT) { + /* code is already in env */ + env->exception_index = EXCP_EXT; + } else if (env->pending_int & INTERRUPT_TOD) { + cpu_inject_ext(env, 0x1004, 0, 0); + env->exception_index = EXCP_EXT; + env->pending_int &= ~INTERRUPT_EXT; + env->pending_int &= ~INTERRUPT_TOD; + } else if (env->pending_int & INTERRUPT_CPUTIMER) { + cpu_inject_ext(env, 0x1005, 0, 0); + env->exception_index = EXCP_EXT; + env->pending_int &= ~INTERRUPT_EXT; + env->pending_int &= ~INTERRUPT_TOD; + } + } + + switch (env->exception_index) { + case EXCP_PGM: + do_program_interrupt(env); + break; + case EXCP_SVC: + do_svc_interrupt(env); + break; + case EXCP_EXT: + do_ext_interrupt(env); + break; + } + env->exception_index = -1; + + if (!env->pending_int) { + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + } +} + #endif /* CONFIG_USER_ONLY */ diff -Nru qemu-kvm-0.14.1+noroms/target-s390x/helpers.h qemu-kvm-0.15.0+noroms/target-s390x/helpers.h --- qemu-kvm-0.14.1+noroms/target-s390x/helpers.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-s390x/helpers.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,151 @@ +#include "def-helper.h" + +DEF_HELPER_1(exception, void, i32) +DEF_HELPER_3(nc, i32, i32, i64, i64) +DEF_HELPER_3(oc, i32, i32, i64, i64) +DEF_HELPER_3(xc, i32, i32, i64, i64) +DEF_HELPER_3(mvc, void, i32, i64, i64) +DEF_HELPER_3(clc, i32, i32, i64, i64) +DEF_HELPER_2(mvcl, i32, i32, i32) +DEF_HELPER_FLAGS_1(set_cc_comp_s32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32) +DEF_HELPER_FLAGS_1(set_cc_comp_s64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64) +DEF_HELPER_FLAGS_2(set_cc_icm, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32) +DEF_HELPER_3(clm, i32, i32, i32, i64) +DEF_HELPER_3(stcm, void, i32, i32, i64) +DEF_HELPER_2(mlg, void, i32, i64) +DEF_HELPER_2(dlg, void, i32, i64) +DEF_HELPER_FLAGS_3(set_cc_add64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64, s64, s64) +DEF_HELPER_FLAGS_3(set_cc_addu64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64) +DEF_HELPER_FLAGS_3(set_cc_add32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32, s32, s32) +DEF_HELPER_FLAGS_3(set_cc_addu32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32) +DEF_HELPER_FLAGS_3(set_cc_sub64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64, s64, s64) +DEF_HELPER_FLAGS_3(set_cc_subu64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64) +DEF_HELPER_FLAGS_3(set_cc_sub32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32, s32, s32) +DEF_HELPER_FLAGS_3(set_cc_subu32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32) +DEF_HELPER_3(srst, i32, i32, i32, i32) +DEF_HELPER_3(clst, i32, i32, i32, i32) +DEF_HELPER_3(mvpg, void, i64, i64, i64) +DEF_HELPER_3(mvst, void, i32, i32, i32) +DEF_HELPER_3(csg, i32, i32, i64, i32) +DEF_HELPER_3(cdsg, i32, i32, i64, i32) +DEF_HELPER_3(cs, i32, i32, i64, i32) +DEF_HELPER_4(ex, i32, i32, i64, i64, i64) +DEF_HELPER_FLAGS_1(abs_i32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32) +DEF_HELPER_FLAGS_1(nabs_i32, TCG_CALL_PURE|TCG_CALL_CONST, s32, s32) +DEF_HELPER_FLAGS_1(abs_i64, TCG_CALL_PURE|TCG_CALL_CONST, i64, s64) +DEF_HELPER_FLAGS_1(nabs_i64, TCG_CALL_PURE|TCG_CALL_CONST, s64, s64) +DEF_HELPER_3(stcmh, void, i32, i64, i32) +DEF_HELPER_3(icmh, i32, i32, i64, i32) +DEF_HELPER_2(ipm, void, i32, i32) +DEF_HELPER_FLAGS_3(addc_u32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32) +DEF_HELPER_FLAGS_3(set_cc_addc_u64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64) +DEF_HELPER_3(stam, void, i32, i64, i32) +DEF_HELPER_3(lam, void, i32, i64, i32) +DEF_HELPER_3(mvcle, i32, i32, i64, i32) +DEF_HELPER_3(clcle, i32, i32, i64, i32) +DEF_HELPER_3(slb, i32, i32, i32, i32) +DEF_HELPER_4(slbg, i32, i32, i32, i64, i64) +DEF_HELPER_2(cefbr, void, i32, s32) +DEF_HELPER_2(cdfbr, void, i32, s32) +DEF_HELPER_2(cxfbr, void, i32, s32) +DEF_HELPER_2(cegbr, void, i32, s64) +DEF_HELPER_2(cdgbr, void, i32, s64) +DEF_HELPER_2(cxgbr, void, i32, s64) +DEF_HELPER_2(adbr, i32, i32, i32) +DEF_HELPER_2(aebr, i32, i32, i32) +DEF_HELPER_2(sebr, i32, i32, i32) +DEF_HELPER_2(sdbr, i32, i32, i32) +DEF_HELPER_2(debr, void, i32, i32) +DEF_HELPER_2(dxbr, void, i32, i32) +DEF_HELPER_2(mdbr, void, i32, i32) +DEF_HELPER_2(mxbr, void, i32, i32) +DEF_HELPER_2(ldebr, void, i32, i32) +DEF_HELPER_2(ldxbr, void, i32, i32) +DEF_HELPER_2(lxdbr, void, i32, i32) +DEF_HELPER_2(ledbr, void, i32, i32) +DEF_HELPER_2(lexbr, void, i32, i32) +DEF_HELPER_2(lpebr, i32, i32, i32) +DEF_HELPER_2(lpdbr, i32, i32, i32) +DEF_HELPER_2(lpxbr, i32, i32, i32) +DEF_HELPER_2(ltebr, i32, i32, i32) +DEF_HELPER_2(ltdbr, i32, i32, i32) +DEF_HELPER_2(ltxbr, i32, i32, i32) +DEF_HELPER_2(lcebr, i32, i32, i32) +DEF_HELPER_2(lcdbr, i32, i32, i32) +DEF_HELPER_2(lcxbr, i32, i32, i32) +DEF_HELPER_2(aeb, void, i32, i32) +DEF_HELPER_2(deb, void, i32, i32) +DEF_HELPER_2(meeb, void, i32, i32) +DEF_HELPER_2(cdb, i32, i32, i64) +DEF_HELPER_2(adb, i32, i32, i64) +DEF_HELPER_2(seb, void, i32, i32) +DEF_HELPER_2(sdb, i32, i32, i64) +DEF_HELPER_2(mdb, void, i32, i64) +DEF_HELPER_2(ddb, void, i32, i64) +DEF_HELPER_FLAGS_2(cebr, TCG_CALL_PURE, i32, i32, i32) +DEF_HELPER_FLAGS_2(cdbr, TCG_CALL_PURE, i32, i32, i32) +DEF_HELPER_FLAGS_2(cxbr, TCG_CALL_PURE, i32, i32, i32) +DEF_HELPER_3(cgebr, i32, i32, i32, i32) +DEF_HELPER_3(cgdbr, i32, i32, i32, i32) +DEF_HELPER_3(cgxbr, i32, i32, i32, i32) +DEF_HELPER_1(lzer, void, i32) +DEF_HELPER_1(lzdr, void, i32) +DEF_HELPER_1(lzxr, void, i32) +DEF_HELPER_3(cfebr, i32, i32, i32, i32) +DEF_HELPER_3(cfdbr, i32, i32, i32, i32) +DEF_HELPER_3(cfxbr, i32, i32, i32, i32) +DEF_HELPER_2(axbr, i32, i32, i32) +DEF_HELPER_2(sxbr, i32, i32, i32) +DEF_HELPER_2(meebr, void, i32, i32) +DEF_HELPER_2(ddbr, void, i32, i32) +DEF_HELPER_3(madb, void, i32, i64, i32) +DEF_HELPER_3(maebr, void, i32, i32, i32) +DEF_HELPER_3(madbr, void, i32, i32, i32) +DEF_HELPER_3(msdbr, void, i32, i32, i32) +DEF_HELPER_2(lxdb, void, i32, i64) +DEF_HELPER_FLAGS_2(tceb, TCG_CALL_PURE, i32, i32, i64) +DEF_HELPER_FLAGS_2(tcdb, TCG_CALL_PURE, i32, i32, i64) +DEF_HELPER_FLAGS_2(tcxb, TCG_CALL_PURE, i32, i32, i64) +DEF_HELPER_2(flogr, i32, i32, i64) +DEF_HELPER_2(sqdbr, void, i32, i32) +DEF_HELPER_FLAGS_1(cvd, TCG_CALL_PURE|TCG_CALL_CONST, i64, s32) +DEF_HELPER_3(unpk, void, i32, i64, i64) +DEF_HELPER_3(tr, void, i32, i64, i64) + +DEF_HELPER_2(servc, i32, i32, i64) +DEF_HELPER_3(diag, i64, i32, i64, i64) +DEF_HELPER_2(load_psw, void, i64, i64) +DEF_HELPER_1(program_interrupt, void, i32) +DEF_HELPER_FLAGS_1(stidp, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_1(spx, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_1(sck, TCG_CALL_CONST, i32, i64) +DEF_HELPER_1(stck, i32, i64) +DEF_HELPER_1(stcke, i32, i64) +DEF_HELPER_FLAGS_1(sckc, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_1(stckc, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_1(spt, TCG_CALL_CONST, void, i64) +DEF_HELPER_FLAGS_1(stpt, TCG_CALL_CONST, void, i64) +DEF_HELPER_3(stsi, i32, i64, i32, i32) +DEF_HELPER_3(lctl, void, i32, i64, i32) +DEF_HELPER_3(lctlg, void, i32, i64, i32) +DEF_HELPER_3(stctl, void, i32, i64, i32) +DEF_HELPER_3(stctg, void, i32, i64, i32) +DEF_HELPER_FLAGS_2(tprot, TCG_CALL_CONST, i32, i64, i64) +DEF_HELPER_FLAGS_1(iske, TCG_CALL_PURE|TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_2(sske, TCG_CALL_CONST, void, i32, i64) +DEF_HELPER_FLAGS_2(rrbe, TCG_CALL_CONST, i32, i32, i64) +DEF_HELPER_2(csp, i32, i32, i32) +DEF_HELPER_3(mvcs, i32, i64, i64, i64) +DEF_HELPER_3(mvcp, i32, i64, i64, i64) +DEF_HELPER_3(sigp, i32, i64, i32, i64) +DEF_HELPER_1(sacf, void, i64) +DEF_HELPER_FLAGS_2(ipte, TCG_CALL_CONST, void, i64, i64) +DEF_HELPER_FLAGS_0(ptlb, TCG_CALL_CONST, void) +DEF_HELPER_2(lra, i32, i64, i32) +DEF_HELPER_2(stura, void, i64, i32) +DEF_HELPER_2(cksm, void, i32, i32) + +DEF_HELPER_FLAGS_4(calc_cc, TCG_CALL_PURE|TCG_CALL_CONST, + i32, i32, i64, i64, i64) + +#include "def-helper.h" diff -Nru qemu-kvm-0.14.1+noroms/target-s390x/kvm.c qemu-kvm-0.15.0+noroms/target-s390x/kvm.c --- qemu-kvm-0.14.1+noroms/target-s390x/kvm.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-s390x/kvm.c 2011-08-09 07:40:29.000000000 -0500 @@ -49,13 +49,6 @@ #define DIAG_KVM_HYPERCALL 0x500 #define DIAG_KVM_BREAKPOINT 0x501 -#define SCP_LENGTH 0x00 -#define SCP_FUNCTION_CODE 0x02 -#define SCP_CONTROL_MASK 0x03 -#define SCP_RESPONSE_CODE 0x06 -#define SCP_MEM_CODE 0x08 -#define SCP_INCREMENT 0x0a - #define ICPT_INSTRUCTION 0x04 #define ICPT_WAITPSW 0x1c #define ICPT_SOFT_INTERCEPT 0x24 @@ -169,23 +162,21 @@ return 0; } -int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) +void kvm_arch_pre_run(CPUState *env, struct kvm_run *run) { - return 0; } -int kvm_arch_post_run(CPUState *env, struct kvm_run *run) +void kvm_arch_post_run(CPUState *env, struct kvm_run *run) { - return 0; } -int kvm_arch_process_irqchip_events(CPUState *env) +int kvm_arch_process_async_events(CPUState *env) { - return 0; + return env->halted; } -static void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm, - uint64_t parm64, int vm) +void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm, + uint64_t parm64, int vm) { struct kvm_s390_interrupt kvmint; int r; @@ -196,6 +187,7 @@ env->halted = 0; env->exception_index = -1; + qemu_cpu_kick(env); kvmint.type = type; kvmint.parm = parm; @@ -219,7 +211,7 @@ token, 1); } -static void kvm_s390_interrupt(CPUState *env, int type, uint32_t code) +void kvm_s390_interrupt(CPUState *env, int type, uint32_t code) { kvm_s390_interrupt_internal(env, type, code, 0, 0); } @@ -229,16 +221,17 @@ kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code); } -static void setcc(CPUState *env, uint64_t cc) +static inline void setcc(CPUState *env, uint64_t cc) { - env->kvm_run->psw_mask &= ~(3ul << 44); + env->kvm_run->psw_mask &= ~(3ull << 44); env->kvm_run->psw_mask |= (cc & 3) << 44; env->psw.mask &= ~(3ul << 44); env->psw.mask |= (cc & 3) << 44; } -static int sclp_service_call(CPUState *env, struct kvm_run *run, uint16_t ipbh0) +static int kvm_sclp_service_call(CPUState *env, struct kvm_run *run, + uint16_t ipbh0) { uint32_t sccb; uint64_t code; @@ -248,35 +241,11 @@ sccb = env->regs[ipbh0 & 0xf]; code = env->regs[(ipbh0 & 0xf0) >> 4]; - dprintf("sclp(0x%x, 0x%lx)\n", sccb, code); - - if (sccb & ~0x7ffffff8ul) { - fprintf(stderr, "KVM: invalid sccb address 0x%x\n", sccb); - r = -1; - goto out; - } - - switch(code) { - case SCLP_CMDW_READ_SCP_INFO: - case SCLP_CMDW_READ_SCP_INFO_FORCED: - stw_phys(sccb + SCP_MEM_CODE, ram_size >> 20); - stb_phys(sccb + SCP_INCREMENT, 1); - stw_phys(sccb + SCP_RESPONSE_CODE, 0x10); - setcc(env, 0); - - kvm_s390_interrupt_internal(env, KVM_S390_INT_SERVICE, - sccb & ~3, 0, 1); - break; - default: - dprintf("KVM: invalid sclp call 0x%x / 0x%lx\n", sccb, code); - r = -1; - break; - } - -out: - if (r < 0) { + r = sclp_service_call(env, sccb, code); + if (r) { setcc(env, 3); } + return 0; } @@ -288,7 +257,7 @@ dprintf("KVM: PRIV: %d\n", ipa1); switch (ipa1) { case PRIV_SCLP_CALL: - r = sclp_service_call(env, run, ipbh0); + r = kvm_sclp_service_call(env, run, ipbh0); break; default: dprintf("KVM: unknown PRIV: 0x%x\n", ipa1); @@ -301,12 +270,10 @@ static int handle_hypercall(CPUState *env, struct kvm_run *run) { - int r; - cpu_synchronize_state(env); - r = s390_virtio_hypercall(env); + env->regs[2] = s390_virtio_hypercall(env, env->regs[2], env->regs[1]); - return r; + return 0; } static int handle_diag(CPUState *env, struct kvm_run *run, int ipb_code) @@ -410,7 +377,7 @@ r = s390_cpu_initial_reset(target_env); break; default: - fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", ipa1); + fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", order_code); break; } @@ -442,7 +409,7 @@ if (r < 0) { enter_pgmcheck(env, 0x0001); } - return r; + return 0; } static int handle_intercept(CPUState *env) @@ -451,7 +418,8 @@ int icpt_code = run->s390_sieic.icptcode; int r = 0; - dprintf("intercept: 0x%x (at 0x%lx)\n", icpt_code, env->kvm_run->psw_addr); + dprintf("intercept: 0x%x (at 0x%lx)\n", icpt_code, + (long)env->kvm_run->psw_addr); switch (icpt_code) { case ICPT_INSTRUCTION: r = handle_instruction(env, run); @@ -498,6 +466,11 @@ break; } + if (ret == 0) { + ret = EXCP_INTERRUPT; + } else if (ret > 0) { + ret = 0; + } return ret; } @@ -505,3 +478,13 @@ { return true; } + +int kvm_arch_on_sigbus_vcpu(CPUState *env, int code, void *addr) +{ + return 1; +} + +int kvm_arch_on_sigbus(int code, void *addr) +{ + return 1; +} diff -Nru qemu-kvm-0.14.1+noroms/target-s390x/op_helper.c qemu-kvm-0.15.0+noroms/target-s390x/op_helper.c --- qemu-kvm-0.14.1+noroms/target-s390x/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-s390x/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -1,6 +1,7 @@ /* * S/390 helper routines * + * Copyright (c) 2009 Ulrich Hecht * Copyright (c) 2009 Alexander Graf * * This library is free software; you can redistribute it and/or @@ -18,6 +19,14 @@ */ #include "exec.h" +#include "host-utils.h" +#include "helpers.h" +#include +#include "kvm.h" +#include "qemu-timer.h" +#ifdef CONFIG_KVM +#include +#endif /*****************************************************************************/ /* Softmmu support */ @@ -61,13 +70,2922 @@ if (likely(tb)) { /* the PC is inside the translated code. It means that we have a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); + cpu_restore_state(tb, env, pc); } } - /* XXX */ - /* helper_raise_exception_err(env->exception_index, env->error_code); */ + cpu_loop_exit(env); } env = saved_env; } #endif + +/* #define DEBUG_HELPER */ +#ifdef DEBUG_HELPER +#define HELPER_LOG(x...) qemu_log(x) +#else +#define HELPER_LOG(x...) +#endif + +/* raise an exception */ +void HELPER(exception)(uint32_t excp) +{ + HELPER_LOG("%s: exception %d\n", __FUNCTION__, excp); + env->exception_index = excp; + cpu_loop_exit(env); +} + +#ifndef CONFIG_USER_ONLY +static void mvc_fast_memset(CPUState *env, uint32_t l, uint64_t dest, + uint8_t byte) +{ + target_phys_addr_t dest_phys; + target_phys_addr_t len = l; + void *dest_p; + uint64_t asc = env->psw.mask & PSW_MASK_ASC; + int flags; + + if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { + stb(dest, byte); + cpu_abort(env, "should never reach here"); + } + dest_phys |= dest & ~TARGET_PAGE_MASK; + + dest_p = cpu_physical_memory_map(dest_phys, &len, 1); + + memset(dest_p, byte, len); + + cpu_physical_memory_unmap(dest_p, 1, len, len); +} + +static void mvc_fast_memmove(CPUState *env, uint32_t l, uint64_t dest, + uint64_t src) +{ + target_phys_addr_t dest_phys; + target_phys_addr_t src_phys; + target_phys_addr_t len = l; + void *dest_p; + void *src_p; + uint64_t asc = env->psw.mask & PSW_MASK_ASC; + int flags; + + if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) { + stb(dest, 0); + cpu_abort(env, "should never reach here"); + } + dest_phys |= dest & ~TARGET_PAGE_MASK; + + if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) { + ldub(src); + cpu_abort(env, "should never reach here"); + } + src_phys |= src & ~TARGET_PAGE_MASK; + + dest_p = cpu_physical_memory_map(dest_phys, &len, 1); + src_p = cpu_physical_memory_map(src_phys, &len, 0); + + memmove(dest_p, src_p, len); + + cpu_physical_memory_unmap(dest_p, 1, len, len); + cpu_physical_memory_unmap(src_p, 0, len, len); +} +#endif + +/* and on array */ +uint32_t HELPER(nc)(uint32_t l, uint64_t dest, uint64_t src) +{ + int i; + unsigned char x; + uint32_t cc = 0; + + HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", + __FUNCTION__, l, dest, src); + for (i = 0; i <= l; i++) { + x = ldub(dest + i) & ldub(src + i); + if (x) { + cc = 1; + } + stb(dest + i, x); + } + return cc; +} + +/* xor on array */ +uint32_t HELPER(xc)(uint32_t l, uint64_t dest, uint64_t src) +{ + int i; + unsigned char x; + uint32_t cc = 0; + + HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", + __FUNCTION__, l, dest, src); + +#ifndef CONFIG_USER_ONLY + /* xor with itself is the same as memset(0) */ + if ((l > 32) && (src == dest) && + (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) { + mvc_fast_memset(env, l + 1, dest, 0); + return 0; + } +#else + if (src == dest) { + memset(g2h(dest), 0, l + 1); + return 0; + } +#endif + + for (i = 0; i <= l; i++) { + x = ldub(dest + i) ^ ldub(src + i); + if (x) { + cc = 1; + } + stb(dest + i, x); + } + return cc; +} + +/* or on array */ +uint32_t HELPER(oc)(uint32_t l, uint64_t dest, uint64_t src) +{ + int i; + unsigned char x; + uint32_t cc = 0; + + HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", + __FUNCTION__, l, dest, src); + for (i = 0; i <= l; i++) { + x = ldub(dest + i) | ldub(src + i); + if (x) { + cc = 1; + } + stb(dest + i, x); + } + return cc; +} + +/* memmove */ +void HELPER(mvc)(uint32_t l, uint64_t dest, uint64_t src) +{ + int i = 0; + int x = 0; + uint32_t l_64 = (l + 1) / 8; + + HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", + __FUNCTION__, l, dest, src); + +#ifndef CONFIG_USER_ONLY + if ((l > 32) && + (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) && + (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) { + if (dest == (src + 1)) { + mvc_fast_memset(env, l + 1, dest, ldub(src)); + return; + } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) { + mvc_fast_memmove(env, l + 1, dest, src); + return; + } + } +#else + if (dest == (src + 1)) { + memset(g2h(dest), ldub(src), l + 1); + return; + } else { + memmove(g2h(dest), g2h(src), l + 1); + return; + } +#endif + + /* handle the parts that fit into 8-byte loads/stores */ + if (dest != (src + 1)) { + for (i = 0; i < l_64; i++) { + stq(dest + x, ldq(src + x)); + x += 8; + } + } + + /* slow version crossing pages with byte accesses */ + for (i = x; i <= l; i++) { + stb(dest + i, ldub(src + i)); + } +} + +/* compare unsigned byte arrays */ +uint32_t HELPER(clc)(uint32_t l, uint64_t s1, uint64_t s2) +{ + int i; + unsigned char x,y; + uint32_t cc; + HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n", + __FUNCTION__, l, s1, s2); + for (i = 0; i <= l; i++) { + x = ldub(s1 + i); + y = ldub(s2 + i); + HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y); + if (x < y) { + cc = 1; + goto done; + } else if (x > y) { + cc = 2; + goto done; + } + } + cc = 0; +done: + HELPER_LOG("\n"); + return cc; +} + +/* compare logical under mask */ +uint32_t HELPER(clm)(uint32_t r1, uint32_t mask, uint64_t addr) +{ + uint8_t r,d; + uint32_t cc; + HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __FUNCTION__, r1, + mask, addr); + cc = 0; + while (mask) { + if (mask & 8) { + d = ldub(addr); + r = (r1 & 0xff000000UL) >> 24; + HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d, + addr); + if (r < d) { + cc = 1; + break; + } else if (r > d) { + cc = 2; + break; + } + addr++; + } + mask = (mask << 1) & 0xf; + r1 <<= 8; + } + HELPER_LOG("\n"); + return cc; +} + +/* store character under mask */ +void HELPER(stcm)(uint32_t r1, uint32_t mask, uint64_t addr) +{ + uint8_t r; + HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __FUNCTION__, r1, mask, + addr); + while (mask) { + if (mask & 8) { + r = (r1 & 0xff000000UL) >> 24; + stb(addr, r); + HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr); + addr++; + } + mask = (mask << 1) & 0xf; + r1 <<= 8; + } + HELPER_LOG("\n"); +} + +/* 64/64 -> 128 unsigned multiplication */ +void HELPER(mlg)(uint32_t r1, uint64_t v2) +{ +#if HOST_LONG_BITS == 64 && defined(__GNUC__) + /* assuming 64-bit hosts have __uint128_t */ + __uint128_t res = (__uint128_t)env->regs[r1 + 1]; + res *= (__uint128_t)v2; + env->regs[r1] = (uint64_t)(res >> 64); + env->regs[r1 + 1] = (uint64_t)res; +#else + mulu64(&env->regs[r1 + 1], &env->regs[r1], env->regs[r1 + 1], v2); +#endif +} + +/* 128 -> 64/64 unsigned division */ +void HELPER(dlg)(uint32_t r1, uint64_t v2) +{ + uint64_t divisor = v2; + + if (!env->regs[r1]) { + /* 64 -> 64/64 case */ + env->regs[r1] = env->regs[r1+1] % divisor; + env->regs[r1+1] = env->regs[r1+1] / divisor; + return; + } else { + +#if HOST_LONG_BITS == 64 && defined(__GNUC__) + /* assuming 64-bit hosts have __uint128_t */ + __uint128_t dividend = (((__uint128_t)env->regs[r1]) << 64) | + (env->regs[r1+1]); + __uint128_t quotient = dividend / divisor; + env->regs[r1+1] = quotient; + __uint128_t remainder = dividend % divisor; + env->regs[r1] = remainder; +#else + /* 32-bit hosts would need special wrapper functionality - just abort if + we encounter such a case; it's very unlikely anyways. */ + cpu_abort(env, "128 -> 64/64 division not implemented\n"); +#endif + } +} + +static inline uint64_t get_address(int x2, int b2, int d2) +{ + uint64_t r = d2; + + if (x2) { + r += env->regs[x2]; + } + + if (b2) { + r += env->regs[b2]; + } + + /* 31-Bit mode */ + if (!(env->psw.mask & PSW_MASK_64)) { + r &= 0x7fffffff; + } + + return r; +} + +static inline uint64_t get_address_31fix(int reg) +{ + uint64_t r = env->regs[reg]; + + /* 31-Bit mode */ + if (!(env->psw.mask & PSW_MASK_64)) { + r &= 0x7fffffff; + } + + return r; +} + +/* search string (c is byte to search, r2 is string, r1 end of string) */ +uint32_t HELPER(srst)(uint32_t c, uint32_t r1, uint32_t r2) +{ + uint64_t i; + uint32_t cc = 2; + uint64_t str = get_address_31fix(r2); + uint64_t end = get_address_31fix(r1); + + HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __FUNCTION__, + c, env->regs[r1], env->regs[r2]); + + for (i = str; i != end; i++) { + if (ldub(i) == c) { + env->regs[r1] = i; + cc = 1; + break; + } + } + + return cc; +} + +/* unsigned string compare (c is string terminator) */ +uint32_t HELPER(clst)(uint32_t c, uint32_t r1, uint32_t r2) +{ + uint64_t s1 = get_address_31fix(r1); + uint64_t s2 = get_address_31fix(r2); + uint8_t v1, v2; + uint32_t cc; + c = c & 0xff; +#ifdef CONFIG_USER_ONLY + if (!c) { + HELPER_LOG("%s: comparing '%s' and '%s'\n", + __FUNCTION__, (char*)g2h(s1), (char*)g2h(s2)); + } +#endif + for (;;) { + v1 = ldub(s1); + v2 = ldub(s2); + if ((v1 == c || v2 == c) || (v1 != v2)) { + break; + } + s1++; + s2++; + } + + if (v1 == v2) { + cc = 0; + } else { + cc = (v1 < v2) ? 1 : 2; + /* FIXME: 31-bit mode! */ + env->regs[r1] = s1; + env->regs[r2] = s2; + } + return cc; +} + +/* move page */ +void HELPER(mvpg)(uint64_t r0, uint64_t r1, uint64_t r2) +{ + /* XXX missing r0 handling */ +#ifdef CONFIG_USER_ONLY + int i; + + for (i = 0; i < TARGET_PAGE_SIZE; i++) { + stb(r1 + i, ldub(r2 + i)); + } +#else + mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2); +#endif +} + +/* string copy (c is string terminator) */ +void HELPER(mvst)(uint32_t c, uint32_t r1, uint32_t r2) +{ + uint64_t dest = get_address_31fix(r1); + uint64_t src = get_address_31fix(r2); + uint8_t v; + c = c & 0xff; +#ifdef CONFIG_USER_ONLY + if (!c) { + HELPER_LOG("%s: copy '%s' to 0x%lx\n", __FUNCTION__, (char*)g2h(src), + dest); + } +#endif + for (;;) { + v = ldub(src); + stb(dest, v); + if (v == c) { + break; + } + src++; + dest++; + } + env->regs[r1] = dest; /* FIXME: 31-bit mode! */ +} + +/* compare and swap 64-bit */ +uint32_t HELPER(csg)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + /* FIXME: locking? */ + uint32_t cc; + uint64_t v2 = ldq(a2); + if (env->regs[r1] == v2) { + cc = 0; + stq(a2, env->regs[r3]); + } else { + cc = 1; + env->regs[r1] = v2; + } + return cc; +} + +/* compare double and swap 64-bit */ +uint32_t HELPER(cdsg)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + /* FIXME: locking? */ + uint32_t cc; + uint64_t v2_hi = ldq(a2); + uint64_t v2_lo = ldq(a2 + 8); + uint64_t v1_hi = env->regs[r1]; + uint64_t v1_lo = env->regs[r1 + 1]; + + if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) { + cc = 0; + stq(a2, env->regs[r3]); + stq(a2 + 8, env->regs[r3 + 1]); + } else { + cc = 1; + env->regs[r1] = v2_hi; + env->regs[r1 + 1] = v2_lo; + } + + return cc; +} + +/* compare and swap 32-bit */ +uint32_t HELPER(cs)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + /* FIXME: locking? */ + uint32_t cc; + HELPER_LOG("%s: r1 %d a2 0x%lx r3 %d\n", __FUNCTION__, r1, a2, r3); + uint32_t v2 = ldl(a2); + if (((uint32_t)env->regs[r1]) == v2) { + cc = 0; + stl(a2, (uint32_t)env->regs[r3]); + } else { + cc = 1; + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | v2; + } + return cc; +} + +static uint32_t helper_icm(uint32_t r1, uint64_t address, uint32_t mask) +{ + int pos = 24; /* top of the lower half of r1 */ + uint64_t rmask = 0xff000000ULL; + uint8_t val = 0; + int ccd = 0; + uint32_t cc = 0; + + while (mask) { + if (mask & 8) { + env->regs[r1] &= ~rmask; + val = ldub(address); + if ((val & 0x80) && !ccd) { + cc = 1; + } + ccd = 1; + if (val && cc == 0) { + cc = 2; + } + env->regs[r1] |= (uint64_t)val << pos; + address++; + } + mask = (mask << 1) & 0xf; + pos -= 8; + rmask >>= 8; + } + + return cc; +} + +/* execute instruction + this instruction executes an insn modified with the contents of r1 + it does not change the executed instruction in memory + it does not change the program counter + in other words: tricky... + currently implemented by interpreting the cases it is most commonly used in + */ +uint32_t HELPER(ex)(uint32_t cc, uint64_t v1, uint64_t addr, uint64_t ret) +{ + uint16_t insn = lduw_code(addr); + HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __FUNCTION__, v1, addr, + insn); + if ((insn & 0xf0ff) == 0xd000) { + uint32_t l, insn2, b1, b2, d1, d2; + l = v1 & 0xff; + insn2 = ldl_code(addr + 2); + b1 = (insn2 >> 28) & 0xf; + b2 = (insn2 >> 12) & 0xf; + d1 = (insn2 >> 16) & 0xfff; + d2 = insn2 & 0xfff; + switch (insn & 0xf00) { + case 0x200: + helper_mvc(l, get_address(0, b1, d1), get_address(0, b2, d2)); + break; + case 0x500: + cc = helper_clc(l, get_address(0, b1, d1), get_address(0, b2, d2)); + break; + case 0x700: + cc = helper_xc(l, get_address(0, b1, d1), get_address(0, b2, d2)); + break; + default: + goto abort; + break; + } + } else if ((insn & 0xff00) == 0x0a00) { + /* supervisor call */ + HELPER_LOG("%s: svc %ld via execute\n", __FUNCTION__, (insn|v1) & 0xff); + env->psw.addr = ret - 4; + env->int_svc_code = (insn|v1) & 0xff; + env->int_svc_ilc = 4; + helper_exception(EXCP_SVC); + } else if ((insn & 0xff00) == 0xbf00) { + uint32_t insn2, r1, r3, b2, d2; + insn2 = ldl_code(addr + 2); + r1 = (insn2 >> 20) & 0xf; + r3 = (insn2 >> 16) & 0xf; + b2 = (insn2 >> 12) & 0xf; + d2 = insn2 & 0xfff; + cc = helper_icm(r1, get_address(0, b2, d2), r3); + } else { +abort: + cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n", + insn); + } + return cc; +} + +/* absolute value 32-bit */ +uint32_t HELPER(abs_i32)(int32_t val) +{ + if (val < 0) { + return -val; + } else { + return val; + } +} + +/* negative absolute value 32-bit */ +int32_t HELPER(nabs_i32)(int32_t val) +{ + if (val < 0) { + return val; + } else { + return -val; + } +} + +/* absolute value 64-bit */ +uint64_t HELPER(abs_i64)(int64_t val) +{ + HELPER_LOG("%s: val 0x%" PRIx64 "\n", __FUNCTION__, val); + + if (val < 0) { + return -val; + } else { + return val; + } +} + +/* negative absolute value 64-bit */ +int64_t HELPER(nabs_i64)(int64_t val) +{ + if (val < 0) { + return val; + } else { + return -val; + } +} + +/* add with carry 32-bit unsigned */ +uint32_t HELPER(addc_u32)(uint32_t cc, uint32_t v1, uint32_t v2) +{ + uint32_t res; + + res = v1 + v2; + if (cc & 2) { + res++; + } + + return res; +} + +/* store character under mask high operates on the upper half of r1 */ +void HELPER(stcmh)(uint32_t r1, uint64_t address, uint32_t mask) +{ + int pos = 56; /* top of the upper half of r1 */ + + while (mask) { + if (mask & 8) { + stb(address, (env->regs[r1] >> pos) & 0xff); + address++; + } + mask = (mask << 1) & 0xf; + pos -= 8; + } +} + +/* insert character under mask high; same as icm, but operates on the + upper half of r1 */ +uint32_t HELPER(icmh)(uint32_t r1, uint64_t address, uint32_t mask) +{ + int pos = 56; /* top of the upper half of r1 */ + uint64_t rmask = 0xff00000000000000ULL; + uint8_t val = 0; + int ccd = 0; + uint32_t cc = 0; + + while (mask) { + if (mask & 8) { + env->regs[r1] &= ~rmask; + val = ldub(address); + if ((val & 0x80) && !ccd) { + cc = 1; + } + ccd = 1; + if (val && cc == 0) { + cc = 2; + } + env->regs[r1] |= (uint64_t)val << pos; + address++; + } + mask = (mask << 1) & 0xf; + pos -= 8; + rmask >>= 8; + } + + return cc; +} + +/* insert psw mask and condition code into r1 */ +void HELPER(ipm)(uint32_t cc, uint32_t r1) +{ + uint64_t r = env->regs[r1]; + + r &= 0xffffffff00ffffffULL; + r |= (cc << 28) | ( (env->psw.mask >> 40) & 0xf ); + env->regs[r1] = r; + HELPER_LOG("%s: cc %d psw.mask 0x%lx r1 0x%lx\n", __FUNCTION__, + cc, env->psw.mask, r); +} + +/* load access registers r1 to r3 from memory at a2 */ +void HELPER(lam)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + int i; + + for (i = r1;; i = (i + 1) % 16) { + env->aregs[i] = ldl(a2); + a2 += 4; + + if (i == r3) { + break; + } + } +} + +/* store access registers r1 to r3 in memory at a2 */ +void HELPER(stam)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + int i; + + for (i = r1;; i = (i + 1) % 16) { + stl(a2, env->aregs[i]); + a2 += 4; + + if (i == r3) { + break; + } + } +} + +/* move long */ +uint32_t HELPER(mvcl)(uint32_t r1, uint32_t r2) +{ + uint64_t destlen = env->regs[r1 + 1] & 0xffffff; + uint64_t dest = get_address_31fix(r1); + uint64_t srclen = env->regs[r2 + 1] & 0xffffff; + uint64_t src = get_address_31fix(r2); + uint8_t pad = src >> 24; + uint8_t v; + uint32_t cc; + + if (destlen == srclen) { + cc = 0; + } else if (destlen < srclen) { + cc = 1; + } else { + cc = 2; + } + + if (srclen > destlen) { + srclen = destlen; + } + + for (; destlen && srclen; src++, dest++, destlen--, srclen--) { + v = ldub(src); + stb(dest, v); + } + + for (; destlen; dest++, destlen--) { + stb(dest, pad); + } + + env->regs[r1 + 1] = destlen; + /* can't use srclen here, we trunc'ed it */ + env->regs[r2 + 1] -= src - env->regs[r2]; + env->regs[r1] = dest; + env->regs[r2] = src; + + return cc; +} + +/* move long extended another memcopy insn with more bells and whistles */ +uint32_t HELPER(mvcle)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + uint64_t destlen = env->regs[r1 + 1]; + uint64_t dest = env->regs[r1]; + uint64_t srclen = env->regs[r3 + 1]; + uint64_t src = env->regs[r3]; + uint8_t pad = a2 & 0xff; + uint8_t v; + uint32_t cc; + + if (!(env->psw.mask & PSW_MASK_64)) { + destlen = (uint32_t)destlen; + srclen = (uint32_t)srclen; + dest &= 0x7fffffff; + src &= 0x7fffffff; + } + + if (destlen == srclen) { + cc = 0; + } else if (destlen < srclen) { + cc = 1; + } else { + cc = 2; + } + + if (srclen > destlen) { + srclen = destlen; + } + + for (; destlen && srclen; src++, dest++, destlen--, srclen--) { + v = ldub(src); + stb(dest, v); + } + + for (; destlen; dest++, destlen--) { + stb(dest, pad); + } + + env->regs[r1 + 1] = destlen; + /* can't use srclen here, we trunc'ed it */ + /* FIXME: 31-bit mode! */ + env->regs[r3 + 1] -= src - env->regs[r3]; + env->regs[r1] = dest; + env->regs[r3] = src; + + return cc; +} + +/* compare logical long extended memcompare insn with padding */ +uint32_t HELPER(clcle)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + uint64_t destlen = env->regs[r1 + 1]; + uint64_t dest = get_address_31fix(r1); + uint64_t srclen = env->regs[r3 + 1]; + uint64_t src = get_address_31fix(r3); + uint8_t pad = a2 & 0xff; + uint8_t v1 = 0,v2 = 0; + uint32_t cc = 0; + + if (!(destlen || srclen)) { + return cc; + } + + if (srclen > destlen) { + srclen = destlen; + } + + for (; destlen || srclen; src++, dest++, destlen--, srclen--) { + v1 = srclen ? ldub(src) : pad; + v2 = destlen ? ldub(dest) : pad; + if (v1 != v2) { + cc = (v1 < v2) ? 1 : 2; + break; + } + } + + env->regs[r1 + 1] = destlen; + /* can't use srclen here, we trunc'ed it */ + env->regs[r3 + 1] -= src - env->regs[r3]; + env->regs[r1] = dest; + env->regs[r3] = src; + + return cc; +} + +/* subtract unsigned v2 from v1 with borrow */ +uint32_t HELPER(slb)(uint32_t cc, uint32_t r1, uint32_t v2) +{ + uint32_t v1 = env->regs[r1]; + uint32_t res = v1 + (~v2) + (cc >> 1); + + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | res; + if (cc & 2) { + /* borrow */ + return v1 ? 1 : 0; + } else { + return v1 ? 3 : 2; + } +} + +/* subtract unsigned v2 from v1 with borrow */ +uint32_t HELPER(slbg)(uint32_t cc, uint32_t r1, uint64_t v1, uint64_t v2) +{ + uint64_t res = v1 + (~v2) + (cc >> 1); + + env->regs[r1] = res; + if (cc & 2) { + /* borrow */ + return v1 ? 1 : 0; + } else { + return v1 ? 3 : 2; + } +} + +static inline int float_comp_to_cc(int float_compare) +{ + switch (float_compare) { + case float_relation_equal: + return 0; + case float_relation_less: + return 1; + case float_relation_greater: + return 2; + case float_relation_unordered: + return 3; + default: + cpu_abort(env, "unknown return value for float compare\n"); + } +} + +/* condition codes for binary FP ops */ +static uint32_t set_cc_f32(float32 v1, float32 v2) +{ + return float_comp_to_cc(float32_compare_quiet(v1, v2, &env->fpu_status)); +} + +static uint32_t set_cc_f64(float64 v1, float64 v2) +{ + return float_comp_to_cc(float64_compare_quiet(v1, v2, &env->fpu_status)); +} + +/* condition codes for unary FP ops */ +static uint32_t set_cc_nz_f32(float32 v) +{ + if (float32_is_any_nan(v)) { + return 3; + } else if (float32_is_zero(v)) { + return 0; + } else if (float32_is_neg(v)) { + return 1; + } else { + return 2; + } +} + +static uint32_t set_cc_nz_f64(float64 v) +{ + if (float64_is_any_nan(v)) { + return 3; + } else if (float64_is_zero(v)) { + return 0; + } else if (float64_is_neg(v)) { + return 1; + } else { + return 2; + } +} + +static uint32_t set_cc_nz_f128(float128 v) +{ + if (float128_is_any_nan(v)) { + return 3; + } else if (float128_is_zero(v)) { + return 0; + } else if (float128_is_neg(v)) { + return 1; + } else { + return 2; + } +} + +/* convert 32-bit int to 64-bit float */ +void HELPER(cdfbr)(uint32_t f1, int32_t v2) +{ + HELPER_LOG("%s: converting %d to f%d\n", __FUNCTION__, v2, f1); + env->fregs[f1].d = int32_to_float64(v2, &env->fpu_status); +} + +/* convert 32-bit int to 128-bit float */ +void HELPER(cxfbr)(uint32_t f1, int32_t v2) +{ + CPU_QuadU v1; + v1.q = int32_to_float128(v2, &env->fpu_status); + env->fregs[f1].ll = v1.ll.upper; + env->fregs[f1 + 2].ll = v1.ll.lower; +} + +/* convert 64-bit int to 32-bit float */ +void HELPER(cegbr)(uint32_t f1, int64_t v2) +{ + HELPER_LOG("%s: converting %ld to f%d\n", __FUNCTION__, v2, f1); + env->fregs[f1].l.upper = int64_to_float32(v2, &env->fpu_status); +} + +/* convert 64-bit int to 64-bit float */ +void HELPER(cdgbr)(uint32_t f1, int64_t v2) +{ + HELPER_LOG("%s: converting %ld to f%d\n", __FUNCTION__, v2, f1); + env->fregs[f1].d = int64_to_float64(v2, &env->fpu_status); +} + +/* convert 64-bit int to 128-bit float */ +void HELPER(cxgbr)(uint32_t f1, int64_t v2) +{ + CPU_QuadU x1; + x1.q = int64_to_float128(v2, &env->fpu_status); + HELPER_LOG("%s: converted %ld to 0x%lx and 0x%lx\n", __FUNCTION__, v2, + x1.ll.upper, x1.ll.lower); + env->fregs[f1].ll = x1.ll.upper; + env->fregs[f1 + 2].ll = x1.ll.lower; +} + +/* convert 32-bit int to 32-bit float */ +void HELPER(cefbr)(uint32_t f1, int32_t v2) +{ + env->fregs[f1].l.upper = int32_to_float32(v2, &env->fpu_status); + HELPER_LOG("%s: converting %d to 0x%d in f%d\n", __FUNCTION__, v2, + env->fregs[f1].l.upper, f1); +} + +/* 32-bit FP addition RR */ +uint32_t HELPER(aebr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].l.upper = float32_add(env->fregs[f1].l.upper, + env->fregs[f2].l.upper, + &env->fpu_status); + HELPER_LOG("%s: adding 0x%d resulting in 0x%d in f%d\n", __FUNCTION__, + env->fregs[f2].l.upper, env->fregs[f1].l.upper, f1); + + return set_cc_nz_f32(env->fregs[f1].l.upper); +} + +/* 64-bit FP addition RR */ +uint32_t HELPER(adbr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].d = float64_add(env->fregs[f1].d, env->fregs[f2].d, + &env->fpu_status); + HELPER_LOG("%s: adding 0x%ld resulting in 0x%ld in f%d\n", __FUNCTION__, + env->fregs[f2].d, env->fregs[f1].d, f1); + + return set_cc_nz_f64(env->fregs[f1].d); +} + +/* 32-bit FP subtraction RR */ +uint32_t HELPER(sebr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].l.upper = float32_sub(env->fregs[f1].l.upper, + env->fregs[f2].l.upper, + &env->fpu_status); + HELPER_LOG("%s: adding 0x%d resulting in 0x%d in f%d\n", __FUNCTION__, + env->fregs[f2].l.upper, env->fregs[f1].l.upper, f1); + + return set_cc_nz_f32(env->fregs[f1].l.upper); +} + +/* 64-bit FP subtraction RR */ +uint32_t HELPER(sdbr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].d = float64_sub(env->fregs[f1].d, env->fregs[f2].d, + &env->fpu_status); + HELPER_LOG("%s: subtracting 0x%ld resulting in 0x%ld in f%d\n", + __FUNCTION__, env->fregs[f2].d, env->fregs[f1].d, f1); + + return set_cc_nz_f64(env->fregs[f1].d); +} + +/* 32-bit FP division RR */ +void HELPER(debr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].l.upper = float32_div(env->fregs[f1].l.upper, + env->fregs[f2].l.upper, + &env->fpu_status); +} + +/* 128-bit FP division RR */ +void HELPER(dxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU v1; + v1.ll.upper = env->fregs[f1].ll; + v1.ll.lower = env->fregs[f1 + 2].ll; + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + CPU_QuadU res; + res.q = float128_div(v1.q, v2.q, &env->fpu_status); + env->fregs[f1].ll = res.ll.upper; + env->fregs[f1 + 2].ll = res.ll.lower; +} + +/* 64-bit FP multiplication RR */ +void HELPER(mdbr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].d = float64_mul(env->fregs[f1].d, env->fregs[f2].d, + &env->fpu_status); +} + +/* 128-bit FP multiplication RR */ +void HELPER(mxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU v1; + v1.ll.upper = env->fregs[f1].ll; + v1.ll.lower = env->fregs[f1 + 2].ll; + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + CPU_QuadU res; + res.q = float128_mul(v1.q, v2.q, &env->fpu_status); + env->fregs[f1].ll = res.ll.upper; + env->fregs[f1 + 2].ll = res.ll.lower; +} + +/* convert 32-bit float to 64-bit float */ +void HELPER(ldebr)(uint32_t r1, uint32_t r2) +{ + env->fregs[r1].d = float32_to_float64(env->fregs[r2].l.upper, + &env->fpu_status); +} + +/* convert 128-bit float to 64-bit float */ +void HELPER(ldxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU x2; + x2.ll.upper = env->fregs[f2].ll; + x2.ll.lower = env->fregs[f2 + 2].ll; + env->fregs[f1].d = float128_to_float64(x2.q, &env->fpu_status); + HELPER_LOG("%s: to 0x%ld\n", __FUNCTION__, env->fregs[f1].d); +} + +/* convert 64-bit float to 128-bit float */ +void HELPER(lxdbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU res; + res.q = float64_to_float128(env->fregs[f2].d, &env->fpu_status); + env->fregs[f1].ll = res.ll.upper; + env->fregs[f1 + 2].ll = res.ll.lower; +} + +/* convert 64-bit float to 32-bit float */ +void HELPER(ledbr)(uint32_t f1, uint32_t f2) +{ + float64 d2 = env->fregs[f2].d; + env->fregs[f1].l.upper = float64_to_float32(d2, &env->fpu_status); +} + +/* convert 128-bit float to 32-bit float */ +void HELPER(lexbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU x2; + x2.ll.upper = env->fregs[f2].ll; + x2.ll.lower = env->fregs[f2 + 2].ll; + env->fregs[f1].l.upper = float128_to_float32(x2.q, &env->fpu_status); + HELPER_LOG("%s: to 0x%d\n", __FUNCTION__, env->fregs[f1].l.upper); +} + +/* absolute value of 32-bit float */ +uint32_t HELPER(lpebr)(uint32_t f1, uint32_t f2) +{ + float32 v1; + float32 v2 = env->fregs[f2].d; + v1 = float32_abs(v2); + env->fregs[f1].d = v1; + return set_cc_nz_f32(v1); +} + +/* absolute value of 64-bit float */ +uint32_t HELPER(lpdbr)(uint32_t f1, uint32_t f2) +{ + float64 v1; + float64 v2 = env->fregs[f2].d; + v1 = float64_abs(v2); + env->fregs[f1].d = v1; + return set_cc_nz_f64(v1); +} + +/* absolute value of 128-bit float */ +uint32_t HELPER(lpxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU v1; + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + v1.q = float128_abs(v2.q); + env->fregs[f1].ll = v1.ll.upper; + env->fregs[f1 + 2].ll = v1.ll.lower; + return set_cc_nz_f128(v1.q); +} + +/* load and test 64-bit float */ +uint32_t HELPER(ltdbr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].d = env->fregs[f2].d; + return set_cc_nz_f64(env->fregs[f1].d); +} + +/* load and test 32-bit float */ +uint32_t HELPER(ltebr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].l.upper = env->fregs[f2].l.upper; + return set_cc_nz_f32(env->fregs[f1].l.upper); +} + +/* load and test 128-bit float */ +uint32_t HELPER(ltxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU x; + x.ll.upper = env->fregs[f2].ll; + x.ll.lower = env->fregs[f2 + 2].ll; + env->fregs[f1].ll = x.ll.upper; + env->fregs[f1 + 2].ll = x.ll.lower; + return set_cc_nz_f128(x.q); +} + +/* load complement of 32-bit float */ +uint32_t HELPER(lcebr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].l.upper = float32_chs(env->fregs[f2].l.upper); + + return set_cc_nz_f32(env->fregs[f1].l.upper); +} + +/* load complement of 64-bit float */ +uint32_t HELPER(lcdbr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].d = float64_chs(env->fregs[f2].d); + + return set_cc_nz_f64(env->fregs[f1].d); +} + +/* load complement of 128-bit float */ +uint32_t HELPER(lcxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU x1, x2; + x2.ll.upper = env->fregs[f2].ll; + x2.ll.lower = env->fregs[f2 + 2].ll; + x1.q = float128_chs(x2.q); + env->fregs[f1].ll = x1.ll.upper; + env->fregs[f1 + 2].ll = x1.ll.lower; + return set_cc_nz_f128(x1.q); +} + +/* 32-bit FP addition RM */ +void HELPER(aeb)(uint32_t f1, uint32_t val) +{ + float32 v1 = env->fregs[f1].l.upper; + CPU_FloatU v2; + v2.l = val; + HELPER_LOG("%s: adding 0x%d from f%d and 0x%d\n", __FUNCTION__, + v1, f1, v2.f); + env->fregs[f1].l.upper = float32_add(v1, v2.f, &env->fpu_status); +} + +/* 32-bit FP division RM */ +void HELPER(deb)(uint32_t f1, uint32_t val) +{ + float32 v1 = env->fregs[f1].l.upper; + CPU_FloatU v2; + v2.l = val; + HELPER_LOG("%s: dividing 0x%d from f%d by 0x%d\n", __FUNCTION__, + v1, f1, v2.f); + env->fregs[f1].l.upper = float32_div(v1, v2.f, &env->fpu_status); +} + +/* 32-bit FP multiplication RM */ +void HELPER(meeb)(uint32_t f1, uint32_t val) +{ + float32 v1 = env->fregs[f1].l.upper; + CPU_FloatU v2; + v2.l = val; + HELPER_LOG("%s: multiplying 0x%d from f%d and 0x%d\n", __FUNCTION__, + v1, f1, v2.f); + env->fregs[f1].l.upper = float32_mul(v1, v2.f, &env->fpu_status); +} + +/* 32-bit FP compare RR */ +uint32_t HELPER(cebr)(uint32_t f1, uint32_t f2) +{ + float32 v1 = env->fregs[f1].l.upper; + float32 v2 = env->fregs[f2].l.upper;; + HELPER_LOG("%s: comparing 0x%d from f%d and 0x%d\n", __FUNCTION__, + v1, f1, v2); + return set_cc_f32(v1, v2); +} + +/* 64-bit FP compare RR */ +uint32_t HELPER(cdbr)(uint32_t f1, uint32_t f2) +{ + float64 v1 = env->fregs[f1].d; + float64 v2 = env->fregs[f2].d;; + HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%ld\n", __FUNCTION__, + v1, f1, v2); + return set_cc_f64(v1, v2); +} + +/* 128-bit FP compare RR */ +uint32_t HELPER(cxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU v1; + v1.ll.upper = env->fregs[f1].ll; + v1.ll.lower = env->fregs[f1 + 2].ll; + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + + return float_comp_to_cc(float128_compare_quiet(v1.q, v2.q, + &env->fpu_status)); +} + +/* 64-bit FP compare RM */ +uint32_t HELPER(cdb)(uint32_t f1, uint64_t a2) +{ + float64 v1 = env->fregs[f1].d; + CPU_DoubleU v2; + v2.ll = ldq(a2); + HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%lx\n", __FUNCTION__, v1, + f1, v2.d); + return set_cc_f64(v1, v2.d); +} + +/* 64-bit FP addition RM */ +uint32_t HELPER(adb)(uint32_t f1, uint64_t a2) +{ + float64 v1 = env->fregs[f1].d; + CPU_DoubleU v2; + v2.ll = ldq(a2); + HELPER_LOG("%s: adding 0x%lx from f%d and 0x%lx\n", __FUNCTION__, + v1, f1, v2.d); + env->fregs[f1].d = v1 = float64_add(v1, v2.d, &env->fpu_status); + return set_cc_nz_f64(v1); +} + +/* 32-bit FP subtraction RM */ +void HELPER(seb)(uint32_t f1, uint32_t val) +{ + float32 v1 = env->fregs[f1].l.upper; + CPU_FloatU v2; + v2.l = val; + env->fregs[f1].l.upper = float32_sub(v1, v2.f, &env->fpu_status); +} + +/* 64-bit FP subtraction RM */ +uint32_t HELPER(sdb)(uint32_t f1, uint64_t a2) +{ + float64 v1 = env->fregs[f1].d; + CPU_DoubleU v2; + v2.ll = ldq(a2); + env->fregs[f1].d = v1 = float64_sub(v1, v2.d, &env->fpu_status); + return set_cc_nz_f64(v1); +} + +/* 64-bit FP multiplication RM */ +void HELPER(mdb)(uint32_t f1, uint64_t a2) +{ + float64 v1 = env->fregs[f1].d; + CPU_DoubleU v2; + v2.ll = ldq(a2); + HELPER_LOG("%s: multiplying 0x%lx from f%d and 0x%ld\n", __FUNCTION__, + v1, f1, v2.d); + env->fregs[f1].d = float64_mul(v1, v2.d, &env->fpu_status); +} + +/* 64-bit FP division RM */ +void HELPER(ddb)(uint32_t f1, uint64_t a2) +{ + float64 v1 = env->fregs[f1].d; + CPU_DoubleU v2; + v2.ll = ldq(a2); + HELPER_LOG("%s: dividing 0x%lx from f%d by 0x%ld\n", __FUNCTION__, + v1, f1, v2.d); + env->fregs[f1].d = float64_div(v1, v2.d, &env->fpu_status); +} + +static void set_round_mode(int m3) +{ + switch (m3) { + case 0: + /* current mode */ + break; + case 1: + /* biased round no nearest */ + case 4: + /* round to nearest */ + set_float_rounding_mode(float_round_nearest_even, &env->fpu_status); + break; + case 5: + /* round to zero */ + set_float_rounding_mode(float_round_to_zero, &env->fpu_status); + break; + case 6: + /* round to +inf */ + set_float_rounding_mode(float_round_up, &env->fpu_status); + break; + case 7: + /* round to -inf */ + set_float_rounding_mode(float_round_down, &env->fpu_status); + break; + } +} + +/* convert 32-bit float to 64-bit int */ +uint32_t HELPER(cgebr)(uint32_t r1, uint32_t f2, uint32_t m3) +{ + float32 v2 = env->fregs[f2].l.upper; + set_round_mode(m3); + env->regs[r1] = float32_to_int64(v2, &env->fpu_status); + return set_cc_nz_f32(v2); +} + +/* convert 64-bit float to 64-bit int */ +uint32_t HELPER(cgdbr)(uint32_t r1, uint32_t f2, uint32_t m3) +{ + float64 v2 = env->fregs[f2].d; + set_round_mode(m3); + env->regs[r1] = float64_to_int64(v2, &env->fpu_status); + return set_cc_nz_f64(v2); +} + +/* convert 128-bit float to 64-bit int */ +uint32_t HELPER(cgxbr)(uint32_t r1, uint32_t f2, uint32_t m3) +{ + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + set_round_mode(m3); + env->regs[r1] = float128_to_int64(v2.q, &env->fpu_status); + if (float128_is_any_nan(v2.q)) { + return 3; + } else if (float128_is_zero(v2.q)) { + return 0; + } else if (float128_is_neg(v2.q)) { + return 1; + } else { + return 2; + } +} + +/* convert 32-bit float to 32-bit int */ +uint32_t HELPER(cfebr)(uint32_t r1, uint32_t f2, uint32_t m3) +{ + float32 v2 = env->fregs[f2].l.upper; + set_round_mode(m3); + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | + float32_to_int32(v2, &env->fpu_status); + return set_cc_nz_f32(v2); +} + +/* convert 64-bit float to 32-bit int */ +uint32_t HELPER(cfdbr)(uint32_t r1, uint32_t f2, uint32_t m3) +{ + float64 v2 = env->fregs[f2].d; + set_round_mode(m3); + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | + float64_to_int32(v2, &env->fpu_status); + return set_cc_nz_f64(v2); +} + +/* convert 128-bit float to 32-bit int */ +uint32_t HELPER(cfxbr)(uint32_t r1, uint32_t f2, uint32_t m3) +{ + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | + float128_to_int32(v2.q, &env->fpu_status); + return set_cc_nz_f128(v2.q); +} + +/* load 32-bit FP zero */ +void HELPER(lzer)(uint32_t f1) +{ + env->fregs[f1].l.upper = float32_zero; +} + +/* load 64-bit FP zero */ +void HELPER(lzdr)(uint32_t f1) +{ + env->fregs[f1].d = float64_zero; +} + +/* load 128-bit FP zero */ +void HELPER(lzxr)(uint32_t f1) +{ + CPU_QuadU x; + x.q = float64_to_float128(float64_zero, &env->fpu_status); + env->fregs[f1].ll = x.ll.upper; + env->fregs[f1 + 1].ll = x.ll.lower; +} + +/* 128-bit FP subtraction RR */ +uint32_t HELPER(sxbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU v1; + v1.ll.upper = env->fregs[f1].ll; + v1.ll.lower = env->fregs[f1 + 2].ll; + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + CPU_QuadU res; + res.q = float128_sub(v1.q, v2.q, &env->fpu_status); + env->fregs[f1].ll = res.ll.upper; + env->fregs[f1 + 2].ll = res.ll.lower; + return set_cc_nz_f128(res.q); +} + +/* 128-bit FP addition RR */ +uint32_t HELPER(axbr)(uint32_t f1, uint32_t f2) +{ + CPU_QuadU v1; + v1.ll.upper = env->fregs[f1].ll; + v1.ll.lower = env->fregs[f1 + 2].ll; + CPU_QuadU v2; + v2.ll.upper = env->fregs[f2].ll; + v2.ll.lower = env->fregs[f2 + 2].ll; + CPU_QuadU res; + res.q = float128_add(v1.q, v2.q, &env->fpu_status); + env->fregs[f1].ll = res.ll.upper; + env->fregs[f1 + 2].ll = res.ll.lower; + return set_cc_nz_f128(res.q); +} + +/* 32-bit FP multiplication RR */ +void HELPER(meebr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].l.upper = float32_mul(env->fregs[f1].l.upper, + env->fregs[f2].l.upper, + &env->fpu_status); +} + +/* 64-bit FP division RR */ +void HELPER(ddbr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].d = float64_div(env->fregs[f1].d, env->fregs[f2].d, + &env->fpu_status); +} + +/* 64-bit FP multiply and add RM */ +void HELPER(madb)(uint32_t f1, uint64_t a2, uint32_t f3) +{ + HELPER_LOG("%s: f1 %d a2 0x%lx f3 %d\n", __FUNCTION__, f1, a2, f3); + CPU_DoubleU v2; + v2.ll = ldq(a2); + env->fregs[f1].d = float64_add(env->fregs[f1].d, + float64_mul(v2.d, env->fregs[f3].d, + &env->fpu_status), + &env->fpu_status); +} + +/* 64-bit FP multiply and add RR */ +void HELPER(madbr)(uint32_t f1, uint32_t f3, uint32_t f2) +{ + HELPER_LOG("%s: f1 %d f2 %d f3 %d\n", __FUNCTION__, f1, f2, f3); + env->fregs[f1].d = float64_add(float64_mul(env->fregs[f2].d, + env->fregs[f3].d, + &env->fpu_status), + env->fregs[f1].d, &env->fpu_status); +} + +/* 64-bit FP multiply and subtract RR */ +void HELPER(msdbr)(uint32_t f1, uint32_t f3, uint32_t f2) +{ + HELPER_LOG("%s: f1 %d f2 %d f3 %d\n", __FUNCTION__, f1, f2, f3); + env->fregs[f1].d = float64_sub(float64_mul(env->fregs[f2].d, + env->fregs[f3].d, + &env->fpu_status), + env->fregs[f1].d, &env->fpu_status); +} + +/* 32-bit FP multiply and add RR */ +void HELPER(maebr)(uint32_t f1, uint32_t f3, uint32_t f2) +{ + env->fregs[f1].l.upper = float32_add(env->fregs[f1].l.upper, + float32_mul(env->fregs[f2].l.upper, + env->fregs[f3].l.upper, + &env->fpu_status), + &env->fpu_status); +} + +/* convert 64-bit float to 128-bit float */ +void HELPER(lxdb)(uint32_t f1, uint64_t a2) +{ + CPU_DoubleU v2; + v2.ll = ldq(a2); + CPU_QuadU v1; + v1.q = float64_to_float128(v2.d, &env->fpu_status); + env->fregs[f1].ll = v1.ll.upper; + env->fregs[f1 + 2].ll = v1.ll.lower; +} + +/* test data class 32-bit */ +uint32_t HELPER(tceb)(uint32_t f1, uint64_t m2) +{ + float32 v1 = env->fregs[f1].l.upper; + int neg = float32_is_neg(v1); + uint32_t cc = 0; + + HELPER_LOG("%s: v1 0x%lx m2 0x%lx neg %d\n", __FUNCTION__, (long)v1, m2, neg); + if ((float32_is_zero(v1) && (m2 & (1 << (11-neg)))) || + (float32_is_infinity(v1) && (m2 & (1 << (5-neg)))) || + (float32_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || + (float32_is_signaling_nan(v1) && (m2 & (1 << (1-neg))))) { + cc = 1; + } else if (m2 & (1 << (9-neg))) { + /* assume normalized number */ + cc = 1; + } + + /* FIXME: denormalized? */ + return cc; +} + +/* test data class 64-bit */ +uint32_t HELPER(tcdb)(uint32_t f1, uint64_t m2) +{ + float64 v1 = env->fregs[f1].d; + int neg = float64_is_neg(v1); + uint32_t cc = 0; + + HELPER_LOG("%s: v1 0x%lx m2 0x%lx neg %d\n", __FUNCTION__, v1, m2, neg); + if ((float64_is_zero(v1) && (m2 & (1 << (11-neg)))) || + (float64_is_infinity(v1) && (m2 & (1 << (5-neg)))) || + (float64_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || + (float64_is_signaling_nan(v1) && (m2 & (1 << (1-neg))))) { + cc = 1; + } else if (m2 & (1 << (9-neg))) { + /* assume normalized number */ + cc = 1; + } + /* FIXME: denormalized? */ + return cc; +} + +/* test data class 128-bit */ +uint32_t HELPER(tcxb)(uint32_t f1, uint64_t m2) +{ + CPU_QuadU v1; + uint32_t cc = 0; + v1.ll.upper = env->fregs[f1].ll; + v1.ll.lower = env->fregs[f1 + 2].ll; + + int neg = float128_is_neg(v1.q); + if ((float128_is_zero(v1.q) && (m2 & (1 << (11-neg)))) || + (float128_is_infinity(v1.q) && (m2 & (1 << (5-neg)))) || + (float128_is_any_nan(v1.q) && (m2 & (1 << (3-neg)))) || + (float128_is_signaling_nan(v1.q) && (m2 & (1 << (1-neg))))) { + cc = 1; + } else if (m2 & (1 << (9-neg))) { + /* assume normalized number */ + cc = 1; + } + /* FIXME: denormalized? */ + return cc; +} + +/* find leftmost one */ +uint32_t HELPER(flogr)(uint32_t r1, uint64_t v2) +{ + uint64_t res = 0; + uint64_t ov2 = v2; + + while (!(v2 & 0x8000000000000000ULL) && v2) { + v2 <<= 1; + res++; + } + + if (!v2) { + env->regs[r1] = 64; + env->regs[r1 + 1] = 0; + return 0; + } else { + env->regs[r1] = res; + env->regs[r1 + 1] = ov2 & ~(0x8000000000000000ULL >> res); + return 2; + } +} + +/* square root 64-bit RR */ +void HELPER(sqdbr)(uint32_t f1, uint32_t f2) +{ + env->fregs[f1].d = float64_sqrt(env->fregs[f2].d, &env->fpu_status); +} + +/* checksum */ +void HELPER(cksm)(uint32_t r1, uint32_t r2) +{ + uint64_t src = get_address_31fix(r2); + uint64_t src_len = env->regs[(r2 + 1) & 15]; + uint64_t cksm = (uint32_t)env->regs[r1]; + + while (src_len >= 4) { + cksm += ldl(src); + + /* move to next word */ + src_len -= 4; + src += 4; + } + + switch (src_len) { + case 0: + break; + case 1: + cksm += ldub(src) << 24; + break; + case 2: + cksm += lduw(src) << 16; + break; + case 3: + cksm += lduw(src) << 16; + cksm += ldub(src + 2) << 8; + break; + } + + /* indicate we've processed everything */ + env->regs[r2] = src + src_len; + env->regs[(r2 + 1) & 15] = 0; + + /* store result */ + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | + ((uint32_t)cksm + (cksm >> 32)); +} + +static inline uint32_t cc_calc_ltgt_32(CPUState *env, int32_t src, + int32_t dst) +{ + if (src == dst) { + return 0; + } else if (src < dst) { + return 1; + } else { + return 2; + } +} + +static inline uint32_t cc_calc_ltgt0_32(CPUState *env, int32_t dst) +{ + return cc_calc_ltgt_32(env, dst, 0); +} + +static inline uint32_t cc_calc_ltgt_64(CPUState *env, int64_t src, + int64_t dst) +{ + if (src == dst) { + return 0; + } else if (src < dst) { + return 1; + } else { + return 2; + } +} + +static inline uint32_t cc_calc_ltgt0_64(CPUState *env, int64_t dst) +{ + return cc_calc_ltgt_64(env, dst, 0); +} + +static inline uint32_t cc_calc_ltugtu_32(CPUState *env, uint32_t src, + uint32_t dst) +{ + if (src == dst) { + return 0; + } else if (src < dst) { + return 1; + } else { + return 2; + } +} + +static inline uint32_t cc_calc_ltugtu_64(CPUState *env, uint64_t src, + uint64_t dst) +{ + if (src == dst) { + return 0; + } else if (src < dst) { + return 1; + } else { + return 2; + } +} + +static inline uint32_t cc_calc_tm_32(CPUState *env, uint32_t val, uint32_t mask) +{ + HELPER_LOG("%s: val 0x%x mask 0x%x\n", __FUNCTION__, val, mask); + uint16_t r = val & mask; + if (r == 0 || mask == 0) { + return 0; + } else if (r == mask) { + return 3; + } else { + return 1; + } +} + +/* set condition code for test under mask */ +static inline uint32_t cc_calc_tm_64(CPUState *env, uint64_t val, uint32_t mask) +{ + uint16_t r = val & mask; + HELPER_LOG("%s: val 0x%lx mask 0x%x r 0x%x\n", __FUNCTION__, val, mask, r); + if (r == 0 || mask == 0) { + return 0; + } else if (r == mask) { + return 3; + } else { + while (!(mask & 0x8000)) { + mask <<= 1; + val <<= 1; + } + if (val & 0x8000) { + return 2; + } else { + return 1; + } + } +} + +static inline uint32_t cc_calc_nz(CPUState *env, uint64_t dst) +{ + return !!dst; +} + +static inline uint32_t cc_calc_add_64(CPUState *env, int64_t a1, int64_t a2, + int64_t ar) +{ + if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) { + return 3; /* overflow */ + } else { + if (ar < 0) { + return 1; + } else if (ar > 0) { + return 2; + } else { + return 0; + } + } +} + +static inline uint32_t cc_calc_addu_64(CPUState *env, uint64_t a1, uint64_t a2, + uint64_t ar) +{ + if (ar == 0) { + if (a1) { + return 2; + } else { + return 0; + } + } else { + if (ar < a1 || ar < a2) { + return 3; + } else { + return 1; + } + } +} + +static inline uint32_t cc_calc_sub_64(CPUState *env, int64_t a1, int64_t a2, + int64_t ar) +{ + if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { + return 3; /* overflow */ + } else { + if (ar < 0) { + return 1; + } else if (ar > 0) { + return 2; + } else { + return 0; + } + } +} + +static inline uint32_t cc_calc_subu_64(CPUState *env, uint64_t a1, uint64_t a2, + uint64_t ar) +{ + if (ar == 0) { + return 2; + } else { + if (a2 > a1) { + return 1; + } else { + return 3; + } + } +} + +static inline uint32_t cc_calc_abs_64(CPUState *env, int64_t dst) +{ + if ((uint64_t)dst == 0x8000000000000000ULL) { + return 3; + } else if (dst) { + return 1; + } else { + return 0; + } +} + +static inline uint32_t cc_calc_nabs_64(CPUState *env, int64_t dst) +{ + return !!dst; +} + +static inline uint32_t cc_calc_comp_64(CPUState *env, int64_t dst) +{ + if ((uint64_t)dst == 0x8000000000000000ULL) { + return 3; + } else if (dst < 0) { + return 1; + } else if (dst > 0) { + return 2; + } else { + return 0; + } +} + + +static inline uint32_t cc_calc_add_32(CPUState *env, int32_t a1, int32_t a2, + int32_t ar) +{ + if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) { + return 3; /* overflow */ + } else { + if (ar < 0) { + return 1; + } else if (ar > 0) { + return 2; + } else { + return 0; + } + } +} + +static inline uint32_t cc_calc_addu_32(CPUState *env, uint32_t a1, uint32_t a2, + uint32_t ar) +{ + if (ar == 0) { + if (a1) { + return 2; + } else { + return 0; + } + } else { + if (ar < a1 || ar < a2) { + return 3; + } else { + return 1; + } + } +} + +static inline uint32_t cc_calc_sub_32(CPUState *env, int32_t a1, int32_t a2, + int32_t ar) +{ + if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { + return 3; /* overflow */ + } else { + if (ar < 0) { + return 1; + } else if (ar > 0) { + return 2; + } else { + return 0; + } + } +} + +static inline uint32_t cc_calc_subu_32(CPUState *env, uint32_t a1, uint32_t a2, + uint32_t ar) +{ + if (ar == 0) { + return 2; + } else { + if (a2 > a1) { + return 1; + } else { + return 3; + } + } +} + +static inline uint32_t cc_calc_abs_32(CPUState *env, int32_t dst) +{ + if ((uint32_t)dst == 0x80000000UL) { + return 3; + } else if (dst) { + return 1; + } else { + return 0; + } +} + +static inline uint32_t cc_calc_nabs_32(CPUState *env, int32_t dst) +{ + return !!dst; +} + +static inline uint32_t cc_calc_comp_32(CPUState *env, int32_t dst) +{ + if ((uint32_t)dst == 0x80000000UL) { + return 3; + } else if (dst < 0) { + return 1; + } else if (dst > 0) { + return 2; + } else { + return 0; + } +} + +/* calculate condition code for insert character under mask insn */ +static inline uint32_t cc_calc_icm_32(CPUState *env, uint32_t mask, uint32_t val) +{ + HELPER_LOG("%s: mask 0x%x val %d\n", __FUNCTION__, mask, val); + uint32_t cc; + + if (mask == 0xf) { + if (!val) { + return 0; + } else if (val & 0x80000000) { + return 1; + } else { + return 2; + } + } + + if (!val || !mask) { + cc = 0; + } else { + while (mask != 1) { + mask >>= 1; + val >>= 8; + } + if (val & 0x80) { + cc = 1; + } else { + cc = 2; + } + } + return cc; +} + +static inline uint32_t cc_calc_slag(CPUState *env, uint64_t src, uint64_t shift) +{ + uint64_t mask = ((1ULL << shift) - 1ULL) << (64 - shift); + uint64_t match, r; + + /* check if the sign bit stays the same */ + if (src & (1ULL << 63)) { + match = mask; + } else { + match = 0; + } + + if ((src & mask) != match) { + /* overflow */ + return 3; + } + + r = ((src << shift) & ((1ULL << 63) - 1)) | (src & (1ULL << 63)); + + if ((int64_t)r == 0) { + return 0; + } else if ((int64_t)r < 0) { + return 1; + } + + return 2; +} + + +static inline uint32_t do_calc_cc(CPUState *env, uint32_t cc_op, uint64_t src, + uint64_t dst, uint64_t vr) +{ + uint32_t r = 0; + + switch (cc_op) { + case CC_OP_CONST0: + case CC_OP_CONST1: + case CC_OP_CONST2: + case CC_OP_CONST3: + /* cc_op value _is_ cc */ + r = cc_op; + break; + case CC_OP_LTGT0_32: + r = cc_calc_ltgt0_32(env, dst); + break; + case CC_OP_LTGT0_64: + r = cc_calc_ltgt0_64(env, dst); + break; + case CC_OP_LTGT_32: + r = cc_calc_ltgt_32(env, src, dst); + break; + case CC_OP_LTGT_64: + r = cc_calc_ltgt_64(env, src, dst); + break; + case CC_OP_LTUGTU_32: + r = cc_calc_ltugtu_32(env, src, dst); + break; + case CC_OP_LTUGTU_64: + r = cc_calc_ltugtu_64(env, src, dst); + break; + case CC_OP_TM_32: + r = cc_calc_tm_32(env, src, dst); + break; + case CC_OP_TM_64: + r = cc_calc_tm_64(env, src, dst); + break; + case CC_OP_NZ: + r = cc_calc_nz(env, dst); + break; + case CC_OP_ADD_64: + r = cc_calc_add_64(env, src, dst, vr); + break; + case CC_OP_ADDU_64: + r = cc_calc_addu_64(env, src, dst, vr); + break; + case CC_OP_SUB_64: + r = cc_calc_sub_64(env, src, dst, vr); + break; + case CC_OP_SUBU_64: + r = cc_calc_subu_64(env, src, dst, vr); + break; + case CC_OP_ABS_64: + r = cc_calc_abs_64(env, dst); + break; + case CC_OP_NABS_64: + r = cc_calc_nabs_64(env, dst); + break; + case CC_OP_COMP_64: + r = cc_calc_comp_64(env, dst); + break; + + case CC_OP_ADD_32: + r = cc_calc_add_32(env, src, dst, vr); + break; + case CC_OP_ADDU_32: + r = cc_calc_addu_32(env, src, dst, vr); + break; + case CC_OP_SUB_32: + r = cc_calc_sub_32(env, src, dst, vr); + break; + case CC_OP_SUBU_32: + r = cc_calc_subu_32(env, src, dst, vr); + break; + case CC_OP_ABS_32: + r = cc_calc_abs_64(env, dst); + break; + case CC_OP_NABS_32: + r = cc_calc_nabs_64(env, dst); + break; + case CC_OP_COMP_32: + r = cc_calc_comp_32(env, dst); + break; + + case CC_OP_ICM: + r = cc_calc_icm_32(env, src, dst); + break; + case CC_OP_SLAG: + r = cc_calc_slag(env, src, dst); + break; + + case CC_OP_LTGT_F32: + r = set_cc_f32(src, dst); + break; + case CC_OP_LTGT_F64: + r = set_cc_f64(src, dst); + break; + case CC_OP_NZ_F32: + r = set_cc_nz_f32(dst); + break; + case CC_OP_NZ_F64: + r = set_cc_nz_f64(dst); + break; + + default: + cpu_abort(env, "Unknown CC operation: %s\n", cc_name(cc_op)); + } + + HELPER_LOG("%s: %15s 0x%016lx 0x%016lx 0x%016lx = %d\n", __FUNCTION__, + cc_name(cc_op), src, dst, vr, r); + return r; +} + +uint32_t calc_cc(CPUState *env, uint32_t cc_op, uint64_t src, uint64_t dst, + uint64_t vr) +{ + return do_calc_cc(env, cc_op, src, dst, vr); +} + +uint32_t HELPER(calc_cc)(uint32_t cc_op, uint64_t src, uint64_t dst, + uint64_t vr) +{ + return do_calc_cc(env, cc_op, src, dst, vr); +} + +uint64_t HELPER(cvd)(int32_t bin) +{ + /* positive 0 */ + uint64_t dec = 0x0c; + int shift = 4; + + if (bin < 0) { + bin = -bin; + dec = 0x0d; + } + + for (shift = 4; (shift < 64) && bin; shift += 4) { + int current_number = bin % 10; + + dec |= (current_number) << shift; + bin /= 10; + } + + return dec; +} + +void HELPER(unpk)(uint32_t len, uint64_t dest, uint64_t src) +{ + int len_dest = len >> 4; + int len_src = len & 0xf; + uint8_t b; + int second_nibble = 0; + + dest += len_dest; + src += len_src; + + /* last byte is special, it only flips the nibbles */ + b = ldub(src); + stb(dest, (b << 4) | (b >> 4)); + src--; + len_src--; + + /* now pad every nibble with 0xf0 */ + + while (len_dest > 0) { + uint8_t cur_byte = 0; + + if (len_src > 0) { + cur_byte = ldub(src); + } + + len_dest--; + dest--; + + /* only advance one nibble at a time */ + if (second_nibble) { + cur_byte >>= 4; + len_src--; + src--; + } + second_nibble = !second_nibble; + + /* digit */ + cur_byte = (cur_byte & 0xf); + /* zone bits */ + cur_byte |= 0xf0; + + stb(dest, cur_byte); + } +} + +void HELPER(tr)(uint32_t len, uint64_t array, uint64_t trans) +{ + int i; + + for (i = 0; i <= len; i++) { + uint8_t byte = ldub(array + i); + uint8_t new_byte = ldub(trans + byte); + stb(array + i, new_byte); + } +} + +#ifndef CONFIG_USER_ONLY + +void HELPER(load_psw)(uint64_t mask, uint64_t addr) +{ + load_psw(env, mask, addr); + cpu_loop_exit(env); +} + +static void program_interrupt(CPUState *env, uint32_t code, int ilc) +{ + qemu_log("program interrupt at %#" PRIx64 "\n", env->psw.addr); + + if (kvm_enabled()) { +#ifdef CONFIG_KVM + kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code); +#endif + } else { + env->int_pgm_code = code; + env->int_pgm_ilc = ilc; + env->exception_index = EXCP_PGM; + cpu_loop_exit(env); + } +} + +static void ext_interrupt(CPUState *env, int type, uint32_t param, + uint64_t param64) +{ + cpu_inject_ext(env, type, param, param64); +} + +int sclp_service_call(CPUState *env, uint32_t sccb, uint64_t code) +{ + int r = 0; + int shift = 0; + +#ifdef DEBUG_HELPER + printf("sclp(0x%x, 0x%" PRIx64 ")\n", sccb, code); +#endif + + if (sccb & ~0x7ffffff8ul) { + fprintf(stderr, "KVM: invalid sccb address 0x%x\n", sccb); + r = -1; + goto out; + } + + switch(code) { + case SCLP_CMDW_READ_SCP_INFO: + case SCLP_CMDW_READ_SCP_INFO_FORCED: + while ((ram_size >> (20 + shift)) > 65535) { + shift++; + } + stw_phys(sccb + SCP_MEM_CODE, ram_size >> (20 + shift)); + stb_phys(sccb + SCP_INCREMENT, 1 << shift); + stw_phys(sccb + SCP_RESPONSE_CODE, 0x10); + + if (kvm_enabled()) { +#ifdef CONFIG_KVM + kvm_s390_interrupt_internal(env, KVM_S390_INT_SERVICE, + sccb & ~3, 0, 1); +#endif + } else { + env->psw.addr += 4; + ext_interrupt(env, EXT_SERVICE, sccb & ~3, 0); + } + break; + default: +#ifdef DEBUG_HELPER + printf("KVM: invalid sclp call 0x%x / 0x%" PRIx64 "x\n", sccb, code); +#endif + r = -1; + break; + } + +out: + return r; +} + +/* SCLP service call */ +uint32_t HELPER(servc)(uint32_t r1, uint64_t r2) +{ + if (sclp_service_call(env, r1, r2)) { + return 3; + } + + return 0; +} + +/* DIAG */ +uint64_t HELPER(diag)(uint32_t num, uint64_t mem, uint64_t code) +{ + uint64_t r; + + switch (num) { + case 0x500: + /* KVM hypercall */ + r = s390_virtio_hypercall(env, mem, code); + break; + case 0x44: + /* yield */ + r = 0; + break; + case 0x308: + /* ipl */ + r = 0; + break; + default: + r = -1; + break; + } + + if (r) { + program_interrupt(env, PGM_OPERATION, ILC_LATER_INC); + } + + return r; +} + +/* Store CPU ID */ +void HELPER(stidp)(uint64_t a1) +{ + stq(a1, env->cpu_num); +} + +/* Set Prefix */ +void HELPER(spx)(uint64_t a1) +{ + uint32_t prefix; + + prefix = ldl(a1); + env->psa = prefix & 0xfffff000; + qemu_log("prefix: %#x\n", prefix); + tlb_flush_page(env, 0); + tlb_flush_page(env, TARGET_PAGE_SIZE); +} + +/* Set Clock */ +uint32_t HELPER(sck)(uint64_t a1) +{ + /* XXX not implemented - is it necessary? */ + + return 0; +} + +static inline uint64_t clock_value(CPUState *env) +{ + uint64_t time; + + time = env->tod_offset + + time2tod(qemu_get_clock_ns(vm_clock) - env->tod_basetime); + + return time; +} + +/* Store Clock */ +uint32_t HELPER(stck)(uint64_t a1) +{ + stq(a1, clock_value(env)); + + return 0; +} + +/* Store Clock Extended */ +uint32_t HELPER(stcke)(uint64_t a1) +{ + stb(a1, 0); + /* basically the same value as stck */ + stq(a1 + 1, clock_value(env) | env->cpu_num); + /* more fine grained than stck */ + stq(a1 + 9, 0); + /* XXX programmable fields */ + stw(a1 + 17, 0); + + + return 0; +} + +/* Set Clock Comparator */ +void HELPER(sckc)(uint64_t a1) +{ + uint64_t time = ldq(a1); + + if (time == -1ULL) { + return; + } + + /* difference between now and then */ + time -= clock_value(env); + /* nanoseconds */ + time = (time * 125) >> 9; + + qemu_mod_timer(env->tod_timer, qemu_get_clock_ns(vm_clock) + time); +} + +/* Store Clock Comparator */ +void HELPER(stckc)(uint64_t a1) +{ + /* XXX implement */ + stq(a1, 0); +} + +/* Set CPU Timer */ +void HELPER(spt)(uint64_t a1) +{ + uint64_t time = ldq(a1); + + if (time == -1ULL) { + return; + } + + /* nanoseconds */ + time = (time * 125) >> 9; + + qemu_mod_timer(env->cpu_timer, qemu_get_clock_ns(vm_clock) + time); +} + +/* Store CPU Timer */ +void HELPER(stpt)(uint64_t a1) +{ + /* XXX implement */ + stq(a1, 0); +} + +/* Store System Information */ +uint32_t HELPER(stsi)(uint64_t a0, uint32_t r0, uint32_t r1) +{ + int cc = 0; + int sel1, sel2; + + if ((r0 & STSI_LEVEL_MASK) <= STSI_LEVEL_3 && + ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK))) { + /* valid function code, invalid reserved bits */ + program_interrupt(env, PGM_SPECIFICATION, 2); + } + + sel1 = r0 & STSI_R0_SEL1_MASK; + sel2 = r1 & STSI_R1_SEL2_MASK; + + /* XXX: spec exception if sysib is not 4k-aligned */ + + switch (r0 & STSI_LEVEL_MASK) { + case STSI_LEVEL_1: + if ((sel1 == 1) && (sel2 == 1)) { + /* Basic Machine Configuration */ + struct sysib_111 sysib; + + memset(&sysib, 0, sizeof(sysib)); + ebcdic_put(sysib.manuf, "QEMU ", 16); + /* same as machine type number in STORE CPU ID */ + ebcdic_put(sysib.type, "QEMU", 4); + /* same as model number in STORE CPU ID */ + ebcdic_put(sysib.model, "QEMU ", 16); + ebcdic_put(sysib.sequence, "QEMU ", 16); + ebcdic_put(sysib.plant, "QEMU", 4); + cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1); + } else if ((sel1 == 2) && (sel2 == 1)) { + /* Basic Machine CPU */ + struct sysib_121 sysib; + + memset(&sysib, 0, sizeof(sysib)); + /* XXX make different for different CPUs? */ + ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16); + ebcdic_put(sysib.plant, "QEMU", 4); + stw_p(&sysib.cpu_addr, env->cpu_num); + cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1); + } else if ((sel1 == 2) && (sel2 == 2)) { + /* Basic Machine CPUs */ + struct sysib_122 sysib; + + memset(&sysib, 0, sizeof(sysib)); + stl_p(&sysib.capability, 0x443afc29); + /* XXX change when SMP comes */ + stw_p(&sysib.total_cpus, 1); + stw_p(&sysib.active_cpus, 1); + stw_p(&sysib.standby_cpus, 0); + stw_p(&sysib.reserved_cpus, 0); + cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1); + } else { + cc = 3; + } + break; + case STSI_LEVEL_2: + { + if ((sel1 == 2) && (sel2 == 1)) { + /* LPAR CPU */ + struct sysib_221 sysib; + + memset(&sysib, 0, sizeof(sysib)); + /* XXX make different for different CPUs? */ + ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16); + ebcdic_put(sysib.plant, "QEMU", 4); + stw_p(&sysib.cpu_addr, env->cpu_num); + stw_p(&sysib.cpu_id, 0); + cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1); + } else if ((sel1 == 2) && (sel2 == 2)) { + /* LPAR CPUs */ + struct sysib_222 sysib; + + memset(&sysib, 0, sizeof(sysib)); + stw_p(&sysib.lpar_num, 0); + sysib.lcpuc = 0; + /* XXX change when SMP comes */ + stw_p(&sysib.total_cpus, 1); + stw_p(&sysib.conf_cpus, 1); + stw_p(&sysib.standby_cpus, 0); + stw_p(&sysib.reserved_cpus, 0); + ebcdic_put(sysib.name, "QEMU ", 8); + stl_p(&sysib.caf, 1000); + stw_p(&sysib.dedicated_cpus, 0); + stw_p(&sysib.shared_cpus, 0); + cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1); + } else { + cc = 3; + } + break; + } + case STSI_LEVEL_3: + { + if ((sel1 == 2) && (sel2 == 2)) { + /* VM CPUs */ + struct sysib_322 sysib; + + memset(&sysib, 0, sizeof(sysib)); + sysib.count = 1; + /* XXX change when SMP comes */ + stw_p(&sysib.vm[0].total_cpus, 1); + stw_p(&sysib.vm[0].conf_cpus, 1); + stw_p(&sysib.vm[0].standby_cpus, 0); + stw_p(&sysib.vm[0].reserved_cpus, 0); + ebcdic_put(sysib.vm[0].name, "KVMguest", 8); + stl_p(&sysib.vm[0].caf, 1000); + ebcdic_put(sysib.vm[0].cpi, "KVM/Linux ", 16); + cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1); + } else { + cc = 3; + } + break; + } + case STSI_LEVEL_CURRENT: + env->regs[0] = STSI_LEVEL_3; + break; + default: + cc = 3; + break; + } + + return cc; +} + +void HELPER(lctlg)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + int i; + uint64_t src = a2; + + for (i = r1;; i = (i + 1) % 16) { + env->cregs[i] = ldq(src); + HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n", + i, src, env->cregs[i]); + src += sizeof(uint64_t); + + if (i == r3) { + break; + } + } + + tlb_flush(env, 1); +} + +void HELPER(lctl)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + int i; + uint64_t src = a2; + + for (i = r1;; i = (i + 1) % 16) { + env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | ldl(src); + src += sizeof(uint32_t); + + if (i == r3) { + break; + } + } + + tlb_flush(env, 1); +} + +void HELPER(stctg)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + int i; + uint64_t dest = a2; + + for (i = r1;; i = (i + 1) % 16) { + stq(dest, env->cregs[i]); + dest += sizeof(uint64_t); + + if (i == r3) { + break; + } + } +} + +void HELPER(stctl)(uint32_t r1, uint64_t a2, uint32_t r3) +{ + int i; + uint64_t dest = a2; + + for (i = r1;; i = (i + 1) % 16) { + stl(dest, env->cregs[i]); + dest += sizeof(uint32_t); + + if (i == r3) { + break; + } + } +} + +uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2) +{ + /* XXX implement */ + + return 0; +} + +/* insert storage key extended */ +uint64_t HELPER(iske)(uint64_t r2) +{ + uint64_t addr = get_address(0, 0, r2); + + if (addr > ram_size) { + return 0; + } + + /* XXX maybe use qemu's internal keys? */ + return env->storage_keys[addr / TARGET_PAGE_SIZE]; +} + +/* set storage key extended */ +void HELPER(sske)(uint32_t r1, uint64_t r2) +{ + uint64_t addr = get_address(0, 0, r2); + + if (addr > ram_size) { + return; + } + + env->storage_keys[addr / TARGET_PAGE_SIZE] = r1; +} + +/* reset reference bit extended */ +uint32_t HELPER(rrbe)(uint32_t r1, uint64_t r2) +{ + if (r2 > ram_size) { + return 0; + } + + /* XXX implement */ +#if 0 + env->storage_keys[r2 / TARGET_PAGE_SIZE] &= ~SK_REFERENCED; +#endif + + /* + * cc + * + * 0 Reference bit zero; change bit zero + * 1 Reference bit zero; change bit one + * 2 Reference bit one; change bit zero + * 3 Reference bit one; change bit one + */ + return 0; +} + +/* compare and swap and purge */ +uint32_t HELPER(csp)(uint32_t r1, uint32_t r2) +{ + uint32_t cc; + uint32_t o1 = env->regs[r1]; + uint64_t a2 = get_address_31fix(r2) & ~3ULL; + uint32_t o2 = ldl(a2); + + if (o1 == o2) { + stl(a2, env->regs[(r1 + 1) & 15]); + if (env->regs[r2] & 0x3) { + /* flush TLB / ALB */ + tlb_flush(env, 1); + } + cc = 0; + } else { + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2; + cc = 1; + } + + return cc; +} + +static uint32_t mvc_asc(int64_t l, uint64_t a1, uint64_t mode1, uint64_t a2, + uint64_t mode2) +{ + target_ulong src, dest; + int flags, cc = 0, i; + + if (!l) { + return 0; + } else if (l > 256) { + /* max 256 */ + l = 256; + cc = 3; + } + + if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) { + cpu_loop_exit(env); + } + dest |= a1 & ~TARGET_PAGE_MASK; + + if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) { + cpu_loop_exit(env); + } + src |= a2 & ~TARGET_PAGE_MASK; + + /* XXX replace w/ memcpy */ + for (i = 0; i < l; i++) { + /* XXX be more clever */ + if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) || + (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) { + mvc_asc(l - i, a1 + i, mode1, a2 + i, mode2); + break; + } + stb_phys(dest + i, ldub_phys(src + i)); + } + + return cc; +} + +uint32_t HELPER(mvcs)(uint64_t l, uint64_t a1, uint64_t a2) +{ + HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", + __FUNCTION__, l, a1, a2); + + return mvc_asc(l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY); +} + +uint32_t HELPER(mvcp)(uint64_t l, uint64_t a1, uint64_t a2) +{ + HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", + __FUNCTION__, l, a1, a2); + + return mvc_asc(l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY); +} + +uint32_t HELPER(sigp)(uint64_t order_code, uint32_t r1, uint64_t cpu_addr) +{ + int cc = 0; + + HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n", + __FUNCTION__, order_code, r1, cpu_addr); + + /* Remember: Use "R1 or R1+1, whichever is the odd-numbered register" + as parameter (input). Status (output) is always R1. */ + + switch (order_code) { + case SIGP_SET_ARCH: + /* switch arch */ + break; + case SIGP_SENSE: + /* enumerate CPU status */ + if (cpu_addr) { + /* XXX implement when SMP comes */ + return 3; + } + env->regs[r1] &= 0xffffffff00000000ULL; + cc = 1; + break; + default: + /* unknown sigp */ + fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code); + cc = 3; + } + + return cc; +} + +void HELPER(sacf)(uint64_t a1) +{ + HELPER_LOG("%s: %16" PRIx64 "\n", __FUNCTION__, a1); + + switch (a1 & 0xf00) { + case 0x000: + env->psw.mask &= ~PSW_MASK_ASC; + env->psw.mask |= PSW_ASC_PRIMARY; + break; + case 0x100: + env->psw.mask &= ~PSW_MASK_ASC; + env->psw.mask |= PSW_ASC_SECONDARY; + break; + case 0x300: + env->psw.mask &= ~PSW_MASK_ASC; + env->psw.mask |= PSW_ASC_HOME; + break; + default: + qemu_log("unknown sacf mode: %" PRIx64 "\n", a1); + program_interrupt(env, PGM_SPECIFICATION, 2); + break; + } +} + +/* invalidate pte */ +void HELPER(ipte)(uint64_t pte_addr, uint64_t vaddr) +{ + uint64_t page = vaddr & TARGET_PAGE_MASK; + uint64_t pte = 0; + + /* XXX broadcast to other CPUs */ + + /* XXX Linux is nice enough to give us the exact pte address. + According to spec we'd have to find it out ourselves */ + /* XXX Linux is fine with overwriting the pte, the spec requires + us to only set the invalid bit */ + stq_phys(pte_addr, pte | _PAGE_INVALID); + + /* XXX we exploit the fact that Linux passes the exact virtual + address here - it's not obliged to! */ + tlb_flush_page(env, page); +} + +/* flush local tlb */ +void HELPER(ptlb)(void) +{ + tlb_flush(env, 1); +} + +/* store using real address */ +void HELPER(stura)(uint64_t addr, uint32_t v1) +{ + stw_phys(get_address(0, 0, addr), v1); +} + +/* load real address */ +uint32_t HELPER(lra)(uint64_t addr, uint32_t r1) +{ + uint32_t cc = 0; + int old_exc = env->exception_index; + uint64_t asc = env->psw.mask & PSW_MASK_ASC; + uint64_t ret; + int flags; + + /* XXX incomplete - has more corner cases */ + if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) { + program_interrupt(env, PGM_SPECIAL_OP, 2); + } + + env->exception_index = old_exc; + if (mmu_translate(env, addr, 0, asc, &ret, &flags)) { + cc = 3; + } + if (env->exception_index == EXCP_PGM) { + ret = env->int_pgm_code | 0x80000000; + } else { + ret |= addr & ~TARGET_PAGE_MASK; + } + env->exception_index = old_exc; + + if (!(env->psw.mask & PSW_MASK_64)) { + env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | (ret & 0xffffffffULL); + } else { + env->regs[r1] = ret; + } + + return cc; +} + +#endif diff -Nru qemu-kvm-0.14.1+noroms/target-s390x/translate.c qemu-kvm-0.15.0+noroms/target-s390x/translate.c --- qemu-kvm-0.14.1+noroms/target-s390x/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-s390x/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -2,6 +2,7 @@ * S/390 translation * * Copyright (c) 2009 Ulrich Hecht + * 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 @@ -16,46 +17,5229 @@ * 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 + +/* #define DEBUG_ILLEGAL_INSTRUCTIONS */ +/* #define DEBUG_INLINE_BRANCHES */ +#define S390X_DEBUG_DISAS +/* #define S390X_DEBUG_DISAS_VERBOSE */ + +#ifdef S390X_DEBUG_DISAS_VERBOSE +# define LOG_DISAS(...) qemu_log(__VA_ARGS__) +#else +# define LOG_DISAS(...) do { } while (0) +#endif #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "qemu-log.h" +/* global register indexes */ +static TCGv_ptr cpu_env; + +#include "gen-icount.h" +#include "helpers.h" +#define GEN_HELPER 1 +#include "helpers.h" + +typedef struct DisasContext DisasContext; +struct DisasContext { + uint64_t pc; + int is_jmp; + enum cc_op cc_op; + struct TranslationBlock *tb; +}; + +#define DISAS_EXCP 4 + +static void gen_op_calc_cc(DisasContext *s); + +#ifdef DEBUG_INLINE_BRANCHES +static uint64_t inline_branch_hit[CC_OP_MAX]; +static uint64_t inline_branch_miss[CC_OP_MAX]; +#endif + +static inline void debug_insn(uint64_t insn) +{ + LOG_DISAS("insn: 0x%" PRIx64 "\n", insn); +} + +static inline uint64_t pc_to_link_info(DisasContext *s, uint64_t pc) +{ + if (!(s->tb->flags & FLAG_MASK_64)) { + if (s->tb->flags & FLAG_MASK_32) { + return pc | 0x80000000; + } + } + return pc; +} + void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i; + + for (i = 0; i < 16; i++) { + cpu_fprintf(f, "R%02d=%016" PRIx64, i, env->regs[i]); + if ((i % 4) == 3) { + cpu_fprintf(f, "\n"); + } else { + cpu_fprintf(f, " "); + } + } + for (i = 0; i < 16; i++) { - cpu_fprintf(f, "R%02d=%016lx", i, env->regs[i]); + cpu_fprintf(f, "F%02d=%016" PRIx64, i, *(uint64_t *)&env->fregs[i]); if ((i % 4) == 3) { cpu_fprintf(f, "\n"); } else { cpu_fprintf(f, " "); } } + + cpu_fprintf(f, "\n"); + +#ifndef CONFIG_USER_ONLY for (i = 0; i < 16; i++) { - cpu_fprintf(f, "F%02d=%016lx", i, (long)env->fregs[i].i); + cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]); if ((i % 4) == 3) { cpu_fprintf(f, "\n"); } else { cpu_fprintf(f, " "); } } - cpu_fprintf(f, "PSW=mask %016lx addr %016lx cc %02x\n", env->psw.mask, env->psw.addr, env->cc); +#endif + + cpu_fprintf(f, "\n"); + + if (env->cc_op > 3) { + cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n", + env->psw.mask, env->psw.addr, cc_name(env->cc_op)); + } else { + cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n", + env->psw.mask, env->psw.addr, env->cc_op); + } + +#ifdef DEBUG_INLINE_BRANCHES + for (i = 0; i < CC_OP_MAX; i++) { + cpu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i), + inline_branch_miss[i], inline_branch_hit[i]); + } +#endif } -void gen_intermediate_code (CPUState *env, struct TranslationBlock *tb) +static TCGv_i64 psw_addr; +static TCGv_i64 psw_mask; + +static TCGv_i32 cc_op; +static TCGv_i64 cc_src; +static TCGv_i64 cc_dst; +static TCGv_i64 cc_vr; + +static char cpu_reg_names[10*3 + 6*4]; +static TCGv_i64 regs[16]; + +static uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; + +void s390x_translate_init(void) +{ + int i; + size_t cpu_reg_names_size = sizeof(cpu_reg_names); + char *p; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + psw_addr = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, psw.addr), + "psw_addr"); + psw_mask = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, psw.mask), + "psw_mask"); + + cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, cc_op), + "cc_op"); + cc_src = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, cc_src), + "cc_src"); + cc_dst = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, cc_dst), + "cc_dst"); + cc_vr = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, cc_vr), + "cc_vr"); + + p = cpu_reg_names; + for (i = 0; i < 16; i++) { + snprintf(p, cpu_reg_names_size, "r%d", i); + regs[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, regs[i]), p); + p += (i < 10) ? 3 : 4; + cpu_reg_names_size -= (i < 10) ? 3 : 4; + } +} + +static inline TCGv_i64 load_reg(int reg) { + TCGv_i64 r = tcg_temp_new_i64(); + tcg_gen_mov_i64(r, regs[reg]); + return r; } -void gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb) +static inline TCGv_i64 load_freg(int reg) { + TCGv_i64 r = tcg_temp_new_i64(); + tcg_gen_ld_i64(r, cpu_env, offsetof(CPUState, fregs[reg].d)); + return r; } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +static inline TCGv_i32 load_freg32(int reg) { - env->psw.addr = gen_opc_pc[pc_pos]; + TCGv_i32 r = tcg_temp_new_i32(); + tcg_gen_ld_i32(r, cpu_env, offsetof(CPUState, fregs[reg].l.upper)); + return r; +} + +static inline TCGv_i32 load_reg32(int reg) +{ + TCGv_i32 r = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(r, regs[reg]); + return r; +} + +static inline TCGv_i64 load_reg32_i64(int reg) +{ + TCGv_i64 r = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(r, regs[reg]); + return r; +} + +static inline void store_reg(int reg, TCGv_i64 v) +{ + tcg_gen_mov_i64(regs[reg], v); +} + +static inline void store_freg(int reg, TCGv_i64 v) +{ + tcg_gen_st_i64(v, cpu_env, offsetof(CPUState, fregs[reg].d)); +} + +static inline void store_reg32(int reg, TCGv_i32 v) +{ +#if HOST_LONG_BITS == 32 + tcg_gen_mov_i32(TCGV_LOW(regs[reg]), v); +#else + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(tmp, v); + /* 32 bit register writes keep the upper half */ + tcg_gen_deposit_i64(regs[reg], regs[reg], tmp, 0, 32); + tcg_temp_free_i64(tmp); +#endif +} + +static inline void store_reg32_i64(int reg, TCGv_i64 v) +{ + /* 32 bit register writes keep the upper half */ +#if HOST_LONG_BITS == 32 + tcg_gen_mov_i32(TCGV_LOW(regs[reg]), TCGV_LOW(v)); +#else + tcg_gen_deposit_i64(regs[reg], regs[reg], v, 0, 32); +#endif +} + +static inline void store_reg16(int reg, TCGv_i32 v) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(tmp, v); + /* 16 bit register writes keep the upper bytes */ + tcg_gen_deposit_i64(regs[reg], regs[reg], tmp, 0, 16); + tcg_temp_free_i64(tmp); +} + +static inline void store_reg8(int reg, TCGv_i64 v) +{ + /* 8 bit register writes keep the upper bytes */ + tcg_gen_deposit_i64(regs[reg], regs[reg], v, 0, 8); +} + +static inline void store_freg32(int reg, TCGv_i32 v) +{ + tcg_gen_st_i32(v, cpu_env, offsetof(CPUState, fregs[reg].l.upper)); +} + +static inline void update_psw_addr(DisasContext *s) +{ + /* psw.addr */ + tcg_gen_movi_i64(psw_addr, s->pc); +} + +static inline void potential_page_fault(DisasContext *s) +{ +#ifndef CONFIG_USER_ONLY + update_psw_addr(s); + gen_op_calc_cc(s); +#endif +} + +static inline uint64_t ld_code2(uint64_t pc) +{ + return (uint64_t)lduw_code(pc); +} + +static inline uint64_t ld_code4(uint64_t pc) +{ + return (uint64_t)ldl_code(pc); +} + +static inline uint64_t ld_code6(uint64_t pc) +{ + uint64_t opc; + opc = (uint64_t)lduw_code(pc) << 32; + opc |= (uint64_t)(uint32_t)ldl_code(pc+2); + return opc; +} + +static inline int get_mem_index(DisasContext *s) +{ + switch (s->tb->flags & FLAG_MASK_ASC) { + case PSW_ASC_PRIMARY >> 32: + return 0; + case PSW_ASC_SECONDARY >> 32: + return 1; + case PSW_ASC_HOME >> 32: + return 2; + default: + tcg_abort(); + break; + } +} + +static inline void gen_debug(DisasContext *s) +{ + TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); + update_psw_addr(s); + gen_op_calc_cc(s); + gen_helper_exception(tmp); + tcg_temp_free_i32(tmp); + s->is_jmp = DISAS_EXCP; +} + +#ifdef CONFIG_USER_ONLY + +static void gen_illegal_opcode(DisasContext *s, int ilc) +{ + TCGv_i32 tmp = tcg_const_i32(EXCP_SPEC); + update_psw_addr(s); + gen_op_calc_cc(s); + gen_helper_exception(tmp); + tcg_temp_free_i32(tmp); + s->is_jmp = DISAS_EXCP; +} + +#else /* CONFIG_USER_ONLY */ + +static void debug_print_inst(DisasContext *s, int ilc) +{ +#ifdef DEBUG_ILLEGAL_INSTRUCTIONS + uint64_t inst = 0; + + switch (ilc & 3) { + case 1: + inst = ld_code2(s->pc); + break; + case 2: + inst = ld_code4(s->pc); + break; + case 3: + inst = ld_code6(s->pc); + break; + } + + fprintf(stderr, "Illegal instruction [%d at %016" PRIx64 "]: 0x%016" + PRIx64 "\n", ilc, s->pc, inst); +#endif +} + +static void gen_program_exception(DisasContext *s, int ilc, int code) +{ + TCGv_i32 tmp; + + debug_print_inst(s, ilc); + + /* remember what pgm exeption this was */ + tmp = tcg_const_i32(code); + tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, int_pgm_code)); + tcg_temp_free_i32(tmp); + + tmp = tcg_const_i32(ilc); + tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, int_pgm_ilc)); + tcg_temp_free_i32(tmp); + + /* advance past instruction */ + s->pc += (ilc * 2); + update_psw_addr(s); + + /* save off cc */ + gen_op_calc_cc(s); + + /* trigger exception */ + tmp = tcg_const_i32(EXCP_PGM); + gen_helper_exception(tmp); + tcg_temp_free_i32(tmp); + + /* end TB here */ + s->is_jmp = DISAS_EXCP; +} + + +static void gen_illegal_opcode(DisasContext *s, int ilc) +{ + gen_program_exception(s, ilc, PGM_SPECIFICATION); +} + +static void gen_privileged_exception(DisasContext *s, int ilc) +{ + gen_program_exception(s, ilc, PGM_PRIVILEGED); +} + +static void check_privileged(DisasContext *s, int ilc) +{ + if (s->tb->flags & (PSW_MASK_PSTATE >> 32)) { + gen_privileged_exception(s, ilc); + } +} + +#endif /* CONFIG_USER_ONLY */ + +static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) +{ + TCGv_i64 tmp; + + /* 31-bitify the immediate part; register contents are dealt with below */ + if (!(s->tb->flags & FLAG_MASK_64)) { + d2 &= 0x7fffffffUL; + } + + if (x2) { + if (d2) { + tmp = tcg_const_i64(d2); + tcg_gen_add_i64(tmp, tmp, regs[x2]); + } else { + tmp = load_reg(x2); + } + if (b2) { + tcg_gen_add_i64(tmp, tmp, regs[b2]); + } + } else if (b2) { + if (d2) { + tmp = tcg_const_i64(d2); + tcg_gen_add_i64(tmp, tmp, regs[b2]); + } else { + tmp = load_reg(b2); + } + } else { + tmp = tcg_const_i64(d2); + } + + /* 31-bit mode mask if there are values loaded from registers */ + if (!(s->tb->flags & FLAG_MASK_64) && (x2 || b2)) { + tcg_gen_andi_i64(tmp, tmp, 0x7fffffffUL); + } + + return tmp; +} + +static void gen_op_movi_cc(DisasContext *s, uint32_t val) +{ + s->cc_op = CC_OP_CONST0 + val; +} + +static void gen_op_update1_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 dst) +{ + tcg_gen_discard_i64(cc_src); + tcg_gen_mov_i64(cc_dst, dst); + tcg_gen_discard_i64(cc_vr); + s->cc_op = op; +} + +static void gen_op_update1_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 dst) +{ + tcg_gen_discard_i64(cc_src); + tcg_gen_extu_i32_i64(cc_dst, dst); + tcg_gen_discard_i64(cc_vr); + s->cc_op = op; +} + +static void gen_op_update2_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 src, + TCGv_i64 dst) +{ + tcg_gen_mov_i64(cc_src, src); + tcg_gen_mov_i64(cc_dst, dst); + tcg_gen_discard_i64(cc_vr); + s->cc_op = op; +} + +static void gen_op_update2_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 src, + TCGv_i32 dst) +{ + tcg_gen_extu_i32_i64(cc_src, src); + tcg_gen_extu_i32_i64(cc_dst, dst); + tcg_gen_discard_i64(cc_vr); + s->cc_op = op; +} + +static void gen_op_update3_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 src, + TCGv_i64 dst, TCGv_i64 vr) +{ + tcg_gen_mov_i64(cc_src, src); + tcg_gen_mov_i64(cc_dst, dst); + tcg_gen_mov_i64(cc_vr, vr); + s->cc_op = op; +} + +static void gen_op_update3_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 src, + TCGv_i32 dst, TCGv_i32 vr) +{ + tcg_gen_extu_i32_i64(cc_src, src); + tcg_gen_extu_i32_i64(cc_dst, dst); + tcg_gen_extu_i32_i64(cc_vr, vr); + s->cc_op = op; +} + +static inline void set_cc_nz_u32(DisasContext *s, TCGv_i32 val) +{ + gen_op_update1_cc_i32(s, CC_OP_NZ, val); +} + +static inline void set_cc_nz_u64(DisasContext *s, TCGv_i64 val) +{ + gen_op_update1_cc_i64(s, CC_OP_NZ, val); +} + +static inline void cmp_32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, + enum cc_op cond) +{ + gen_op_update2_cc_i32(s, cond, v1, v2); +} + +static inline void cmp_64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, + enum cc_op cond) +{ + gen_op_update2_cc_i64(s, cond, v1, v2); +} + +static inline void cmp_s32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2) +{ + cmp_32(s, v1, v2, CC_OP_LTGT_32); +} + +static inline void cmp_u32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2) +{ + cmp_32(s, v1, v2, CC_OP_LTUGTU_32); +} + +static inline void cmp_s32c(DisasContext *s, TCGv_i32 v1, int32_t v2) +{ + /* XXX optimize for the constant? put it in s? */ + TCGv_i32 tmp = tcg_const_i32(v2); + cmp_32(s, v1, tmp, CC_OP_LTGT_32); + tcg_temp_free_i32(tmp); +} + +static inline void cmp_u32c(DisasContext *s, TCGv_i32 v1, uint32_t v2) +{ + TCGv_i32 tmp = tcg_const_i32(v2); + cmp_32(s, v1, tmp, CC_OP_LTUGTU_32); + tcg_temp_free_i32(tmp); +} + +static inline void cmp_s64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2) +{ + cmp_64(s, v1, v2, CC_OP_LTGT_64); +} + +static inline void cmp_u64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2) +{ + cmp_64(s, v1, v2, CC_OP_LTUGTU_64); +} + +static inline void cmp_s64c(DisasContext *s, TCGv_i64 v1, int64_t v2) +{ + TCGv_i64 tmp = tcg_const_i64(v2); + cmp_s64(s, v1, tmp); + tcg_temp_free_i64(tmp); +} + +static inline void cmp_u64c(DisasContext *s, TCGv_i64 v1, uint64_t v2) +{ + TCGv_i64 tmp = tcg_const_i64(v2); + cmp_u64(s, v1, tmp); + tcg_temp_free_i64(tmp); +} + +static inline void set_cc_s32(DisasContext *s, TCGv_i32 val) +{ + gen_op_update1_cc_i32(s, CC_OP_LTGT0_32, val); +} + +static inline void set_cc_s64(DisasContext *s, TCGv_i64 val) +{ + gen_op_update1_cc_i64(s, CC_OP_LTGT0_64, val); +} + +static void set_cc_add64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, TCGv_i64 vr) +{ + gen_op_update3_cc_i64(s, CC_OP_ADD_64, v1, v2, vr); +} + +static void set_cc_addu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, + TCGv_i64 vr) +{ + gen_op_update3_cc_i64(s, CC_OP_ADDU_64, v1, v2, vr); +} + +static void set_cc_sub64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, TCGv_i64 vr) +{ + gen_op_update3_cc_i64(s, CC_OP_SUB_64, v1, v2, vr); +} + +static void set_cc_subu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, + TCGv_i64 vr) +{ + gen_op_update3_cc_i64(s, CC_OP_SUBU_64, v1, v2, vr); +} + +static void set_cc_abs64(DisasContext *s, TCGv_i64 v1) +{ + gen_op_update1_cc_i64(s, CC_OP_ABS_64, v1); +} + +static void set_cc_nabs64(DisasContext *s, TCGv_i64 v1) +{ + gen_op_update1_cc_i64(s, CC_OP_NABS_64, v1); +} + +static void set_cc_add32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, TCGv_i32 vr) +{ + gen_op_update3_cc_i32(s, CC_OP_ADD_32, v1, v2, vr); +} + +static void set_cc_addu32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, + TCGv_i32 vr) +{ + gen_op_update3_cc_i32(s, CC_OP_ADDU_32, v1, v2, vr); +} + +static void set_cc_sub32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, TCGv_i32 vr) +{ + gen_op_update3_cc_i32(s, CC_OP_SUB_32, v1, v2, vr); +} + +static void set_cc_subu32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, + TCGv_i32 vr) +{ + gen_op_update3_cc_i32(s, CC_OP_SUBU_32, v1, v2, vr); +} + +static void set_cc_abs32(DisasContext *s, TCGv_i32 v1) +{ + gen_op_update1_cc_i32(s, CC_OP_ABS_32, v1); +} + +static void set_cc_nabs32(DisasContext *s, TCGv_i32 v1) +{ + gen_op_update1_cc_i32(s, CC_OP_NABS_32, v1); +} + +static void set_cc_comp32(DisasContext *s, TCGv_i32 v1) +{ + gen_op_update1_cc_i32(s, CC_OP_COMP_32, v1); +} + +static void set_cc_comp64(DisasContext *s, TCGv_i64 v1) +{ + gen_op_update1_cc_i64(s, CC_OP_COMP_64, v1); +} + +static void set_cc_icm(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2) +{ + gen_op_update2_cc_i32(s, CC_OP_ICM, v1, v2); +} + +static void set_cc_cmp_f32_i64(DisasContext *s, TCGv_i32 v1, TCGv_i64 v2) +{ + tcg_gen_extu_i32_i64(cc_src, v1); + tcg_gen_mov_i64(cc_dst, v2); + tcg_gen_discard_i64(cc_vr); + s->cc_op = CC_OP_LTGT_F32; +} + +static void set_cc_nz_f32(DisasContext *s, TCGv_i32 v1) +{ + gen_op_update1_cc_i32(s, CC_OP_NZ_F32, v1); +} + +static inline void set_cc_nz_f64(DisasContext *s, TCGv_i64 v1) +{ + gen_op_update1_cc_i64(s, CC_OP_NZ_F64, v1); +} + +/* CC value is in env->cc_op */ +static inline void set_cc_static(DisasContext *s) +{ + tcg_gen_discard_i64(cc_src); + tcg_gen_discard_i64(cc_dst); + tcg_gen_discard_i64(cc_vr); + s->cc_op = CC_OP_STATIC; +} + +static inline void gen_op_set_cc_op(DisasContext *s) +{ + if (s->cc_op != CC_OP_DYNAMIC && s->cc_op != CC_OP_STATIC) { + tcg_gen_movi_i32(cc_op, s->cc_op); + } +} + +static inline void gen_update_cc_op(DisasContext *s) +{ + gen_op_set_cc_op(s); +} + +/* calculates cc into cc_op */ +static void gen_op_calc_cc(DisasContext *s) +{ + TCGv_i32 local_cc_op = tcg_const_i32(s->cc_op); + TCGv_i64 dummy = tcg_const_i64(0); + + switch (s->cc_op) { + case CC_OP_CONST0: + case CC_OP_CONST1: + case CC_OP_CONST2: + case CC_OP_CONST3: + /* s->cc_op is the cc value */ + tcg_gen_movi_i32(cc_op, s->cc_op - CC_OP_CONST0); + break; + case CC_OP_STATIC: + /* env->cc_op already is the cc value */ + break; + case CC_OP_NZ: + case CC_OP_ABS_64: + case CC_OP_NABS_64: + case CC_OP_ABS_32: + case CC_OP_NABS_32: + case CC_OP_LTGT0_32: + case CC_OP_LTGT0_64: + case CC_OP_COMP_32: + case CC_OP_COMP_64: + case CC_OP_NZ_F32: + case CC_OP_NZ_F64: + /* 1 argument */ + gen_helper_calc_cc(cc_op, local_cc_op, dummy, cc_dst, dummy); + break; + case CC_OP_ICM: + case CC_OP_LTGT_32: + case CC_OP_LTGT_64: + case CC_OP_LTUGTU_32: + case CC_OP_LTUGTU_64: + case CC_OP_TM_32: + case CC_OP_TM_64: + case CC_OP_LTGT_F32: + case CC_OP_LTGT_F64: + case CC_OP_SLAG: + /* 2 arguments */ + gen_helper_calc_cc(cc_op, local_cc_op, cc_src, cc_dst, dummy); + break; + case CC_OP_ADD_64: + case CC_OP_ADDU_64: + case CC_OP_SUB_64: + case CC_OP_SUBU_64: + case CC_OP_ADD_32: + case CC_OP_ADDU_32: + case CC_OP_SUB_32: + case CC_OP_SUBU_32: + /* 3 arguments */ + gen_helper_calc_cc(cc_op, local_cc_op, cc_src, cc_dst, cc_vr); + break; + case CC_OP_DYNAMIC: + /* unknown operation - assume 3 arguments and cc_op in env */ + gen_helper_calc_cc(cc_op, cc_op, cc_src, cc_dst, cc_vr); + break; + default: + tcg_abort(); + } + + tcg_temp_free_i32(local_cc_op); + + /* We now have cc in cc_op as constant */ + set_cc_static(s); +} + +static inline void decode_rr(DisasContext *s, uint64_t insn, int *r1, int *r2) +{ + debug_insn(insn); + + *r1 = (insn >> 4) & 0xf; + *r2 = insn & 0xf; +} + +static inline TCGv_i64 decode_rx(DisasContext *s, uint64_t insn, int *r1, + int *x2, int *b2, int *d2) +{ + debug_insn(insn); + + *r1 = (insn >> 20) & 0xf; + *x2 = (insn >> 16) & 0xf; + *b2 = (insn >> 12) & 0xf; + *d2 = insn & 0xfff; + + return get_address(s, *x2, *b2, *d2); +} + +static inline void decode_rs(DisasContext *s, uint64_t insn, int *r1, int *r3, + int *b2, int *d2) +{ + debug_insn(insn); + + *r1 = (insn >> 20) & 0xf; + /* aka m3 */ + *r3 = (insn >> 16) & 0xf; + *b2 = (insn >> 12) & 0xf; + *d2 = insn & 0xfff; +} + +static inline TCGv_i64 decode_si(DisasContext *s, uint64_t insn, int *i2, + int *b1, int *d1) +{ + debug_insn(insn); + + *i2 = (insn >> 16) & 0xff; + *b1 = (insn >> 12) & 0xf; + *d1 = insn & 0xfff; + + return get_address(s, 0, *b1, *d1); +} + +static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong pc) +{ + TranslationBlock *tb; + + gen_update_cc_op(s); + + tb = s->tb; + /* NOTE: we handle the case where the TB spans two pages here */ + if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) || + (pc & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK)) { + /* jump to same page: we can use a direct jump */ + tcg_gen_goto_tb(tb_num); + tcg_gen_movi_i64(psw_addr, pc); + tcg_gen_exit_tb((long)tb + tb_num); + } else { + /* jump to another page: currently not optimized */ + tcg_gen_movi_i64(psw_addr, pc); + tcg_gen_exit_tb(0); + } +} + +static inline void account_noninline_branch(DisasContext *s, int cc_op) +{ +#ifdef DEBUG_INLINE_BRANCHES + inline_branch_miss[cc_op]++; +#endif +} + +static inline void account_inline_branch(DisasContext *s) +{ +#ifdef DEBUG_INLINE_BRANCHES + inline_branch_hit[s->cc_op]++; +#endif +} + +static void gen_jcc(DisasContext *s, uint32_t mask, int skip) +{ + TCGv_i32 tmp, tmp2, r; + TCGv_i64 tmp64; + int old_cc_op; + + switch (s->cc_op) { + case CC_OP_LTGT0_32: + tmp = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, cc_dst); + switch (mask) { + case 0x8 | 0x4: /* dst <= 0 */ + tcg_gen_brcondi_i32(TCG_COND_GT, tmp, 0, skip); + break; + case 0x8 | 0x2: /* dst >= 0 */ + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, skip); + break; + case 0x8: /* dst == 0 */ + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip); + break; + case 0x7: /* dst != 0 */ + case 0x6: /* dst != 0 */ + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip); + break; + case 0x4: /* dst < 0 */ + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, skip); + break; + case 0x2: /* dst > 0 */ + tcg_gen_brcondi_i32(TCG_COND_LE, tmp, 0, skip); + break; + default: + tcg_temp_free_i32(tmp); + goto do_dynamic; + } + account_inline_branch(s); + tcg_temp_free_i32(tmp); + break; + case CC_OP_LTGT0_64: + switch (mask) { + case 0x8 | 0x4: /* dst <= 0 */ + tcg_gen_brcondi_i64(TCG_COND_GT, cc_dst, 0, skip); + break; + case 0x8 | 0x2: /* dst >= 0 */ + tcg_gen_brcondi_i64(TCG_COND_LT, cc_dst, 0, skip); + break; + case 0x8: /* dst == 0 */ + tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip); + break; + case 0x7: /* dst != 0 */ + case 0x6: /* dst != 0 */ + tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip); + break; + case 0x4: /* dst < 0 */ + tcg_gen_brcondi_i64(TCG_COND_GE, cc_dst, 0, skip); + break; + case 0x2: /* dst > 0 */ + tcg_gen_brcondi_i64(TCG_COND_LE, cc_dst, 0, skip); + break; + default: + goto do_dynamic; + } + account_inline_branch(s); + break; + case CC_OP_LTGT_32: + tmp = tcg_temp_new_i32(); + tmp2 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, cc_src); + tcg_gen_trunc_i64_i32(tmp2, cc_dst); + switch (mask) { + case 0x8 | 0x4: /* src <= dst */ + tcg_gen_brcond_i32(TCG_COND_GT, tmp, tmp2, skip); + break; + case 0x8 | 0x2: /* src >= dst */ + tcg_gen_brcond_i32(TCG_COND_LT, tmp, tmp2, skip); + break; + case 0x8: /* src == dst */ + tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip); + break; + case 0x7: /* src != dst */ + case 0x6: /* src != dst */ + tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip); + break; + case 0x4: /* src < dst */ + tcg_gen_brcond_i32(TCG_COND_GE, tmp, tmp2, skip); + break; + case 0x2: /* src > dst */ + tcg_gen_brcond_i32(TCG_COND_LE, tmp, tmp2, skip); + break; + default: + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + goto do_dynamic; + } + account_inline_branch(s); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + break; + case CC_OP_LTGT_64: + switch (mask) { + case 0x8 | 0x4: /* src <= dst */ + tcg_gen_brcond_i64(TCG_COND_GT, cc_src, cc_dst, skip); + break; + case 0x8 | 0x2: /* src >= dst */ + tcg_gen_brcond_i64(TCG_COND_LT, cc_src, cc_dst, skip); + break; + case 0x8: /* src == dst */ + tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip); + break; + case 0x7: /* src != dst */ + case 0x6: /* src != dst */ + tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip); + break; + case 0x4: /* src < dst */ + tcg_gen_brcond_i64(TCG_COND_GE, cc_src, cc_dst, skip); + break; + case 0x2: /* src > dst */ + tcg_gen_brcond_i64(TCG_COND_LE, cc_src, cc_dst, skip); + break; + default: + goto do_dynamic; + } + account_inline_branch(s); + break; + case CC_OP_LTUGTU_32: + tmp = tcg_temp_new_i32(); + tmp2 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, cc_src); + tcg_gen_trunc_i64_i32(tmp2, cc_dst); + switch (mask) { + case 0x8 | 0x4: /* src <= dst */ + tcg_gen_brcond_i32(TCG_COND_GTU, tmp, tmp2, skip); + break; + case 0x8 | 0x2: /* src >= dst */ + tcg_gen_brcond_i32(TCG_COND_LTU, tmp, tmp2, skip); + break; + case 0x8: /* src == dst */ + tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip); + break; + case 0x7: /* src != dst */ + case 0x6: /* src != dst */ + tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip); + break; + case 0x4: /* src < dst */ + tcg_gen_brcond_i32(TCG_COND_GEU, tmp, tmp2, skip); + break; + case 0x2: /* src > dst */ + tcg_gen_brcond_i32(TCG_COND_LEU, tmp, tmp2, skip); + break; + default: + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + goto do_dynamic; + } + account_inline_branch(s); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + break; + case CC_OP_LTUGTU_64: + switch (mask) { + case 0x8 | 0x4: /* src <= dst */ + tcg_gen_brcond_i64(TCG_COND_GTU, cc_src, cc_dst, skip); + break; + case 0x8 | 0x2: /* src >= dst */ + tcg_gen_brcond_i64(TCG_COND_LTU, cc_src, cc_dst, skip); + break; + case 0x8: /* src == dst */ + tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip); + break; + case 0x7: /* src != dst */ + case 0x6: /* src != dst */ + tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip); + break; + case 0x4: /* src < dst */ + tcg_gen_brcond_i64(TCG_COND_GEU, cc_src, cc_dst, skip); + break; + case 0x2: /* src > dst */ + tcg_gen_brcond_i64(TCG_COND_LEU, cc_src, cc_dst, skip); + break; + default: + goto do_dynamic; + } + account_inline_branch(s); + break; + case CC_OP_NZ: + switch (mask) { + /* dst == 0 || dst != 0 */ + case 0x8 | 0x4: + case 0x8 | 0x4 | 0x2: + case 0x8 | 0x4 | 0x2 | 0x1: + case 0x8 | 0x4 | 0x1: + break; + /* dst == 0 */ + case 0x8: + case 0x8 | 0x2: + case 0x8 | 0x2 | 0x1: + case 0x8 | 0x1: + tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip); + break; + /* dst != 0 */ + case 0x4: + case 0x4 | 0x2: + case 0x4 | 0x2 | 0x1: + case 0x4 | 0x1: + tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip); + break; + default: + goto do_dynamic; + } + account_inline_branch(s); + break; + case CC_OP_TM_32: + tmp = tcg_temp_new_i32(); + tmp2 = tcg_temp_new_i32(); + + tcg_gen_trunc_i64_i32(tmp, cc_src); + tcg_gen_trunc_i64_i32(tmp2, cc_dst); + tcg_gen_and_i32(tmp, tmp, tmp2); + switch (mask) { + case 0x8: /* val & mask == 0 */ + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip); + break; + case 0x4 | 0x2 | 0x1: /* val & mask != 0 */ + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip); + break; + default: + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + goto do_dynamic; + } + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + account_inline_branch(s); + break; + case CC_OP_TM_64: + tmp64 = tcg_temp_new_i64(); + + tcg_gen_and_i64(tmp64, cc_src, cc_dst); + switch (mask) { + case 0x8: /* val & mask == 0 */ + tcg_gen_brcondi_i64(TCG_COND_NE, tmp64, 0, skip); + break; + case 0x4 | 0x2 | 0x1: /* val & mask != 0 */ + tcg_gen_brcondi_i64(TCG_COND_EQ, tmp64, 0, skip); + break; + default: + tcg_temp_free_i64(tmp64); + goto do_dynamic; + } + tcg_temp_free_i64(tmp64); + account_inline_branch(s); + break; + case CC_OP_ICM: + switch (mask) { + case 0x8: /* val == 0 */ + tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip); + break; + case 0x4 | 0x2 | 0x1: /* val != 0 */ + case 0x4 | 0x2: /* val != 0 */ + tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip); + break; + default: + goto do_dynamic; + } + account_inline_branch(s); + break; + case CC_OP_STATIC: + old_cc_op = s->cc_op; + goto do_dynamic_nocccalc; + case CC_OP_DYNAMIC: + default: +do_dynamic: + old_cc_op = s->cc_op; + /* calculate cc value */ + gen_op_calc_cc(s); + +do_dynamic_nocccalc: + /* jump based on cc */ + account_noninline_branch(s, old_cc_op); + + switch (mask) { + case 0x8 | 0x4 | 0x2 | 0x1: + /* always true */ + break; + case 0x8 | 0x4 | 0x2: /* cc != 3 */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 3, skip); + break; + case 0x8 | 0x4 | 0x1: /* cc != 2 */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 2, skip); + break; + case 0x8 | 0x2 | 0x1: /* cc != 1 */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 1, skip); + break; + case 0x8 | 0x2: /* cc == 0 || cc == 2 */ + tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, cc_op, 1); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip); + tcg_temp_free_i32(tmp); + break; + case 0x8 | 0x4: /* cc < 2 */ + tcg_gen_brcondi_i32(TCG_COND_GEU, cc_op, 2, skip); + break; + case 0x8: /* cc == 0 */ + tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 0, skip); + break; + case 0x4 | 0x2 | 0x1: /* cc != 0 */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 0, skip); + break; + case 0x4 | 0x1: /* cc == 1 || cc == 3 */ + tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, cc_op, 1); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip); + tcg_temp_free_i32(tmp); + break; + case 0x4: /* cc == 1 */ + tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 1, skip); + break; + case 0x2 | 0x1: /* cc > 1 */ + tcg_gen_brcondi_i32(TCG_COND_LEU, cc_op, 1, skip); + break; + case 0x2: /* cc == 2 */ + tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 2, skip); + break; + case 0x1: /* cc == 3 */ + tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 3, skip); + break; + default: /* cc is masked by something else */ + tmp = tcg_const_i32(3); + /* 3 - cc */ + tcg_gen_sub_i32(tmp, tmp, cc_op); + tmp2 = tcg_const_i32(1); + /* 1 << (3 - cc) */ + tcg_gen_shl_i32(tmp2, tmp2, tmp); + r = tcg_const_i32(mask); + /* mask & (1 << (3 - cc)) */ + tcg_gen_and_i32(r, r, tmp2); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + + tcg_gen_brcondi_i32(TCG_COND_EQ, r, 0, skip); + tcg_temp_free_i32(r); + break; + } + break; + } +} + +static void gen_bcr(DisasContext *s, uint32_t mask, TCGv_i64 target, + uint64_t offset) +{ + int skip; + + if (mask == 0xf) { + /* unconditional */ + tcg_gen_mov_i64(psw_addr, target); + tcg_gen_exit_tb(0); + } else if (mask == 0) { + /* ignore cc and never match */ + gen_goto_tb(s, 0, offset + 2); + } else { + TCGv_i64 new_addr = tcg_temp_local_new_i64(); + + tcg_gen_mov_i64(new_addr, target); + skip = gen_new_label(); + gen_jcc(s, mask, skip); + tcg_gen_mov_i64(psw_addr, new_addr); + tcg_temp_free_i64(new_addr); + tcg_gen_exit_tb(0); + gen_set_label(skip); + tcg_temp_free_i64(new_addr); + gen_goto_tb(s, 1, offset + 2); + } +} + +static void gen_brc(uint32_t mask, DisasContext *s, int32_t offset) +{ + int skip; + + if (mask == 0xf) { + /* unconditional */ + gen_goto_tb(s, 0, s->pc + offset); + } else if (mask == 0) { + /* ignore cc and never match */ + gen_goto_tb(s, 0, s->pc + 4); + } else { + skip = gen_new_label(); + gen_jcc(s, mask, skip); + gen_goto_tb(s, 0, s->pc + offset); + gen_set_label(skip); + gen_goto_tb(s, 1, s->pc + 4); + } + s->is_jmp = DISAS_TB_JUMP; +} + +static void gen_op_mvc(DisasContext *s, int l, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 tmp, tmp2; + int i; + int l_memset = gen_new_label(); + int l_out = gen_new_label(); + TCGv_i64 dest = tcg_temp_local_new_i64(); + TCGv_i64 src = tcg_temp_local_new_i64(); + TCGv_i32 vl; + + /* Find out if we should use the inline version of mvc */ + switch (l) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 11: + case 15: + /* use inline */ + break; + default: + /* Fall back to helper */ + vl = tcg_const_i32(l); + potential_page_fault(s); + gen_helper_mvc(vl, s1, s2); + tcg_temp_free_i32(vl); + return; + } + + tcg_gen_mov_i64(dest, s1); + tcg_gen_mov_i64(src, s2); + + if (!(s->tb->flags & FLAG_MASK_64)) { + /* XXX what if we overflow while moving? */ + tcg_gen_andi_i64(dest, dest, 0x7fffffffUL); + tcg_gen_andi_i64(src, src, 0x7fffffffUL); + } + + tmp = tcg_temp_new_i64(); + tcg_gen_addi_i64(tmp, src, 1); + tcg_gen_brcond_i64(TCG_COND_EQ, dest, tmp, l_memset); + tcg_temp_free_i64(tmp); + + switch (l) { + case 0: + tmp = tcg_temp_new_i64(); + + tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s)); + tcg_gen_qemu_st8(tmp, dest, get_mem_index(s)); + + tcg_temp_free_i64(tmp); + break; + case 1: + tmp = tcg_temp_new_i64(); + + tcg_gen_qemu_ld16u(tmp, src, get_mem_index(s)); + tcg_gen_qemu_st16(tmp, dest, get_mem_index(s)); + + tcg_temp_free_i64(tmp); + break; + case 3: + tmp = tcg_temp_new_i64(); + + tcg_gen_qemu_ld32u(tmp, src, get_mem_index(s)); + tcg_gen_qemu_st32(tmp, dest, get_mem_index(s)); + + tcg_temp_free_i64(tmp); + break; + case 4: + tmp = tcg_temp_new_i64(); + tmp2 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld32u(tmp, src, get_mem_index(s)); + tcg_gen_addi_i64(src, src, 4); + tcg_gen_qemu_ld8u(tmp2, src, get_mem_index(s)); + tcg_gen_qemu_st32(tmp, dest, get_mem_index(s)); + tcg_gen_addi_i64(dest, dest, 4); + tcg_gen_qemu_st8(tmp2, dest, get_mem_index(s)); + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 7: + tmp = tcg_temp_new_i64(); + + tcg_gen_qemu_ld64(tmp, src, get_mem_index(s)); + tcg_gen_qemu_st64(tmp, dest, get_mem_index(s)); + + tcg_temp_free_i64(tmp); + break; + default: + /* The inline version can become too big for too uneven numbers, only + use it on known good lengths */ + tmp = tcg_temp_new_i64(); + tmp2 = tcg_const_i64(8); + for (i = 0; (i + 7) <= l; i += 8) { + tcg_gen_qemu_ld64(tmp, src, get_mem_index(s)); + tcg_gen_qemu_st64(tmp, dest, get_mem_index(s)); + + tcg_gen_add_i64(src, src, tmp2); + tcg_gen_add_i64(dest, dest, tmp2); + } + + tcg_temp_free_i64(tmp2); + tmp2 = tcg_const_i64(1); + + for (; i <= l; i++) { + tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s)); + tcg_gen_qemu_st8(tmp, dest, get_mem_index(s)); + + tcg_gen_add_i64(src, src, tmp2); + tcg_gen_add_i64(dest, dest, tmp2); + } + + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp); + break; + } + + tcg_gen_br(l_out); + + gen_set_label(l_memset); + /* memset case (dest == (src + 1)) */ + + tmp = tcg_temp_new_i64(); + tmp2 = tcg_temp_new_i64(); + /* fill tmp with the byte */ + tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s)); + tcg_gen_shli_i64(tmp2, tmp, 8); + tcg_gen_or_i64(tmp, tmp, tmp2); + tcg_gen_shli_i64(tmp2, tmp, 16); + tcg_gen_or_i64(tmp, tmp, tmp2); + tcg_gen_shli_i64(tmp2, tmp, 32); + tcg_gen_or_i64(tmp, tmp, tmp2); + tcg_temp_free_i64(tmp2); + + tmp2 = tcg_const_i64(8); + + for (i = 0; (i + 7) <= l; i += 8) { + tcg_gen_qemu_st64(tmp, dest, get_mem_index(s)); + tcg_gen_addi_i64(dest, dest, 8); + } + + tcg_temp_free_i64(tmp2); + tmp2 = tcg_const_i64(1); + + for (; i <= l; i++) { + tcg_gen_qemu_st8(tmp, dest, get_mem_index(s)); + tcg_gen_addi_i64(dest, dest, 1); + } + + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp); + + gen_set_label(l_out); + + tcg_temp_free(dest); + tcg_temp_free(src); +} + +static void gen_op_clc(DisasContext *s, int l, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 tmp; + TCGv_i64 tmp2; + TCGv_i32 vl; + + /* check for simple 32bit or 64bit match */ + switch (l) { + case 0: + tmp = tcg_temp_new_i64(); + tmp2 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld8u(tmp, s1, get_mem_index(s)); + tcg_gen_qemu_ld8u(tmp2, s2, get_mem_index(s)); + cmp_u64(s, tmp, tmp2); + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + return; + case 1: + tmp = tcg_temp_new_i64(); + tmp2 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld16u(tmp, s1, get_mem_index(s)); + tcg_gen_qemu_ld16u(tmp2, s2, get_mem_index(s)); + cmp_u64(s, tmp, tmp2); + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + return; + case 3: + tmp = tcg_temp_new_i64(); + tmp2 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld32u(tmp, s1, get_mem_index(s)); + tcg_gen_qemu_ld32u(tmp2, s2, get_mem_index(s)); + cmp_u64(s, tmp, tmp2); + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + return; + case 7: + tmp = tcg_temp_new_i64(); + tmp2 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld64(tmp, s1, get_mem_index(s)); + tcg_gen_qemu_ld64(tmp2, s2, get_mem_index(s)); + cmp_u64(s, tmp, tmp2); + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + return; + } + + potential_page_fault(s); + vl = tcg_const_i32(l); + gen_helper_clc(cc_op, vl, s1, s2); + tcg_temp_free_i32(vl); + set_cc_static(s); +} + +static void disas_e3(DisasContext* s, int op, int r1, int x2, int b2, int d2) +{ + TCGv_i64 addr, tmp, tmp2, tmp3, tmp4; + TCGv_i32 tmp32_1, tmp32_2, tmp32_3; + + LOG_DISAS("disas_e3: op 0x%x r1 %d x2 %d b2 %d d2 %d\n", + op, r1, x2, b2, d2); + addr = get_address(s, x2, b2, d2); + switch (op) { + case 0x2: /* LTG R1,D2(X2,B2) [RXY] */ + case 0x4: /* lg r1,d2(x2,b2) */ + tcg_gen_qemu_ld64(regs[r1], addr, get_mem_index(s)); + if (op == 0x2) { + set_cc_s64(s, regs[r1]); + } + break; + case 0x12: /* LT R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + store_reg32(r1, tmp32_1); + set_cc_s32(s, tmp32_1); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0xc: /* MSG R1,D2(X2,B2) [RXY] */ + case 0x1c: /* MSGF R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + if (op == 0xc) { + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + } else { + tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); + } + tcg_gen_mul_i64(regs[r1], regs[r1], tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0xd: /* DSG R1,D2(X2,B2) [RXY] */ + case 0x1d: /* DSGF R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + if (op == 0x1d) { + tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); + } else { + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + } + tmp4 = load_reg(r1 + 1); + tmp3 = tcg_temp_new_i64(); + tcg_gen_div_i64(tmp3, tmp4, tmp2); + store_reg(r1 + 1, tmp3); + tcg_gen_rem_i64(tmp3, tmp4, tmp2); + store_reg(r1, tmp3); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + tcg_temp_free_i64(tmp4); + break; + case 0x8: /* AG R1,D2(X2,B2) [RXY] */ + case 0xa: /* ALG R1,D2(X2,B2) [RXY] */ + case 0x18: /* AGF R1,D2(X2,B2) [RXY] */ + case 0x1a: /* ALGF R1,D2(X2,B2) [RXY] */ + if (op == 0x1a) { + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + } else if (op == 0x18) { + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); + } else { + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + } + tmp4 = load_reg(r1); + tmp3 = tcg_temp_new_i64(); + tcg_gen_add_i64(tmp3, tmp4, tmp2); + store_reg(r1, tmp3); + switch (op) { + case 0x8: + case 0x18: + set_cc_add64(s, tmp4, tmp2, tmp3); + break; + case 0xa: + case 0x1a: + set_cc_addu64(s, tmp4, tmp2, tmp3); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + tcg_temp_free_i64(tmp4); + break; + case 0x9: /* SG R1,D2(X2,B2) [RXY] */ + case 0xb: /* SLG R1,D2(X2,B2) [RXY] */ + case 0x19: /* SGF R1,D2(X2,B2) [RXY] */ + case 0x1b: /* SLGF R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + if (op == 0x19) { + tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); + } else if (op == 0x1b) { + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + } else { + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + } + tmp4 = load_reg(r1); + tmp3 = tcg_temp_new_i64(); + tcg_gen_sub_i64(tmp3, tmp4, tmp2); + store_reg(r1, tmp3); + switch (op) { + case 0x9: + case 0x19: + set_cc_sub64(s, tmp4, tmp2, tmp3); + break; + case 0xb: + case 0x1b: + set_cc_subu64(s, tmp4, tmp2, tmp3); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + tcg_temp_free_i64(tmp4); + break; + case 0xf: /* LRVG R1,D2(X2,B2) [RXE] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + tcg_gen_bswap64_i64(tmp2, tmp2); + store_reg(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x14: /* LGF R1,D2(X2,B2) [RXY] */ + case 0x16: /* LLGF R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + if (op == 0x14) { + tcg_gen_ext32s_i64(tmp2, tmp2); + } + store_reg(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x15: /* LGH R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld16s(tmp2, addr, get_mem_index(s)); + store_reg(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x17: /* LLGT R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + tcg_gen_andi_i64(tmp2, tmp2, 0x7fffffffULL); + store_reg(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x1e: /* LRV R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + tcg_temp_free_i64(tmp2); + tcg_gen_bswap32_i32(tmp32_1, tmp32_1); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x1f: /* LRVH R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_qemu_ld16u(tmp2, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + tcg_temp_free_i64(tmp2); + tcg_gen_bswap16_i32(tmp32_1, tmp32_1); + store_reg16(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x20: /* CG R1,D2(X2,B2) [RXY] */ + case 0x21: /* CLG R1,D2(X2,B2) */ + case 0x30: /* CGF R1,D2(X2,B2) [RXY] */ + case 0x31: /* CLGF R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + switch (op) { + case 0x20: + case 0x21: + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + break; + case 0x30: + tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); + break; + case 0x31: + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + break; + default: + tcg_abort(); + } + switch (op) { + case 0x20: + case 0x30: + cmp_s64(s, regs[r1], tmp2); + break; + case 0x21: + case 0x31: + cmp_u64(s, regs[r1], tmp2); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp2); + break; + case 0x24: /* stg r1, d2(x2,b2) */ + tcg_gen_qemu_st64(regs[r1], addr, get_mem_index(s)); + break; + case 0x3e: /* STRV R1,D2(X2,B2) [RXY] */ + tmp32_1 = load_reg32(r1); + tmp2 = tcg_temp_new_i64(); + tcg_gen_bswap32_i32(tmp32_1, tmp32_1); + tcg_gen_extu_i32_i64(tmp2, tmp32_1); + tcg_temp_free_i32(tmp32_1); + tcg_gen_qemu_st32(tmp2, addr, get_mem_index(s)); + tcg_temp_free_i64(tmp2); + break; + case 0x50: /* STY R1,D2(X2,B2) [RXY] */ + tmp32_1 = load_reg32(r1); + tmp2 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(tmp2, tmp32_1); + tcg_temp_free_i32(tmp32_1); + tcg_gen_qemu_st32(tmp2, addr, get_mem_index(s)); + tcg_temp_free_i64(tmp2); + break; + case 0x57: /* XY R1,D2(X2,B2) [RXY] */ + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + tcg_temp_free_i64(tmp2); + tcg_gen_xor_i32(tmp32_2, tmp32_1, tmp32_2); + store_reg32(r1, tmp32_2); + set_cc_nz_u32(s, tmp32_2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x58: /* LY R1,D2(X2,B2) [RXY] */ + tmp3 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(tmp3, addr, get_mem_index(s)); + store_reg32_i64(r1, tmp3); + tcg_temp_free_i64(tmp3); + break; + case 0x5a: /* AY R1,D2(X2,B2) [RXY] */ + case 0x5b: /* SY R1,D2(X2,B2) [RXY] */ + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp32_3 = tcg_temp_new_i32(); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + tcg_temp_free_i64(tmp2); + switch (op) { + case 0x5a: + tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); + break; + case 0x5b: + tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); + break; + default: + tcg_abort(); + } + store_reg32(r1, tmp32_3); + switch (op) { + case 0x5a: + set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x5b: + set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); + break; + default: + tcg_abort(); + } + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x71: /* LAY R1,D2(X2,B2) [RXY] */ + store_reg(r1, addr); + break; + case 0x72: /* STCY R1,D2(X2,B2) [RXY] */ + tmp32_1 = load_reg32(r1); + tmp2 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp2, tmp32_1); + tcg_gen_qemu_st8(tmp2, addr, get_mem_index(s)); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp2); + break; + case 0x73: /* ICY R1,D2(X2,B2) [RXY] */ + tmp3 = tcg_temp_new_i64(); + tcg_gen_qemu_ld8u(tmp3, addr, get_mem_index(s)); + store_reg8(r1, tmp3); + tcg_temp_free_i64(tmp3); + break; + case 0x76: /* LB R1,D2(X2,B2) [RXY] */ + case 0x77: /* LGB R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld8s(tmp2, addr, get_mem_index(s)); + switch (op) { + case 0x76: + tcg_gen_ext8s_i64(tmp2, tmp2); + store_reg32_i64(r1, tmp2); + break; + case 0x77: + tcg_gen_ext8s_i64(tmp2, tmp2); + store_reg(r1, tmp2); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp2); + break; + case 0x78: /* LHY R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld16s(tmp2, addr, get_mem_index(s)); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x80: /* NG R1,D2(X2,B2) [RXY] */ + case 0x81: /* OG R1,D2(X2,B2) [RXY] */ + case 0x82: /* XG R1,D2(X2,B2) [RXY] */ + tmp3 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp3, addr, get_mem_index(s)); + switch (op) { + case 0x80: + tcg_gen_and_i64(regs[r1], regs[r1], tmp3); + break; + case 0x81: + tcg_gen_or_i64(regs[r1], regs[r1], tmp3); + break; + case 0x82: + tcg_gen_xor_i64(regs[r1], regs[r1], tmp3); + break; + default: + tcg_abort(); + } + set_cc_nz_u64(s, regs[r1]); + tcg_temp_free_i64(tmp3); + break; + case 0x86: /* MLG R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_const_i32(r1); + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + gen_helper_mlg(tmp32_1, tmp2); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x87: /* DLG R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_const_i32(r1); + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + gen_helper_dlg(tmp32_1, tmp2); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x88: /* ALCG R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp3 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + /* XXX possible optimization point */ + gen_op_calc_cc(s); + tcg_gen_extu_i32_i64(tmp3, cc_op); + tcg_gen_shri_i64(tmp3, tmp3, 1); + tcg_gen_andi_i64(tmp3, tmp3, 1); + tcg_gen_add_i64(tmp3, tmp2, tmp3); + tcg_gen_add_i64(tmp3, regs[r1], tmp3); + store_reg(r1, tmp3); + set_cc_addu64(s, regs[r1], tmp2, tmp3); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x89: /* SLBG R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_const_i32(r1); + tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); + /* XXX possible optimization point */ + gen_op_calc_cc(s); + gen_helper_slbg(cc_op, cc_op, tmp32_1, regs[r1], tmp2); + set_cc_static(s); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x90: /* LLGC R1,D2(X2,B2) [RXY] */ + tcg_gen_qemu_ld8u(regs[r1], addr, get_mem_index(s)); + break; + case 0x91: /* LLGH R1,D2(X2,B2) [RXY] */ + tcg_gen_qemu_ld16u(regs[r1], addr, get_mem_index(s)); + break; + case 0x94: /* LLC R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld8u(tmp2, addr, get_mem_index(s)); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x95: /* LLH R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld16u(tmp2, addr, get_mem_index(s)); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x96: /* ML R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp3 = load_reg((r1 + 1) & 15); + tcg_gen_ext32u_i64(tmp3, tmp3); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + tcg_gen_mul_i64(tmp2, tmp2, tmp3); + store_reg32_i64((r1 + 1) & 15, tmp2); + tcg_gen_shri_i64(tmp2, tmp2, 32); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x97: /* DL R1,D2(X2,B2) [RXY] */ + /* reg(r1) = reg(r1, r1+1) % ld32(addr) */ + /* reg(r1+1) = reg(r1, r1+1) / ld32(addr) */ + tmp = load_reg(r1); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + tmp3 = load_reg((r1 + 1) & 15); + tcg_gen_ext32u_i64(tmp2, tmp2); + tcg_gen_ext32u_i64(tmp3, tmp3); + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_or_i64(tmp, tmp, tmp3); + + tcg_gen_rem_i64(tmp3, tmp, tmp2); + tcg_gen_div_i64(tmp, tmp, tmp2); + store_reg32_i64((r1 + 1) & 15, tmp); + store_reg32_i64(r1, tmp3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x98: /* ALC R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp32_3 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + /* XXX possible optimization point */ + gen_op_calc_cc(s); + gen_helper_addc_u32(tmp32_3, cc_op, tmp32_1, tmp32_2); + set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); + store_reg32(r1, tmp32_3); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x99: /* SLB R1,D2(X2,B2) [RXY] */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + /* XXX possible optimization point */ + gen_op_calc_cc(s); + gen_helper_slb(cc_op, cc_op, tmp32_1, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + default: + LOG_DISAS("illegal e3 operation 0x%x\n", op); + gen_illegal_opcode(s, 3); + break; + } + tcg_temp_free_i64(addr); +} + +#ifndef CONFIG_USER_ONLY +static void disas_e5(DisasContext* s, uint64_t insn) +{ + TCGv_i64 tmp, tmp2; + int op = (insn >> 32) & 0xff; + + tmp = get_address(s, 0, (insn >> 28) & 0xf, (insn >> 16) & 0xfff); + tmp2 = get_address(s, 0, (insn >> 12) & 0xf, insn & 0xfff); + + LOG_DISAS("disas_e5: insn %" PRIx64 "\n", insn); + switch (op) { + case 0x01: /* TPROT D1(B1),D2(B2) [SSE] */ + /* Test Protection */ + potential_page_fault(s); + gen_helper_tprot(cc_op, tmp, tmp2); + set_cc_static(s); + break; + default: + LOG_DISAS("illegal e5 operation 0x%x\n", op); + gen_illegal_opcode(s, 3); + break; + } + + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); +} +#endif + +static void disas_eb(DisasContext *s, int op, int r1, int r3, int b2, int d2) +{ + TCGv_i64 tmp, tmp2, tmp3, tmp4; + TCGv_i32 tmp32_1, tmp32_2; + int i, stm_len; + int ilc = 3; + + LOG_DISAS("disas_eb: op 0x%x r1 %d r3 %d b2 %d d2 0x%x\n", + op, r1, r3, b2, d2); + switch (op) { + case 0xc: /* SRLG R1,R3,D2(B2) [RSY] */ + case 0xd: /* SLLG R1,R3,D2(B2) [RSY] */ + case 0xa: /* SRAG R1,R3,D2(B2) [RSY] */ + case 0xb: /* SLAG R1,R3,D2(B2) [RSY] */ + case 0x1c: /* RLLG R1,R3,D2(B2) [RSY] */ + if (b2) { + tmp = get_address(s, 0, b2, d2); + tcg_gen_andi_i64(tmp, tmp, 0x3f); + } else { + tmp = tcg_const_i64(d2 & 0x3f); + } + switch (op) { + case 0xc: + tcg_gen_shr_i64(regs[r1], regs[r3], tmp); + break; + case 0xd: + tcg_gen_shl_i64(regs[r1], regs[r3], tmp); + break; + case 0xa: + tcg_gen_sar_i64(regs[r1], regs[r3], tmp); + break; + case 0xb: + tmp2 = tcg_temp_new_i64(); + tmp3 = tcg_temp_new_i64(); + gen_op_update2_cc_i64(s, CC_OP_SLAG, regs[r3], tmp); + tcg_gen_shl_i64(tmp2, regs[r3], tmp); + /* override sign bit with source sign */ + tcg_gen_andi_i64(tmp2, tmp2, ~0x8000000000000000ULL); + tcg_gen_andi_i64(tmp3, regs[r3], 0x8000000000000000ULL); + tcg_gen_or_i64(regs[r1], tmp2, tmp3); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x1c: + tcg_gen_rotl_i64(regs[r1], regs[r3], tmp); + break; + default: + tcg_abort(); + break; + } + if (op == 0xa) { + set_cc_s64(s, regs[r1]); + } + tcg_temp_free_i64(tmp); + break; + case 0x1d: /* RLL R1,R3,D2(B2) [RSY] */ + if (b2) { + tmp = get_address(s, 0, b2, d2); + tcg_gen_andi_i64(tmp, tmp, 0x3f); + } else { + tmp = tcg_const_i64(d2 & 0x3f); + } + tmp32_1 = tcg_temp_new_i32(); + tmp32_2 = load_reg32(r3); + tcg_gen_trunc_i64_i32(tmp32_1, tmp); + switch (op) { + case 0x1d: + tcg_gen_rotl_i32(tmp32_1, tmp32_2, tmp32_1); + break; + default: + tcg_abort(); + break; + } + store_reg32(r1, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x4: /* LMG R1,R3,D2(B2) [RSE] */ + case 0x24: /* STMG R1,R3,D2(B2) [RSE] */ + stm_len = 8; + goto do_mh; + case 0x26: /* STMH R1,R3,D2(B2) [RSE] */ + case 0x96: /* LMH R1,R3,D2(B2) [RSE] */ + stm_len = 4; +do_mh: + /* Apparently, unrolling lmg/stmg of any size gains performance - + even for very long ones... */ + tmp = get_address(s, 0, b2, d2); + tmp3 = tcg_const_i64(stm_len); + tmp4 = tcg_const_i64(op == 0x26 ? 32 : 4); + for (i = r1;; i = (i + 1) % 16) { + switch (op) { + case 0x4: + tcg_gen_qemu_ld64(regs[i], tmp, get_mem_index(s)); + break; + case 0x96: + tmp2 = tcg_temp_new_i64(); +#if HOST_LONG_BITS == 32 + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(TCGV_HIGH(regs[i]), tmp2); +#else + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_shl_i64(tmp2, tmp2, tmp4); + tcg_gen_ext32u_i64(regs[i], regs[i]); + tcg_gen_or_i64(regs[i], regs[i], tmp2); +#endif + tcg_temp_free_i64(tmp2); + break; + case 0x24: + tcg_gen_qemu_st64(regs[i], tmp, get_mem_index(s)); + break; + case 0x26: + tmp2 = tcg_temp_new_i64(); + tcg_gen_shr_i64(tmp2, regs[i], tmp4); + tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp2); + break; + default: + tcg_abort(); + } + if (i == r3) { + break; + } + tcg_gen_add_i64(tmp, tmp, tmp3); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp3); + tcg_temp_free_i64(tmp4); + break; + case 0x2c: /* STCMH R1,M3,D2(B2) [RSY] */ + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_stcmh(tmp32_1, tmp, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; +#ifndef CONFIG_USER_ONLY + case 0x2f: /* LCTLG R1,R3,D2(B2) [RSE] */ + /* Load Control */ + check_privileged(s, ilc); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_lctlg(tmp32_1, tmp, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x25: /* STCTG R1,R3,D2(B2) [RSE] */ + /* Store Control */ + check_privileged(s, ilc); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_stctg(tmp32_1, tmp, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; +#endif + case 0x30: /* CSG R1,R3,D2(B2) [RSY] */ + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + /* XXX rewrite in tcg */ + gen_helper_csg(cc_op, tmp32_1, tmp, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x3e: /* CDSG R1,R3,D2(B2) [RSY] */ + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + /* XXX rewrite in tcg */ + gen_helper_cdsg(cc_op, tmp32_1, tmp, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x51: /* TMY D1(B1),I2 [SIY] */ + tmp = get_address(s, 0, b2, d2); /* SIY -> this is the destination */ + tmp2 = tcg_const_i64((r1 << 4) | r3); + tcg_gen_qemu_ld8u(tmp, tmp, get_mem_index(s)); + /* yes, this is a 32 bit operation with 64 bit tcg registers, because + that incurs less conversions */ + cmp_64(s, tmp, tmp2, CC_OP_TM_32); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x52: /* MVIY D1(B1),I2 [SIY] */ + tmp = get_address(s, 0, b2, d2); /* SIY -> this is the destination */ + tmp2 = tcg_const_i64((r1 << 4) | r3); + tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x55: /* CLIY D1(B1),I2 [SIY] */ + tmp3 = get_address(s, 0, b2, d2); /* SIY -> this is the 1st operand */ + tmp = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_qemu_ld8u(tmp, tmp3, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp); + cmp_u32c(s, tmp32_1, (r1 << 4) | r3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp3); + tcg_temp_free_i32(tmp32_1); + break; + case 0x80: /* ICMH R1,M3,D2(B2) [RSY] */ + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + /* XXX split CC calculation out */ + gen_helper_icmh(cc_op, tmp32_1, tmp, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + default: + LOG_DISAS("illegal eb operation 0x%x\n", op); + gen_illegal_opcode(s, ilc); + break; + } +} + +static void disas_ed(DisasContext *s, int op, int r1, int x2, int b2, int d2, + int r1b) +{ + TCGv_i32 tmp_r1, tmp32; + TCGv_i64 addr, tmp; + addr = get_address(s, x2, b2, d2); + tmp_r1 = tcg_const_i32(r1); + switch (op) { + case 0x5: /* LXDB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_lxdb(tmp_r1, addr); + break; + case 0x9: /* CEB R1,D2(X2,B2) [RXE] */ + tmp = tcg_temp_new_i64(); + tmp32 = load_freg32(r1); + tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); + set_cc_cmp_f32_i64(s, tmp32, tmp); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32); + break; + case 0xa: /* AEB R1,D2(X2,B2) [RXE] */ + tmp = tcg_temp_new_i64(); + tmp32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32, tmp); + gen_helper_aeb(tmp_r1, tmp32); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32); + + tmp32 = load_freg32(r1); + set_cc_nz_f32(s, tmp32); + tcg_temp_free_i32(tmp32); + break; + case 0xb: /* SEB R1,D2(X2,B2) [RXE] */ + tmp = tcg_temp_new_i64(); + tmp32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32, tmp); + gen_helper_seb(tmp_r1, tmp32); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32); + + tmp32 = load_freg32(r1); + set_cc_nz_f32(s, tmp32); + tcg_temp_free_i32(tmp32); + break; + case 0xd: /* DEB R1,D2(X2,B2) [RXE] */ + tmp = tcg_temp_new_i64(); + tmp32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32, tmp); + gen_helper_deb(tmp_r1, tmp32); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32); + break; + case 0x10: /* TCEB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_tceb(cc_op, tmp_r1, addr); + set_cc_static(s); + break; + case 0x11: /* TCDB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_tcdb(cc_op, tmp_r1, addr); + set_cc_static(s); + break; + case 0x12: /* TCXB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_tcxb(cc_op, tmp_r1, addr); + set_cc_static(s); + break; + case 0x17: /* MEEB R1,D2(X2,B2) [RXE] */ + tmp = tcg_temp_new_i64(); + tmp32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32, tmp); + gen_helper_meeb(tmp_r1, tmp32); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32); + break; + case 0x19: /* CDB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_cdb(cc_op, tmp_r1, addr); + set_cc_static(s); + break; + case 0x1a: /* ADB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_adb(cc_op, tmp_r1, addr); + set_cc_static(s); + break; + case 0x1b: /* SDB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_sdb(cc_op, tmp_r1, addr); + set_cc_static(s); + break; + case 0x1c: /* MDB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_mdb(tmp_r1, addr); + break; + case 0x1d: /* DDB R1,D2(X2,B2) [RXE] */ + potential_page_fault(s); + gen_helper_ddb(tmp_r1, addr); + break; + case 0x1e: /* MADB R1,R3,D2(X2,B2) [RXF] */ + /* for RXF insns, r1 is R3 and r1b is R1 */ + tmp32 = tcg_const_i32(r1b); + potential_page_fault(s); + gen_helper_madb(tmp32, addr, tmp_r1); + tcg_temp_free_i32(tmp32); + break; + default: + LOG_DISAS("illegal ed operation 0x%x\n", op); + gen_illegal_opcode(s, 3); + return; + } + tcg_temp_free_i32(tmp_r1); + tcg_temp_free_i64(addr); +} + +static void disas_a5(DisasContext *s, int op, int r1, int i2) +{ + TCGv_i64 tmp, tmp2; + TCGv_i32 tmp32; + LOG_DISAS("disas_a5: op 0x%x r1 %d i2 0x%x\n", op, r1, i2); + switch (op) { + case 0x0: /* IIHH R1,I2 [RI] */ + tmp = tcg_const_i64(i2); + tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 48, 16); + tcg_temp_free_i64(tmp); + break; + case 0x1: /* IIHL R1,I2 [RI] */ + tmp = tcg_const_i64(i2); + tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 32, 16); + tcg_temp_free_i64(tmp); + break; + case 0x2: /* IILH R1,I2 [RI] */ + tmp = tcg_const_i64(i2); + tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 16, 16); + tcg_temp_free_i64(tmp); + break; + case 0x3: /* IILL R1,I2 [RI] */ + tmp = tcg_const_i64(i2); + tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 0, 16); + tcg_temp_free_i64(tmp); + break; + case 0x4: /* NIHH R1,I2 [RI] */ + case 0x8: /* OIHH R1,I2 [RI] */ + tmp = load_reg(r1); + tmp32 = tcg_temp_new_i32(); + switch (op) { + case 0x4: + tmp2 = tcg_const_i64((((uint64_t)i2) << 48) + | 0x0000ffffffffffffULL); + tcg_gen_and_i64(tmp, tmp, tmp2); + break; + case 0x8: + tmp2 = tcg_const_i64(((uint64_t)i2) << 48); + tcg_gen_or_i64(tmp, tmp, tmp2); + break; + default: + tcg_abort(); + } + store_reg(r1, tmp); + tcg_gen_shri_i64(tmp2, tmp, 48); + tcg_gen_trunc_i64_i32(tmp32, tmp2); + set_cc_nz_u32(s, tmp32); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32); + tcg_temp_free_i64(tmp); + break; + case 0x5: /* NIHL R1,I2 [RI] */ + case 0x9: /* OIHL R1,I2 [RI] */ + tmp = load_reg(r1); + tmp32 = tcg_temp_new_i32(); + switch (op) { + case 0x5: + tmp2 = tcg_const_i64((((uint64_t)i2) << 32) + | 0xffff0000ffffffffULL); + tcg_gen_and_i64(tmp, tmp, tmp2); + break; + case 0x9: + tmp2 = tcg_const_i64(((uint64_t)i2) << 32); + tcg_gen_or_i64(tmp, tmp, tmp2); + break; + default: + tcg_abort(); + } + store_reg(r1, tmp); + tcg_gen_shri_i64(tmp2, tmp, 32); + tcg_gen_trunc_i64_i32(tmp32, tmp2); + tcg_gen_andi_i32(tmp32, tmp32, 0xffff); + set_cc_nz_u32(s, tmp32); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32); + tcg_temp_free_i64(tmp); + break; + case 0x6: /* NILH R1,I2 [RI] */ + case 0xa: /* OILH R1,I2 [RI] */ + tmp = load_reg(r1); + tmp32 = tcg_temp_new_i32(); + switch (op) { + case 0x6: + tmp2 = tcg_const_i64((((uint64_t)i2) << 16) + | 0xffffffff0000ffffULL); + tcg_gen_and_i64(tmp, tmp, tmp2); + break; + case 0xa: + tmp2 = tcg_const_i64(((uint64_t)i2) << 16); + tcg_gen_or_i64(tmp, tmp, tmp2); + break; + default: + tcg_abort(); + } + store_reg(r1, tmp); + tcg_gen_shri_i64(tmp, tmp, 16); + tcg_gen_trunc_i64_i32(tmp32, tmp); + tcg_gen_andi_i32(tmp32, tmp32, 0xffff); + set_cc_nz_u32(s, tmp32); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32); + tcg_temp_free_i64(tmp); + break; + case 0x7: /* NILL R1,I2 [RI] */ + case 0xb: /* OILL R1,I2 [RI] */ + tmp = load_reg(r1); + tmp32 = tcg_temp_new_i32(); + switch (op) { + case 0x7: + tmp2 = tcg_const_i64(i2 | 0xffffffffffff0000ULL); + tcg_gen_and_i64(tmp, tmp, tmp2); + break; + case 0xb: + tmp2 = tcg_const_i64(i2); + tcg_gen_or_i64(tmp, tmp, tmp2); + break; + default: + tcg_abort(); + } + store_reg(r1, tmp); + tcg_gen_trunc_i64_i32(tmp32, tmp); + tcg_gen_andi_i32(tmp32, tmp32, 0xffff); + set_cc_nz_u32(s, tmp32); /* signedness should not matter here */ + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32); + tcg_temp_free_i64(tmp); + break; + case 0xc: /* LLIHH R1,I2 [RI] */ + tmp = tcg_const_i64( ((uint64_t)i2) << 48 ); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0xd: /* LLIHL R1,I2 [RI] */ + tmp = tcg_const_i64( ((uint64_t)i2) << 32 ); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0xe: /* LLILH R1,I2 [RI] */ + tmp = tcg_const_i64( ((uint64_t)i2) << 16 ); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0xf: /* LLILL R1,I2 [RI] */ + tmp = tcg_const_i64(i2); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + default: + LOG_DISAS("illegal a5 operation 0x%x\n", op); + gen_illegal_opcode(s, 2); + return; + } +} + +static void disas_a7(DisasContext *s, int op, int r1, int i2) +{ + TCGv_i64 tmp, tmp2; + TCGv_i32 tmp32_1, tmp32_2, tmp32_3; + int l1; + + LOG_DISAS("disas_a7: op 0x%x r1 %d i2 0x%x\n", op, r1, i2); + switch (op) { + case 0x0: /* TMLH or TMH R1,I2 [RI] */ + case 0x1: /* TMLL or TML R1,I2 [RI] */ + case 0x2: /* TMHH R1,I2 [RI] */ + case 0x3: /* TMHL R1,I2 [RI] */ + tmp = load_reg(r1); + tmp2 = tcg_const_i64((uint16_t)i2); + switch (op) { + case 0x0: + tcg_gen_shri_i64(tmp, tmp, 16); + break; + case 0x1: + break; + case 0x2: + tcg_gen_shri_i64(tmp, tmp, 48); + break; + case 0x3: + tcg_gen_shri_i64(tmp, tmp, 32); + break; + } + tcg_gen_andi_i64(tmp, tmp, 0xffff); + cmp_64(s, tmp, tmp2, CC_OP_TM_64); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x4: /* brc m1, i2 */ + gen_brc(r1, s, i2 * 2LL); + return; + case 0x5: /* BRAS R1,I2 [RI] */ + tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 4)); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + gen_goto_tb(s, 0, s->pc + i2 * 2LL); + s->is_jmp = DISAS_TB_JUMP; + break; + case 0x6: /* BRCT R1,I2 [RI] */ + tmp32_1 = load_reg32(r1); + tcg_gen_subi_i32(tmp32_1, tmp32_1, 1); + store_reg32(r1, tmp32_1); + gen_update_cc_op(s); + l1 = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp32_1, 0, l1); + gen_goto_tb(s, 0, s->pc + (i2 * 2LL)); + gen_set_label(l1); + gen_goto_tb(s, 1, s->pc + 4); + s->is_jmp = DISAS_TB_JUMP; + tcg_temp_free_i32(tmp32_1); + break; + case 0x7: /* BRCTG R1,I2 [RI] */ + tmp = load_reg(r1); + tcg_gen_subi_i64(tmp, tmp, 1); + store_reg(r1, tmp); + gen_update_cc_op(s); + l1 = gen_new_label(); + tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1); + gen_goto_tb(s, 0, s->pc + (i2 * 2LL)); + gen_set_label(l1); + gen_goto_tb(s, 1, s->pc + 4); + s->is_jmp = DISAS_TB_JUMP; + tcg_temp_free_i64(tmp); + break; + case 0x8: /* lhi r1, i2 */ + tmp32_1 = tcg_const_i32(i2); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x9: /* lghi r1, i2 */ + tmp = tcg_const_i64(i2); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0xa: /* AHI R1,I2 [RI] */ + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp32_3 = tcg_const_i32(i2); + + if (i2 < 0) { + tcg_gen_subi_i32(tmp32_2, tmp32_1, -i2); + } else { + tcg_gen_add_i32(tmp32_2, tmp32_1, tmp32_3); + } + + store_reg32(r1, tmp32_2); + set_cc_add32(s, tmp32_1, tmp32_3, tmp32_2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0xb: /* aghi r1, i2 */ + tmp = load_reg(r1); + tmp2 = tcg_const_i64(i2); + + if (i2 < 0) { + tcg_gen_subi_i64(regs[r1], tmp, -i2); + } else { + tcg_gen_add_i64(regs[r1], tmp, tmp2); + } + set_cc_add64(s, tmp, tmp2, regs[r1]); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0xc: /* MHI R1,I2 [RI] */ + tmp32_1 = load_reg32(r1); + tcg_gen_muli_i32(tmp32_1, tmp32_1, i2); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0xd: /* MGHI R1,I2 [RI] */ + tmp = load_reg(r1); + tcg_gen_muli_i64(tmp, tmp, i2); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0xe: /* CHI R1,I2 [RI] */ + tmp32_1 = load_reg32(r1); + cmp_s32c(s, tmp32_1, i2); + tcg_temp_free_i32(tmp32_1); + break; + case 0xf: /* CGHI R1,I2 [RI] */ + tmp = load_reg(r1); + cmp_s64c(s, tmp, i2); + tcg_temp_free_i64(tmp); + break; + default: + LOG_DISAS("illegal a7 operation 0x%x\n", op); + gen_illegal_opcode(s, 2); + return; + } +} + +static void disas_b2(DisasContext *s, int op, uint32_t insn) +{ + TCGv_i64 tmp, tmp2, tmp3; + TCGv_i32 tmp32_1, tmp32_2, tmp32_3; + int r1, r2; + int ilc = 2; +#ifndef CONFIG_USER_ONLY + int r3, d2, b2; +#endif + + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + + LOG_DISAS("disas_b2: op 0x%x r1 %d r2 %d\n", op, r1, r2); + + switch (op) { + case 0x22: /* IPM R1 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + gen_op_calc_cc(s); + gen_helper_ipm(cc_op, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x41: /* CKSM R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r2); + potential_page_fault(s); + gen_helper_cksm(tmp32_1, tmp32_2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + gen_op_movi_cc(s, 0); + break; + case 0x4e: /* SAR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, aregs[r1])); + tcg_temp_free_i32(tmp32_1); + break; + case 0x4f: /* EAR R1,R2 [RRE] */ + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, aregs[r2])); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x52: /* MSR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r2); + tcg_gen_mul_i32(tmp32_1, tmp32_1, tmp32_2); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x54: /* MVPG R1,R2 [RRE] */ + tmp = load_reg(0); + tmp2 = load_reg(r1); + tmp3 = load_reg(r2); + potential_page_fault(s); + gen_helper_mvpg(tmp, tmp2, tmp3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + /* XXX check CCO bit and set CC accordingly */ + gen_op_movi_cc(s, 0); + break; + case 0x55: /* MVST R1,R2 [RRE] */ + tmp32_1 = load_reg32(0); + tmp32_2 = tcg_const_i32(r1); + tmp32_3 = tcg_const_i32(r2); + potential_page_fault(s); + gen_helper_mvst(tmp32_1, tmp32_2, tmp32_3); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + gen_op_movi_cc(s, 1); + break; + case 0x5d: /* CLST R1,R2 [RRE] */ + tmp32_1 = load_reg32(0); + tmp32_2 = tcg_const_i32(r1); + tmp32_3 = tcg_const_i32(r2); + potential_page_fault(s); + gen_helper_clst(cc_op, tmp32_1, tmp32_2, tmp32_3); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x5e: /* SRST R1,R2 [RRE] */ + tmp32_1 = load_reg32(0); + tmp32_2 = tcg_const_i32(r1); + tmp32_3 = tcg_const_i32(r2); + potential_page_fault(s); + gen_helper_srst(cc_op, tmp32_1, tmp32_2, tmp32_3); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + +#ifndef CONFIG_USER_ONLY + case 0x02: /* STIDP D2(B2) [S] */ + /* Store CPU ID */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_stidp(tmp); + tcg_temp_free_i64(tmp); + break; + case 0x04: /* SCK D2(B2) [S] */ + /* Set Clock */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_sck(cc_op, tmp); + set_cc_static(s); + tcg_temp_free_i64(tmp); + break; + case 0x05: /* STCK D2(B2) [S] */ + /* Store Clock */ + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_stck(cc_op, tmp); + set_cc_static(s); + tcg_temp_free_i64(tmp); + break; + case 0x06: /* SCKC D2(B2) [S] */ + /* Set Clock Comparator */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_sckc(tmp); + tcg_temp_free_i64(tmp); + break; + case 0x07: /* STCKC D2(B2) [S] */ + /* Store Clock Comparator */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_stckc(tmp); + tcg_temp_free_i64(tmp); + break; + case 0x08: /* SPT D2(B2) [S] */ + /* Set CPU Timer */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_spt(tmp); + tcg_temp_free_i64(tmp); + break; + case 0x09: /* STPT D2(B2) [S] */ + /* Store CPU Timer */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_stpt(tmp); + tcg_temp_free_i64(tmp); + break; + case 0x0a: /* SPKA D2(B2) [S] */ + /* Set PSW Key from Address */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_andi_i64(tmp2, psw_mask, ~PSW_MASK_KEY); + tcg_gen_shli_i64(tmp, tmp, PSW_SHIFT_KEY - 4); + tcg_gen_or_i64(psw_mask, tmp2, tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp); + break; + case 0x0d: /* PTLB [S] */ + /* Purge TLB */ + check_privileged(s, ilc); + gen_helper_ptlb(); + break; + case 0x10: /* SPX D2(B2) [S] */ + /* Set Prefix Register */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_spx(tmp); + tcg_temp_free_i64(tmp); + break; + case 0x11: /* STPX D2(B2) [S] */ + /* Store Prefix */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp2, cpu_env, offsetof(CPUState, psa)); + tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x12: /* STAP D2(B2) [S] */ + /* Store CPU Address */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, cpu_num)); + tcg_gen_extu_i32_i64(tmp2, tmp32_1); + tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x21: /* IPTE R1,R2 [RRE] */ + /* Invalidate PTE */ + check_privileged(s, ilc); + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + tmp = load_reg(r1); + tmp2 = load_reg(r2); + gen_helper_ipte(tmp, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x29: /* ISKE R1,R2 [RRE] */ + /* Insert Storage Key Extended */ + check_privileged(s, ilc); + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + tmp = load_reg(r2); + tmp2 = tcg_temp_new_i64(); + gen_helper_iske(tmp2, tmp); + store_reg(r1, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x2a: /* RRBE R1,R2 [RRE] */ + /* Set Storage Key Extended */ + check_privileged(s, ilc); + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + tmp32_1 = load_reg32(r1); + tmp = load_reg(r2); + gen_helper_rrbe(cc_op, tmp32_1, tmp); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0x2b: /* SSKE R1,R2 [RRE] */ + /* Set Storage Key Extended */ + check_privileged(s, ilc); + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + tmp32_1 = load_reg32(r1); + tmp = load_reg(r2); + gen_helper_sske(tmp32_1, tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0x34: /* STCH ? */ + /* Store Subchannel */ + check_privileged(s, ilc); + gen_op_movi_cc(s, 3); + break; + case 0x46: /* STURA R1,R2 [RRE] */ + /* Store Using Real Address */ + check_privileged(s, ilc); + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + tmp32_1 = load_reg32(r1); + tmp = load_reg(r2); + potential_page_fault(s); + gen_helper_stura(tmp, tmp32_1); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0x50: /* CSP R1,R2 [RRE] */ + /* Compare And Swap And Purge */ + check_privileged(s, ilc); + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r2); + gen_helper_csp(cc_op, tmp32_1, tmp32_2); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x5f: /* CHSC ? */ + /* Channel Subsystem Call */ + check_privileged(s, ilc); + gen_op_movi_cc(s, 3); + break; + case 0x78: /* STCKE D2(B2) [S] */ + /* Store Clock Extended */ + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_stcke(cc_op, tmp); + set_cc_static(s); + tcg_temp_free_i64(tmp); + break; + case 0x79: /* SACF D2(B2) [S] */ + /* Store Clock Extended */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + potential_page_fault(s); + gen_helper_sacf(tmp); + tcg_temp_free_i64(tmp); + /* addressing mode has changed, so end the block */ + s->pc += ilc * 2; + update_psw_addr(s); + s->is_jmp = DISAS_EXCP; + break; + case 0x7d: /* STSI D2,(B2) [S] */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = load_reg32(0); + tmp32_2 = load_reg32(1); + potential_page_fault(s); + gen_helper_stsi(cc_op, tmp, tmp32_1, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x9d: /* LFPC D2(B2) [S] */ + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0xb1: /* STFL D2(B2) [S] */ + /* Store Facility List (CPU features) at 200 */ + check_privileged(s, ilc); + tmp2 = tcg_const_i64(0xc0000000); + tmp = tcg_const_i64(200); + tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp); + break; + case 0xb2: /* LPSWE D2(B2) [S] */ + /* Load PSW Extended */ + check_privileged(s, ilc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tmp3 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp2, tmp, get_mem_index(s)); + tcg_gen_addi_i64(tmp, tmp, 8); + tcg_gen_qemu_ld64(tmp3, tmp, get_mem_index(s)); + gen_helper_load_psw(tmp2, tmp3); + /* we need to keep cc_op intact */ + s->is_jmp = DISAS_JUMP; + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x20: /* SERVC R1,R2 [RRE] */ + /* SCLP Service call (PV hypercall) */ + check_privileged(s, ilc); + potential_page_fault(s); + tmp32_1 = load_reg32(r2); + tmp = load_reg(r1); + gen_helper_servc(cc_op, tmp32_1, tmp); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; +#endif + default: + LOG_DISAS("illegal b2 operation 0x%x\n", op); + gen_illegal_opcode(s, ilc); + break; + } +} + +static void disas_b3(DisasContext *s, int op, int m3, int r1, int r2) +{ + TCGv_i64 tmp; + TCGv_i32 tmp32_1, tmp32_2, tmp32_3; + LOG_DISAS("disas_b3: op 0x%x m3 0x%x r1 %d r2 %d\n", op, m3, r1, r2); +#define FP_HELPER(i) \ + tmp32_1 = tcg_const_i32(r1); \ + tmp32_2 = tcg_const_i32(r2); \ + gen_helper_ ## i (tmp32_1, tmp32_2); \ + tcg_temp_free_i32(tmp32_1); \ + tcg_temp_free_i32(tmp32_2); + +#define FP_HELPER_CC(i) \ + tmp32_1 = tcg_const_i32(r1); \ + tmp32_2 = tcg_const_i32(r2); \ + gen_helper_ ## i (cc_op, tmp32_1, tmp32_2); \ + set_cc_static(s); \ + tcg_temp_free_i32(tmp32_1); \ + tcg_temp_free_i32(tmp32_2); + + switch (op) { + case 0x0: /* LPEBR R1,R2 [RRE] */ + FP_HELPER_CC(lpebr); + break; + case 0x2: /* LTEBR R1,R2 [RRE] */ + FP_HELPER_CC(ltebr); + break; + case 0x3: /* LCEBR R1,R2 [RRE] */ + FP_HELPER_CC(lcebr); + break; + case 0x4: /* LDEBR R1,R2 [RRE] */ + FP_HELPER(ldebr); + break; + case 0x5: /* LXDBR R1,R2 [RRE] */ + FP_HELPER(lxdbr); + break; + case 0x9: /* CEBR R1,R2 [RRE] */ + FP_HELPER_CC(cebr); + break; + case 0xa: /* AEBR R1,R2 [RRE] */ + FP_HELPER_CC(aebr); + break; + case 0xb: /* SEBR R1,R2 [RRE] */ + FP_HELPER_CC(sebr); + break; + case 0xd: /* DEBR R1,R2 [RRE] */ + FP_HELPER(debr); + break; + case 0x10: /* LPDBR R1,R2 [RRE] */ + FP_HELPER_CC(lpdbr); + break; + case 0x12: /* LTDBR R1,R2 [RRE] */ + FP_HELPER_CC(ltdbr); + break; + case 0x13: /* LCDBR R1,R2 [RRE] */ + FP_HELPER_CC(lcdbr); + break; + case 0x15: /* SQBDR R1,R2 [RRE] */ + FP_HELPER(sqdbr); + break; + case 0x17: /* MEEBR R1,R2 [RRE] */ + FP_HELPER(meebr); + break; + case 0x19: /* CDBR R1,R2 [RRE] */ + FP_HELPER_CC(cdbr); + break; + case 0x1a: /* ADBR R1,R2 [RRE] */ + FP_HELPER_CC(adbr); + break; + case 0x1b: /* SDBR R1,R2 [RRE] */ + FP_HELPER_CC(sdbr); + break; + case 0x1c: /* MDBR R1,R2 [RRE] */ + FP_HELPER(mdbr); + break; + case 0x1d: /* DDBR R1,R2 [RRE] */ + FP_HELPER(ddbr); + break; + case 0xe: /* MAEBR R1,R3,R2 [RRF] */ + case 0x1e: /* MADBR R1,R3,R2 [RRF] */ + case 0x1f: /* MSDBR R1,R3,R2 [RRF] */ + /* for RRF insns, m3 is R1, r1 is R3, and r2 is R2 */ + tmp32_1 = tcg_const_i32(m3); + tmp32_2 = tcg_const_i32(r2); + tmp32_3 = tcg_const_i32(r1); + switch (op) { + case 0xe: + gen_helper_maebr(tmp32_1, tmp32_3, tmp32_2); + break; + case 0x1e: + gen_helper_madbr(tmp32_1, tmp32_3, tmp32_2); + break; + case 0x1f: + gen_helper_msdbr(tmp32_1, tmp32_3, tmp32_2); + break; + default: + tcg_abort(); + } + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x40: /* LPXBR R1,R2 [RRE] */ + FP_HELPER_CC(lpxbr); + break; + case 0x42: /* LTXBR R1,R2 [RRE] */ + FP_HELPER_CC(ltxbr); + break; + case 0x43: /* LCXBR R1,R2 [RRE] */ + FP_HELPER_CC(lcxbr); + break; + case 0x44: /* LEDBR R1,R2 [RRE] */ + FP_HELPER(ledbr); + break; + case 0x45: /* LDXBR R1,R2 [RRE] */ + FP_HELPER(ldxbr); + break; + case 0x46: /* LEXBR R1,R2 [RRE] */ + FP_HELPER(lexbr); + break; + case 0x49: /* CXBR R1,R2 [RRE] */ + FP_HELPER_CC(cxbr); + break; + case 0x4a: /* AXBR R1,R2 [RRE] */ + FP_HELPER_CC(axbr); + break; + case 0x4b: /* SXBR R1,R2 [RRE] */ + FP_HELPER_CC(sxbr); + break; + case 0x4c: /* MXBR R1,R2 [RRE] */ + FP_HELPER(mxbr); + break; + case 0x4d: /* DXBR R1,R2 [RRE] */ + FP_HELPER(dxbr); + break; + case 0x65: /* LXR R1,R2 [RRE] */ + tmp = load_freg(r2); + store_freg(r1, tmp); + tcg_temp_free_i64(tmp); + tmp = load_freg(r2 + 2); + store_freg(r1 + 2, tmp); + tcg_temp_free_i64(tmp); + break; + case 0x74: /* LZER R1 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + gen_helper_lzer(tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x75: /* LZDR R1 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + gen_helper_lzdr(tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x76: /* LZXR R1 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + gen_helper_lzxr(tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x84: /* SFPC R1 [RRE] */ + tmp32_1 = load_reg32(r1); + tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc)); + tcg_temp_free_i32(tmp32_1); + break; + case 0x8c: /* EFPC R1 [RRE] */ + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc)); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x94: /* CEFBR R1,R2 [RRE] */ + case 0x95: /* CDFBR R1,R2 [RRE] */ + case 0x96: /* CXFBR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = load_reg32(r2); + switch (op) { + case 0x94: + gen_helper_cefbr(tmp32_1, tmp32_2); + break; + case 0x95: + gen_helper_cdfbr(tmp32_1, tmp32_2); + break; + case 0x96: + gen_helper_cxfbr(tmp32_1, tmp32_2); + break; + default: + tcg_abort(); + } + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x98: /* CFEBR R1,R2 [RRE] */ + case 0x99: /* CFDBR R1,R2 [RRE] */ + case 0x9a: /* CFXBR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r2); + tmp32_3 = tcg_const_i32(m3); + switch (op) { + case 0x98: + gen_helper_cfebr(cc_op, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x99: + gen_helper_cfdbr(cc_op, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x9a: + gen_helper_cfxbr(cc_op, tmp32_1, tmp32_2, tmp32_3); + break; + default: + tcg_abort(); + } + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0xa4: /* CEGBR R1,R2 [RRE] */ + case 0xa5: /* CDGBR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp = load_reg(r2); + switch (op) { + case 0xa4: + gen_helper_cegbr(tmp32_1, tmp); + break; + case 0xa5: + gen_helper_cdgbr(tmp32_1, tmp); + break; + default: + tcg_abort(); + } + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0xa6: /* CXGBR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp = load_reg(r2); + gen_helper_cxgbr(tmp32_1, tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0xa8: /* CGEBR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r2); + tmp32_3 = tcg_const_i32(m3); + gen_helper_cgebr(cc_op, tmp32_1, tmp32_2, tmp32_3); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0xa9: /* CGDBR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r2); + tmp32_3 = tcg_const_i32(m3); + gen_helper_cgdbr(cc_op, tmp32_1, tmp32_2, tmp32_3); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0xaa: /* CGXBR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r2); + tmp32_3 = tcg_const_i32(m3); + gen_helper_cgxbr(cc_op, tmp32_1, tmp32_2, tmp32_3); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + default: + LOG_DISAS("illegal b3 operation 0x%x\n", op); + gen_illegal_opcode(s, 2); + break; + } + +#undef FP_HELPER_CC +#undef FP_HELPER +} + +static void disas_b9(DisasContext *s, int op, int r1, int r2) +{ + TCGv_i64 tmp, tmp2, tmp3; + TCGv_i32 tmp32_1, tmp32_2, tmp32_3; + + LOG_DISAS("disas_b9: op 0x%x r1 %d r2 %d\n", op, r1, r2); + switch (op) { + case 0x0: /* LPGR R1,R2 [RRE] */ + case 0x1: /* LNGR R1,R2 [RRE] */ + case 0x2: /* LTGR R1,R2 [RRE] */ + case 0x3: /* LCGR R1,R2 [RRE] */ + case 0x10: /* LPGFR R1,R2 [RRE] */ + case 0x11: /* LNFGR R1,R2 [RRE] */ + case 0x12: /* LTGFR R1,R2 [RRE] */ + case 0x13: /* LCGFR R1,R2 [RRE] */ + if (op & 0x10) { + tmp = load_reg32_i64(r2); + } else { + tmp = load_reg(r2); + } + switch (op & 0xf) { + case 0x0: /* LP?GR */ + set_cc_abs64(s, tmp); + gen_helper_abs_i64(tmp, tmp); + store_reg(r1, tmp); + break; + case 0x1: /* LN?GR */ + set_cc_nabs64(s, tmp); + gen_helper_nabs_i64(tmp, tmp); + store_reg(r1, tmp); + break; + case 0x2: /* LT?GR */ + if (r1 != r2) { + store_reg(r1, tmp); + } + set_cc_s64(s, tmp); + break; + case 0x3: /* LC?GR */ + tcg_gen_neg_i64(regs[r1], tmp); + set_cc_comp64(s, regs[r1]); + break; + } + tcg_temp_free_i64(tmp); + break; + case 0x4: /* LGR R1,R2 [RRE] */ + store_reg(r1, regs[r2]); + break; + case 0x6: /* LGBR R1,R2 [RRE] */ + tmp2 = load_reg(r2); + tcg_gen_ext8s_i64(tmp2, tmp2); + store_reg(r1, tmp2); + tcg_temp_free_i64(tmp2); + break; + case 0x8: /* AGR R1,R2 [RRE] */ + case 0xa: /* ALGR R1,R2 [RRE] */ + tmp = load_reg(r1); + tmp2 = load_reg(r2); + tmp3 = tcg_temp_new_i64(); + tcg_gen_add_i64(tmp3, tmp, tmp2); + store_reg(r1, tmp3); + switch (op) { + case 0x8: + set_cc_add64(s, tmp, tmp2, tmp3); + break; + case 0xa: + set_cc_addu64(s, tmp, tmp2, tmp3); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x9: /* SGR R1,R2 [RRE] */ + case 0xb: /* SLGR R1,R2 [RRE] */ + case 0x1b: /* SLGFR R1,R2 [RRE] */ + case 0x19: /* SGFR R1,R2 [RRE] */ + tmp = load_reg(r1); + switch (op) { + case 0x1b: + tmp32_1 = load_reg32(r2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(tmp2, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x19: + tmp32_1 = load_reg32(r2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp2, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + default: + tmp2 = load_reg(r2); + break; + } + tmp3 = tcg_temp_new_i64(); + tcg_gen_sub_i64(tmp3, tmp, tmp2); + store_reg(r1, tmp3); + switch (op) { + case 0x9: + case 0x19: + set_cc_sub64(s, tmp, tmp2, tmp3); + break; + case 0xb: + case 0x1b: + set_cc_subu64(s, tmp, tmp2, tmp3); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0xc: /* MSGR R1,R2 [RRE] */ + case 0x1c: /* MSGFR R1,R2 [RRE] */ + tmp = load_reg(r1); + tmp2 = load_reg(r2); + if (op == 0x1c) { + tcg_gen_ext32s_i64(tmp2, tmp2); + } + tcg_gen_mul_i64(tmp, tmp, tmp2); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0xd: /* DSGR R1,R2 [RRE] */ + case 0x1d: /* DSGFR R1,R2 [RRE] */ + tmp = load_reg(r1 + 1); + if (op == 0xd) { + tmp2 = load_reg(r2); + } else { + tmp32_1 = load_reg32(r2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp2, tmp32_1); + tcg_temp_free_i32(tmp32_1); + } + tmp3 = tcg_temp_new_i64(); + tcg_gen_div_i64(tmp3, tmp, tmp2); + store_reg(r1 + 1, tmp3); + tcg_gen_rem_i64(tmp3, tmp, tmp2); + store_reg(r1, tmp3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x14: /* LGFR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tmp = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp, tmp32_1); + store_reg(r1, tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0x16: /* LLGFR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tmp = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(tmp, tmp32_1); + store_reg(r1, tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0x17: /* LLGTR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tmp = tcg_temp_new_i64(); + tcg_gen_andi_i32(tmp32_1, tmp32_1, 0x7fffffffUL); + tcg_gen_extu_i32_i64(tmp, tmp32_1); + store_reg(r1, tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0x18: /* AGFR R1,R2 [RRE] */ + case 0x1a: /* ALGFR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tmp2 = tcg_temp_new_i64(); + if (op == 0x18) { + tcg_gen_ext_i32_i64(tmp2, tmp32_1); + } else { + tcg_gen_extu_i32_i64(tmp2, tmp32_1); + } + tcg_temp_free_i32(tmp32_1); + tmp = load_reg(r1); + tmp3 = tcg_temp_new_i64(); + tcg_gen_add_i64(tmp3, tmp, tmp2); + store_reg(r1, tmp3); + if (op == 0x18) { + set_cc_add64(s, tmp, tmp2, tmp3); + } else { + set_cc_addu64(s, tmp, tmp2, tmp3); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x0f: /* LRVGR R1,R2 [RRE] */ + tcg_gen_bswap64_i64(regs[r1], regs[r2]); + break; + case 0x1f: /* LRVR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tcg_gen_bswap32_i32(tmp32_1, tmp32_1); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x20: /* CGR R1,R2 [RRE] */ + case 0x30: /* CGFR R1,R2 [RRE] */ + tmp2 = load_reg(r2); + if (op == 0x30) { + tcg_gen_ext32s_i64(tmp2, tmp2); + } + tmp = load_reg(r1); + cmp_s64(s, tmp, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x21: /* CLGR R1,R2 [RRE] */ + case 0x31: /* CLGFR R1,R2 [RRE] */ + tmp2 = load_reg(r2); + if (op == 0x31) { + tcg_gen_ext32u_i64(tmp2, tmp2); + } + tmp = load_reg(r1); + cmp_u64(s, tmp, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x26: /* LBR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tcg_gen_ext8s_i32(tmp32_1, tmp32_1); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x27: /* LHR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tcg_gen_ext16s_i32(tmp32_1, tmp32_1); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x80: /* NGR R1,R2 [RRE] */ + case 0x81: /* OGR R1,R2 [RRE] */ + case 0x82: /* XGR R1,R2 [RRE] */ + tmp = load_reg(r1); + tmp2 = load_reg(r2); + switch (op) { + case 0x80: + tcg_gen_and_i64(tmp, tmp, tmp2); + break; + case 0x81: + tcg_gen_or_i64(tmp, tmp, tmp2); + break; + case 0x82: + tcg_gen_xor_i64(tmp, tmp, tmp2); + break; + default: + tcg_abort(); + } + store_reg(r1, tmp); + set_cc_nz_u64(s, tmp); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x83: /* FLOGR R1,R2 [RRE] */ + tmp = load_reg(r2); + tmp32_1 = tcg_const_i32(r1); + gen_helper_flogr(cc_op, tmp32_1, tmp); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + break; + case 0x84: /* LLGCR R1,R2 [RRE] */ + tmp = load_reg(r2); + tcg_gen_andi_i64(tmp, tmp, 0xff); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0x85: /* LLGHR R1,R2 [RRE] */ + tmp = load_reg(r2); + tcg_gen_andi_i64(tmp, tmp, 0xffff); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0x87: /* DLGR R1,R2 [RRE] */ + tmp32_1 = tcg_const_i32(r1); + tmp = load_reg(r2); + gen_helper_dlg(tmp32_1, tmp); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + break; + case 0x88: /* ALCGR R1,R2 [RRE] */ + tmp = load_reg(r1); + tmp2 = load_reg(r2); + tmp3 = tcg_temp_new_i64(); + gen_op_calc_cc(s); + tcg_gen_extu_i32_i64(tmp3, cc_op); + tcg_gen_shri_i64(tmp3, tmp3, 1); + tcg_gen_andi_i64(tmp3, tmp3, 1); + tcg_gen_add_i64(tmp3, tmp2, tmp3); + tcg_gen_add_i64(tmp3, tmp, tmp3); + store_reg(r1, tmp3); + set_cc_addu64(s, tmp, tmp2, tmp3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x89: /* SLBGR R1,R2 [RRE] */ + tmp = load_reg(r1); + tmp2 = load_reg(r2); + tmp32_1 = tcg_const_i32(r1); + gen_op_calc_cc(s); + gen_helper_slbg(cc_op, cc_op, tmp32_1, tmp, tmp2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x94: /* LLCR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tcg_gen_andi_i32(tmp32_1, tmp32_1, 0xff); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x95: /* LLHR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tcg_gen_andi_i32(tmp32_1, tmp32_1, 0xffff); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x96: /* MLR R1,R2 [RRE] */ + /* reg(r1, r1+1) = reg(r1+1) * reg(r2) */ + tmp2 = load_reg(r2); + tmp3 = load_reg((r1 + 1) & 15); + tcg_gen_ext32u_i64(tmp2, tmp2); + tcg_gen_ext32u_i64(tmp3, tmp3); + tcg_gen_mul_i64(tmp2, tmp2, tmp3); + store_reg32_i64((r1 + 1) & 15, tmp2); + tcg_gen_shri_i64(tmp2, tmp2, 32); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x97: /* DLR R1,R2 [RRE] */ + /* reg(r1) = reg(r1, r1+1) % reg(r2) */ + /* reg(r1+1) = reg(r1, r1+1) / reg(r2) */ + tmp = load_reg(r1); + tmp2 = load_reg(r2); + tmp3 = load_reg((r1 + 1) & 15); + tcg_gen_ext32u_i64(tmp2, tmp2); + tcg_gen_ext32u_i64(tmp3, tmp3); + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_or_i64(tmp, tmp, tmp3); + + tcg_gen_rem_i64(tmp3, tmp, tmp2); + tcg_gen_div_i64(tmp, tmp, tmp2); + store_reg32_i64((r1 + 1) & 15, tmp); + store_reg32_i64(r1, tmp3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x98: /* ALCR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r2); + tmp32_3 = tcg_temp_new_i32(); + /* XXX possible optimization point */ + gen_op_calc_cc(s); + gen_helper_addc_u32(tmp32_3, cc_op, tmp32_1, tmp32_2); + set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); + store_reg32(r1, tmp32_3); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x99: /* SLBR R1,R2 [RRE] */ + tmp32_1 = load_reg32(r2); + tmp32_2 = tcg_const_i32(r1); + gen_op_calc_cc(s); + gen_helper_slb(cc_op, cc_op, tmp32_2, tmp32_1); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + default: + LOG_DISAS("illegal b9 operation 0x%x\n", op); + gen_illegal_opcode(s, 2); + break; + } +} + +static void disas_c0(DisasContext *s, int op, int r1, int i2) +{ + TCGv_i64 tmp; + TCGv_i32 tmp32_1, tmp32_2; + uint64_t target = s->pc + i2 * 2LL; + int l1; + + LOG_DISAS("disas_c0: op 0x%x r1 %d i2 %d\n", op, r1, i2); + + switch (op) { + case 0: /* larl r1, i2 */ + tmp = tcg_const_i64(target); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0x1: /* LGFI R1,I2 [RIL] */ + tmp = tcg_const_i64((int64_t)i2); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0x4: /* BRCL M1,I2 [RIL] */ + /* m1 & (1 << (3 - cc)) */ + tmp32_1 = tcg_const_i32(3); + tmp32_2 = tcg_const_i32(1); + gen_op_calc_cc(s); + tcg_gen_sub_i32(tmp32_1, tmp32_1, cc_op); + tcg_gen_shl_i32(tmp32_2, tmp32_2, tmp32_1); + tcg_temp_free_i32(tmp32_1); + tmp32_1 = tcg_const_i32(r1); /* m1 == r1 */ + tcg_gen_and_i32(tmp32_1, tmp32_1, tmp32_2); + l1 = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp32_1, 0, l1); + gen_goto_tb(s, 0, target); + gen_set_label(l1); + gen_goto_tb(s, 1, s->pc + 6); + s->is_jmp = DISAS_TB_JUMP; + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x5: /* brasl r1, i2 */ + tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 6)); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + gen_goto_tb(s, 0, target); + s->is_jmp = DISAS_TB_JUMP; + break; + case 0x7: /* XILF R1,I2 [RIL] */ + case 0xb: /* NILF R1,I2 [RIL] */ + case 0xd: /* OILF R1,I2 [RIL] */ + tmp32_1 = load_reg32(r1); + switch (op) { + case 0x7: + tcg_gen_xori_i32(tmp32_1, tmp32_1, (uint32_t)i2); + break; + case 0xb: + tcg_gen_andi_i32(tmp32_1, tmp32_1, (uint32_t)i2); + break; + case 0xd: + tcg_gen_ori_i32(tmp32_1, tmp32_1, (uint32_t)i2); + break; + default: + tcg_abort(); + } + store_reg32(r1, tmp32_1); + set_cc_nz_u32(s, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x9: /* IILF R1,I2 [RIL] */ + tmp32_1 = tcg_const_i32((uint32_t)i2); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0xa: /* NIHF R1,I2 [RIL] */ + tmp = load_reg(r1); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_andi_i64(tmp, tmp, (((uint64_t)((uint32_t)i2)) << 32) + | 0xffffffffULL); + store_reg(r1, tmp); + tcg_gen_shri_i64(tmp, tmp, 32); + tcg_gen_trunc_i64_i32(tmp32_1, tmp); + set_cc_nz_u32(s, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + break; + case 0xe: /* LLIHF R1,I2 [RIL] */ + tmp = tcg_const_i64(((uint64_t)(uint32_t)i2) << 32); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0xf: /* LLILF R1,I2 [RIL] */ + tmp = tcg_const_i64((uint32_t)i2); + store_reg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + default: + LOG_DISAS("illegal c0 operation 0x%x\n", op); + gen_illegal_opcode(s, 3); + break; + } +} + +static void disas_c2(DisasContext *s, int op, int r1, int i2) +{ + TCGv_i64 tmp, tmp2, tmp3; + TCGv_i32 tmp32_1, tmp32_2, tmp32_3; + + switch (op) { + case 0x4: /* SLGFI R1,I2 [RIL] */ + case 0xa: /* ALGFI R1,I2 [RIL] */ + tmp = load_reg(r1); + tmp2 = tcg_const_i64((uint64_t)(uint32_t)i2); + tmp3 = tcg_temp_new_i64(); + switch (op) { + case 0x4: + tcg_gen_sub_i64(tmp3, tmp, tmp2); + set_cc_subu64(s, tmp, tmp2, tmp3); + break; + case 0xa: + tcg_gen_add_i64(tmp3, tmp, tmp2); + set_cc_addu64(s, tmp, tmp2, tmp3); + break; + default: + tcg_abort(); + } + store_reg(r1, tmp3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x5: /* SLFI R1,I2 [RIL] */ + case 0xb: /* ALFI R1,I2 [RIL] */ + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_const_i32(i2); + tmp32_3 = tcg_temp_new_i32(); + switch (op) { + case 0x5: + tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); + set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3); + break; + case 0xb: + tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); + set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); + break; + default: + tcg_abort(); + } + store_reg32(r1, tmp32_3); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0xc: /* CGFI R1,I2 [RIL] */ + tmp = load_reg(r1); + cmp_s64c(s, tmp, (int64_t)i2); + tcg_temp_free_i64(tmp); + break; + case 0xe: /* CLGFI R1,I2 [RIL] */ + tmp = load_reg(r1); + cmp_u64c(s, tmp, (uint64_t)(uint32_t)i2); + tcg_temp_free_i64(tmp); + break; + case 0xd: /* CFI R1,I2 [RIL] */ + tmp32_1 = load_reg32(r1); + cmp_s32c(s, tmp32_1, i2); + tcg_temp_free_i32(tmp32_1); + break; + case 0xf: /* CLFI R1,I2 [RIL] */ + tmp32_1 = load_reg32(r1); + cmp_u32c(s, tmp32_1, i2); + tcg_temp_free_i32(tmp32_1); + break; + default: + LOG_DISAS("illegal c2 operation 0x%x\n", op); + gen_illegal_opcode(s, 3); + break; + } +} + +static void gen_and_or_xor_i32(int opc, TCGv_i32 tmp, TCGv_i32 tmp2) +{ + switch (opc & 0xf) { + case 0x4: + tcg_gen_and_i32(tmp, tmp, tmp2); + break; + case 0x6: + tcg_gen_or_i32(tmp, tmp, tmp2); + break; + case 0x7: + tcg_gen_xor_i32(tmp, tmp, tmp2); + break; + default: + tcg_abort(); + } +} + +static void disas_s390_insn(DisasContext *s) +{ + TCGv_i64 tmp, tmp2, tmp3, tmp4; + TCGv_i32 tmp32_1, tmp32_2, tmp32_3, tmp32_4; + unsigned char opc; + uint64_t insn; + int op, r1, r2, r3, d1, d2, x2, b1, b2, i, i2, r1b; + TCGv_i32 vl; + int ilc; + int l1; + + opc = ldub_code(s->pc); + LOG_DISAS("opc 0x%x\n", opc); + + ilc = get_ilc(opc); + + switch (opc) { +#ifndef CONFIG_USER_ONLY + case 0x01: /* SAM */ + insn = ld_code2(s->pc); + /* set addressing mode, but we only do 64bit anyways */ + break; +#endif + case 0x6: /* BCTR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r1); + tcg_gen_subi_i32(tmp32_1, tmp32_1, 1); + store_reg32(r1, tmp32_1); + + if (r2) { + gen_update_cc_op(s); + l1 = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp32_1, 0, l1); + + /* not taking the branch, jump to after the instruction */ + gen_goto_tb(s, 0, s->pc + 2); + gen_set_label(l1); + + /* take the branch, move R2 into psw.addr */ + tmp32_1 = load_reg32(r2); + tmp = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(tmp, tmp32_1); + tcg_gen_mov_i64(psw_addr, tmp); + s->is_jmp = DISAS_JUMP; + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + } + break; + case 0x7: /* BCR M1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + if (r2) { + tmp = load_reg(r2); + gen_bcr(s, r1, tmp, s->pc); + tcg_temp_free_i64(tmp); + s->is_jmp = DISAS_TB_JUMP; + } else { + /* XXX: "serialization and checkpoint-synchronization function"? */ + } + break; + case 0xa: /* SVC I [RR] */ + insn = ld_code2(s->pc); + debug_insn(insn); + i = insn & 0xff; + update_psw_addr(s); + gen_op_calc_cc(s); + tmp32_1 = tcg_const_i32(i); + tmp32_2 = tcg_const_i32(ilc * 2); + tmp32_3 = tcg_const_i32(EXCP_SVC); + tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, int_svc_code)); + tcg_gen_st_i32(tmp32_2, cpu_env, offsetof(CPUState, int_svc_ilc)); + gen_helper_exception(tmp32_3); + s->is_jmp = DISAS_EXCP; + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0xd: /* BASR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 2)); + store_reg(r1, tmp); + if (r2) { + tmp2 = load_reg(r2); + tcg_gen_mov_i64(psw_addr, tmp2); + tcg_temp_free_i64(tmp2); + s->is_jmp = DISAS_JUMP; + } + tcg_temp_free_i64(tmp); + break; + case 0xe: /* MVCL R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r2); + potential_page_fault(s); + gen_helper_mvcl(cc_op, tmp32_1, tmp32_2); + set_cc_static(s); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x10: /* LPR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r2); + set_cc_abs32(s, tmp32_1); + gen_helper_abs_i32(tmp32_1, tmp32_1); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x11: /* LNR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r2); + set_cc_nabs32(s, tmp32_1); + gen_helper_nabs_i32(tmp32_1, tmp32_1); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x12: /* LTR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r2); + if (r1 != r2) { + store_reg32(r1, tmp32_1); + } + set_cc_s32(s, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x13: /* LCR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r2); + tcg_gen_neg_i32(tmp32_1, tmp32_1); + store_reg32(r1, tmp32_1); + set_cc_comp32(s, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x14: /* NR R1,R2 [RR] */ + case 0x16: /* OR R1,R2 [RR] */ + case 0x17: /* XR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_2 = load_reg32(r2); + tmp32_1 = load_reg32(r1); + gen_and_or_xor_i32(opc, tmp32_1, tmp32_2); + store_reg32(r1, tmp32_1); + set_cc_nz_u32(s, tmp32_1); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x18: /* LR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r2); + store_reg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x15: /* CLR R1,R2 [RR] */ + case 0x19: /* CR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r2); + if (opc == 0x15) { + cmp_u32(s, tmp32_1, tmp32_2); + } else { + cmp_s32(s, tmp32_1, tmp32_2); + } + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x1a: /* AR R1,R2 [RR] */ + case 0x1e: /* ALR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r2); + tmp32_3 = tcg_temp_new_i32(); + tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); + store_reg32(r1, tmp32_3); + if (opc == 0x1a) { + set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); + } else { + set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); + } + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x1b: /* SR R1,R2 [RR] */ + case 0x1f: /* SLR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r2); + tmp32_3 = tcg_temp_new_i32(); + tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); + store_reg32(r1, tmp32_3); + if (opc == 0x1b) { + set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); + } else { + set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3); + } + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x1c: /* MR R1,R2 [RR] */ + /* reg(r1, r1+1) = reg(r1+1) * reg(r2) */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp2 = load_reg(r2); + tmp3 = load_reg((r1 + 1) & 15); + tcg_gen_ext32s_i64(tmp2, tmp2); + tcg_gen_ext32s_i64(tmp3, tmp3); + tcg_gen_mul_i64(tmp2, tmp2, tmp3); + store_reg32_i64((r1 + 1) & 15, tmp2); + tcg_gen_shri_i64(tmp2, tmp2, 32); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x1d: /* DR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r1 + 1); + tmp32_3 = load_reg32(r2); + + tmp = tcg_temp_new_i64(); /* dividend */ + tmp2 = tcg_temp_new_i64(); /* divisor */ + tmp3 = tcg_temp_new_i64(); + + /* dividend is r(r1 << 32) | r(r1 + 1) */ + tcg_gen_extu_i32_i64(tmp, tmp32_1); + tcg_gen_extu_i32_i64(tmp2, tmp32_2); + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_or_i64(tmp, tmp, tmp2); + + /* divisor is r(r2) */ + tcg_gen_ext_i32_i64(tmp2, tmp32_3); + + tcg_gen_div_i64(tmp3, tmp, tmp2); + tcg_gen_rem_i64(tmp, tmp, tmp2); + + tcg_gen_trunc_i64_i32(tmp32_1, tmp); + tcg_gen_trunc_i64_i32(tmp32_2, tmp3); + + store_reg32(r1, tmp32_1); /* remainder */ + store_reg32(r1 + 1, tmp32_2); /* quotient */ + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x28: /* LDR R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp = load_freg(r2); + store_freg(r1, tmp); + tcg_temp_free_i64(tmp); + break; + case 0x38: /* LER R1,R2 [RR] */ + insn = ld_code2(s->pc); + decode_rr(s, insn, &r1, &r2); + tmp32_1 = load_freg32(r2); + store_freg32(r1, tmp32_1); + tcg_temp_free_i32(tmp32_1); + break; + case 0x40: /* STH R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = load_reg(r1); + tcg_gen_qemu_st16(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x41: /* la */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + store_reg(r1, tmp); /* FIXME: 31/24-bit addressing */ + tcg_temp_free_i64(tmp); + break; + case 0x42: /* STC R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = load_reg(r1); + tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x43: /* IC R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); + store_reg8(r1, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x44: /* EX R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = load_reg(r1); + tmp3 = tcg_const_i64(s->pc + 4); + update_psw_addr(s); + gen_op_calc_cc(s); + gen_helper_ex(cc_op, cc_op, tmp2, tmp, tmp3); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x46: /* BCT R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tcg_temp_free_i64(tmp); + + tmp32_1 = load_reg32(r1); + tcg_gen_subi_i32(tmp32_1, tmp32_1, 1); + store_reg32(r1, tmp32_1); + + gen_update_cc_op(s); + l1 = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp32_1, 0, l1); + + /* not taking the branch, jump to after the instruction */ + gen_goto_tb(s, 0, s->pc + 4); + gen_set_label(l1); + + /* take the branch, move R2 into psw.addr */ + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tcg_gen_mov_i64(psw_addr, tmp); + s->is_jmp = DISAS_JUMP; + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + break; + case 0x47: /* BC M1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + gen_bcr(s, r1, tmp, s->pc + 4); + tcg_temp_free_i64(tmp); + s->is_jmp = DISAS_TB_JUMP; + break; + case 0x48: /* LH R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s)); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x49: /* CH R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + cmp_s32(s, tmp32_1, tmp32_2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x4a: /* AH R1,D2(X2,B2) [RX] */ + case 0x4b: /* SH R1,D2(X2,B2) [RX] */ + case 0x4c: /* MH R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp32_3 = tcg_temp_new_i32(); + + tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + switch (opc) { + case 0x4a: + tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); + set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x4b: + tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); + set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x4c: + tcg_gen_mul_i32(tmp32_3, tmp32_1, tmp32_2); + break; + default: + tcg_abort(); + } + store_reg32(r1, tmp32_3); + + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x4d: /* BAS R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_const_i64(pc_to_link_info(s, s->pc + 4)); + store_reg(r1, tmp2); + tcg_gen_mov_i64(psw_addr, tmp); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + s->is_jmp = DISAS_JUMP; + break; + case 0x4e: /* CVD R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp32_1, regs[r1]); + gen_helper_cvd(tmp2, tmp32_1); + tcg_gen_qemu_st64(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x50: /* st r1, d2(x2, b2) */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = load_reg(r1); + tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x55: /* CL R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tmp32_2 = load_reg32(r1); + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + cmp_u32(s, tmp32_2, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x54: /* N R1,D2(X2,B2) [RX] */ + case 0x56: /* O R1,D2(X2,B2) [RX] */ + case 0x57: /* X R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + gen_and_or_xor_i32(opc, tmp32_1, tmp32_2); + store_reg32(r1, tmp32_1); + set_cc_nz_u32(s, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x58: /* l r1, d2(x2, b2) */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + store_reg32(r1, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x59: /* C R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tmp32_2 = load_reg32(r1); + tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + cmp_s32(s, tmp32_2, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x5a: /* A R1,D2(X2,B2) [RX] */ + case 0x5b: /* S R1,D2(X2,B2) [RX] */ + case 0x5e: /* AL R1,D2(X2,B2) [RX] */ + case 0x5f: /* SL R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp32_3 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32s(tmp, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp); + switch (opc) { + case 0x5a: + case 0x5e: + tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); + break; + case 0x5b: + case 0x5f: + tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); + break; + default: + tcg_abort(); + } + store_reg32(r1, tmp32_3); + switch (opc) { + case 0x5a: + set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x5e: + set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x5b: + set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); + break; + case 0x5f: + set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + break; + case 0x5c: /* M R1,D2(X2,B2) [RX] */ + /* reg(r1, r1+1) = reg(r1+1) * *(s32*)addr */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s)); + tmp3 = load_reg((r1 + 1) & 15); + tcg_gen_ext32s_i64(tmp2, tmp2); + tcg_gen_ext32s_i64(tmp3, tmp3); + tcg_gen_mul_i64(tmp2, tmp2, tmp3); + store_reg32_i64((r1 + 1) & 15, tmp2); + tcg_gen_shri_i64(tmp2, tmp2, 32); + store_reg32_i64(r1, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x5d: /* D R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp3 = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r1 + 1); + + tmp = tcg_temp_new_i64(); + tmp2 = tcg_temp_new_i64(); + + /* dividend is r(r1 << 32) | r(r1 + 1) */ + tcg_gen_extu_i32_i64(tmp, tmp32_1); + tcg_gen_extu_i32_i64(tmp2, tmp32_2); + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_or_i64(tmp, tmp, tmp2); + + /* divisor is in memory */ + tcg_gen_qemu_ld32s(tmp2, tmp3, get_mem_index(s)); + + /* XXX divisor == 0 -> FixP divide exception */ + + tcg_gen_div_i64(tmp3, tmp, tmp2); + tcg_gen_rem_i64(tmp, tmp, tmp2); + + tcg_gen_trunc_i64_i32(tmp32_1, tmp); + tcg_gen_trunc_i64_i32(tmp32_2, tmp3); + + store_reg32(r1, tmp32_1); /* remainder */ + store_reg32(r1 + 1, tmp32_2); /* quotient */ + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x60: /* STD R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = load_freg(r1); + tcg_gen_qemu_st64(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x68: /* LD R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp2, tmp, get_mem_index(s)); + store_freg(r1, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x70: /* STE R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = load_freg32(r1); + tcg_gen_extu_i32_i64(tmp2, tmp32_1); + tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0x71: /* MS R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + tcg_gen_mul_i32(tmp32_1, tmp32_1, tmp32_2); + store_reg32(r1, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x78: /* LE R1,D2(X2,B2) [RX] */ + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_1, tmp2); + store_freg32(r1, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; +#ifndef CONFIG_USER_ONLY + case 0x80: /* SSM D2(B2) [S] */ + /* Set System Mask */ + check_privileged(s, ilc); + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tmp3 = tcg_temp_new_i64(); + tcg_gen_andi_i64(tmp3, psw_mask, ~0xff00000000000000ULL); + tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); + tcg_gen_shli_i64(tmp2, tmp2, 56); + tcg_gen_or_i64(psw_mask, tmp3, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; + case 0x82: /* LPSW D2(B2) [S] */ + /* Load PSW */ + check_privileged(s, ilc); + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tmp3 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_addi_i64(tmp, tmp, 4); + tcg_gen_qemu_ld32u(tmp3, tmp, get_mem_index(s)); + gen_helper_load_psw(tmp2, tmp3); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + /* we need to keep cc_op intact */ + s->is_jmp = DISAS_JUMP; + break; + case 0x83: /* DIAG R1,R3,D2 [RS] */ + /* Diagnose call (KVM hypercall) */ + check_privileged(s, ilc); + potential_page_fault(s); + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp32_1 = tcg_const_i32(insn & 0xfff); + tmp2 = load_reg(2); + tmp3 = load_reg(1); + gen_helper_diag(tmp2, tmp32_1, tmp2, tmp3); + store_reg(2, tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; +#endif + case 0x88: /* SRL R1,D2(B2) [RS] */ + case 0x89: /* SLL R1,D2(B2) [RS] */ + case 0x8a: /* SRA R1,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp32_2, tmp); + tcg_gen_andi_i32(tmp32_2, tmp32_2, 0x3f); + switch (opc) { + case 0x88: + tcg_gen_shr_i32(tmp32_1, tmp32_1, tmp32_2); + break; + case 0x89: + tcg_gen_shl_i32(tmp32_1, tmp32_1, tmp32_2); + break; + case 0x8a: + tcg_gen_sar_i32(tmp32_1, tmp32_1, tmp32_2); + set_cc_s32(s, tmp32_1); + break; + default: + tcg_abort(); + } + store_reg32(r1, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x8c: /* SRDL R1,D2(B2) [RS] */ + case 0x8d: /* SLDL R1,D2(B2) [RS] */ + case 0x8e: /* SRDA R1,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); /* shift */ + tmp2 = tcg_temp_new_i64(); + tmp32_1 = load_reg32(r1); + tmp32_2 = load_reg32(r1 + 1); + tcg_gen_concat_i32_i64(tmp2, tmp32_2, tmp32_1); /* operand */ + switch (opc) { + case 0x8c: + tcg_gen_shr_i64(tmp2, tmp2, tmp); + break; + case 0x8d: + tcg_gen_shl_i64(tmp2, tmp2, tmp); + break; + case 0x8e: + tcg_gen_sar_i64(tmp2, tmp2, tmp); + set_cc_s64(s, tmp2); + break; + } + tcg_gen_shri_i64(tmp, tmp2, 32); + tcg_gen_trunc_i64_i32(tmp32_1, tmp); + store_reg32(r1, tmp32_1); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + store_reg32(r1 + 1, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x98: /* LM R1,R3,D2(B2) [RS] */ + case 0x90: /* STM R1,R3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tmp3 = tcg_const_i64(4); + tmp4 = tcg_const_i64(0xffffffff00000000ULL); + for (i = r1;; i = (i + 1) % 16) { + if (opc == 0x98) { + tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); + tcg_gen_and_i64(regs[i], regs[i], tmp4); + tcg_gen_or_i64(regs[i], regs[i], tmp2); + } else { + tcg_gen_qemu_st32(regs[i], tmp, get_mem_index(s)); + } + if (i == r3) { + break; + } + tcg_gen_add_i64(tmp, tmp, tmp3); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + tcg_temp_free_i64(tmp4); + break; + case 0x91: /* TM D1(B1),I2 [SI] */ + insn = ld_code4(s->pc); + tmp = decode_si(s, insn, &i2, &b1, &d1); + tmp2 = tcg_const_i64(i2); + tcg_gen_qemu_ld8u(tmp, tmp, get_mem_index(s)); + cmp_64(s, tmp, tmp2, CC_OP_TM_32); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x92: /* MVI D1(B1),I2 [SI] */ + insn = ld_code4(s->pc); + tmp = decode_si(s, insn, &i2, &b1, &d1); + tmp2 = tcg_const_i64(i2); + tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x94: /* NI D1(B1),I2 [SI] */ + case 0x96: /* OI D1(B1),I2 [SI] */ + case 0x97: /* XI D1(B1),I2 [SI] */ + insn = ld_code4(s->pc); + tmp = decode_si(s, insn, &i2, &b1, &d1); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); + switch (opc) { + case 0x94: + tcg_gen_andi_i64(tmp2, tmp2, i2); + break; + case 0x96: + tcg_gen_ori_i64(tmp2, tmp2, i2); + break; + case 0x97: + tcg_gen_xori_i64(tmp2, tmp2, i2); + break; + default: + tcg_abort(); + } + tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); + set_cc_nz_u64(s, tmp2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x95: /* CLI D1(B1),I2 [SI] */ + insn = ld_code4(s->pc); + tmp = decode_si(s, insn, &i2, &b1, &d1); + tmp2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); + cmp_u64c(s, tmp2, i2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0x9a: /* LAM R1,R3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_lam(tmp32_1, tmp, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0x9b: /* STAM R1,R3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_stam(tmp32_1, tmp, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0xa5: + insn = ld_code4(s->pc); + r1 = (insn >> 20) & 0xf; + op = (insn >> 16) & 0xf; + i2 = insn & 0xffff; + disas_a5(s, op, r1, i2); + break; + case 0xa7: + insn = ld_code4(s->pc); + r1 = (insn >> 20) & 0xf; + op = (insn >> 16) & 0xf; + i2 = (short)insn; + disas_a7(s, op, r1, i2); + break; + case 0xa8: /* MVCLE R1,R3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_mvcle(cc_op, tmp32_1, tmp, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0xa9: /* CLCLE R1,R3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_clcle(cc_op, tmp32_1, tmp, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; +#ifndef CONFIG_USER_ONLY + case 0xac: /* STNSM D1(B1),I2 [SI] */ + case 0xad: /* STOSM D1(B1),I2 [SI] */ + check_privileged(s, ilc); + insn = ld_code4(s->pc); + tmp = decode_si(s, insn, &i2, &b1, &d1); + tmp2 = tcg_temp_new_i64(); + tcg_gen_shri_i64(tmp2, psw_mask, 56); + tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); + if (opc == 0xac) { + tcg_gen_andi_i64(psw_mask, psw_mask, + ((uint64_t)i2 << 56) | 0x00ffffffffffffffULL); + } else { + tcg_gen_ori_i64(psw_mask, psw_mask, (uint64_t)i2 << 56); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + case 0xae: /* SIGP R1,R3,D2(B2) [RS] */ + check_privileged(s, ilc); + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp2 = load_reg(r3); + tmp32_1 = tcg_const_i32(r1); + potential_page_fault(s); + gen_helper_sigp(cc_op, tmp, tmp32_1, tmp2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + break; + case 0xb1: /* LRA R1,D2(X2, B2) [RX] */ + check_privileged(s, ilc); + insn = ld_code4(s->pc); + tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); + tmp32_1 = tcg_const_i32(r1); + potential_page_fault(s); + gen_helper_lra(cc_op, tmp, tmp32_1); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + break; +#endif + case 0xb2: + insn = ld_code4(s->pc); + op = (insn >> 16) & 0xff; + switch (op) { + case 0x9c: /* STFPC D2(B2) [S] */ + d2 = insn & 0xfff; + b2 = (insn >> 12) & 0xf; + tmp32_1 = tcg_temp_new_i32(); + tmp = tcg_temp_new_i64(); + tmp2 = get_address(s, 0, b2, d2); + tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc)); + tcg_gen_extu_i32_i64(tmp, tmp32_1); + tcg_gen_qemu_st32(tmp, tmp2, get_mem_index(s)); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; + default: + disas_b2(s, op, insn); + break; + } + break; + case 0xb3: + insn = ld_code4(s->pc); + op = (insn >> 16) & 0xff; + r3 = (insn >> 12) & 0xf; /* aka m3 */ + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + disas_b3(s, op, r3, r1, r2); + break; +#ifndef CONFIG_USER_ONLY + case 0xb6: /* STCTL R1,R3,D2(B2) [RS] */ + /* Store Control */ + check_privileged(s, ilc); + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_stctl(tmp32_1, tmp, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0xb7: /* LCTL R1,R3,D2(B2) [RS] */ + /* Load Control */ + check_privileged(s, ilc); + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_lctl(tmp32_1, tmp, tmp32_2); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; +#endif + case 0xb9: + insn = ld_code4(s->pc); + r1 = (insn >> 4) & 0xf; + r2 = insn & 0xf; + op = (insn >> 16) & 0xff; + disas_b9(s, op, r1, r2); + break; + case 0xba: /* CS R1,R3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_const_i32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_cs(cc_op, tmp32_1, tmp, tmp32_2); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0xbd: /* CLM R1,M3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_clm(cc_op, tmp32_1, tmp32_2, tmp); + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0xbe: /* STCM R1,M3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + tmp = get_address(s, 0, b2, d2); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_const_i32(r3); + potential_page_fault(s); + gen_helper_stcm(tmp32_1, tmp32_2, tmp); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + break; + case 0xbf: /* ICM R1,M3,D2(B2) [RS] */ + insn = ld_code4(s->pc); + decode_rs(s, insn, &r1, &r3, &b2, &d2); + if (r3 == 15) { + /* effectively a 32-bit load */ + tmp = get_address(s, 0, b2, d2); + tmp32_1 = tcg_temp_new_i32(); + tmp32_2 = tcg_const_i32(r3); + tcg_gen_qemu_ld32u(tmp, tmp, get_mem_index(s)); + store_reg32_i64(r1, tmp); + tcg_gen_trunc_i64_i32(tmp32_1, tmp); + set_cc_icm(s, tmp32_2, tmp32_1); + tcg_temp_free_i64(tmp); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + } else if (r3) { + uint32_t mask = 0x00ffffffUL; + uint32_t shift = 24; + int m3 = r3; + tmp = get_address(s, 0, b2, d2); + tmp2 = tcg_temp_new_i64(); + tmp32_1 = load_reg32(r1); + tmp32_2 = tcg_temp_new_i32(); + tmp32_3 = tcg_const_i32(r3); + tmp32_4 = tcg_const_i32(0); + while (m3) { + if (m3 & 8) { + tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); + tcg_gen_trunc_i64_i32(tmp32_2, tmp2); + if (shift) { + tcg_gen_shli_i32(tmp32_2, tmp32_2, shift); + } + tcg_gen_andi_i32(tmp32_1, tmp32_1, mask); + tcg_gen_or_i32(tmp32_1, tmp32_1, tmp32_2); + tcg_gen_or_i32(tmp32_4, tmp32_4, tmp32_2); + tcg_gen_addi_i64(tmp, tmp, 1); + } + m3 = (m3 << 1) & 0xf; + mask = (mask >> 8) | 0xff000000UL; + shift -= 8; + } + store_reg32(r1, tmp32_1); + set_cc_icm(s, tmp32_3, tmp32_4); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i32(tmp32_1); + tcg_temp_free_i32(tmp32_2); + tcg_temp_free_i32(tmp32_3); + tcg_temp_free_i32(tmp32_4); + } else { + /* i.e. env->cc = 0 */ + gen_op_movi_cc(s, 0); + } + break; + case 0xc0: + case 0xc2: + insn = ld_code6(s->pc); + r1 = (insn >> 36) & 0xf; + op = (insn >> 32) & 0xf; + i2 = (int)insn; + switch (opc) { + case 0xc0: + disas_c0(s, op, r1, i2); + break; + case 0xc2: + disas_c2(s, op, r1, i2); + break; + default: + tcg_abort(); + } + break; + case 0xd2: /* MVC D1(L,B1),D2(B2) [SS] */ + case 0xd4: /* NC D1(L,B1),D2(B2) [SS] */ + case 0xd5: /* CLC D1(L,B1),D2(B2) [SS] */ + case 0xd6: /* OC D1(L,B1),D2(B2) [SS] */ + case 0xd7: /* XC D1(L,B1),D2(B2) [SS] */ + case 0xdc: /* TR D1(L,B1),D2(B2) [SS] */ + case 0xf3: /* UNPK D1(L1,B1),D2(L2,B2) [SS] */ + insn = ld_code6(s->pc); + vl = tcg_const_i32((insn >> 32) & 0xff); + b1 = (insn >> 28) & 0xf; + b2 = (insn >> 12) & 0xf; + d1 = (insn >> 16) & 0xfff; + d2 = insn & 0xfff; + tmp = get_address(s, 0, b1, d1); + tmp2 = get_address(s, 0, b2, d2); + switch (opc) { + case 0xd2: + gen_op_mvc(s, (insn >> 32) & 0xff, tmp, tmp2); + break; + case 0xd4: + potential_page_fault(s); + gen_helper_nc(cc_op, vl, tmp, tmp2); + set_cc_static(s); + break; + case 0xd5: + gen_op_clc(s, (insn >> 32) & 0xff, tmp, tmp2); + break; + case 0xd6: + potential_page_fault(s); + gen_helper_oc(cc_op, vl, tmp, tmp2); + set_cc_static(s); + break; + case 0xd7: + potential_page_fault(s); + gen_helper_xc(cc_op, vl, tmp, tmp2); + set_cc_static(s); + break; + case 0xdc: + potential_page_fault(s); + gen_helper_tr(vl, tmp, tmp2); + set_cc_static(s); + break; + case 0xf3: + potential_page_fault(s); + gen_helper_unpk(vl, tmp, tmp2); + break; + default: + tcg_abort(); + } + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + break; +#ifndef CONFIG_USER_ONLY + case 0xda: /* MVCP D1(R1,B1),D2(B2),R3 [SS] */ + case 0xdb: /* MVCS D1(R1,B1),D2(B2),R3 [SS] */ + check_privileged(s, ilc); + potential_page_fault(s); + insn = ld_code6(s->pc); + r1 = (insn >> 36) & 0xf; + r3 = (insn >> 32) & 0xf; + b1 = (insn >> 28) & 0xf; + d1 = (insn >> 16) & 0xfff; + b2 = (insn >> 12) & 0xf; + d2 = insn & 0xfff; + tmp = load_reg(r1); + /* XXX key in r3 */ + tmp2 = get_address(s, 0, b1, d1); + tmp3 = get_address(s, 0, b2, d2); + if (opc == 0xda) { + gen_helper_mvcp(cc_op, tmp, tmp2, tmp3); + } else { + gen_helper_mvcs(cc_op, tmp, tmp2, tmp3); + } + set_cc_static(s); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(tmp2); + tcg_temp_free_i64(tmp3); + break; +#endif + case 0xe3: + insn = ld_code6(s->pc); + debug_insn(insn); + op = insn & 0xff; + r1 = (insn >> 36) & 0xf; + x2 = (insn >> 32) & 0xf; + b2 = (insn >> 28) & 0xf; + d2 = ((int)((((insn >> 16) & 0xfff) + | ((insn << 4) & 0xff000)) << 12)) >> 12; + disas_e3(s, op, r1, x2, b2, d2 ); + break; +#ifndef CONFIG_USER_ONLY + case 0xe5: + /* Test Protection */ + check_privileged(s, ilc); + insn = ld_code6(s->pc); + debug_insn(insn); + disas_e5(s, insn); + break; +#endif + case 0xeb: + insn = ld_code6(s->pc); + debug_insn(insn); + op = insn & 0xff; + r1 = (insn >> 36) & 0xf; + r3 = (insn >> 32) & 0xf; + b2 = (insn >> 28) & 0xf; + d2 = ((int)((((insn >> 16) & 0xfff) + | ((insn << 4) & 0xff000)) << 12)) >> 12; + disas_eb(s, op, r1, r3, b2, d2); + break; + case 0xed: + insn = ld_code6(s->pc); + debug_insn(insn); + op = insn & 0xff; + r1 = (insn >> 36) & 0xf; + x2 = (insn >> 32) & 0xf; + b2 = (insn >> 28) & 0xf; + d2 = (short)((insn >> 16) & 0xfff); + r1b = (insn >> 12) & 0xf; + disas_ed(s, op, r1, x2, b2, d2, r1b); + break; + default: + LOG_DISAS("unimplemented opcode 0x%x\n", opc); + gen_illegal_opcode(s, ilc); + break; + } + + /* Instruction length is encoded in the opcode */ + s->pc += (ilc * 2); +} + +static inline void gen_intermediate_code_internal(CPUState *env, + TranslationBlock *tb, + int search_pc) +{ + DisasContext dc; + target_ulong pc_start; + uint64_t next_page_start; + uint16_t *gen_opc_end; + int j, lj = -1; + int num_insns, max_insns; + CPUBreakpoint *bp; + + pc_start = tb->pc; + + /* 31-bit mode */ + if (!(tb->flags & FLAG_MASK_64)) { + pc_start &= 0x7fffffff; + } + + dc.pc = pc_start; + dc.is_jmp = DISAS_NEXT; + dc.tb = tb; + dc.cc_op = CC_OP_DYNAMIC; + + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + + gen_icount_start(); + + do { + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc.pc) { + gen_debug(&dc); + break; + } + } + } + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) { + gen_opc_instr_start[lj++] = 0; + } + } + gen_opc_pc[lj] = dc.pc; + gen_opc_cc_op[lj] = dc.cc_op; + gen_opc_instr_start[lj] = 1; + gen_opc_icount[lj] = num_insns; + } + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } +#if defined(S390X_DEBUG_DISAS_VERBOSE) + LOG_DISAS("pc " TARGET_FMT_lx "\n", + dc.pc); +#endif + disas_s390_insn(&dc); + + num_insns++; + if (env->singlestep_enabled) { + gen_debug(&dc); + } + } while (!dc.is_jmp && gen_opc_ptr < gen_opc_end && dc.pc < next_page_start + && num_insns < max_insns && !env->singlestep_enabled + && !singlestep); + + if (!dc.is_jmp) { + update_psw_addr(&dc); + } + + if (singlestep && dc.cc_op != CC_OP_DYNAMIC) { + gen_op_calc_cc(&dc); + } else { + /* next TB starts off with CC_OP_DYNAMIC, so make sure the cc op type + is in env */ + gen_op_set_cc_op(&dc); + } + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + /* Generate the return instruction */ + if (dc.is_jmp != DISAS_TB_JUMP) { + tcg_gen_exit_tb(0); + } + gen_icount_end(tb, num_insns); + *gen_opc_ptr = INDEX_op_end; + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) { + gen_opc_instr_start[lj++] = 0; + } + } else { + tb->size = dc.pc - pc_start; + tb->icount = num_insns; + } +#if defined(S390X_DEBUG_DISAS) + log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0); + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("IN: %s\n", lookup_symbol(pc_start)); + log_target_disas(pc_start, dc.pc - pc_start, 1); + qemu_log("\n"); + } +#endif +} + +void gen_intermediate_code (CPUState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 0); +} + +void gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 1); +} + +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) +{ + int cc_op; + env->psw.addr = gen_opc_pc[pc_pos]; + cc_op = gen_opc_cc_op[pc_pos]; + if ((cc_op != CC_OP_DYNAMIC) && (cc_op != CC_OP_STATIC)) { + env->cc_op = cc_op; + } } diff -Nru qemu-kvm-0.14.1+noroms/target-sh4/cpu.h qemu-kvm-0.15.0+noroms/target-sh4/cpu.h --- qemu-kvm-0.14.1+noroms/target-sh4/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sh4/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -184,7 +184,7 @@ uint32_t cvr; /* Cache Version Register */ void *intc_handle; - int intr_at_halt; /* SR_BL ignored during sleep */ + int in_sleep; /* SR_BL ignored during sleep */ memory_content *movcal_backup; memory_content **movcal_backup_tail; } CPUSH4State; @@ -361,4 +361,17 @@ | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */ } +static inline bool cpu_has_work(CPUState *env) +{ + return env->interrupt_request & CPU_INTERRUPT_HARD; +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; + env->flags = tb->flags; +} + #endif /* _CPU_SH4_H */ diff -Nru qemu-kvm-0.14.1+noroms/target-sh4/exec.h qemu-kvm-0.15.0+noroms/target-sh4/exec.h --- qemu-kvm-0.14.1+noroms/target-sh4/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sh4/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -25,32 +25,9 @@ register struct CPUSH4State *env asm(AREG0); #include "cpu.h" -#include "exec-all.h" - -static inline int cpu_has_work(CPUState *env) -{ - return (env->interrupt_request & CPU_INTERRUPT_HARD); -} - -static inline int cpu_halted(CPUState *env) { - if (!env->halted) - return 0; - if (cpu_has_work(env)) { - env->halted = 0; - env->intr_at_halt = 1; - return 0; - } - return EXCP_HALTED; -} #ifndef CONFIG_USER_ONLY #include "softmmu_exec.h" #endif -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; - env->flags = tb->flags; -} - #endif /* _EXEC_SH4_H */ diff -Nru qemu-kvm-0.14.1+noroms/target-sh4/helper.c qemu-kvm-0.15.0+noroms/target-sh4/helper.c --- qemu-kvm-0.14.1+noroms/target-sh4/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sh4/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "hw/sh_intc.h" #if defined(CONFIG_USER_ONLY) @@ -90,11 +89,11 @@ if (do_exp && env->exception_index != 0x1e0) { env->exception_index = 0x000; /* masked exception -> reset */ } - if (do_irq && !env->intr_at_halt) { + if (do_irq && !env->in_sleep) { return; /* masked */ } - env->intr_at_halt = 0; } + env->in_sleep = 0; if (do_irq) { irq_vector = sh_intc_get_pending_vector(env->intc_handle, diff -Nru qemu-kvm-0.14.1+noroms/target-sh4/helper.h qemu-kvm-0.15.0+noroms/target-sh4/helper.h --- qemu-kvm-0.14.1+noroms/target-sh4/helper.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sh4/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -23,31 +23,31 @@ DEF_HELPER_1(ld_fpscr, void, i32) -DEF_HELPER_1(fabs_FT, i32, i32) -DEF_HELPER_1(fabs_DT, i64, i64) -DEF_HELPER_2(fadd_FT, i32, i32, i32) -DEF_HELPER_2(fadd_DT, i64, i64, i64) -DEF_HELPER_1(fcnvsd_FT_DT, i64, i32) -DEF_HELPER_1(fcnvds_DT_FT, i32, i64) +DEF_HELPER_1(fabs_FT, f32, f32) +DEF_HELPER_1(fabs_DT, f64, f64) +DEF_HELPER_2(fadd_FT, f32, f32, f32) +DEF_HELPER_2(fadd_DT, f64, f64, f64) +DEF_HELPER_1(fcnvsd_FT_DT, f64, f32) +DEF_HELPER_1(fcnvds_DT_FT, f32, f64) -DEF_HELPER_2(fcmp_eq_FT, void, i32, i32) -DEF_HELPER_2(fcmp_eq_DT, void, i64, i64) -DEF_HELPER_2(fcmp_gt_FT, void, i32, i32) -DEF_HELPER_2(fcmp_gt_DT, void, i64, i64) -DEF_HELPER_2(fdiv_FT, i32, i32, i32) -DEF_HELPER_2(fdiv_DT, i64, i64, i64) -DEF_HELPER_1(float_FT, i32, i32) -DEF_HELPER_1(float_DT, i64, i32) -DEF_HELPER_3(fmac_FT, i32, i32, i32, i32) -DEF_HELPER_2(fmul_FT, i32, i32, i32) -DEF_HELPER_2(fmul_DT, i64, i64, i64) -DEF_HELPER_1(fneg_T, i32, i32) -DEF_HELPER_2(fsub_FT, i32, i32, i32) -DEF_HELPER_2(fsub_DT, i64, i64, i64) -DEF_HELPER_1(fsqrt_FT, i32, i32) -DEF_HELPER_1(fsqrt_DT, i64, i64) -DEF_HELPER_1(ftrc_FT, i32, i32) -DEF_HELPER_1(ftrc_DT, i32, i64) +DEF_HELPER_2(fcmp_eq_FT, void, f32, f32) +DEF_HELPER_2(fcmp_eq_DT, void, f64, f64) +DEF_HELPER_2(fcmp_gt_FT, void, f32, f32) +DEF_HELPER_2(fcmp_gt_DT, void, f64, f64) +DEF_HELPER_2(fdiv_FT, f32, f32, f32) +DEF_HELPER_2(fdiv_DT, f64, f64, f64) +DEF_HELPER_1(float_FT, f32, i32) +DEF_HELPER_1(float_DT, f64, i32) +DEF_HELPER_3(fmac_FT, f32, f32, f32, f32) +DEF_HELPER_2(fmul_FT, f32, f32, f32) +DEF_HELPER_2(fmul_DT, f64, f64, f64) +DEF_HELPER_1(fneg_T, f32, f32) +DEF_HELPER_2(fsub_FT, f32, f32, f32) +DEF_HELPER_2(fsub_DT, f64, f64, f64) +DEF_HELPER_1(fsqrt_FT, f32, f32) +DEF_HELPER_1(fsqrt_DT, f64, f64) +DEF_HELPER_1(ftrc_FT, i32, f32) +DEF_HELPER_1(ftrc_DT, i32, f64) DEF_HELPER_2(fipr, void, i32, i32) DEF_HELPER_1(ftrv, void, i32) diff -Nru qemu-kvm-0.14.1+noroms/target-sh4/op_helper.c qemu-kvm-0.15.0+noroms/target-sh4/op_helper.c --- qemu-kvm-0.14.1+noroms/target-sh4/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sh4/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -32,7 +32,7 @@ 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); + cpu_restore_state(tb, env, pc); } } } @@ -66,7 +66,7 @@ if (ret) { /* now we have a real cpu fault */ cpu_restore_state_from_retaddr(retaddr); - cpu_loop_exit(); + cpu_loop_exit(env); } env = saved_env; } @@ -87,7 +87,7 @@ { env->exception_index = index; cpu_restore_state_from_retaddr(retaddr); - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_raise_illegal_instruction(void) @@ -113,15 +113,16 @@ void helper_debug(void) { env->exception_index = EXCP_DEBUG; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_sleep(uint32_t next_pc) { env->halted = 1; + env->in_sleep = 1; env->exception_index = EXCP_HLT; env->pc = next_pc; - cpu_loop_exit(); + cpu_loop_exit(env); } void helper_trapa(uint32_t tra) @@ -481,58 +482,43 @@ if (cause & enable) { cpu_restore_state_from_retaddr(retaddr); env->exception_index = 0x120; - cpu_loop_exit(); + cpu_loop_exit(env); } } } -uint32_t helper_fabs_FT(uint32_t t0) +float32 helper_fabs_FT(float32 t0) { - CPU_FloatU f; - f.l = t0; - f.f = float32_abs(f.f); - return f.l; + return float32_abs(t0); } -uint64_t helper_fabs_DT(uint64_t t0) +float64 helper_fabs_DT(float64 t0) { - CPU_DoubleU d; - d.ll = t0; - d.d = float64_abs(d.d); - return d.ll; + return float64_abs(t0); } -uint32_t helper_fadd_FT(uint32_t t0, uint32_t t1) +float32 helper_fadd_FT(float32 t0, float32 t1) { - 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); + t0 = float32_add(t0, t1, &env->fp_status); update_fpscr(GETPC()); - return f0.l; + return t0; } -uint64_t helper_fadd_DT(uint64_t t0, uint64_t t1) +float64 helper_fadd_DT(float64 t0, float64 t1) { - 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); + t0 = float64_add(t0, t1, &env->fp_status); update_fpscr(GETPC()); - return d0.ll; + return t0; } -void helper_fcmp_eq_FT(uint32_t t0, uint32_t t1) +void helper_fcmp_eq_FT(float32 t0, float32 t1) { - CPU_FloatU f0, f1; int relation; - f0.l = t0; - f1.l = t1; set_float_exception_flags(0, &env->fp_status); - relation = float32_compare(f0.f, f1.f, &env->fp_status); + relation = float32_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(GETPC()); } else if (relation == float_relation_equal) { @@ -542,15 +528,12 @@ } } -void helper_fcmp_eq_DT(uint64_t t0, uint64_t t1) +void helper_fcmp_eq_DT(float64 t0, float64 t1) { - CPU_DoubleU d0, d1; int relation; - d0.ll = t0; - d1.ll = t1; set_float_exception_flags(0, &env->fp_status); - relation = float64_compare(d0.d, d1.d, &env->fp_status); + relation = float64_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(GETPC()); } else if (relation == float_relation_equal) { @@ -560,15 +543,12 @@ } } -void helper_fcmp_gt_FT(uint32_t t0, uint32_t t1) +void helper_fcmp_gt_FT(float32 t0, float32 t1) { - CPU_FloatU f0, f1; int relation; - f0.l = t0; - f1.l = t1; set_float_exception_flags(0, &env->fp_status); - relation = float32_compare(f0.f, f1.f, &env->fp_status); + relation = float32_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(GETPC()); } else if (relation == float_relation_greater) { @@ -578,15 +558,12 @@ } } -void helper_fcmp_gt_DT(uint64_t t0, uint64_t t1) +void helper_fcmp_gt_DT(float64 t0, float64 t1) { - CPU_DoubleU d0, d1; int relation; - d0.ll = t0; - d1.ll = t1; set_float_exception_flags(0, &env->fp_status); - relation = float64_compare(d0.d, d1.d, &env->fp_status); + relation = float64_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(GETPC()); } else if (relation == float_relation_greater) { @@ -596,176 +573,134 @@ } } -uint64_t helper_fcnvsd_FT_DT(uint32_t t0) +float64 helper_fcnvsd_FT_DT(float32 t0) { - CPU_DoubleU d; - CPU_FloatU f; - f.l = t0; + float64 ret; set_float_exception_flags(0, &env->fp_status); - d.d = float32_to_float64(f.f, &env->fp_status); + ret = float32_to_float64(t0, &env->fp_status); update_fpscr(GETPC()); - return d.ll; + return ret; } -uint32_t helper_fcnvds_DT_FT(uint64_t t0) +float32 helper_fcnvds_DT_FT(float64 t0) { - CPU_DoubleU d; - CPU_FloatU f; - d.ll = t0; + float32 ret; set_float_exception_flags(0, &env->fp_status); - f.f = float64_to_float32(d.d, &env->fp_status); + ret = float64_to_float32(t0, &env->fp_status); update_fpscr(GETPC()); - return f.l; + return ret; } -uint32_t helper_fdiv_FT(uint32_t t0, uint32_t t1) +float32 helper_fdiv_FT(float32 t0, float32 t1) { - 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); + t0 = float32_div(t0, t1, &env->fp_status); update_fpscr(GETPC()); - return f0.l; + return t0; } -uint64_t helper_fdiv_DT(uint64_t t0, uint64_t t1) +float64 helper_fdiv_DT(float64 t0, float64 t1) { - 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); + t0 = float64_div(t0, t1, &env->fp_status); update_fpscr(GETPC()); - return d0.ll; + return t0; } -uint32_t helper_float_FT(uint32_t t0) +float32 helper_float_FT(uint32_t t0) { - CPU_FloatU f; - + float32 ret; set_float_exception_flags(0, &env->fp_status); - f.f = int32_to_float32(t0, &env->fp_status); + ret = int32_to_float32(t0, &env->fp_status); update_fpscr(GETPC()); - - return f.l; + return ret; } -uint64_t helper_float_DT(uint32_t t0) +float64 helper_float_DT(uint32_t t0) { - CPU_DoubleU d; + float64 ret; set_float_exception_flags(0, &env->fp_status); - d.d = int32_to_float64(t0, &env->fp_status); + ret = int32_to_float64(t0, &env->fp_status); update_fpscr(GETPC()); - return d.ll; + return ret; } -uint32_t helper_fmac_FT(uint32_t t0, uint32_t t1, uint32_t t2) +float32 helper_fmac_FT(float32 t0, float32 t1, float32 t2) { - CPU_FloatU f0, f1, f2; - 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); + t0 = float32_mul(t0, t1, &env->fp_status); + t0 = float32_add(t0, t2, &env->fp_status); update_fpscr(GETPC()); - - return f0.l; + return t0; } -uint32_t helper_fmul_FT(uint32_t t0, uint32_t t1) +float32 helper_fmul_FT(float32 t0, float32 t1) { - 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); + t0 = float32_mul(t0, t1, &env->fp_status); update_fpscr(GETPC()); - return f0.l; + return t0; } -uint64_t helper_fmul_DT(uint64_t t0, uint64_t t1) +float64 helper_fmul_DT(float64 t0, float64 t1) { - 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); + t0 = float64_mul(t0, t1, &env->fp_status); update_fpscr(GETPC()); - - return d0.ll; + return t0; } -uint32_t helper_fneg_T(uint32_t t0) +float32 helper_fneg_T(float32 t0) { - CPU_FloatU f; - f.l = t0; - f.f = float32_chs(f.f); - return f.l; + return float32_chs(t0); } -uint32_t helper_fsqrt_FT(uint32_t t0) +float32 helper_fsqrt_FT(float32 t0) { - CPU_FloatU f; - f.l = t0; set_float_exception_flags(0, &env->fp_status); - f.f = float32_sqrt(f.f, &env->fp_status); + t0 = float32_sqrt(t0, &env->fp_status); update_fpscr(GETPC()); - return f.l; + return t0; } -uint64_t helper_fsqrt_DT(uint64_t t0) +float64 helper_fsqrt_DT(float64 t0) { - CPU_DoubleU d; - d.ll = t0; set_float_exception_flags(0, &env->fp_status); - d.d = float64_sqrt(d.d, &env->fp_status); + t0 = float64_sqrt(t0, &env->fp_status); update_fpscr(GETPC()); - return d.ll; + return t0; } -uint32_t helper_fsub_FT(uint32_t t0, uint32_t t1) +float32 helper_fsub_FT(float32 t0, float32 t1) { - 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); + t0 = float32_sub(t0, t1, &env->fp_status); update_fpscr(GETPC()); - return f0.l; + return t0; } -uint64_t helper_fsub_DT(uint64_t t0, uint64_t t1) +float64 helper_fsub_DT(float64 t0, float64 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); + t0 = float64_sub(t0, t1, &env->fp_status); update_fpscr(GETPC()); - return d0.ll; + return t0; } -uint32_t helper_ftrc_FT(uint32_t t0) +uint32_t helper_ftrc_FT(float32 t0) { - CPU_FloatU f; uint32_t ret; - f.l = t0; set_float_exception_flags(0, &env->fp_status); - ret = float32_to_int32_round_to_zero(f.f, &env->fp_status); + ret = float32_to_int32_round_to_zero(t0, &env->fp_status); update_fpscr(GETPC()); return ret; } -uint32_t helper_ftrc_DT(uint64_t t0) +uint32_t helper_ftrc_DT(float64 t0) { - CPU_DoubleU d; uint32_t ret; - d.ll = t0; set_float_exception_flags(0, &env->fp_status); - ret = float64_to_int32_round_to_zero(d.d, &env->fp_status); + ret = float64_to_int32_round_to_zero(t0, &env->fp_status); update_fpscr(GETPC()); return ret; } diff -Nru qemu-kvm-0.14.1+noroms/target-sh4/translate.c qemu-kvm-0.15.0+noroms/target-sh4/translate.c --- qemu-kvm-0.14.1+noroms/target-sh4/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sh4/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,6 @@ //#define SH4_SINGLE_STEP #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg-op.h" #include "qemu-common.h" @@ -302,7 +301,7 @@ /* Use a direct jump if in same page and singlestep not enabled */ tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb((long) tb + n); + tcg_gen_exit_tb((tcg_target_long)tb + n); } else { tcg_gen_movi_i32(cpu_pc, dest); if (ctx->singlestep_enabled) @@ -2069,8 +2068,7 @@ gen_intermediate_code_internal(env, tb, 1); } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { env->pc = gen_opc_pc[pc_pos]; env->flags = gen_opc_hflags[pc_pos]; diff -Nru qemu-kvm-0.14.1+noroms/target-sparc/cpu.h qemu-kvm-0.15.0+noroms/target-sparc/cpu.h --- qemu-kvm-0.14.1+noroms/target-sparc/cpu.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sparc/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -290,18 +290,51 @@ #endif #define TTE_VALID_BIT (1ULL << 63) +#define TTE_NFO_BIT (1ULL << 60) #define TTE_USED_BIT (1ULL << 41) #define TTE_LOCKED_BIT (1ULL << 6) +#define TTE_SIDEEFFECT_BIT (1ULL << 3) +#define TTE_PRIV_BIT (1ULL << 2) +#define TTE_W_OK_BIT (1ULL << 1) #define TTE_GLOBAL_BIT (1ULL << 0) #define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT) +#define TTE_IS_NFO(tte) ((tte) & TTE_NFO_BIT) #define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT) #define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT) +#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT) +#define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT) +#define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT) #define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT) #define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT) #define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT) +#define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL) +#define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL) + +#define SFSR_NF_BIT (1ULL << 24) /* JPS1 NoFault */ +#define SFSR_TM_BIT (1ULL << 15) /* JPS1 TLB Miss */ +#define SFSR_FT_VA_IMMU_BIT (1ULL << 13) /* USIIi VA out of range (IMMU) */ +#define SFSR_FT_VA_DMMU_BIT (1ULL << 12) /* USIIi VA out of range (DMMU) */ +#define SFSR_FT_NFO_BIT (1ULL << 11) /* NFO page access */ +#define SFSR_FT_ILL_BIT (1ULL << 10) /* illegal LDA/STA ASI */ +#define SFSR_FT_ATOMIC_BIT (1ULL << 9) /* atomic op on noncacheable area */ +#define SFSR_FT_NF_E_BIT (1ULL << 8) /* NF access on side effect area */ +#define SFSR_FT_PRIV_BIT (1ULL << 7) /* privilege violation */ +#define SFSR_PR_BIT (1ULL << 3) /* privilege mode */ +#define SFSR_WRITE_BIT (1ULL << 2) /* write access mode */ +#define SFSR_OW_BIT (1ULL << 1) /* status overwritten */ +#define SFSR_VALID_BIT (1ULL << 0) /* status valid */ + +#define SFSR_ASI_SHIFT 16 /* 23:16 ASI value */ +#define SFSR_ASI_MASK (0xffULL << SFSR_ASI_SHIFT) +#define SFSR_CT_PRIMARY (0ULL << 4) /* 5:4 context type */ +#define SFSR_CT_SECONDARY (1ULL << 4) +#define SFSR_CT_NUCLEUS (2ULL << 4) +#define SFSR_CT_NOTRANS (3ULL << 4) +#define SFSR_CT_MASK (3ULL << 4) + typedef struct SparcTLBEntry { uint64_t tag; uint64_t tte; @@ -403,6 +436,8 @@ uint32_t mmuregs[32]; uint64_t mxccdata[4]; uint64_t mxccregs[8]; + uint32_t mmubpctrv, mmubpctrc, mmubpctrs; + uint64_t mmubpaction; uint64_t mmubpregs[4]; uint64_t prom_addr; #endif @@ -474,6 +509,7 @@ void cpu_put_ccr(CPUState *env1, target_ulong val); target_ulong cpu_get_cwp64(CPUState *env1); void cpu_put_cwp64(CPUState *env1, int cwp); +void cpu_change_pstate(CPUState *env1, uint32_t new_pstate); #endif int cpu_cwp_inc(CPUState *env1, int cwp); int cpu_cwp_dec(CPUState *env1, int cwp); @@ -507,12 +543,14 @@ /* 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); +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int is_asi, int size); +#if defined(TARGET_SPARC64) target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, int mmu_idx); #endif +#endif int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); #define cpu_init cpu_sparc_init @@ -521,7 +559,7 @@ #define cpu_signal_handler cpu_sparc_signal_handler #define cpu_list sparc_cpu_list -#define CPU_SAVE_VERSION 6 +#define CPU_SAVE_VERSION 7 /* MMU modes definitions */ #if defined (TARGET_SPARC64) @@ -603,17 +641,6 @@ #endif } -static inline int cpu_fpu_enabled(CPUState *env1) -{ -#if defined(CONFIG_USER_ONLY) - return 1; -#elif !defined(TARGET_SPARC64) - return env1->psref; -#else - return ((env1->pstate & PS_PEF) != 0) && ((env1->fprs & FPRS_FEF) != 0); -#endif -} - #if defined(CONFIG_USER_ONLY) static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) { @@ -636,6 +663,9 @@ trap_state* cpu_tsptr(CPUState* env); #endif +#define TB_FLAG_FPU_ENABLED (1 << 4) +#define TB_FLAG_AM_ENABLED (1 << 5) + static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -643,17 +673,59 @@ *cs_base = env->npc; #ifdef TARGET_SPARC64 // AM . Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled - *flags = ((env->pstate & PS_AM) << 2) /* 5 */ - | (((env->pstate & PS_PEF) >> 1) /* 3 */ - | ((env->fprs & FPRS_FEF) << 2)) /* 4 */ - | (env->pstate & PS_PRIV) /* 2 */ + *flags = (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... */ + if (env->pstate & PS_AM) { + *flags |= TB_FLAG_AM_ENABLED; + } + if ((env->def->features & CPU_FEATURE_FLOAT) && (env->pstate & PS_PEF) + && (env->fprs & FPRS_FEF)) { + *flags |= TB_FLAG_FPU_ENABLED; + } #else // FPU enable . Supervisor - *flags = (env->psref << 4) | env->psrs; + *flags = env->psrs; + if ((env->def->features & CPU_FEATURE_FLOAT) && env->psref) { + *flags |= TB_FLAG_FPU_ENABLED; + } #endif } +static inline bool tb_fpu_enabled(int tb_flags) +{ +#if defined(CONFIG_USER_ONLY) + return true; +#else + return tb_flags & TB_FLAG_FPU_ENABLED; +#endif +} + +static inline bool tb_am_enabled(int tb_flags) +{ +#ifndef TARGET_SPARC64 + return false; +#else + return tb_flags & TB_FLAG_AM_ENABLED; +#endif +} + +/* helper.c */ +void do_interrupt(CPUState *env); + +static inline bool cpu_has_work(CPUState *env1) +{ + return (env1->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_interrupts_enabled(env1); +} + +#include "exec-all.h" + +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.14.1+noroms/target-sparc/exec.h qemu-kvm-0.15.0+noroms/target-sparc/exec.h --- qemu-kvm-0.14.1+noroms/target-sparc/exec.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sparc/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -12,30 +12,4 @@ #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -/* op_helper.c */ -void do_interrupt(CPUState *env); - -static inline int cpu_has_work(CPUState *env1) -{ - return (env1->interrupt_request & CPU_INTERRUPT_HARD) && - cpu_interrupts_enabled(env1); -} - - -static inline int cpu_halted(CPUState *env1) { - if (!env1->halted) - return 0; - if (cpu_has_work(env1)) { - env1->halted = 0; - return 0; - } - 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.14.1+noroms/target-sparc/helper.c qemu-kvm-0.15.0+noroms/target-sparc/helper.c --- qemu-kvm-0.14.1+noroms/target-sparc/helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sparc/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -21,10 +21,8 @@ #include #include #include -#include #include "cpu.h" -#include "exec-all.h" #include "qemu-common.h" //#define DEBUG_MMU @@ -380,7 +378,7 @@ { uint64_t mask; - switch ((tlb->tte >> 61) & 3) { + switch (TTE_PGSIZE(tlb->tte)) { default: case 0x0: // 8k mask = 0xffffffffffffe000ULL; @@ -415,6 +413,7 @@ { unsigned int i; uint64_t context; + uint64_t sfsr = 0; int is_user = (mmu_idx == MMU_USER_IDX || mmu_idx == MMU_USER_SECONDARY_IDX); @@ -429,54 +428,88 @@ case MMU_USER_IDX: case MMU_KERNEL_IDX: context = env->dmmu.mmu_primary_context & 0x1fff; + sfsr |= SFSR_CT_PRIMARY; break; case MMU_USER_SECONDARY_IDX: case MMU_KERNEL_SECONDARY_IDX: context = env->dmmu.mmu_secondary_context & 0x1fff; + sfsr |= SFSR_CT_SECONDARY; break; case MMU_NUCLEUS_IDX: + sfsr |= SFSR_CT_NUCLEUS; + /* FALLTHRU */ default: context = 0; break; } + if (rw == 1) { + sfsr |= SFSR_WRITE_BIT; + } else if (rw == 4) { + sfsr |= SFSR_NF_BIT; + } + for (i = 0; i < 64; i++) { // ctx match, vaddr match, valid? if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { - - uint8_t fault_type = 0; + int do_fault = 0; // access ok? - if ((env->dtlb[i].tte & 0x4) && is_user) { - fault_type |= 1; /* privilege violation */ - env->exception_index = TT_DFAULT; + /* multiple bits in SFSR.FT may be set on TT_DFAULT */ + if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) { + do_fault = 1; + sfsr |= SFSR_FT_PRIV_BIT; /* 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)) { + } + if (rw == 4) { + if (TTE_IS_SIDEEFFECT(env->dtlb[i].tte)) { + do_fault = 1; + sfsr |= SFSR_FT_NF_E_BIT; + } + } else { + if (TTE_IS_NFO(env->dtlb[i].tte)) { + do_fault = 1; + sfsr |= SFSR_FT_NFO_BIT; + } + } + + if (do_fault) { + /* faults above are reported with TT_DFAULT. */ + env->exception_index = TT_DFAULT; + } else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) { + do_fault = 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 { + } + + if (!do_fault) { *prot = PAGE_READ; - if (env->dtlb[i].tte & 0x2) + if (TTE_IS_W_OK(env->dtlb[i].tte)) { *prot |= PAGE_WRITE; + } TTE_SET_USED(env->dtlb[i].tte); return 0; } - if (env->dmmu.sfsr & 1) /* Fault status register */ - env->dmmu.sfsr = 2; /* overflow (not read before - another fault) */ + if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */ + sfsr |= SFSR_OW_BIT; /* overflow (not read before + another fault) */ + } - env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1; + if (env->pstate & PS_PRIV) { + sfsr |= SFSR_PR_BIT; + } - env->dmmu.sfsr |= (fault_type << 7); + /* FIXME: ASI field in SFSR must be set */ + env->dmmu.sfsr = sfsr | SFSR_VALID_BIT; env->dmmu.sfar = address; /* Fault address register */ @@ -489,6 +522,11 @@ DPRINTF_MMU("DMISS at %" PRIx64 " context %" PRIx64 "\n", address, context); + /* + * On MMU misses: + * - UltraSPARC IIi: SFSR and SFAR unmodified + * - JPS1: SFAR updated and some fields of SFSR updated + */ env->dmmu.tag_access = (address & ~0x1fffULL) | context; env->exception_index = TT_DMISS; return 1; @@ -524,11 +562,23 @@ if (ultrasparc_tag_match(&env->itlb[i], address, context, physical)) { // access ok? - if ((env->itlb[i].tte & 0x4) && is_user) { - if (env->immu.sfsr) /* Fault status register */ - env->immu.sfsr = 2; /* overflow (not read before - another fault) */ - env->immu.sfsr |= (is_user << 3) | 1; + if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) { + /* Fault status register */ + if (env->immu.sfsr & SFSR_VALID_BIT) { + env->immu.sfsr = SFSR_OW_BIT; /* overflow (not read before + another fault) */ + } else { + env->immu.sfsr = 0; + } + if (env->pstate & PS_PRIV) { + env->immu.sfsr |= SFSR_PR_BIT; + } + if (env->tl > 0) { + env->immu.sfsr |= SFSR_CT_NUCLEUS; + } + + /* FIXME: ASI field in SFSR must be set */ + env->immu.sfsr |= SFSR_FT_PRIV_BIT | SFSR_VALID_BIT; env->exception_index = TT_TFAULT; env->immu.tag_access = (address & ~0x1fffULL) | context; @@ -634,7 +684,7 @@ } else { (*cpu_fprintf)(f, "DMMU dump\n"); for (i = 0; i < 64; i++) { - switch ((env->dtlb[i].tte >> 61) & 3) { + switch (TTE_PGSIZE(env->dtlb[i].tte)) { default: case 0x0: mask = " 8k"; @@ -649,16 +699,17 @@ mask = " 4M"; break; } - if ((env->dtlb[i].tte & 0x8000000000000000ULL) != 0) { - (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + if (TTE_IS_VALID(env->dtlb[i].tte)) { + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" ", %s, %s, %s, %s, ctx %" PRId64 " %s\n", i, env->dtlb[i].tag & (uint64_t)~0x1fffULL, - env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL, + TTE_PA(env->dtlb[i].tte), mask, - env->dtlb[i].tte & 0x4? "priv": "user", - env->dtlb[i].tte & 0x2? "RW": "RO", - env->dtlb[i].tte & 0x40? "locked": "unlocked", + TTE_IS_PRIV(env->dtlb[i].tte) ? "priv" : "user", + TTE_IS_W_OK(env->dtlb[i].tte) ? "RW" : "RO", + TTE_IS_LOCKED(env->dtlb[i].tte) ? + "locked" : "unlocked", env->dtlb[i].tag & (uint64_t)0x1fffULL, TTE_IS_GLOBAL(env->dtlb[i].tte)? "global" : "local"); @@ -670,7 +721,7 @@ } else { (*cpu_fprintf)(f, "IMMU dump\n"); for (i = 0; i < 64; i++) { - switch ((env->itlb[i].tte >> 61) & 3) { + switch (TTE_PGSIZE(env->itlb[i].tte)) { default: case 0x0: mask = " 8k"; @@ -685,15 +736,16 @@ mask = " 4M"; break; } - if ((env->itlb[i].tte & 0x8000000000000000ULL) != 0) { - (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + if (TTE_IS_VALID(env->itlb[i].tte)) { + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" ", %s, %s, %s, ctx %" PRId64 " %s\n", i, env->itlb[i].tag & (uint64_t)~0x1fffULL, - env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL, + TTE_PA(env->itlb[i].tte), mask, - env->itlb[i].tte & 0x4? "priv": "user", - env->itlb[i].tte & 0x40? "locked": "unlocked", + TTE_IS_PRIV(env->itlb[i].tte) ? "priv" : "user", + TTE_IS_LOCKED(env->itlb[i].tte) ? + "locked" : "unlocked", env->itlb[i].tag & (uint64_t)0x1fffULL, TTE_IS_GLOBAL(env->itlb[i].tte)? "global" : "local"); @@ -707,26 +759,285 @@ #if !defined(CONFIG_USER_ONLY) +static int cpu_sparc_get_phys_page(CPUState *env, target_phys_addr_t *phys, + target_ulong addr, int rw, int mmu_idx) +{ + target_ulong page_size; + int prot, access_index; + + return get_physical_address(env, phys, &prot, &access_index, addr, rw, + mmu_idx, &page_size); +} + +#if defined(TARGET_SPARC64) 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_idx, &page_size) != 0) - if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, - 0, mmu_idx, &page_size) != 0) - return -1; - if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 4, mmu_idx) != 0) { return -1; + } return phys_addr; } +#endif 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)); + target_phys_addr_t phys_addr; + int mmu_idx = cpu_mmu_index(env); + + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { + return -1; + } + } + if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) { + return -1; + } + return phys_addr; +} +#endif + +#ifdef TARGET_SPARC64 +#ifdef DEBUG_PCALL +static const char * const excp_names[0x80] = { + [TT_TFAULT] = "Instruction Access Fault", + [TT_TMISS] = "Instruction Access MMU Miss", + [TT_CODE_ACCESS] = "Instruction Access Error", + [TT_ILL_INSN] = "Illegal Instruction", + [TT_PRIV_INSN] = "Privileged Instruction", + [TT_NFPU_INSN] = "FPU Disabled", + [TT_FP_EXCP] = "FPU Exception", + [TT_TOVF] = "Tag Overflow", + [TT_CLRWIN] = "Clean Windows", + [TT_DIV_ZERO] = "Division By Zero", + [TT_DFAULT] = "Data Access Fault", + [TT_DMISS] = "Data Access MMU Miss", + [TT_DATA_ACCESS] = "Data Access Error", + [TT_DPROT] = "Data Protection Error", + [TT_UNALIGNED] = "Unaligned Memory Access", + [TT_PRIV_ACT] = "Privileged Action", + [TT_EXTINT | 0x1] = "External Interrupt 1", + [TT_EXTINT | 0x2] = "External Interrupt 2", + [TT_EXTINT | 0x3] = "External Interrupt 3", + [TT_EXTINT | 0x4] = "External Interrupt 4", + [TT_EXTINT | 0x5] = "External Interrupt 5", + [TT_EXTINT | 0x6] = "External Interrupt 6", + [TT_EXTINT | 0x7] = "External Interrupt 7", + [TT_EXTINT | 0x8] = "External Interrupt 8", + [TT_EXTINT | 0x9] = "External Interrupt 9", + [TT_EXTINT | 0xa] = "External Interrupt 10", + [TT_EXTINT | 0xb] = "External Interrupt 11", + [TT_EXTINT | 0xc] = "External Interrupt 12", + [TT_EXTINT | 0xd] = "External Interrupt 13", + [TT_EXTINT | 0xe] = "External Interrupt 14", + [TT_EXTINT | 0xf] = "External Interrupt 15", +}; +#endif + +void do_interrupt(CPUState *env) +{ + int intno = env->exception_index; + trap_state *tsptr; + +#ifdef DEBUG_PCALL + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + const char *name; + + if (intno < 0 || intno >= 0x180) { + name = "Unknown"; + } else if (intno >= 0x100) { + name = "Trap Instruction"; + } else if (intno >= 0xc0) { + name = "Window Fill"; + } else if (intno >= 0x80) { + name = "Window Spill"; + } else { + name = excp_names[intno]; + if (!name) { + name = "Unknown"; + } + } + + qemu_log("%6d: %s (v=%04x) pc=%016" PRIx64 " npc=%016" PRIx64 + " SP=%016" PRIx64 "\n", + count, name, intno, + env->pc, + env->npc, env->regwptr[6]); + log_cpu_state(env, 0); +#if 0 + { + int i; + uint8_t *ptr; + + qemu_log(" code="); + ptr = (uint8_t *)env->pc; + for (i = 0; i < 16; i++) { + qemu_log(" %02x", ldub(ptr + i)); + } + qemu_log("\n"); + } +#endif + count++; + } +#endif +#if !defined(CONFIG_USER_ONLY) + if (env->tl >= env->maxtl) { + cpu_abort(env, "Trap 0x%04x while trap level (%d) >= MAXTL (%d)," + " Error state", env->exception_index, env->tl, env->maxtl); + return; + } +#endif + if (env->tl < env->maxtl - 1) { + env->tl++; + } else { + env->pstate |= PS_RED; + if (env->tl < env->maxtl) { + env->tl++; + } + } + tsptr = cpu_tsptr(env); + + tsptr->tstate = (cpu_get_ccr(env) << 32) | + ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) | + cpu_get_cwp64(env); + tsptr->tpc = env->pc; + tsptr->tnpc = env->npc; + tsptr->tt = intno; + + switch (intno) { + case TT_IVEC: + cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_IG); + break; + case TT_TFAULT: + case TT_DFAULT: + case TT_TMISS ... TT_TMISS + 3: + case TT_DMISS ... TT_DMISS + 3: + case TT_DPROT ... TT_DPROT + 3: + cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_MG); + break; + default: + cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_AG); + 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)); + } + 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 = -1; +} +#else +#ifdef DEBUG_PCALL +static const char * const excp_names[0x80] = { + [TT_TFAULT] = "Instruction Access Fault", + [TT_ILL_INSN] = "Illegal Instruction", + [TT_PRIV_INSN] = "Privileged Instruction", + [TT_NFPU_INSN] = "FPU Disabled", + [TT_WIN_OVF] = "Window Overflow", + [TT_WIN_UNF] = "Window Underflow", + [TT_UNALIGNED] = "Unaligned Memory Access", + [TT_FP_EXCP] = "FPU Exception", + [TT_DFAULT] = "Data Access Fault", + [TT_TOVF] = "Tag Overflow", + [TT_EXTINT | 0x1] = "External Interrupt 1", + [TT_EXTINT | 0x2] = "External Interrupt 2", + [TT_EXTINT | 0x3] = "External Interrupt 3", + [TT_EXTINT | 0x4] = "External Interrupt 4", + [TT_EXTINT | 0x5] = "External Interrupt 5", + [TT_EXTINT | 0x6] = "External Interrupt 6", + [TT_EXTINT | 0x7] = "External Interrupt 7", + [TT_EXTINT | 0x8] = "External Interrupt 8", + [TT_EXTINT | 0x9] = "External Interrupt 9", + [TT_EXTINT | 0xa] = "External Interrupt 10", + [TT_EXTINT | 0xb] = "External Interrupt 11", + [TT_EXTINT | 0xc] = "External Interrupt 12", + [TT_EXTINT | 0xd] = "External Interrupt 13", + [TT_EXTINT | 0xe] = "External Interrupt 14", + [TT_EXTINT | 0xf] = "External Interrupt 15", + [TT_TOVF] = "Tag Overflow", + [TT_CODE_ACCESS] = "Instruction Access Error", + [TT_DATA_ACCESS] = "Data Access Error", + [TT_DIV_ZERO] = "Division By Zero", + [TT_NCP_INSN] = "Coprocessor Disabled", +}; +#endif + +void do_interrupt(CPUState *env) +{ + int cwp, intno = env->exception_index; + +#ifdef DEBUG_PCALL + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + const char *name; + + if (intno < 0 || intno >= 0x100) { + name = "Unknown"; + } else if (intno >= 0x80) { + name = "Trap Instruction"; + } else { + name = excp_names[intno]; + if (!name) { + name = "Unknown"; + } + } + + qemu_log("%6d: %s (v=%02x) pc=%08x npc=%08x SP=%08x\n", + count, name, intno, + env->pc, + env->npc, env->regwptr[6]); + log_cpu_state(env, 0); +#if 0 + { + int i; + uint8_t *ptr; + + qemu_log(" code="); + ptr = (uint8_t *)env->pc; + for (i = 0; i < 16; i++) { + qemu_log(" %02x", ldub(ptr + i)); + } + qemu_log("\n"); + } +#endif + count++; + } +#endif +#if !defined(CONFIG_USER_ONLY) + if (env->psret == 0) { + cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state", + env->exception_index); + return; + } +#endif + env->psret = 0; + cwp = cpu_cwp_dec(env, env->cwp - 1); + cpu_set_cwp(env, cwp); + env->regwptr[9] = env->pc; + env->regwptr[10] = env->npc; + env->psrps = env->psrs; + env->psrs = 1; + env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); + env->pc = env->tbr; + env->npc = env->pc + 4; + 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 diff -Nru qemu-kvm-0.14.1+noroms/target-sparc/helper.h qemu-kvm-0.15.0+noroms/target-sparc/helper.h --- qemu-kvm-0.14.1+noroms/target-sparc/helper.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sparc/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -35,7 +35,6 @@ DEF_HELPER_0(debug, void) DEF_HELPER_0(save, void) 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) @@ -149,8 +148,8 @@ VIS_HELPER(padd); VIS_HELPER(psub); #define VIS_CMPHELPER(name) \ - F_HELPER_0_0(name##16); \ - F_HELPER_0_0(name##32) + DEF_HELPER_0(f##name##16, i64); \ + DEF_HELPER_0(f##name##32, i64) VIS_CMPHELPER(cmpgt); VIS_CMPHELPER(cmpeq); VIS_CMPHELPER(cmple); diff -Nru qemu-kvm-0.14.1+noroms/target-sparc/machine.c qemu-kvm-0.15.0+noroms/target-sparc/machine.c --- qemu-kvm-0.14.1+noroms/target-sparc/machine.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sparc/machine.c 2011-08-09 07:40:29.000000000 -0500 @@ -2,7 +2,7 @@ #include "hw/boards.h" #include "qemu-timer.h" -#include "exec-all.h" +#include "cpu.h" void cpu_save(QEMUFile *f, void *opaque) { @@ -45,6 +45,19 @@ /* MMU */ for (i = 0; i < 32; i++) qemu_put_be32s(f, &env->mmuregs[i]); + for (i = 0; i < 4; i++) { + qemu_put_be64s(f, &env->mxccdata[i]); + } + for (i = 0; i < 8; i++) { + qemu_put_be64s(f, &env->mxccregs[i]); + } + qemu_put_be32s(f, &env->mmubpctrv); + qemu_put_be32s(f, &env->mmubpctrc); + qemu_put_be32s(f, &env->mmubpctrs); + qemu_put_be64s(f, &env->mmubpaction); + for (i = 0; i < 4; i++) { + qemu_put_be64s(f, &env->mmubpregs[i]); + } #else qemu_put_be64s(f, &env->lsu); for (i = 0; i < 16; i++) { @@ -141,6 +154,19 @@ /* MMU */ for (i = 0; i < 32; i++) qemu_get_be32s(f, &env->mmuregs[i]); + for (i = 0; i < 4; i++) { + qemu_get_be64s(f, &env->mxccdata[i]); + } + for (i = 0; i < 8; i++) { + qemu_get_be64s(f, &env->mxccregs[i]); + } + qemu_get_be32s(f, &env->mmubpctrv); + qemu_get_be32s(f, &env->mmubpctrc); + qemu_get_be32s(f, &env->mmubpctrs); + qemu_get_be64s(f, &env->mmubpaction); + for (i = 0; i < 4; i++) { + qemu_get_be64s(f, &env->mmubpregs[i]); + } #else qemu_get_be64s(f, &env->lsu); for (i = 0; i < 16; i++) { diff -Nru qemu-kvm-0.14.1+noroms/target-sparc/op_helper.c qemu-kvm-0.15.0+noroms/target-sparc/op_helper.c --- qemu-kvm-0.14.1+noroms/target-sparc/op_helper.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sparc/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -79,9 +79,14 @@ #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) +#if !defined(CONFIG_USER_ONLY) +static void do_unassigned_access(target_phys_addr_t addr, int is_write, + int is_exec, int is_asi, int size); +#else +#ifdef TARGET_SPARC64 static void do_unassigned_access(target_ulong addr, int is_write, int is_exec, - int is_asi, int size); + int is_asi, int size); +#endif #endif #if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) @@ -287,7 +292,8 @@ */ switch (asi) { case 0x04 ... 0x11: - case 0x18 ... 0x19: + case 0x16 ... 0x19: + case 0x1E ... 0x1F: case 0x24 ... 0x2C: case 0x70 ... 0x73: case 0x78 ... 0x79: @@ -316,7 +322,7 @@ static void raise_exception(int tt) { env->exception_index = tt; - cpu_loop_exit(); + cpu_loop_exit(env); } void HELPER(raise_exception)(int tt) @@ -525,6 +531,7 @@ uint16_t w[4]; int16_t sw[4]; uint32_t l[2]; + uint64_t ll; float64 d; } vis64; @@ -789,32 +796,34 @@ VIS_HELPER(helper_fpsub, FSUB) #define VIS_CMPHELPER(name, F) \ - void name##16(void) \ + uint64_t name##16(void) \ { \ vis64 s, d; \ \ s.d = DT0; \ d.d = DT1; \ \ - d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0))? 1: 0; \ - d.VIS_W64(0) |= F(d.VIS_W64(1), s.VIS_W64(1))? 2: 0; \ - d.VIS_W64(0) |= F(d.VIS_W64(2), s.VIS_W64(2))? 4: 0; \ - d.VIS_W64(0) |= F(d.VIS_W64(3), s.VIS_W64(3))? 8: 0; \ + d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \ + d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \ + d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0; \ + d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0; \ + d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0; \ \ - DT0 = d.d; \ + return d.ll; \ } \ \ - void name##32(void) \ + uint64_t name##32(void) \ { \ vis64 s, d; \ \ s.d = DT0; \ d.d = DT1; \ \ - d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0))? 1: 0; \ - d.VIS_L64(0) |= F(d.VIS_L64(1), s.VIS_L64(1))? 2: 0; \ + d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \ + d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \ + d.VIS_L64(1) = 0; \ \ - DT0 = d.d; \ + return d.ll; \ } #define FCMPGT(a, b) ((a) > (b)) @@ -1940,7 +1949,6 @@ 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 */ @@ -1966,6 +1974,18 @@ ret); } break; + case 0x49: /* SuperSPARC MMU Counter Breakpoint Value */ + ret = env->mmubpctrv; + break; + case 0x4a: /* SuperSPARC MMU Counter Breakpoint Control */ + ret = env->mmubpctrc; + break; + case 0x4b: /* SuperSPARC MMU Counter Breakpoint Status */ + ret = env->mmubpctrs; + break; + case 0x4c: /* SuperSPARC MMU Breakpoint Action */ + ret = env->mmubpaction; + break; case 8: /* User code access, XXX */ default: do_unassigned_access(addr, 0, 0, asi, size); @@ -2304,7 +2324,6 @@ // descriptor diagnostic case 0x36: /* I-cache flash clear */ case 0x37: /* D-cache flash clear */ - case 0x4c: /* breakpoint action */ break; case 0x38: /* SuperSPARC MMU Breakpoint Control Registers*/ { @@ -2328,6 +2347,18 @@ env->mmuregs[reg]); } break; + case 0x49: /* SuperSPARC MMU Counter Breakpoint Value */ + env->mmubpctrv = val & 0xffffffff; + break; + case 0x4a: /* SuperSPARC MMU Counter Breakpoint Control */ + env->mmubpctrc = val & 0x3; + break; + case 0x4b: /* SuperSPARC MMU Counter Breakpoint Status */ + env->mmubpctrs = val & 0x3; + break; + case 0x4c: /* SuperSPARC MMU Breakpoint Action */ + env->mmubpaction = val & 0x1fff; + break; case 8: /* User code access, XXX */ case 9: /* Supervisor code access, XXX */ default: @@ -2536,24 +2567,30 @@ 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 - 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; + /* process nonfaulting loads first */ + if ((asi & 0xf6) == 0x82) { + int mmu_idx; + + /* secondary space access has lowest asi bit equal to 1 */ + if (env->pstate & PS_PRIV) { + mmu_idx = (asi & 1) ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX; + } else { + mmu_idx = (asi & 1) ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX; + } - if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) { + if (cpu_get_phys_page_nofault(env, addr, 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; - } + /* env->exception_index is set in get_physical_address_data(). */ + raise_exception(env->exception_index); } - // Fall through + + /* convert nonfaulting load ASIs to normal load ASIs */ + asi &= ~0x02; + } + + switch (asi) { case 0x10: // As if user primary case 0x11: // As if user secondary case 0x18: // As if user primary LE @@ -2831,8 +2868,6 @@ case 0x1d: // Bypass, non-cacheable LE case 0x88: // Primary LE case 0x89: // Secondary LE - case 0x8a: // Primary no-fault LE - case 0x8b: // Secondary no-fault LE switch(size) { case 2: ret = bswap16(ret); @@ -3309,16 +3344,16 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) { unsigned int i; - target_ulong val; + CPU_DoubleU u; helper_check_align(addr, 3); addr = asi_address_mask(env, asi, addr); switch (asi) { - case 0xf0: // Block load primary - case 0xf1: // Block load secondary - case 0xf8: // Block load primary LE - case 0xf9: // Block load secondary LE + case 0xf0: /* UA2007/JPS1 Block load primary */ + case 0xf1: /* UA2007/JPS1 Block load secondary */ + case 0xf8: /* UA2007/JPS1 Block load primary LE */ + case 0xf9: /* UA2007/JPS1 Block load secondary LE */ if (rd & 7) { raise_exception(TT_ILL_INSN); return; @@ -3331,15 +3366,21 @@ } return; - case 0x70: // Block load primary, user privilege - case 0x71: // Block load secondary, user privilege + case 0x16: /* UA2007 Block load primary, user privilege */ + case 0x17: /* UA2007 Block load secondary, user privilege */ + case 0x1e: /* UA2007 Block load primary LE, user privilege */ + case 0x1f: /* UA2007 Block load secondary LE, user privilege */ + case 0x70: /* JPS1 Block load primary, user privilege */ + case 0x71: /* JPS1 Block load secondary, user privilege */ + case 0x78: /* JPS1 Block load primary LE, user privilege */ + case 0x79: /* JPS1 Block load secondary LE, 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, + *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x19, 4, 0); addr += 4; } @@ -3349,17 +3390,23 @@ break; } - val = helper_ld_asi(addr, asi, size, 0); switch(size) { default: case 4: - *((uint32_t *)&env->fpr[rd]) = val; + *((uint32_t *)&env->fpr[rd]) = helper_ld_asi(addr, asi, size, 0); break; case 8: - *((int64_t *)&DT0) = val; + u.ll = helper_ld_asi(addr, asi, size, 0); + *((uint32_t *)&env->fpr[rd++]) = u.l.upper; + *((uint32_t *)&env->fpr[rd++]) = u.l.lower; break; case 16: - // XXX + u.ll = helper_ld_asi(addr, asi, 8, 0); + *((uint32_t *)&env->fpr[rd++]) = u.l.upper; + *((uint32_t *)&env->fpr[rd++]) = u.l.lower; + u.ll = helper_ld_asi(addr + 8, asi, 8, 0); + *((uint32_t *)&env->fpr[rd++]) = u.l.upper; + *((uint32_t *)&env->fpr[rd++]) = u.l.lower; break; } } @@ -3368,17 +3415,18 @@ { unsigned int i; target_ulong val = 0; + CPU_DoubleU u; 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) - case 0xf0: // Block store primary - case 0xf1: // Block store secondary - case 0xf8: // Block store primary LE - case 0xf9: // Block store secondary LE + case 0xe0: /* UA2007/JPS1 Block commit store primary (cache flush) */ + case 0xe1: /* UA2007/JPS1 Block commit store secondary (cache flush) */ + case 0xf0: /* UA2007/JPS1 Block store primary */ + case 0xf1: /* UA2007/JPS1 Block store secondary */ + case 0xf8: /* UA2007/JPS1 Block store primary LE */ + case 0xf9: /* UA2007/JPS1 Block store secondary LE */ if (rd & 7) { raise_exception(TT_ILL_INSN); return; @@ -3391,8 +3439,14 @@ } return; - case 0x70: // Block store primary, user privilege - case 0x71: // Block store secondary, user privilege + case 0x16: /* UA2007 Block load primary, user privilege */ + case 0x17: /* UA2007 Block load secondary, user privilege */ + case 0x1e: /* UA2007 Block load primary LE, user privilege */ + case 0x1f: /* UA2007 Block load secondary LE, user privilege */ + case 0x70: /* JPS1 Block store primary, user privilege */ + case 0x71: /* JPS1 Block store secondary, user privilege */ + case 0x78: /* JPS1 Block load primary LE, user privilege */ + case 0x79: /* JPS1 Block load secondary LE, user privilege */ if (rd & 7) { raise_exception(TT_ILL_INSN); return; @@ -3400,7 +3454,7 @@ 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); + helper_st_asi(addr, val, asi & 0x19, 4); addr += 4; } @@ -3412,16 +3466,22 @@ switch(size) { default: case 4: - val = *((uint32_t *)&env->fpr[rd]); + helper_st_asi(addr, *(uint32_t *)&env->fpr[rd], asi, size); break; case 8: - val = *((int64_t *)&DT0); + u.l.upper = *(uint32_t *)&env->fpr[rd++]; + u.l.lower = *(uint32_t *)&env->fpr[rd++]; + helper_st_asi(addr, u.ll, asi, size); break; case 16: - // XXX + u.l.upper = *(uint32_t *)&env->fpr[rd++]; + u.l.lower = *(uint32_t *)&env->fpr[rd++]; + helper_st_asi(addr, u.ll, asi, 8); + u.l.upper = *(uint32_t *)&env->fpr[rd++]; + u.l.lower = *(uint32_t *)&env->fpr[rd++]; + helper_st_asi(addr + 8, u.ll, asi, 8); break; } - helper_st_asi(addr, val, asi, size); } target_ulong helper_cas_asi(target_ulong addr, target_ulong val1, @@ -3702,7 +3762,7 @@ void helper_debug(void) { env->exception_index = EXCP_DEBUG; - cpu_loop_exit(); + cpu_loop_exit(env); } #ifndef TARGET_SPARC64 @@ -4007,6 +4067,16 @@ #endif } +void cpu_change_pstate(CPUState *env1, uint32_t new_pstate) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + change_pstate(new_pstate); + env = saved_env; +} + void helper_wrpil(target_ulong new_pil) { #if !defined(CONFIG_USER_ONLY) @@ -4092,254 +4162,11 @@ } #endif -void helper_flush(target_ulong addr) -{ - addr &= ~7; - tb_invalidate_page_range(addr, addr + 8); -} - #ifdef TARGET_SPARC64 -#ifdef DEBUG_PCALL -static const char * const excp_names[0x80] = { - [TT_TFAULT] = "Instruction Access Fault", - [TT_TMISS] = "Instruction Access MMU Miss", - [TT_CODE_ACCESS] = "Instruction Access Error", - [TT_ILL_INSN] = "Illegal Instruction", - [TT_PRIV_INSN] = "Privileged Instruction", - [TT_NFPU_INSN] = "FPU Disabled", - [TT_FP_EXCP] = "FPU Exception", - [TT_TOVF] = "Tag Overflow", - [TT_CLRWIN] = "Clean Windows", - [TT_DIV_ZERO] = "Division By Zero", - [TT_DFAULT] = "Data Access Fault", - [TT_DMISS] = "Data Access MMU Miss", - [TT_DATA_ACCESS] = "Data Access Error", - [TT_DPROT] = "Data Protection Error", - [TT_UNALIGNED] = "Unaligned Memory Access", - [TT_PRIV_ACT] = "Privileged Action", - [TT_EXTINT | 0x1] = "External Interrupt 1", - [TT_EXTINT | 0x2] = "External Interrupt 2", - [TT_EXTINT | 0x3] = "External Interrupt 3", - [TT_EXTINT | 0x4] = "External Interrupt 4", - [TT_EXTINT | 0x5] = "External Interrupt 5", - [TT_EXTINT | 0x6] = "External Interrupt 6", - [TT_EXTINT | 0x7] = "External Interrupt 7", - [TT_EXTINT | 0x8] = "External Interrupt 8", - [TT_EXTINT | 0x9] = "External Interrupt 9", - [TT_EXTINT | 0xa] = "External Interrupt 10", - [TT_EXTINT | 0xb] = "External Interrupt 11", - [TT_EXTINT | 0xc] = "External Interrupt 12", - [TT_EXTINT | 0xd] = "External Interrupt 13", - [TT_EXTINT | 0xe] = "External Interrupt 14", - [TT_EXTINT | 0xf] = "External Interrupt 15", -}; -#endif - trap_state* cpu_tsptr(CPUState* env) { return &env->ts[env->tl & MAXTL_MASK]; } - -void do_interrupt(CPUState *env) -{ - int intno = env->exception_index; - trap_state* tsptr; - -#ifdef DEBUG_PCALL - if (qemu_loglevel_mask(CPU_LOG_INT)) { - static int count; - const char *name; - - if (intno < 0 || intno >= 0x180) - name = "Unknown"; - else if (intno >= 0x100) - name = "Trap Instruction"; - else if (intno >= 0xc0) - name = "Window Fill"; - else if (intno >= 0x80) - name = "Window Spill"; - else { - name = excp_names[intno]; - if (!name) - name = "Unknown"; - } - - qemu_log("%6d: %s (v=%04x) pc=%016" PRIx64 " npc=%016" PRIx64 - " SP=%016" PRIx64 "\n", - count, name, intno, - env->pc, - env->npc, env->regwptr[6]); - log_cpu_state(env, 0); -#if 0 - { - int i; - uint8_t *ptr; - - qemu_log(" code="); - ptr = (uint8_t *)env->pc; - for(i = 0; i < 16; i++) { - qemu_log(" %02x", ldub(ptr + i)); - } - qemu_log("\n"); - } -#endif - count++; - } -#endif -#if !defined(CONFIG_USER_ONLY) - if (env->tl >= env->maxtl) { - cpu_abort(env, "Trap 0x%04x while trap level (%d) >= MAXTL (%d)," - " Error state", env->exception_index, env->tl, env->maxtl); - return; - } -#endif - if (env->tl < env->maxtl - 1) { - env->tl++; - } else { - env->pstate |= PS_RED; - if (env->tl < env->maxtl) - env->tl++; - } - tsptr = cpu_tsptr(env); - - tsptr->tstate = (get_ccr() << 32) | - ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) | - get_cwp64(); - tsptr->tpc = env->pc; - tsptr->tnpc = env->npc; - tsptr->tt = intno; - - switch (intno) { - case TT_IVEC: - change_pstate(PS_PEF | PS_PRIV | PS_IG); - break; - case TT_TFAULT: - case TT_DFAULT: - 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: - change_pstate(PS_PEF | PS_PRIV | PS_AG); - break; - } - - 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 = -1; -} -#else -#ifdef DEBUG_PCALL -static const char * const excp_names[0x80] = { - [TT_TFAULT] = "Instruction Access Fault", - [TT_ILL_INSN] = "Illegal Instruction", - [TT_PRIV_INSN] = "Privileged Instruction", - [TT_NFPU_INSN] = "FPU Disabled", - [TT_WIN_OVF] = "Window Overflow", - [TT_WIN_UNF] = "Window Underflow", - [TT_UNALIGNED] = "Unaligned Memory Access", - [TT_FP_EXCP] = "FPU Exception", - [TT_DFAULT] = "Data Access Fault", - [TT_TOVF] = "Tag Overflow", - [TT_EXTINT | 0x1] = "External Interrupt 1", - [TT_EXTINT | 0x2] = "External Interrupt 2", - [TT_EXTINT | 0x3] = "External Interrupt 3", - [TT_EXTINT | 0x4] = "External Interrupt 4", - [TT_EXTINT | 0x5] = "External Interrupt 5", - [TT_EXTINT | 0x6] = "External Interrupt 6", - [TT_EXTINT | 0x7] = "External Interrupt 7", - [TT_EXTINT | 0x8] = "External Interrupt 8", - [TT_EXTINT | 0x9] = "External Interrupt 9", - [TT_EXTINT | 0xa] = "External Interrupt 10", - [TT_EXTINT | 0xb] = "External Interrupt 11", - [TT_EXTINT | 0xc] = "External Interrupt 12", - [TT_EXTINT | 0xd] = "External Interrupt 13", - [TT_EXTINT | 0xe] = "External Interrupt 14", - [TT_EXTINT | 0xf] = "External Interrupt 15", - [TT_TOVF] = "Tag Overflow", - [TT_CODE_ACCESS] = "Instruction Access Error", - [TT_DATA_ACCESS] = "Data Access Error", - [TT_DIV_ZERO] = "Division By Zero", - [TT_NCP_INSN] = "Coprocessor Disabled", -}; -#endif - -void do_interrupt(CPUState *env) -{ - int cwp, intno = env->exception_index; - -#ifdef DEBUG_PCALL - if (qemu_loglevel_mask(CPU_LOG_INT)) { - static int count; - const char *name; - - if (intno < 0 || intno >= 0x100) - name = "Unknown"; - else if (intno >= 0x80) - name = "Trap Instruction"; - else { - name = excp_names[intno]; - if (!name) - name = "Unknown"; - } - - qemu_log("%6d: %s (v=%02x) pc=%08x npc=%08x SP=%08x\n", - count, name, intno, - env->pc, - env->npc, env->regwptr[6]); - log_cpu_state(env, 0); -#if 0 - { - int i; - uint8_t *ptr; - - qemu_log(" code="); - ptr = (uint8_t *)env->pc; - for(i = 0; i < 16; i++) { - qemu_log(" %02x", ldub(ptr + i)); - } - qemu_log("\n"); - } -#endif - count++; - } -#endif -#if !defined(CONFIG_USER_ONLY) - if (env->psret == 0) { - cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state", - env->exception_index); - return; - } -#endif - env->psret = 0; - cwp = cwp_dec(env->cwp - 1); - set_cwp(cwp); - env->regwptr[9] = env->pc; - env->regwptr[10] = env->npc; - env->psrps = env->psrs; - env->psrs = 1; - env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); - env->pc = env->tbr; - env->npc = env->pc + 4; - 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 #if !defined(CONFIG_USER_ONLY) @@ -4375,7 +4202,7 @@ if (tb) { /* the PC is inside the translated code. It means that we have a virtual CPU fault */ - cpu_restore_state(tb, env, pc, (void *)(long)env->cond); + cpu_restore_state(tb, env, pc); } } } @@ -4408,7 +4235,7 @@ ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); if (ret) { cpu_restore_state2(retaddr); - cpu_loop_exit(); + cpu_loop_exit(env); } env = saved_env; } @@ -4417,8 +4244,8 @@ #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) +static 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; @@ -4483,8 +4310,8 @@ 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) +static void do_unassigned_access(target_phys_addr_t addr, int is_write, + int is_exec, int is_asi, int size) #endif { CPUState *saved_env; @@ -4533,3 +4360,12 @@ #endif } #endif + +#if !defined(CONFIG_USER_ONLY) +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int is_asi, int size) +{ + env = env1; + do_unassigned_access(addr, is_write, is_exec, is_asi, size); +} +#endif diff -Nru qemu-kvm-0.14.1+noroms/target-sparc/translate.c qemu-kvm-0.15.0+noroms/target-sparc/translate.c --- qemu-kvm-0.14.1+noroms/target-sparc/translate.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/target-sparc/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -25,7 +25,6 @@ #include #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "helper.h" #include "tcg-op.h" @@ -241,7 +240,7 @@ tcg_gen_goto_tb(tb_num); tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb((long)tb + tb_num); + tcg_gen_exit_tb((tcg_target_long)tb + tb_num); } else { /* jump to another page: currently not optimized */ tcg_gen_movi_tl(cpu_pc, pc); @@ -728,19 +727,24 @@ static inline void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2) { int l1, l2; + TCGv r_temp1, r_temp2; l1 = gen_new_label(); l2 = gen_new_label(); - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - gen_trap_ifdivzero_tl(cpu_cc_src2); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cc_src, INT64_MIN, l1); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cc_src2, -1, l1); + r_temp1 = tcg_temp_local_new(); + r_temp2 = tcg_temp_local_new(); + tcg_gen_mov_tl(r_temp1, src1); + tcg_gen_mov_tl(r_temp2, src2); + gen_trap_ifdivzero_tl(r_temp2); + tcg_gen_brcondi_tl(TCG_COND_NE, r_temp1, INT64_MIN, l1); + tcg_gen_brcondi_tl(TCG_COND_NE, r_temp2, -1, l1); tcg_gen_movi_i64(dst, INT64_MIN); tcg_gen_br(l2); gen_set_label(l1); - tcg_gen_div_i64(dst, cpu_cc_src, cpu_cc_src2); + tcg_gen_div_i64(dst, r_temp1, r_temp2); gen_set_label(l2); + tcg_temp_free(r_temp1); + tcg_temp_free(r_temp2); } #endif @@ -1893,7 +1897,7 @@ int cc; target = GET_FIELD_SP(insn, 0, 18); - target = sign_extend(target, 18); + target = sign_extend(target, 19); target <<= 2; cc = GET_FIELD_SP(insn, 20, 21); if (cc == 0) @@ -2860,6 +2864,7 @@ break; case 0x102: /* V9 fmovdcc %icc */ FMOVDCC(0); + break; case 0x103: /* V9 fmovqcc %icc */ CHECK_FPU_FEATURE(dc, FLOAT128); FMOVQCC(0); @@ -3174,10 +3179,17 @@ break; #ifdef TARGET_SPARC64 case 0xd: /* V9 udivx */ - tcg_gen_mov_tl(cpu_cc_src, cpu_src1); - tcg_gen_mov_tl(cpu_cc_src2, cpu_src2); - gen_trap_ifdivzero_tl(cpu_cc_src2); - tcg_gen_divu_i64(cpu_dst, cpu_cc_src, cpu_cc_src2); + { + TCGv r_temp1, r_temp2; + r_temp1 = tcg_temp_local_new(); + r_temp2 = tcg_temp_local_new(); + tcg_gen_mov_tl(r_temp1, cpu_src1); + tcg_gen_mov_tl(r_temp2, cpu_src2); + gen_trap_ifdivzero_tl(r_temp2); + tcg_gen_divu_i64(cpu_dst, r_temp1, r_temp2); + tcg_temp_free(r_temp1); + tcg_temp_free(r_temp2); + } break; #endif case 0xe: /* udiv */ @@ -3505,16 +3517,28 @@ tcg_gen_mov_tl(cpu_tbr, cpu_tmp0); break; case 6: // pstate - save_state(dc, cpu_cond); - gen_helper_wrpstate(cpu_tmp0); - dc->npc = DYNAMIC_PC; + { + TCGv r_tmp = tcg_temp_local_new(); + + tcg_gen_mov_tl(r_tmp, cpu_tmp0); + save_state(dc, cpu_cond); + gen_helper_wrpstate(r_tmp); + tcg_temp_free(r_tmp); + 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; + { + TCGv r_tmp = tcg_temp_local_new(); + + tcg_gen_mov_tl(r_tmp, cpu_tmp0); + save_state(dc, cpu_cond); + tcg_gen_trunc_tl_i32(cpu_tmp32, r_tmp); + tcg_temp_free(r_tmp); + tcg_gen_st_i32(cpu_tmp32, cpu_env, + offsetof(CPUSPARCState, tl)); + dc->npc = DYNAMIC_PC; + } break; case 8: // pil gen_helper_wrpil(cpu_tmp0); @@ -3766,57 +3790,57 @@ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmple16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmple16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x022: /* VIS I fcmpne16 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpne16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpne16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x024: /* VIS I fcmple32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmple32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmple32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x026: /* VIS I fcmpne32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpne32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpne32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x028: /* VIS I fcmpgt16 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpgt16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpgt16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x02a: /* VIS I fcmpeq16 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpeq16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpeq16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x02c: /* VIS I fcmpgt32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpgt32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpgt32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x02e: /* VIS I fcmpeq32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpeq32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpeq32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x031: /* VIS I fmul8x16 */ CHECK_FPU_FEATURE(dc, VIS1); @@ -4214,7 +4238,7 @@ case 0x3b: /* flush */ if (!((dc)->def->features & CPU_FEATURE_FLUSH)) goto unimp_flush; - gen_helper_flush(cpu_dst); + /* nop */ break; case 0x3c: /* save */ save_state(dc, cpu_cond); @@ -4461,10 +4485,16 @@ case 0x2d: /* V9 prefetch, no effect */ goto skip_move; case 0x30: /* V9 ldfa */ + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 4, rd); goto skip_move; case 0x33: /* V9 lddfa */ + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 8, DFPREG(rd)); goto skip_move; @@ -4472,6 +4502,9 @@ goto skip_move; case 0x32: /* V9 ldqfa */ CHECK_FPU_FEATURE(dc, FLOAT128); + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 16, QFPREG(rd)); goto skip_move; @@ -4700,6 +4733,9 @@ switch (xop) { #ifdef TARGET_SPARC64 case 0x34: /* V9 stfa */ + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } gen_stf_asi(cpu_addr, insn, 4, rd); break; case 0x36: /* V9 stqfa */ @@ -4707,15 +4743,19 @@ TCGv_i32 r_const; CHECK_FPU_FEATURE(dc, FLOAT128); + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } r_const = tcg_const_i32(7); gen_helper_check_align(cpu_addr, r_const); tcg_temp_free_i32(r_const); - gen_op_load_fpr_QT0(QFPREG(rd)); gen_stf_asi(cpu_addr, insn, 16, QFPREG(rd)); } break; case 0x37: /* V9 stdfa */ - gen_op_load_fpr_DT0(DFPREG(rd)); + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } gen_stf_asi(cpu_addr, insn, 8, DFPREG(rd)); break; case 0x3c: /* V9 casa */ @@ -4840,13 +4880,8 @@ dc->cc_op = CC_OP_DYNAMIC; dc->mem_idx = cpu_mmu_index(env); dc->def = env->def; - if ((dc->def->features & CPU_FEATURE_FLOAT)) - dc->fpu_enabled = cpu_fpu_enabled(env); - else - dc->fpu_enabled = 0; -#ifdef TARGET_SPARC64 - dc->address_mask_32bit = env->pstate & PS_AM; -#endif + dc->fpu_enabled = tb_fpu_enabled(tb->flags); + dc->address_mask_32bit = tb_am_enabled(tb->flags); dc->singlestep = (env->singlestep_enabled || singlestep); gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; @@ -5080,8 +5115,7 @@ } } -void gen_pc_load(CPUState *env, TranslationBlock *tb, - unsigned long searched_pc, int pc_pos, void *puc) +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) { target_ulong npc; env->pc = gen_opc_pc[pc_pos]; diff -Nru qemu-kvm-0.14.1+noroms/target-unicore32/cpu.h qemu-kvm-0.15.0+noroms/target-unicore32/cpu.h --- qemu-kvm-0.14.1+noroms/target-unicore32/cpu.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-unicore32/cpu.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,188 @@ +/* + * UniCore32 virtual CPU header + * + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#ifndef __CPU_UC32_H__ +#define __CPU_UC32_H__ + +#define TARGET_LONG_BITS 32 +#define TARGET_PAGE_BITS 12 + +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#define ELF_MACHINE EM_UNICORE32 + +#define CPUState struct CPUState_UniCore32 + +#include "cpu-defs.h" +#include "softfloat.h" + +#define NB_MMU_MODES 2 + +typedef struct CPUState_UniCore32 { + /* Regs for current mode. */ + uint32_t regs[32]; + /* Frequently accessed ASR bits are stored separately for efficiently. + This contains all the other bits. Use asr_{read,write} to access + the whole ASR. */ + uint32_t uncached_asr; + uint32_t bsr; + + /* Banked registers. */ + uint32_t banked_bsr[6]; + uint32_t banked_r29[6]; + uint32_t banked_r30[6]; + + /* asr flag cache for faster execution */ + uint32_t CF; /* 0 or 1 */ + uint32_t VF; /* V is the bit 31. All other bits are undefined */ + uint32_t NF; /* N is bit 31. All other bits are undefined. */ + uint32_t ZF; /* Z set if zero. */ + + /* System control coprocessor (cp0) */ + struct { + uint32_t c0_cpuid; + uint32_t c0_cachetype; + uint32_t c1_sys; /* System control register. */ + uint32_t c2_base; /* MMU translation table base. */ + uint32_t c3_faultstatus; /* Fault status registers. */ + uint32_t c4_faultaddr; /* Fault address registers. */ + uint32_t c5_cacheop; /* Cache operation registers. */ + uint32_t c6_tlbop; /* TLB operation registers. */ + } cp0; + + /* UniCore-F64 coprocessor state. */ + struct { + float64 regs[16]; + uint32_t xregs[32]; + float_status fp_status; + } ucf64; + + CPU_COMMON + + /* Internal CPU feature flags. */ + uint32_t features; + +} CPUState_UniCore32; + +#define ASR_M (0x1f) +#define ASR_MODE_USER (0x10) +#define ASR_MODE_INTR (0x12) +#define ASR_MODE_PRIV (0x13) +#define ASR_MODE_TRAP (0x17) +#define ASR_MODE_EXTN (0x1b) +#define ASR_MODE_SUSR (0x1f) +#define ASR_I (1 << 7) +#define ASR_V (1 << 28) +#define ASR_C (1 << 29) +#define ASR_Z (1 << 30) +#define ASR_N (1 << 31) +#define ASR_NZCV (ASR_N | ASR_Z | ASR_C | ASR_V) +#define ASR_RESERVED (~(ASR_M | ASR_I | ASR_NZCV)) + +#define UC32_EXCP_PRIV (ASR_MODE_PRIV) +#define UC32_EXCP_TRAP (ASR_MODE_TRAP) + +/* Return the current ASR value. */ +target_ulong cpu_asr_read(CPUState *env1); +/* Set the ASR. Note that some bits of mask must be all-set or all-clear. */ +void cpu_asr_write(CPUState *env1, target_ulong val, target_ulong mask); + +/* UniCore-F64 system registers. */ +#define UC32_UCF64_FPSCR (31) +#define UCF64_FPSCR_MASK (0x27ffffff) +#define UCF64_FPSCR_RND_MASK (0x7) +#define UCF64_FPSCR_RND(r) (((r) >> 0) & UCF64_FPSCR_RND_MASK) +#define UCF64_FPSCR_TRAPEN_MASK (0x7f) +#define UCF64_FPSCR_TRAPEN(r) (((r) >> 10) & UCF64_FPSCR_TRAPEN_MASK) +#define UCF64_FPSCR_FLAG_MASK (0x3ff) +#define UCF64_FPSCR_FLAG(r) (((r) >> 17) & UCF64_FPSCR_FLAG_MASK) +#define UCF64_FPSCR_FLAG_ZERO (1 << 17) +#define UCF64_FPSCR_FLAG_INFINITY (1 << 18) +#define UCF64_FPSCR_FLAG_INVALID (1 << 19) +#define UCF64_FPSCR_FLAG_UNDERFLOW (1 << 20) +#define UCF64_FPSCR_FLAG_OVERFLOW (1 << 21) +#define UCF64_FPSCR_FLAG_INEXACT (1 << 22) +#define UCF64_FPSCR_FLAG_HUGEINT (1 << 23) +#define UCF64_FPSCR_FLAG_DENORMAL (1 << 24) +#define UCF64_FPSCR_FLAG_UNIMP (1 << 25) +#define UCF64_FPSCR_FLAG_DIVZERO (1 << 26) + +#define UC32_HWCAP_CMOV 4 /* 1 << 2 */ +#define UC32_HWCAP_UCF64 8 /* 1 << 3 */ + +#define UC32_CPUID(env) (env->cp0.c0_cpuid) +#define UC32_CPUID_UCV2 0x40010863 +#define UC32_CPUID_ANY 0xffffffff + +#define cpu_init uc32_cpu_init +#define cpu_exec uc32_cpu_exec +#define cpu_signal_handler uc32_cpu_signal_handler +#define cpu_handle_mmu_fault uc32_cpu_handle_mmu_fault + +CPUState *uc32_cpu_init(const char *cpu_model); +int uc32_cpu_exec(CPUState *s); +int uc32_cpu_signal_handler(int host_signum, void *pinfo, void *puc); +int uc32_cpu_handle_mmu_fault(CPUState *env, target_ulong address, int rw, + int mmu_idx, int is_softmuu); + +#define CPU_SAVE_VERSION 2 + +/* MMU modes definitions */ +#define MMU_MODE0_SUFFIX _kernel +#define MMU_MODE1_SUFFIX _user +#define MMU_USER_IDX 1 +static inline int cpu_mmu_index(CPUState *env) +{ + return (env->uncached_asr & ASR_M) == ASR_MODE_USER ? 1 : 0; +} + +static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) +{ + if (newsp) { + env->regs[29] = newsp; + } + env->regs[0] = 0; +} + +static inline void cpu_set_tls(CPUState *env, target_ulong newtls) +{ + env->regs[16] = newtls; +} + +#include "cpu-all.h" +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->regs[31] = tb->pc; +} + +static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = env->regs[31]; + *cs_base = 0; + *flags = 0; + if ((env->uncached_asr & ASR_M) != ASR_MODE_USER) { + *flags |= (1 << 6); + } +} + +void uc32_translate_init(void); +void do_interrupt(CPUState *); +void switch_mode(CPUState_UniCore32 *, int); + +static inline bool cpu_has_work(CPUState *env) +{ + return env->interrupt_request & + (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB); +} + +#endif /* __CPU_UC32_H__ */ diff -Nru qemu-kvm-0.14.1+noroms/target-unicore32/exec.h qemu-kvm-0.15.0+noroms/target-unicore32/exec.h --- qemu-kvm-0.14.1+noroms/target-unicore32/exec.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-unicore32/exec.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,43 @@ +/* + * UniCore32 execution defines + * + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#ifndef __UC32_EXEC_H__ +#define __UC32_EXEC_H__ + +#include "config.h" +#include "dyngen-exec.h" + +register struct CPUState_UniCore32 *env asm(AREG0); + +#include "cpu.h" + +static inline void env_to_regs(void) +{ +} + +static inline void regs_to_env(void) +{ +} + +static inline int cpu_halted(CPUState *env) +{ + if (!env->halted) { + return 0; + } + /* An interrupt wakes the CPU even if the I and R ASR bits are + set. We use EXITTB to silently wake CPU without causing an + actual interrupt. */ + if (cpu_has_work(env)) { + env->halted = 0; + return 0; + } + return EXCP_HALTED; +} + +#endif /* __UC32_EXEC_H__ */ diff -Nru qemu-kvm-0.14.1+noroms/target-unicore32/helper.c qemu-kvm-0.15.0+noroms/target-unicore32/helper.c --- qemu-kvm-0.14.1+noroms/target-unicore32/helper.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-unicore32/helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#include +#include +#include + +#include "cpu.h" +#include "gdbstub.h" +#include "helper.h" +#include "qemu-common.h" +#include "host-utils.h" + +static inline void set_feature(CPUState *env, int feature) +{ + env->features |= feature; +} + +struct uc32_cpu_t { + uint32_t id; + const char *name; +}; + +static const struct uc32_cpu_t uc32_cpu_names[] = { + { UC32_CPUID_UCV2, "UniCore-II"}, + { UC32_CPUID_ANY, "any"}, + { 0, NULL} +}; + +/* return 0 if not found */ +static uint32_t uc32_cpu_find_by_name(const char *name) +{ + int i; + uint32_t id; + + id = 0; + for (i = 0; uc32_cpu_names[i].name; i++) { + if (strcmp(name, uc32_cpu_names[i].name) == 0) { + id = uc32_cpu_names[i].id; + break; + } + } + return id; +} + +CPUState *uc32_cpu_init(const char *cpu_model) +{ + CPUState *env; + uint32_t id; + static int inited = 1; + + env = qemu_mallocz(sizeof(CPUState)); + cpu_exec_init(env); + + id = uc32_cpu_find_by_name(cpu_model); + switch (id) { + case UC32_CPUID_UCV2: + set_feature(env, UC32_HWCAP_CMOV); + set_feature(env, UC32_HWCAP_UCF64); + env->ucf64.xregs[UC32_UCF64_FPSCR] = 0; + env->cp0.c0_cachetype = 0x1dd20d2; + env->cp0.c1_sys = 0x00090078; + break; + case UC32_CPUID_ANY: /* For userspace emulation. */ + set_feature(env, UC32_HWCAP_CMOV); + set_feature(env, UC32_HWCAP_UCF64); + break; + default: + cpu_abort(env, "Bad CPU ID: %x\n", id); + } + + env->cpu_model_str = cpu_model; + env->cp0.c0_cpuid = id; + env->uncached_asr = ASR_MODE_USER; + env->regs[31] = 0; + + if (inited) { + inited = 0; + uc32_translate_init(); + } + + tlb_flush(env, 1); + qemu_init_vcpu(env); + return env; +} + +uint32_t HELPER(clo)(uint32_t x) +{ + return clo32(x); +} + +uint32_t HELPER(clz)(uint32_t x) +{ + return clz32(x); +} + +void do_interrupt(CPUState *env) +{ + env->exception_index = -1; +} + +int uc32_cpu_handle_mmu_fault(CPUState *env, target_ulong address, int rw, + int mmu_idx, int is_softmmu) +{ + env->exception_index = UC32_EXCP_TRAP; + env->cp0.c4_faultaddr = address; + return 1; +} + +/* These should probably raise undefined insn exceptions. */ +void HELPER(set_cp)(CPUState *env, uint32_t insn, uint32_t val) +{ + int op1 = (insn >> 8) & 0xf; + cpu_abort(env, "cp%i insn %08x\n", op1, insn); + return; +} + +uint32_t HELPER(get_cp)(CPUState *env, uint32_t insn) +{ + int op1 = (insn >> 8) & 0xf; + cpu_abort(env, "cp%i insn %08x\n", op1, insn); + return 0; +} + +void HELPER(set_cp0)(CPUState *env, uint32_t insn, uint32_t val) +{ + cpu_abort(env, "cp0 insn %08x\n", insn); +} + +uint32_t HELPER(get_cp0)(CPUState *env, uint32_t insn) +{ + cpu_abort(env, "cp0 insn %08x\n", insn); + return 0; +} + +void switch_mode(CPUState *env, int mode) +{ + if (mode != ASR_MODE_USER) { + cpu_abort(env, "Tried to switch out of user mode\n"); + } +} + +void HELPER(set_r29_banked)(CPUState *env, uint32_t mode, uint32_t val) +{ + cpu_abort(env, "banked r29 write\n"); +} + +uint32_t HELPER(get_r29_banked)(CPUState *env, uint32_t mode) +{ + cpu_abort(env, "banked r29 read\n"); + return 0; +} + +/* UniCore-F64 support. We follow the convention used for F64 instrunctions: + Single precition routines have a "s" suffix, double precision a + "d" suffix. */ + +/* Convert host exception flags to f64 form. */ +static inline int ucf64_exceptbits_from_host(int host_bits) +{ + int target_bits = 0; + + if (host_bits & float_flag_invalid) { + target_bits |= UCF64_FPSCR_FLAG_INVALID; + } + if (host_bits & float_flag_divbyzero) { + target_bits |= UCF64_FPSCR_FLAG_DIVZERO; + } + if (host_bits & float_flag_overflow) { + target_bits |= UCF64_FPSCR_FLAG_OVERFLOW; + } + if (host_bits & float_flag_underflow) { + target_bits |= UCF64_FPSCR_FLAG_UNDERFLOW; + } + if (host_bits & float_flag_inexact) { + target_bits |= UCF64_FPSCR_FLAG_INEXACT; + } + return target_bits; +} + +uint32_t HELPER(ucf64_get_fpscr)(CPUState *env) +{ + int i; + uint32_t fpscr; + + fpscr = (env->ucf64.xregs[UC32_UCF64_FPSCR] & UCF64_FPSCR_MASK); + i = get_float_exception_flags(&env->ucf64.fp_status); + fpscr |= ucf64_exceptbits_from_host(i); + return fpscr; +} + +/* Convert ucf64 exception flags to target form. */ +static inline int ucf64_exceptbits_to_host(int target_bits) +{ + int host_bits = 0; + + if (target_bits & UCF64_FPSCR_FLAG_INVALID) { + host_bits |= float_flag_invalid; + } + if (target_bits & UCF64_FPSCR_FLAG_DIVZERO) { + host_bits |= float_flag_divbyzero; + } + if (target_bits & UCF64_FPSCR_FLAG_OVERFLOW) { + host_bits |= float_flag_overflow; + } + if (target_bits & UCF64_FPSCR_FLAG_UNDERFLOW) { + host_bits |= float_flag_underflow; + } + if (target_bits & UCF64_FPSCR_FLAG_INEXACT) { + host_bits |= float_flag_inexact; + } + return host_bits; +} + +void HELPER(ucf64_set_fpscr)(CPUState *env, uint32_t val) +{ + int i; + uint32_t changed; + + changed = env->ucf64.xregs[UC32_UCF64_FPSCR]; + env->ucf64.xregs[UC32_UCF64_FPSCR] = (val & UCF64_FPSCR_MASK); + + changed ^= val; + if (changed & (UCF64_FPSCR_RND_MASK)) { + i = UCF64_FPSCR_RND(val); + switch (i) { + case 0: + i = float_round_nearest_even; + break; + case 1: + i = float_round_to_zero; + break; + case 2: + i = float_round_up; + break; + case 3: + i = float_round_down; + break; + default: /* 100 and 101 not implement */ + cpu_abort(env, "Unsupported UniCore-F64 round mode"); + } + set_float_rounding_mode(i, &env->ucf64.fp_status); + } + + i = ucf64_exceptbits_to_host(UCF64_FPSCR_TRAPEN(val)); + set_float_exception_flags(i, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_adds)(float32 a, float32 b, CPUState *env) +{ + return float32_add(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_addd)(float64 a, float64 b, CPUState *env) +{ + return float64_add(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_subs)(float32 a, float32 b, CPUState *env) +{ + return float32_sub(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_subd)(float64 a, float64 b, CPUState *env) +{ + return float64_sub(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_muls)(float32 a, float32 b, CPUState *env) +{ + return float32_mul(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_muld)(float64 a, float64 b, CPUState *env) +{ + return float64_mul(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_divs)(float32 a, float32 b, CPUState *env) +{ + return float32_div(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_divd)(float64 a, float64 b, CPUState *env) +{ + return float64_div(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_negs)(float32 a) +{ + return float32_chs(a); +} + +float64 HELPER(ucf64_negd)(float64 a) +{ + return float64_chs(a); +} + +float32 HELPER(ucf64_abss)(float32 a) +{ + return float32_abs(a); +} + +float64 HELPER(ucf64_absd)(float64 a) +{ + return float64_abs(a); +} + +/* XXX: check quiet/signaling case */ +void HELPER(ucf64_cmps)(float32 a, float32 b, uint32_t c, CPUState *env) +{ + int flag; + flag = float32_compare_quiet(a, b, &env->ucf64.fp_status); + env->CF = 0; + switch (c & 0x7) { + case 0: /* F */ + break; + case 1: /* UN */ + if (flag == 2) { + env->CF = 1; + } + break; + case 2: /* EQ */ + if (flag == 0) { + env->CF = 1; + } + break; + case 3: /* UEQ */ + if ((flag == 0) || (flag == 2)) { + env->CF = 1; + } + break; + case 4: /* OLT */ + if (flag == -1) { + env->CF = 1; + } + break; + case 5: /* ULT */ + if ((flag == -1) || (flag == 2)) { + env->CF = 1; + } + break; + case 6: /* OLE */ + if ((flag == -1) || (flag == 0)) { + env->CF = 1; + } + break; + case 7: /* ULE */ + if (flag != 1) { + env->CF = 1; + } + break; + } + env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29) + | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff); +} + +void HELPER(ucf64_cmpd)(float64 a, float64 b, uint32_t c, CPUState *env) +{ + int flag; + flag = float64_compare_quiet(a, b, &env->ucf64.fp_status); + env->CF = 0; + switch (c & 0x7) { + case 0: /* F */ + break; + case 1: /* UN */ + if (flag == 2) { + env->CF = 1; + } + break; + case 2: /* EQ */ + if (flag == 0) { + env->CF = 1; + } + break; + case 3: /* UEQ */ + if ((flag == 0) || (flag == 2)) { + env->CF = 1; + } + break; + case 4: /* OLT */ + if (flag == -1) { + env->CF = 1; + } + break; + case 5: /* ULT */ + if ((flag == -1) || (flag == 2)) { + env->CF = 1; + } + break; + case 6: /* OLE */ + if ((flag == -1) || (flag == 0)) { + env->CF = 1; + } + break; + case 7: /* ULE */ + if (flag != 1) { + env->CF = 1; + } + break; + } + env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29) + | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff); +} + +/* Helper routines to perform bitwise copies between float and int. */ +static inline float32 ucf64_itos(uint32_t i) +{ + union { + uint32_t i; + float32 s; + } v; + + v.i = i; + return v.s; +} + +static inline uint32_t ucf64_stoi(float32 s) +{ + union { + uint32_t i; + float32 s; + } v; + + v.s = s; + return v.i; +} + +static inline float64 ucf64_itod(uint64_t i) +{ + union { + uint64_t i; + float64 d; + } v; + + v.i = i; + return v.d; +} + +static inline uint64_t ucf64_dtoi(float64 d) +{ + union { + uint64_t i; + float64 d; + } v; + + v.d = d; + return v.i; +} + +/* Integer to float conversion. */ +float32 HELPER(ucf64_si2sf)(float32 x, CPUState *env) +{ + return int32_to_float32(ucf64_stoi(x), &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_si2df)(float32 x, CPUState *env) +{ + return int32_to_float64(ucf64_stoi(x), &env->ucf64.fp_status); +} + +/* Float to integer conversion. */ +float32 HELPER(ucf64_sf2si)(float32 x, CPUState *env) +{ + return ucf64_itos(float32_to_int32(x, &env->ucf64.fp_status)); +} + +float32 HELPER(ucf64_df2si)(float64 x, CPUState *env) +{ + return ucf64_itos(float64_to_int32(x, &env->ucf64.fp_status)); +} + +/* floating point conversion */ +float64 HELPER(ucf64_sf2df)(float32 x, CPUState *env) +{ + return float32_to_float64(x, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_df2sf)(float64 x, CPUState *env) +{ + return float64_to_float32(x, &env->ucf64.fp_status); +} diff -Nru qemu-kvm-0.14.1+noroms/target-unicore32/helper.h qemu-kvm-0.15.0+noroms/target-unicore32/helper.h --- qemu-kvm-0.14.1+noroms/target-unicore32/helper.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-unicore32/helper.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#include "def-helper.h" + +DEF_HELPER_1(clz, i32, i32) +DEF_HELPER_1(clo, i32, i32) + +DEF_HELPER_1(exception, void, i32) + +DEF_HELPER_2(asr_write, void, i32, i32) +DEF_HELPER_0(asr_read, i32) + +DEF_HELPER_3(set_cp0, void, env, i32, i32) +DEF_HELPER_2(get_cp0, i32, env, i32) + +DEF_HELPER_3(set_cp, void, env, i32, i32) +DEF_HELPER_2(get_cp, i32, env, i32) + +DEF_HELPER_1(get_user_reg, i32, i32) +DEF_HELPER_2(set_user_reg, void, i32, i32) + +DEF_HELPER_2(add_cc, i32, i32, i32) +DEF_HELPER_2(adc_cc, i32, i32, i32) +DEF_HELPER_2(sub_cc, i32, i32, i32) +DEF_HELPER_2(sbc_cc, i32, i32, i32) + +DEF_HELPER_2(shl, i32, i32, i32) +DEF_HELPER_2(shr, i32, i32, i32) +DEF_HELPER_2(sar, i32, i32, i32) +DEF_HELPER_2(shl_cc, i32, i32, i32) +DEF_HELPER_2(shr_cc, i32, i32, i32) +DEF_HELPER_2(sar_cc, i32, i32, i32) +DEF_HELPER_2(ror_cc, i32, i32, i32) + +DEF_HELPER_2(get_r29_banked, i32, env, i32) +DEF_HELPER_3(set_r29_banked, void, env, i32, i32) + +DEF_HELPER_1(ucf64_get_fpscr, i32, env) +DEF_HELPER_2(ucf64_set_fpscr, void, env, i32) + +DEF_HELPER_3(ucf64_adds, f32, f32, f32, env) +DEF_HELPER_3(ucf64_addd, f64, f64, f64, env) +DEF_HELPER_3(ucf64_subs, f32, f32, f32, env) +DEF_HELPER_3(ucf64_subd, f64, f64, f64, env) +DEF_HELPER_3(ucf64_muls, f32, f32, f32, env) +DEF_HELPER_3(ucf64_muld, f64, f64, f64, env) +DEF_HELPER_3(ucf64_divs, f32, f32, f32, env) +DEF_HELPER_3(ucf64_divd, f64, f64, f64, env) +DEF_HELPER_1(ucf64_negs, f32, f32) +DEF_HELPER_1(ucf64_negd, f64, f64) +DEF_HELPER_1(ucf64_abss, f32, f32) +DEF_HELPER_1(ucf64_absd, f64, f64) +DEF_HELPER_4(ucf64_cmps, void, f32, f32, i32, env) +DEF_HELPER_4(ucf64_cmpd, void, f64, f64, i32, env) + +DEF_HELPER_2(ucf64_sf2df, f64, f32, env) +DEF_HELPER_2(ucf64_df2sf, f32, f64, env) + +DEF_HELPER_2(ucf64_si2sf, f32, f32, env) +DEF_HELPER_2(ucf64_si2df, f64, f32, env) + +DEF_HELPER_2(ucf64_sf2si, f32, f32, env) +DEF_HELPER_2(ucf64_df2si, f32, f64, env) + +#include "def-helper.h" diff -Nru qemu-kvm-0.14.1+noroms/target-unicore32/op_helper.c qemu-kvm-0.15.0+noroms/target-unicore32/op_helper.c --- qemu-kvm-0.14.1+noroms/target-unicore32/op_helper.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-unicore32/op_helper.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,248 @@ +/* + * UniCore32 helper routines + * + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#include "exec.h" +#include "helper.h" + +#define SIGNBIT (uint32_t)0x80000000 +#define SIGNBIT64 ((uint64_t)1 << 63) + +void HELPER(exception)(uint32_t excp) +{ + env->exception_index = excp; + cpu_loop_exit(env); +} + +static target_ulong asr_read(void) +{ + int ZF; + ZF = (env->ZF == 0); + return env->uncached_asr | (env->NF & 0x80000000) | (ZF << 30) | + (env->CF << 29) | ((env->VF & 0x80000000) >> 3); +} + +target_ulong cpu_asr_read(CPUState *env1) +{ + CPUState *saved_env; + target_ulong ret; + + saved_env = env; + env = env1; + ret = asr_read(); + env = saved_env; + return ret; +} + +target_ulong HELPER(asr_read)(void) +{ + return asr_read(); +} + +static void asr_write(target_ulong val, target_ulong mask) +{ + if (mask & ASR_NZCV) { + env->ZF = (~val) & ASR_Z; + env->NF = val; + env->CF = (val >> 29) & 1; + env->VF = (val << 3) & 0x80000000; + } + + if ((env->uncached_asr ^ val) & mask & ASR_M) { + switch_mode(env, val & ASR_M); + } + mask &= ~ASR_NZCV; + env->uncached_asr = (env->uncached_asr & ~mask) | (val & mask); +} + +void cpu_asr_write(CPUState *env1, target_ulong val, target_ulong mask) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + asr_write(val, mask); + env = saved_env; +} + +void HELPER(asr_write)(target_ulong val, target_ulong mask) +{ + asr_write(val, mask); +} + +/* Access to user mode registers from privileged modes. */ +uint32_t HELPER(get_user_reg)(uint32_t regno) +{ + uint32_t val; + + if (regno == 29) { + val = env->banked_r29[0]; + } else if (regno == 30) { + val = env->banked_r30[0]; + } else { + val = env->regs[regno]; + } + return val; +} + +void HELPER(set_user_reg)(uint32_t regno, uint32_t val) +{ + if (regno == 29) { + env->banked_r29[0] = val; + } else if (regno == 30) { + env->banked_r30[0] = val; + } else { + env->regs[regno] = val; + } +} + +/* ??? Flag setting arithmetic is awkward because we need to do comparisons. + The only way to do that in TCG is a conditional branch, which clobbers + all our temporaries. For now implement these as helper functions. */ + +uint32_t HELPER(add_cc)(uint32_t a, uint32_t b) +{ + uint32_t result; + result = a + b; + env->NF = env->ZF = result; + env->CF = result < a; + env->VF = (a ^ b ^ -1) & (a ^ result); + return result; +} + +uint32_t HELPER(adc_cc)(uint32_t a, uint32_t b) +{ + uint32_t result; + if (!env->CF) { + result = a + b; + env->CF = result < a; + } else { + result = a + b + 1; + env->CF = result <= a; + } + env->VF = (a ^ b ^ -1) & (a ^ result); + env->NF = env->ZF = result; + return result; +} + +uint32_t HELPER(sub_cc)(uint32_t a, uint32_t b) +{ + uint32_t result; + result = a - b; + env->NF = env->ZF = result; + env->CF = a >= b; + env->VF = (a ^ b) & (a ^ result); + return result; +} + +uint32_t HELPER(sbc_cc)(uint32_t a, uint32_t b) +{ + uint32_t result; + if (!env->CF) { + result = a - b - 1; + env->CF = a > b; + } else { + result = a - b; + env->CF = a >= b; + } + env->VF = (a ^ b) & (a ^ result); + env->NF = env->ZF = result; + return result; +} + +/* Similarly for variable shift instructions. */ + +uint32_t HELPER(shl)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + return 0; + } + return x << shift; +} + +uint32_t HELPER(shr)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + return 0; + } + return (uint32_t)x >> shift; +} + +uint32_t HELPER(sar)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + shift = 31; + } + return (int32_t)x >> shift; +} + +uint32_t HELPER(shl_cc)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + if (shift == 32) { + env->CF = x & 1; + } else { + env->CF = 0; + } + return 0; + } else if (shift != 0) { + env->CF = (x >> (32 - shift)) & 1; + return x << shift; + } + return x; +} + +uint32_t HELPER(shr_cc)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + if (shift == 32) { + env->CF = (x >> 31) & 1; + } else { + env->CF = 0; + } + return 0; + } else if (shift != 0) { + env->CF = (x >> (shift - 1)) & 1; + return x >> shift; + } + return x; +} + +uint32_t HELPER(sar_cc)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + env->CF = (x >> 31) & 1; + return (int32_t)x >> 31; + } else if (shift != 0) { + env->CF = (x >> (shift - 1)) & 1; + return (int32_t)x >> shift; + } + return x; +} + +uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i) +{ + int shift1, shift; + shift1 = i & 0xff; + shift = shift1 & 0x1f; + if (shift == 0) { + if (shift1 != 0) { + env->CF = (x >> 31) & 1; + } + return x; + } else { + env->CF = (x >> (shift - 1)) & 1; + return ((uint32_t)x >> shift) | (x << (32 - shift)); + } +} diff -Nru qemu-kvm-0.14.1+noroms/target-unicore32/translate.c qemu-kvm-0.15.0+noroms/target-unicore32/translate.c --- qemu-kvm-0.14.1+noroms/target-unicore32/translate.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/target-unicore32/translate.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,2103 @@ +/* + * UniCore32 translation + * + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * 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. + */ +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "disas.h" +#include "tcg-op.h" +#include "qemu-log.h" + +#include "helper.h" +#define GEN_HELPER 1 +#include "helper.h" + +/* internal defines */ +typedef struct DisasContext { + target_ulong pc; + int is_jmp; + /* Nonzero if this instruction has been conditionally skipped. */ + int condjmp; + /* The label that will be jumped to when the instruction is skipped. */ + int condlabel; + struct TranslationBlock *tb; + int singlestep_enabled; +} DisasContext; + +#define IS_USER(s) 1 + +/* These instructions trap after executing, so defer them until after the + conditional executions state has been updated. */ +#define DISAS_SYSCALL 5 + +static TCGv_ptr cpu_env; +static TCGv_i32 cpu_R[32]; + +/* FIXME: These should be removed. */ +static TCGv cpu_F0s, cpu_F1s; +static TCGv_i64 cpu_F0d, cpu_F1d; + +#include "gen-icount.h" + +static const char *regnames[] = { + "r00", "r01", "r02", "r03", "r04", "r05", "r06", "r07", + "r08", "r09", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "pc" }; + +/* initialize TCG globals. */ +void uc32_translate_init(void) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + for (i = 0; i < 32; i++) { + cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUState, regs[i]), regnames[i]); + } + +#define GEN_HELPER 2 +#include "helper.h" +} + +static int num_temps; + +/* Allocate a temporary variable. */ +static TCGv_i32 new_tmp(void) +{ + num_temps++; + return tcg_temp_new_i32(); +} + +/* Release a temporary variable. */ +static void dead_tmp(TCGv tmp) +{ + tcg_temp_free(tmp); + num_temps--; +} + +static inline TCGv load_cpu_offset(int offset) +{ + TCGv tmp = new_tmp(); + tcg_gen_ld_i32(tmp, cpu_env, offset); + return tmp; +} + +#define load_cpu_field(name) load_cpu_offset(offsetof(CPUState, name)) + +static inline void store_cpu_offset(TCGv var, int offset) +{ + tcg_gen_st_i32(var, cpu_env, offset); + dead_tmp(var); +} + +#define store_cpu_field(var, name) \ + store_cpu_offset(var, offsetof(CPUState, name)) + +/* Set a variable to the value of a CPU register. */ +static void load_reg_var(DisasContext *s, TCGv var, int reg) +{ + if (reg == 31) { + uint32_t addr; + /* normaly, since we updated PC */ + addr = (long)s->pc; + tcg_gen_movi_i32(var, addr); + } else { + tcg_gen_mov_i32(var, cpu_R[reg]); + } +} + +/* Create a new temporary and set it to the value of a CPU register. */ +static inline TCGv load_reg(DisasContext *s, int reg) +{ + TCGv tmp = new_tmp(); + load_reg_var(s, tmp, reg); + return tmp; +} + +/* Set a CPU register. The source must be a temporary and will be + marked as dead. */ +static void store_reg(DisasContext *s, int reg, TCGv var) +{ + if (reg == 31) { + tcg_gen_andi_i32(var, var, ~3); + s->is_jmp = DISAS_JUMP; + } + tcg_gen_mov_i32(cpu_R[reg], var); + dead_tmp(var); +} + +/* Value extensions. */ +#define gen_uxtb(var) tcg_gen_ext8u_i32(var, var) +#define gen_uxth(var) tcg_gen_ext16u_i32(var, var) +#define gen_sxtb(var) tcg_gen_ext8s_i32(var, var) +#define gen_sxth(var) tcg_gen_ext16s_i32(var, var) + +#define UCOP_REG_M (((insn) >> 0) & 0x1f) +#define UCOP_REG_N (((insn) >> 19) & 0x1f) +#define UCOP_REG_D (((insn) >> 14) & 0x1f) +#define UCOP_REG_S (((insn) >> 9) & 0x1f) +#define UCOP_REG_LO (((insn) >> 14) & 0x1f) +#define UCOP_REG_HI (((insn) >> 9) & 0x1f) +#define UCOP_SH_OP (((insn) >> 6) & 0x03) +#define UCOP_SH_IM (((insn) >> 9) & 0x1f) +#define UCOP_OPCODES (((insn) >> 25) & 0x0f) +#define UCOP_IMM_9 (((insn) >> 0) & 0x1ff) +#define UCOP_IMM10 (((insn) >> 0) & 0x3ff) +#define UCOP_IMM14 (((insn) >> 0) & 0x3fff) +#define UCOP_COND (((insn) >> 25) & 0x0f) +#define UCOP_CMOV_COND (((insn) >> 19) & 0x0f) +#define UCOP_CPNUM (((insn) >> 10) & 0x0f) +#define UCOP_UCF64_FMT (((insn) >> 24) & 0x03) +#define UCOP_UCF64_FUNC (((insn) >> 6) & 0x0f) +#define UCOP_UCF64_COND (((insn) >> 6) & 0x0f) + +#define UCOP_SET(i) ((insn) & (1 << (i))) +#define UCOP_SET_P UCOP_SET(28) +#define UCOP_SET_U UCOP_SET(27) +#define UCOP_SET_B UCOP_SET(26) +#define UCOP_SET_W UCOP_SET(25) +#define UCOP_SET_L UCOP_SET(24) +#define UCOP_SET_S UCOP_SET(24) + +#define ILLEGAL cpu_abort(env, \ + "Illegal UniCore32 instruction %x at line %d!", \ + insn, __LINE__) + +static inline void gen_set_asr(TCGv var, uint32_t mask) +{ + TCGv tmp_mask = tcg_const_i32(mask); + gen_helper_asr_write(var, tmp_mask); + tcg_temp_free_i32(tmp_mask); +} +/* Set NZCV flags from the high 4 bits of var. */ +#define gen_set_nzcv(var) gen_set_asr(var, ASR_NZCV) + +static void gen_exception(int excp) +{ + TCGv tmp = new_tmp(); + tcg_gen_movi_i32(tmp, excp); + gen_helper_exception(tmp); + dead_tmp(tmp); +} + +/* FIXME: Most targets have native widening multiplication. + It would be good to use that instead of a full wide multiply. */ +/* 32x32->64 multiply. Marks inputs as dead. */ +static TCGv_i64 gen_mulu_i64_i32(TCGv a, TCGv b) +{ + TCGv_i64 tmp1 = tcg_temp_new_i64(); + TCGv_i64 tmp2 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(tmp1, a); + dead_tmp(a); + tcg_gen_extu_i32_i64(tmp2, b); + dead_tmp(b); + tcg_gen_mul_i64(tmp1, tmp1, tmp2); + tcg_temp_free_i64(tmp2); + return tmp1; +} + +static TCGv_i64 gen_muls_i64_i32(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); + dead_tmp(a); + tcg_gen_ext_i32_i64(tmp2, b); + dead_tmp(b); + tcg_gen_mul_i64(tmp1, tmp1, tmp2); + tcg_temp_free_i64(tmp2); + return tmp1; +} + +#define gen_set_CF(var) tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, CF)) + +/* Set CF to the top bit of var. */ +static void gen_set_CF_bit31(TCGv var) +{ + TCGv tmp = new_tmp(); + tcg_gen_shri_i32(tmp, var, 31); + gen_set_CF(tmp); + dead_tmp(tmp); +} + +/* Set N and Z flags from var. */ +static inline void gen_logic_CC(TCGv var) +{ + tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, NF)); + tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, ZF)); +} + +/* dest = T0 + T1 + CF. */ +static void gen_add_carry(TCGv dest, TCGv t0, TCGv t1) +{ + TCGv tmp; + tcg_gen_add_i32(dest, t0, t1); + tmp = load_cpu_field(CF); + tcg_gen_add_i32(dest, dest, tmp); + dead_tmp(tmp); +} + +/* dest = T0 - T1 + CF - 1. */ +static void gen_sub_carry(TCGv dest, TCGv t0, TCGv t1) +{ + TCGv tmp; + tcg_gen_sub_i32(dest, t0, t1); + tmp = load_cpu_field(CF); + tcg_gen_add_i32(dest, dest, tmp); + tcg_gen_subi_i32(dest, dest, 1); + dead_tmp(tmp); +} + +static void shifter_out_im(TCGv var, int shift) +{ + TCGv tmp = new_tmp(); + if (shift == 0) { + tcg_gen_andi_i32(tmp, var, 1); + } else { + tcg_gen_shri_i32(tmp, var, shift); + if (shift != 31) { + tcg_gen_andi_i32(tmp, tmp, 1); + } + } + gen_set_CF(tmp); + dead_tmp(tmp); +} + +/* Shift by immediate. Includes special handling for shift == 0. */ +static inline void gen_uc32_shift_im(TCGv var, int shiftop, int shift, + int flags) +{ + switch (shiftop) { + case 0: /* LSL */ + if (shift != 0) { + if (flags) { + shifter_out_im(var, 32 - shift); + } + tcg_gen_shli_i32(var, var, shift); + } + break; + case 1: /* LSR */ + if (shift == 0) { + if (flags) { + tcg_gen_shri_i32(var, var, 31); + gen_set_CF(var); + } + tcg_gen_movi_i32(var, 0); + } else { + if (flags) { + shifter_out_im(var, shift - 1); + } + tcg_gen_shri_i32(var, var, shift); + } + break; + case 2: /* ASR */ + if (shift == 0) { + shift = 32; + } + if (flags) { + shifter_out_im(var, shift - 1); + } + if (shift == 32) { + shift = 31; + } + tcg_gen_sari_i32(var, var, shift); + break; + case 3: /* ROR/RRX */ + if (shift != 0) { + if (flags) { + shifter_out_im(var, shift - 1); + } + tcg_gen_rotri_i32(var, var, shift); break; + } else { + TCGv tmp = load_cpu_field(CF); + if (flags) { + shifter_out_im(var, 0); + } + tcg_gen_shri_i32(var, var, 1); + tcg_gen_shli_i32(tmp, tmp, 31); + tcg_gen_or_i32(var, var, tmp); + dead_tmp(tmp); + } + } +}; + +static inline void gen_uc32_shift_reg(TCGv var, int shiftop, + TCGv shift, int flags) +{ + if (flags) { + switch (shiftop) { + case 0: + gen_helper_shl_cc(var, var, shift); + break; + case 1: + gen_helper_shr_cc(var, var, shift); + break; + case 2: + gen_helper_sar_cc(var, var, shift); + break; + case 3: + gen_helper_ror_cc(var, var, shift); + break; + } + } else { + switch (shiftop) { + case 0: + gen_helper_shl(var, var, shift); + break; + case 1: + gen_helper_shr(var, var, shift); + break; + case 2: + gen_helper_sar(var, var, shift); + break; + case 3: + tcg_gen_andi_i32(shift, shift, 0x1f); + tcg_gen_rotr_i32(var, var, shift); + break; + } + } + dead_tmp(shift); +} + +static void gen_test_cc(int cc, int label) +{ + TCGv tmp; + TCGv tmp2; + int inv; + + switch (cc) { + case 0: /* eq: Z */ + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + break; + case 1: /* ne: !Z */ + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label); + break; + case 2: /* cs: C */ + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label); + break; + case 3: /* cc: !C */ + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + break; + case 4: /* mi: N */ + tmp = load_cpu_field(NF); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + case 5: /* pl: !N */ + tmp = load_cpu_field(NF); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + break; + case 6: /* vs: V */ + tmp = load_cpu_field(VF); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + case 7: /* vc: !V */ + tmp = load_cpu_field(VF); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + break; + case 8: /* hi: C && !Z */ + inv = gen_new_label(); + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv); + dead_tmp(tmp); + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label); + gen_set_label(inv); + break; + case 9: /* ls: !C || Z */ + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + dead_tmp(tmp); + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + break; + case 10: /* ge: N == V -> N ^ V == 0 */ + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + break; + case 11: /* lt: N != V -> N ^ V != 0 */ + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + case 12: /* gt: !Z && N == V */ + inv = gen_new_label(); + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv); + dead_tmp(tmp); + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + gen_set_label(inv); + break; + case 13: /* le: Z || N != V */ + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + dead_tmp(tmp); + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + default: + fprintf(stderr, "Bad condition code 0x%x\n", cc); + abort(); + } + dead_tmp(tmp); +} + +static const uint8_t table_logic_cc[16] = { + 1, /* and */ 1, /* xor */ 0, /* sub */ 0, /* rsb */ + 0, /* add */ 0, /* adc */ 0, /* sbc */ 0, /* rsc */ + 1, /* andl */ 1, /* xorl */ 0, /* cmp */ 0, /* cmn */ + 1, /* orr */ 1, /* mov */ 1, /* bic */ 1, /* mvn */ +}; + +/* Set PC state from an immediate address. */ +static inline void gen_bx_im(DisasContext *s, uint32_t addr) +{ + s->is_jmp = DISAS_UPDATE; + tcg_gen_movi_i32(cpu_R[31], addr & ~3); +} + +/* Set PC state from var. var is marked as dead. */ +static inline void gen_bx(DisasContext *s, TCGv var) +{ + s->is_jmp = DISAS_UPDATE; + tcg_gen_andi_i32(cpu_R[31], var, ~3); + dead_tmp(var); +} + +static inline void store_reg_bx(DisasContext *s, int reg, TCGv var) +{ + store_reg(s, reg, var); +} + +static inline TCGv gen_ld8s(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld8s(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld8u(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld8u(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld16s(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld16s(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld16u(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld16u(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld32(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld32u(tmp, addr, index); + return tmp; +} + +static inline TCGv_i64 gen_ld64(TCGv addr, int index) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp, addr, index); + return tmp; +} + +static inline void gen_st8(TCGv val, TCGv addr, int index) +{ + tcg_gen_qemu_st8(val, addr, index); + dead_tmp(val); +} + +static inline void gen_st16(TCGv val, TCGv addr, int index) +{ + tcg_gen_qemu_st16(val, addr, index); + dead_tmp(val); +} + +static inline void gen_st32(TCGv val, TCGv addr, int index) +{ + tcg_gen_qemu_st32(val, addr, index); + dead_tmp(val); +} + +static inline void gen_st64(TCGv_i64 val, TCGv addr, int index) +{ + tcg_gen_qemu_st64(val, addr, index); + tcg_temp_free_i64(val); +} + +static inline void gen_set_pc_im(uint32_t val) +{ + tcg_gen_movi_i32(cpu_R[31], val); +} + +/* Force a TB lookup after an instruction that changes the CPU state. */ +static inline void gen_lookup_tb(DisasContext *s) +{ + tcg_gen_movi_i32(cpu_R[31], s->pc & ~1); + s->is_jmp = DISAS_UPDATE; +} + +static inline void gen_add_data_offset(DisasContext *s, unsigned int insn, + TCGv var) +{ + int val; + TCGv offset; + + if (UCOP_SET(29)) { + /* immediate */ + val = UCOP_IMM14; + if (!UCOP_SET_U) { + val = -val; + } + if (val != 0) { + tcg_gen_addi_i32(var, var, val); + } + } else { + /* shift/register */ + offset = load_reg(s, UCOP_REG_M); + gen_uc32_shift_im(offset, UCOP_SH_OP, UCOP_SH_IM, 0); + if (!UCOP_SET_U) { + tcg_gen_sub_i32(var, var, offset); + } else { + tcg_gen_add_i32(var, var, offset); + } + dead_tmp(offset); + } +} + +static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn, + TCGv var) +{ + int val; + TCGv offset; + + if (UCOP_SET(26)) { + /* immediate */ + val = (insn & 0x1f) | ((insn >> 4) & 0x3e0); + if (!UCOP_SET_U) { + val = -val; + } + if (val != 0) { + tcg_gen_addi_i32(var, var, val); + } + } else { + /* register */ + offset = load_reg(s, UCOP_REG_M); + if (!UCOP_SET_U) { + tcg_gen_sub_i32(var, var, offset); + } else { + tcg_gen_add_i32(var, var, offset); + } + dead_tmp(offset); + } +} + +static inline long ucf64_reg_offset(int reg) +{ + if (reg & 1) { + return offsetof(CPUState, ucf64.regs[reg >> 1]) + + offsetof(CPU_DoubleU, l.upper); + } else { + return offsetof(CPUState, ucf64.regs[reg >> 1]) + + offsetof(CPU_DoubleU, l.lower); + } +} + +#define ucf64_gen_ld32(reg) load_cpu_offset(ucf64_reg_offset(reg)) +#define ucf64_gen_st32(var, reg) store_cpu_offset(var, ucf64_reg_offset(reg)) + +/* UniCore-F64 single load/store I_offset */ +static void do_ucf64_ldst_i(CPUState *env, DisasContext *s, uint32_t insn) +{ + int offset; + TCGv tmp; + TCGv addr; + + addr = load_reg(s, UCOP_REG_N); + if (!UCOP_SET_P && !UCOP_SET_W) { + ILLEGAL; + } + + if (UCOP_SET_P) { + offset = UCOP_IMM10 << 2; + if (!UCOP_SET_U) { + offset = -offset; + } + if (offset != 0) { + tcg_gen_addi_i32(addr, addr, offset); + } + } + + if (UCOP_SET_L) { /* load */ + tmp = gen_ld32(addr, IS_USER(s)); + ucf64_gen_st32(tmp, UCOP_REG_D); + } else { /* store */ + tmp = ucf64_gen_ld32(UCOP_REG_D); + gen_st32(tmp, addr, IS_USER(s)); + } + + if (!UCOP_SET_P) { + offset = UCOP_IMM10 << 2; + if (!UCOP_SET_U) { + offset = -offset; + } + if (offset != 0) { + tcg_gen_addi_i32(addr, addr, offset); + } + } + if (UCOP_SET_W) { + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } +} + +/* UniCore-F64 load/store multiple words */ +static void do_ucf64_ldst_m(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int i; + int j, n, freg; + TCGv tmp; + TCGv addr; + + if (UCOP_REG_D != 0) { + ILLEGAL; + } + if (UCOP_REG_N == 31) { + ILLEGAL; + } + if ((insn << 24) == 0) { + ILLEGAL; + } + + addr = load_reg(s, UCOP_REG_N); + + n = 0; + for (i = 0; i < 8; i++) { + if (UCOP_SET(i)) { + n++; + } + } + + if (UCOP_SET_U) { + if (UCOP_SET_P) { /* pre increment */ + tcg_gen_addi_i32(addr, addr, 4); + } /* unnecessary to do anything when post increment */ + } else { + if (UCOP_SET_P) { /* pre decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } else { /* post decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } + } + + freg = ((insn >> 8) & 3) << 3; /* freg should be 0, 8, 16, 24 */ + + for (i = 0, j = 0; i < 8; i++, freg++) { + if (!UCOP_SET(i)) { + continue; + } + + if (UCOP_SET_L) { /* load */ + tmp = gen_ld32(addr, IS_USER(s)); + ucf64_gen_st32(tmp, freg); + } else { /* store */ + tmp = ucf64_gen_ld32(freg); + gen_st32(tmp, addr, IS_USER(s)); + } + + j++; + /* unnecessary to add after the last transfer */ + if (j != n) { + tcg_gen_addi_i32(addr, addr, 4); + } + } + + if (UCOP_SET_W) { /* write back */ + if (UCOP_SET_U) { + if (!UCOP_SET_P) { /* post increment */ + tcg_gen_addi_i32(addr, addr, 4); + } /* unnecessary to do anything when pre increment */ + } else { + if (UCOP_SET_P) { + /* pre decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } else { + /* post decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } + } + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } +} + +/* UniCore-F64 mrc/mcr */ +static void do_ucf64_trans(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv tmp; + + if ((insn & 0xfe0003ff) == 0xe2000000) { + /* control register */ + if ((UCOP_REG_N != UC32_UCF64_FPSCR) || (UCOP_REG_D == 31)) { + ILLEGAL; + } + if (UCOP_SET(24)) { + /* CFF */ + tmp = new_tmp(); + gen_helper_ucf64_get_fpscr(tmp, cpu_env); + store_reg(s, UCOP_REG_D, tmp); + } else { + /* CTF */ + tmp = load_reg(s, UCOP_REG_D); + gen_helper_ucf64_set_fpscr(cpu_env, tmp); + dead_tmp(tmp); + gen_lookup_tb(s); + } + return; + } + if ((insn & 0xfe0003ff) == 0xe0000000) { + /* general register */ + if (UCOP_REG_D == 31) { + ILLEGAL; + } + if (UCOP_SET(24)) { /* MFF */ + tmp = ucf64_gen_ld32(UCOP_REG_N); + store_reg(s, UCOP_REG_D, tmp); + } else { /* MTF */ + tmp = load_reg(s, UCOP_REG_D); + ucf64_gen_st32(tmp, UCOP_REG_N); + } + return; + } + if ((insn & 0xfb000000) == 0xe9000000) { + /* MFFC */ + if (UCOP_REG_D != 31) { + ILLEGAL; + } + if (UCOP_UCF64_COND & 0x8) { + ILLEGAL; + } + + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, UCOP_UCF64_COND); + if (UCOP_SET(26)) { + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i64(cpu_F1d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_cmpd(cpu_F0d, cpu_F1d, tmp, cpu_env); + } else { + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i32(cpu_F1s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_cmps(cpu_F0s, cpu_F1s, tmp, cpu_env); + } + dead_tmp(tmp); + return; + } + ILLEGAL; +} + +/* UniCore-F64 convert instructions */ +static void do_ucf64_fcvt(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (UCOP_UCF64_FMT == 3) { + ILLEGAL; + } + if (UCOP_REG_N != 0) { + ILLEGAL; + } + switch (UCOP_UCF64_FUNC) { + case 0: /* cvt.s */ + switch (UCOP_UCF64_FMT) { + case 1 /* d */: + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_df2sf(cpu_F0s, cpu_F0d, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + case 2 /* w */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_si2sf(cpu_F0s, cpu_F0s, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + default /* s */: + ILLEGAL; + break; + } + break; + case 1: /* cvt.d */ + switch (UCOP_UCF64_FMT) { + case 0 /* s */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_sf2df(cpu_F0d, cpu_F0s, cpu_env); + tcg_gen_st_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + case 2 /* w */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_si2df(cpu_F0d, cpu_F0s, cpu_env); + tcg_gen_st_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + default /* d */: + ILLEGAL; + break; + } + break; + case 4: /* cvt.w */ + switch (UCOP_UCF64_FMT) { + case 0 /* s */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_sf2si(cpu_F0s, cpu_F0s, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + case 1 /* d */: + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_df2si(cpu_F0s, cpu_F0d, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + default /* w */: + ILLEGAL; + break; + } + break; + default: + ILLEGAL; + } +} + +/* UniCore-F64 compare instructions */ +static void do_ucf64_fcmp(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (UCOP_SET(25)) { + ILLEGAL; + } + if (UCOP_REG_D != 0) { + ILLEGAL; + } + + ILLEGAL; /* TODO */ + if (UCOP_SET(24)) { + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i64(cpu_F1d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + /* gen_helper_ucf64_cmpd(cpu_F0d, cpu_F1d, cpu_env); */ + } else { + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i32(cpu_F1s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + /* gen_helper_ucf64_cmps(cpu_F0s, cpu_F1s, cpu_env); */ + } +} + +#define gen_helper_ucf64_movs(x, y) do { } while (0) +#define gen_helper_ucf64_movd(x, y) do { } while (0) + +#define UCF64_OP1(name) do { \ + if (UCOP_REG_N != 0) { \ + ILLEGAL; \ + } \ + switch (UCOP_UCF64_FMT) { \ + case 0 /* s */: \ + tcg_gen_ld_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##s(cpu_F0s, cpu_F0s); \ + tcg_gen_st_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 1 /* d */: \ + tcg_gen_ld_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##d(cpu_F0d, cpu_F0d); \ + tcg_gen_st_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 2 /* w */: \ + ILLEGAL; \ + break; \ + } \ + } while (0) + +#define UCF64_OP2(name) do { \ + switch (UCOP_UCF64_FMT) { \ + case 0 /* s */: \ + tcg_gen_ld_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_N)); \ + tcg_gen_ld_i32(cpu_F1s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##s(cpu_F0s, \ + cpu_F0s, cpu_F1s, cpu_env); \ + tcg_gen_st_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 1 /* d */: \ + tcg_gen_ld_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_N)); \ + tcg_gen_ld_i64(cpu_F1d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##d(cpu_F0d, \ + cpu_F0d, cpu_F1d, cpu_env); \ + tcg_gen_st_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 2 /* w */: \ + ILLEGAL; \ + break; \ + } \ + } while (0) + +/* UniCore-F64 data processing */ +static void do_ucf64_datap(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (UCOP_UCF64_FMT == 3) { + ILLEGAL; + } + switch (UCOP_UCF64_FUNC) { + case 0: /* add */ + UCF64_OP2(add); + break; + case 1: /* sub */ + UCF64_OP2(sub); + break; + case 2: /* mul */ + UCF64_OP2(mul); + break; + case 4: /* div */ + UCF64_OP2(div); + break; + case 5: /* abs */ + UCF64_OP1(abs); + break; + case 6: /* mov */ + UCF64_OP1(mov); + break; + case 7: /* neg */ + UCF64_OP1(neg); + break; + default: + ILLEGAL; + } +} + +/* Disassemble an F64 instruction */ +static void disas_ucf64_insn(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (!UCOP_SET(29)) { + if (UCOP_SET(26)) { + do_ucf64_ldst_m(env, s, insn); + } else { + do_ucf64_ldst_i(env, s, insn); + } + } else { + if (UCOP_SET(5)) { + switch ((insn >> 26) & 0x3) { + case 0: + do_ucf64_datap(env, s, insn); + break; + case 1: + ILLEGAL; + break; + case 2: + do_ucf64_fcvt(env, s, insn); + break; + case 3: + do_ucf64_fcmp(env, s, insn); + break; + } + } else { + do_ucf64_trans(env, s, insn); + } + } +} + +static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest) +{ + TranslationBlock *tb; + + tb = s->tb; + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { + tcg_gen_goto_tb(n); + gen_set_pc_im(dest); + tcg_gen_exit_tb((long)tb + n); + } else { + gen_set_pc_im(dest); + tcg_gen_exit_tb(0); + } +} + +static inline void gen_jmp(DisasContext *s, uint32_t dest) +{ + if (unlikely(s->singlestep_enabled)) { + /* An indirect jump so that we still trigger the debug exception. */ + gen_bx_im(s, dest); + } else { + gen_goto_tb(s, 0, dest); + s->is_jmp = DISAS_TB_JUMP; + } +} + +static inline void gen_mulxy(TCGv t0, TCGv t1, int x, int y) +{ + if (x) { + tcg_gen_sari_i32(t0, t0, 16); + } else { + gen_sxth(t0); + } + if (y) { + tcg_gen_sari_i32(t1, t1, 16); + } else { + gen_sxth(t1); + } + tcg_gen_mul_i32(t0, t0, t1); +} + +/* Returns nonzero if access to the PSR is not permitted. Marks t0 as dead. */ +static int gen_set_psr(DisasContext *s, uint32_t mask, int bsr, TCGv t0) +{ + TCGv tmp; + if (bsr) { + /* ??? This is also undefined in system mode. */ + if (IS_USER(s)) { + return 1; + } + + tmp = load_cpu_field(bsr); + tcg_gen_andi_i32(tmp, tmp, ~mask); + tcg_gen_andi_i32(t0, t0, mask); + tcg_gen_or_i32(tmp, tmp, t0); + store_cpu_field(tmp, bsr); + } else { + gen_set_asr(t0, mask); + } + dead_tmp(t0); + gen_lookup_tb(s); + return 0; +} + +/* Generate an old-style exception return. Marks pc as dead. */ +static void gen_exception_return(DisasContext *s, TCGv pc) +{ + TCGv tmp; + store_reg(s, 31, pc); + tmp = load_cpu_field(bsr); + gen_set_asr(tmp, 0xffffffff); + dead_tmp(tmp); + s->is_jmp = DISAS_UPDATE; +} + +static void disas_coproc_insn(CPUState *env, DisasContext *s, uint32_t insn) +{ + switch (UCOP_CPNUM) { + case 2: + disas_ucf64_insn(env, s, insn); + break; + default: + /* Unknown coprocessor. */ + cpu_abort(env, "Unknown coprocessor!"); + } +} + + +/* Store a 64-bit value to a register pair. Clobbers val. */ +static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) +{ + TCGv tmp; + tmp = new_tmp(); + tcg_gen_trunc_i64_i32(tmp, val); + store_reg(s, rlow, tmp); + tmp = new_tmp(); + tcg_gen_shri_i64(val, val, 32); + tcg_gen_trunc_i64_i32(tmp, val); + store_reg(s, rhigh, tmp); +} + +/* load and add a 64-bit value from a register pair. */ +static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh) +{ + TCGv_i64 tmp; + TCGv tmpl; + TCGv tmph; + + /* Load 64-bit value rd:rn. */ + tmpl = load_reg(s, rlow); + tmph = load_reg(s, rhigh); + tmp = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(tmp, tmpl, tmph); + dead_tmp(tmpl); + dead_tmp(tmph); + tcg_gen_add_i64(val, val, tmp); + tcg_temp_free_i64(tmp); +} + +/* data processing instructions */ +static void do_datap(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv tmp; + TCGv tmp2; + int logic_cc; + + if (UCOP_OPCODES == 0x0f || UCOP_OPCODES == 0x0d) { + if (UCOP_SET(23)) { /* CMOV instructions */ + if ((UCOP_CMOV_COND == 0xe) || (UCOP_CMOV_COND == 0xf)) { + ILLEGAL; + } + /* if not always execute, we generate a conditional jump to + next instruction */ + s->condlabel = gen_new_label(); + gen_test_cc(UCOP_CMOV_COND ^ 1, s->condlabel); + s->condjmp = 1; + } + } + + logic_cc = table_logic_cc[UCOP_OPCODES] & (UCOP_SET_S >> 24); + + if (UCOP_SET(29)) { + unsigned int val; + /* immediate operand */ + val = UCOP_IMM_9; + if (UCOP_SH_IM) { + val = (val >> UCOP_SH_IM) | (val << (32 - UCOP_SH_IM)); + } + tmp2 = new_tmp(); + tcg_gen_movi_i32(tmp2, val); + if (logic_cc && UCOP_SH_IM) { + gen_set_CF_bit31(tmp2); + } + } else { + /* register */ + tmp2 = load_reg(s, UCOP_REG_M); + if (UCOP_SET(5)) { + tmp = load_reg(s, UCOP_REG_S); + gen_uc32_shift_reg(tmp2, UCOP_SH_OP, tmp, logic_cc); + } else { + gen_uc32_shift_im(tmp2, UCOP_SH_OP, UCOP_SH_IM, logic_cc); + } + } + + if (UCOP_OPCODES != 0x0f && UCOP_OPCODES != 0x0d) { + tmp = load_reg(s, UCOP_REG_N); + } else { + TCGV_UNUSED(tmp); + } + + switch (UCOP_OPCODES) { + case 0x00: + tcg_gen_and_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x01: + tcg_gen_xor_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x02: + if (UCOP_SET_S && UCOP_REG_D == 31) { + /* SUBS r31, ... is used for exception return. */ + if (IS_USER(s)) { + ILLEGAL; + } + gen_helper_sub_cc(tmp, tmp, tmp2); + gen_exception_return(s, tmp); + } else { + if (UCOP_SET_S) { + gen_helper_sub_cc(tmp, tmp, tmp2); + } else { + tcg_gen_sub_i32(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + } + break; + case 0x03: + if (UCOP_SET_S) { + gen_helper_sub_cc(tmp, tmp2, tmp); + } else { + tcg_gen_sub_i32(tmp, tmp2, tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x04: + if (UCOP_SET_S) { + gen_helper_add_cc(tmp, tmp, tmp2); + } else { + tcg_gen_add_i32(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x05: + if (UCOP_SET_S) { + gen_helper_adc_cc(tmp, tmp, tmp2); + } else { + gen_add_carry(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x06: + if (UCOP_SET_S) { + gen_helper_sbc_cc(tmp, tmp, tmp2); + } else { + gen_sub_carry(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x07: + if (UCOP_SET_S) { + gen_helper_sbc_cc(tmp, tmp2, tmp); + } else { + gen_sub_carry(tmp, tmp2, tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x08: + if (UCOP_SET_S) { + tcg_gen_and_i32(tmp, tmp, tmp2); + gen_logic_CC(tmp); + } + dead_tmp(tmp); + break; + case 0x09: + if (UCOP_SET_S) { + tcg_gen_xor_i32(tmp, tmp, tmp2); + gen_logic_CC(tmp); + } + dead_tmp(tmp); + break; + case 0x0a: + if (UCOP_SET_S) { + gen_helper_sub_cc(tmp, tmp, tmp2); + } + dead_tmp(tmp); + break; + case 0x0b: + if (UCOP_SET_S) { + gen_helper_add_cc(tmp, tmp, tmp2); + } + dead_tmp(tmp); + break; + case 0x0c: + tcg_gen_or_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x0d: + if (logic_cc && UCOP_REG_D == 31) { + /* MOVS r31, ... is used for exception return. */ + if (IS_USER(s)) { + ILLEGAL; + } + gen_exception_return(s, tmp2); + } else { + if (logic_cc) { + gen_logic_CC(tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp2); + } + break; + case 0x0e: + tcg_gen_andc_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + default: + case 0x0f: + tcg_gen_not_i32(tmp2, tmp2); + if (logic_cc) { + gen_logic_CC(tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp2); + break; + } + if (UCOP_OPCODES != 0x0f && UCOP_OPCODES != 0x0d) { + dead_tmp(tmp2); + } +} + +/* multiply */ +static void do_mult(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv tmp; + TCGv tmp2; + TCGv_i64 tmp64; + + if (UCOP_SET(27)) { + /* 64 bit mul */ + tmp = load_reg(s, UCOP_REG_M); + tmp2 = load_reg(s, UCOP_REG_N); + if (UCOP_SET(26)) { + tmp64 = gen_muls_i64_i32(tmp, tmp2); + } else { + tmp64 = gen_mulu_i64_i32(tmp, tmp2); + } + if (UCOP_SET(25)) { /* mult accumulate */ + gen_addq(s, tmp64, UCOP_REG_LO, UCOP_REG_HI); + } + gen_storeq_reg(s, UCOP_REG_LO, UCOP_REG_HI, tmp64); + tcg_temp_free_i64(tmp64); + } else { + /* 32 bit mul */ + tmp = load_reg(s, UCOP_REG_M); + tmp2 = load_reg(s, UCOP_REG_N); + tcg_gen_mul_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + if (UCOP_SET(25)) { + /* Add */ + tmp2 = load_reg(s, UCOP_REG_S); + tcg_gen_add_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + } + if (UCOP_SET_S) { + gen_logic_CC(tmp); + } + store_reg(s, UCOP_REG_D, tmp); + } +} + +/* miscellaneous instructions */ +static void do_misc(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int val; + TCGv tmp; + + if ((insn & 0xffffffe0) == 0x10ffc120) { + /* Trivial implementation equivalent to bx. */ + tmp = load_reg(s, UCOP_REG_M); + gen_bx(s, tmp); + return; + } + + if ((insn & 0xfbffc000) == 0x30ffc000) { + /* PSR = immediate */ + val = UCOP_IMM_9; + if (UCOP_SH_IM) { + val = (val >> UCOP_SH_IM) | (val << (32 - UCOP_SH_IM)); + } + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, val); + if (gen_set_psr(s, ~ASR_RESERVED, UCOP_SET_B, tmp)) { + ILLEGAL; + } + return; + } + + if ((insn & 0xfbffffe0) == 0x12ffc020) { + /* PSR.flag = reg */ + tmp = load_reg(s, UCOP_REG_M); + if (gen_set_psr(s, ASR_NZCV, UCOP_SET_B, tmp)) { + ILLEGAL; + } + return; + } + + if ((insn & 0xfbffffe0) == 0x10ffc020) { + /* PSR = reg */ + tmp = load_reg(s, UCOP_REG_M); + if (gen_set_psr(s, ~ASR_RESERVED, UCOP_SET_B, tmp)) { + ILLEGAL; + } + return; + } + + if ((insn & 0xfbf83fff) == 0x10f80000) { + /* reg = PSR */ + if (UCOP_SET_B) { + if (IS_USER(s)) { + ILLEGAL; + } + tmp = load_cpu_field(bsr); + } else { + tmp = new_tmp(); + gen_helper_asr_read(tmp); + } + store_reg(s, UCOP_REG_D, tmp); + return; + } + + if ((insn & 0xfbf83fe0) == 0x12f80120) { + /* clz */ + tmp = load_reg(s, UCOP_REG_M); + if (UCOP_SET(26)) { + gen_helper_clo(tmp, tmp); + } else { + gen_helper_clz(tmp, tmp); + } + store_reg(s, UCOP_REG_D, tmp); + return; + } + + /* otherwise */ + ILLEGAL; +} + +/* load/store I_offset and R_offset */ +static void do_ldst_ir(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int i; + TCGv tmp; + TCGv tmp2; + + tmp2 = load_reg(s, UCOP_REG_N); + i = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W)); + + /* immediate */ + if (UCOP_SET_P) { + gen_add_data_offset(s, insn, tmp2); + } + + if (UCOP_SET_L) { + /* load */ + if (UCOP_SET_B) { + tmp = gen_ld8u(tmp2, i); + } else { + tmp = gen_ld32(tmp2, i); + } + } else { + /* store */ + tmp = load_reg(s, UCOP_REG_D); + if (UCOP_SET_B) { + gen_st8(tmp, tmp2, i); + } else { + gen_st32(tmp, tmp2, i); + } + } + if (!UCOP_SET_P) { + gen_add_data_offset(s, insn, tmp2); + store_reg(s, UCOP_REG_N, tmp2); + } else if (UCOP_SET_W) { + store_reg(s, UCOP_REG_N, tmp2); + } else { + dead_tmp(tmp2); + } + if (UCOP_SET_L) { + /* Complete the load. */ + if (UCOP_REG_D == 31) { + gen_bx(s, tmp); + } else { + store_reg(s, UCOP_REG_D, tmp); + } + } +} + +/* SWP instruction */ +static void do_swap(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv addr; + TCGv tmp; + TCGv tmp2; + + if ((insn & 0xff003fe0) != 0x40000120) { + ILLEGAL; + } + + /* ??? This is not really atomic. However we know + we never have multiple CPUs running in parallel, + so it is good enough. */ + addr = load_reg(s, UCOP_REG_N); + tmp = load_reg(s, UCOP_REG_M); + if (UCOP_SET_B) { + tmp2 = gen_ld8u(addr, IS_USER(s)); + gen_st8(tmp, addr, IS_USER(s)); + } else { + tmp2 = gen_ld32(addr, IS_USER(s)); + gen_st32(tmp, addr, IS_USER(s)); + } + dead_tmp(addr); + store_reg(s, UCOP_REG_D, tmp2); +} + +/* load/store hw/sb */ +static void do_ldst_hwsb(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv addr; + TCGv tmp; + + if (UCOP_SH_OP == 0) { + do_swap(env, s, insn); + return; + } + + addr = load_reg(s, UCOP_REG_N); + if (UCOP_SET_P) { + gen_add_datah_offset(s, insn, addr); + } + + if (UCOP_SET_L) { /* load */ + switch (UCOP_SH_OP) { + case 1: + tmp = gen_ld16u(addr, IS_USER(s)); + break; + case 2: + tmp = gen_ld8s(addr, IS_USER(s)); + break; + default: /* see do_swap */ + case 3: + tmp = gen_ld16s(addr, IS_USER(s)); + break; + } + } else { /* store */ + if (UCOP_SH_OP != 1) { + ILLEGAL; + } + tmp = load_reg(s, UCOP_REG_D); + gen_st16(tmp, addr, IS_USER(s)); + } + /* Perform base writeback before the loaded value to + ensure correct behavior with overlapping index registers. */ + if (!UCOP_SET_P) { + gen_add_datah_offset(s, insn, addr); + store_reg(s, UCOP_REG_N, addr); + } else if (UCOP_SET_W) { + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } + if (UCOP_SET_L) { + /* Complete the load. */ + store_reg(s, UCOP_REG_D, tmp); + } +} + +/* load/store multiple words */ +static void do_ldst_m(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int val, i; + int j, n, reg, user, loaded_base; + TCGv tmp; + TCGv tmp2; + TCGv addr; + TCGv loaded_var; + + if (UCOP_SET(7)) { + ILLEGAL; + } + /* XXX: store correct base if write back */ + user = 0; + if (UCOP_SET_B) { /* S bit in instruction table */ + if (IS_USER(s)) { + ILLEGAL; /* only usable in supervisor mode */ + } + if (UCOP_SET(18) == 0) { /* pc reg */ + user = 1; + } + } + + addr = load_reg(s, UCOP_REG_N); + + /* compute total size */ + loaded_base = 0; + TCGV_UNUSED(loaded_var); + n = 0; + for (i = 0; i < 6; i++) { + if (UCOP_SET(i)) { + n++; + } + } + for (i = 9; i < 19; i++) { + if (UCOP_SET(i)) { + n++; + } + } + /* XXX: test invalid n == 0 case ? */ + if (UCOP_SET_U) { + if (UCOP_SET_P) { + /* pre increment */ + tcg_gen_addi_i32(addr, addr, 4); + } else { + /* post increment */ + } + } else { + if (UCOP_SET_P) { + /* pre decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } else { + /* post decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } + } + + j = 0; + reg = UCOP_SET(6) ? 16 : 0; + for (i = 0; i < 19; i++, reg++) { + if (i == 6) { + i = i + 3; + } + if (UCOP_SET(i)) { + if (UCOP_SET_L) { /* load */ + tmp = gen_ld32(addr, IS_USER(s)); + if (reg == 31) { + gen_bx(s, tmp); + } else if (user) { + tmp2 = tcg_const_i32(reg); + gen_helper_set_user_reg(tmp2, tmp); + tcg_temp_free_i32(tmp2); + dead_tmp(tmp); + } else if (reg == UCOP_REG_N) { + loaded_var = tmp; + loaded_base = 1; + } else { + store_reg(s, reg, tmp); + } + } else { /* store */ + if (reg == 31) { + /* special case: r31 = PC + 4 */ + val = (long)s->pc; + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, val); + } else if (user) { + tmp = new_tmp(); + tmp2 = tcg_const_i32(reg); + gen_helper_get_user_reg(tmp, tmp2); + tcg_temp_free_i32(tmp2); + } else { + tmp = load_reg(s, reg); + } + gen_st32(tmp, addr, IS_USER(s)); + } + j++; + /* no need to add after the last transfer */ + if (j != n) { + tcg_gen_addi_i32(addr, addr, 4); + } + } + } + if (UCOP_SET_W) { /* write back */ + if (UCOP_SET_U) { + if (UCOP_SET_P) { + /* pre increment */ + } else { + /* post increment */ + tcg_gen_addi_i32(addr, addr, 4); + } + } else { + if (UCOP_SET_P) { + /* pre decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } else { + /* post decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } + } + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } + if (loaded_base) { + store_reg(s, UCOP_REG_N, loaded_var); + } + if (UCOP_SET_B && !user) { + /* Restore ASR from BSR. */ + tmp = load_cpu_field(bsr); + gen_set_asr(tmp, 0xffffffff); + dead_tmp(tmp); + s->is_jmp = DISAS_UPDATE; + } +} + +/* branch (and link) */ +static void do_branch(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int val; + int32_t offset; + TCGv tmp; + + if (UCOP_COND == 0xf) { + ILLEGAL; + } + + if (UCOP_COND != 0xe) { + /* if not always execute, we generate a conditional jump to + next instruction */ + s->condlabel = gen_new_label(); + gen_test_cc(UCOP_COND ^ 1, s->condlabel); + s->condjmp = 1; + } + + val = (int32_t)s->pc; + if (UCOP_SET_L) { + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, val); + store_reg(s, 30, tmp); + } + offset = (((int32_t)insn << 8) >> 8); + val += (offset << 2); /* unicore is pc+4 */ + gen_jmp(s, val); +} + +static void disas_uc32_insn(CPUState *env, DisasContext *s) +{ + unsigned int insn; + + insn = ldl_code(s->pc); + s->pc += 4; + + /* UniCore instructions class: + * AAAB BBBC xxxx xxxx xxxx xxxD xxEx xxxx + * AAA : see switch case + * BBBB : opcodes or cond or PUBW + * C : S OR L + * D : 8 + * E : 5 + */ + switch (insn >> 29) { + case 0b000: + if (UCOP_SET(5) && UCOP_SET(8) && !UCOP_SET(28)) { + do_mult(env, s, insn); + break; + } + + if (UCOP_SET(8)) { + do_misc(env, s, insn); + break; + } + case 0b001: + if (((UCOP_OPCODES >> 2) == 2) && !UCOP_SET_S) { + do_misc(env, s, insn); + break; + } + do_datap(env, s, insn); + break; + + case 0b010: + if (UCOP_SET(8) && UCOP_SET(5)) { + do_ldst_hwsb(env, s, insn); + break; + } + if (UCOP_SET(8) || UCOP_SET(5)) { + ILLEGAL; + } + case 0b011: + do_ldst_ir(env, s, insn); + break; + + case 0b100: + if (UCOP_SET(8)) { + ILLEGAL; /* extended instructions */ + } + do_ldst_m(env, s, insn); + break; + case 0b101: + do_branch(env, s, insn); + break; + case 0b110: + /* Coprocessor. */ + disas_coproc_insn(env, s, insn); + break; + case 0b111: + if (!UCOP_SET(28)) { + disas_coproc_insn(env, s, insn); + break; + } + if ((insn & 0xff000000) == 0xff000000) { /* syscall */ + gen_set_pc_im(s->pc); + s->is_jmp = DISAS_SYSCALL; + break; + } + ILLEGAL; + } + + return; +} + +/* generate intermediate code in gen_opc_buf and gen_opparam_buf for + basic block 'tb'. If search_pc is TRUE, also generate PC + information for each intermediate instruction. */ +static inline void gen_intermediate_code_internal(CPUState *env, + TranslationBlock *tb, int search_pc) +{ + DisasContext dc1, *dc = &dc1; + CPUBreakpoint *bp; + uint16_t *gen_opc_end; + int j, lj; + target_ulong pc_start; + uint32_t next_page_start; + int num_insns; + int max_insns; + + /* generate intermediate code */ + num_temps = 0; + + pc_start = tb->pc; + + dc->tb = tb; + + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + dc->is_jmp = DISAS_NEXT; + dc->pc = pc_start; + dc->singlestep_enabled = env->singlestep_enabled; + dc->condjmp = 0; + cpu_F0s = tcg_temp_new_i32(); + cpu_F1s = tcg_temp_new_i32(); + cpu_F0d = tcg_temp_new_i64(); + cpu_F1d = tcg_temp_new_i64(); + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + lj = -1; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + + gen_icount_start(); + do { + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc->pc) { + gen_set_pc_im(dc->pc); + gen_exception(EXCP_DEBUG); + dc->is_jmp = DISAS_JUMP; + /* Advance PC so that clearing the breakpoint will + invalidate this TB. */ + dc->pc += 2; /* FIXME */ + goto done_generating; + break; + } + } + } + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) { + gen_opc_instr_start[lj++] = 0; + } + } + gen_opc_pc[lj] = dc->pc; + gen_opc_instr_start[lj] = 1; + gen_opc_icount[lj] = num_insns; + } + + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + disas_uc32_insn(env, dc); + + if (num_temps) { + fprintf(stderr, "Internal resource leak before %08x\n", dc->pc); + num_temps = 0; + } + + if (dc->condjmp && !dc->is_jmp) { + gen_set_label(dc->condlabel); + dc->condjmp = 0; + } + /* Translation stops when a conditional branch is encountered. + * Otherwise the subsequent code could get translated several times. + * Also stop translation when a page boundary is reached. This + * ensures prefetch aborts occur at the right place. */ + num_insns++; + } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end && + !env->singlestep_enabled && + !singlestep && + dc->pc < next_page_start && + num_insns < max_insns); + + if (tb->cflags & CF_LAST_IO) { + if (dc->condjmp) { + /* FIXME: This can theoretically happen with self-modifying + code. */ + cpu_abort(env, "IO on conditional branch instruction"); + } + gen_io_end(); + } + + /* At this stage dc->condjmp will only be set when the skipped + instruction was a conditional branch or trap, and the PC has + already been written. */ + if (unlikely(env->singlestep_enabled)) { + /* Make sure the pc is updated, and raise a debug exception. */ + if (dc->condjmp) { + if (dc->is_jmp == DISAS_SYSCALL) { + gen_exception(UC32_EXCP_PRIV); + } else { + gen_exception(EXCP_DEBUG); + } + gen_set_label(dc->condlabel); + } + if (dc->condjmp || !dc->is_jmp) { + gen_set_pc_im(dc->pc); + dc->condjmp = 0; + } + if (dc->is_jmp == DISAS_SYSCALL && !dc->condjmp) { + gen_exception(UC32_EXCP_PRIV); + } else { + gen_exception(EXCP_DEBUG); + } + } else { + /* While branches must always occur at the end of an IT block, + there are a few other things that can cause us to terminate + the TB in the middel of an IT block: + - Exception generating instructions (bkpt, swi, undefined). + - Page boundaries. + - Hardware watchpoints. + Hardware breakpoints have already been handled and skip this code. + */ + switch (dc->is_jmp) { + case DISAS_NEXT: + gen_goto_tb(dc, 1, dc->pc); + break; + default: + case DISAS_JUMP: + case DISAS_UPDATE: + /* indicate that the hash table must be used to find the next TB */ + tcg_gen_exit_tb(0); + break; + case DISAS_TB_JUMP: + /* nothing more to generate */ + break; + case DISAS_SYSCALL: + gen_exception(UC32_EXCP_PRIV); + break; + } + if (dc->condjmp) { + gen_set_label(dc->condlabel); + gen_goto_tb(dc, 1, dc->pc); + dc->condjmp = 0; + } + } + +done_generating: + gen_icount_end(tb, num_insns); + *gen_opc_ptr = INDEX_op_end; + +#ifdef DEBUG_DISAS + 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, 0); + qemu_log("\n"); + } +#endif + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) { + gen_opc_instr_start[lj++] = 0; + } + } else { + tb->size = dc->pc - pc_start; + tb->icount = num_insns; + } +} + +void gen_intermediate_code(CPUState *env, TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 0); +} + +void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 1); +} + +static const char *cpu_mode_names[16] = { + "USER", "REAL", "INTR", "PRIV", "UM14", "UM15", "UM16", "TRAP", + "UM18", "UM19", "UM1A", "EXTN", "UM1C", "UM1D", "UM1E", "SUSR" +}; + +#define UCF64_DUMP_STATE +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + int i; +#ifdef UCF64_DUMP_STATE + union { + uint32_t i; + float s; + } s0, s1; + CPU_DoubleU d; + /* ??? This assumes float64 and double have the same layout. + Oh well, it's only debug dumps. */ + union { + float64 f64; + double d; + } d0; +#endif + uint32_t psr; + + for (i = 0; i < 32; i++) { + cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]); + if ((i % 4) == 3) { + cpu_fprintf(f, "\n"); + } else { + cpu_fprintf(f, " "); + } + } + psr = cpu_asr_read(env); + cpu_fprintf(f, "PSR=%08x %c%c%c%c %s\n", + psr, + psr & (1 << 31) ? 'N' : '-', + psr & (1 << 30) ? 'Z' : '-', + psr & (1 << 29) ? 'C' : '-', + psr & (1 << 28) ? 'V' : '-', + cpu_mode_names[psr & 0xf]); + +#ifdef UCF64_DUMP_STATE + for (i = 0; i < 16; i++) { + d.d = env->ucf64.regs[i]; + s0.i = d.l.lower; + s1.i = d.l.upper; + d0.f64 = d.d; + cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g) d%02d=%" PRIx64 "(%8g)\n", + i * 2, (int)s0.i, s0.s, + i * 2 + 1, (int)s1.i, s1.s, + i, (uint64_t)d0.f64, d0.d); + } + cpu_fprintf(f, "FPSCR: %08x\n", (int)env->ucf64.xregs[UC32_UCF64_FPSCR]); +#endif +} + +void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) +{ + env->regs[31] = gen_opc_pc[pc_pos]; +} diff -Nru qemu-kvm-0.14.1+noroms/tcg/arm/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/arm/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/arm/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/arm/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -375,6 +375,12 @@ tcg_out32(s, (cond << 28) | 0x012fff30 | rn); } +static inline void tcg_out_blx_imm(TCGContext *s, int32_t offset) +{ + tcg_out32(s, 0xfa000000 | ((offset & 2) << 23) | + (((offset - 8) >> 2) & 0x00ffffff)); +} + static inline void tcg_out_dat_reg(TCGContext *s, int cond, int opc, int rd, int rn, int rm, int shift) { @@ -840,6 +846,11 @@ { int32_t val; + if (addr & 1) { + /* goto to a Thumb destination isn't supported */ + tcg_abort(); + } + val = addr - (tcg_target_long) s->code_ptr; if (val - 8 < 0x01fffffd && val - 8 > -0x01fffffd) tcg_out_b(s, cond, val); @@ -860,14 +871,22 @@ } } -static inline void tcg_out_call(TCGContext *s, int cond, uint32_t addr) +static inline void tcg_out_call(TCGContext *s, uint32_t addr) { int32_t val; val = addr - (tcg_target_long) s->code_ptr; - if (val < 0x01fffffd && val > -0x01fffffd) - tcg_out_bl(s, cond, val); - else { + if (val - 8 < 0x02000000 && val - 8 >= -0x02000000) { + if (addr & 1) { + /* Use BLX if the target is in Thumb mode */ + if (!use_armv5_instructions) { + tcg_abort(); + } + tcg_out_blx_imm(s, val); + } else { + tcg_out_bl(s, COND_AL, val); + } + } else { #if 1 tcg_abort(); #else @@ -1063,8 +1082,7 @@ 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_AL, (tcg_target_long) qemu_ld_helpers[s_bits] - - (tcg_target_long) s->code_ptr); + tcg_out_call(s, (tcg_target_long) qemu_ld_helpers[s_bits]); switch (opc) { case 0 | 4: @@ -1330,8 +1348,7 @@ } # endif - tcg_out_bl(s, COND_AL, (tcg_target_long) qemu_st_helpers[s_bits] - - (tcg_target_long) s->code_ptr); + tcg_out_call(s, (tcg_target_long) qemu_st_helpers[s_bits]); if (opc == 3) tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R13, TCG_REG_R13, 0x10); @@ -1443,7 +1460,7 @@ break; case INDEX_op_call: if (const_args[0]) - tcg_out_call(s, COND_AL, args[0]); + tcg_out_call(s, args[0]); else tcg_out_callr(s, COND_AL, args[0]); break; @@ -1787,6 +1804,8 @@ tcg_regset_set_reg(s->reserved_regs, TCG_REG_PC); tcg_add_target_add_op_defs(arm_op_defs); + tcg_set_frame(s, TCG_AREG0, offsetof(CPUState, temp_buf), + CPU_TEMP_BUF_NLONGS * sizeof(long)); } static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg, @@ -1829,15 +1848,18 @@ static void tcg_target_qemu_prologue(TCGContext *s) { - /* There is no need to save r7, it is used to store the address - of the env structure and is not modified by GCC. */ + /* Calling convention requires us to save r4-r11 and lr; + * save also r12 to maintain stack 8-alignment. + */ + + /* 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_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); - tcg_out_bx(s, COND_AL, TCG_REG_R0); + tcg_out_bx(s, COND_AL, tcg_target_call_iarg_regs[1]); tb_ret_addr = s->code_ptr; - /* ldmia sp!, { r4 - r6, r8 - r11, pc } */ - tcg_out32(s, (COND_AL << 28) | 0x08bd8f70); + /* ldmia sp!, { r4 - r12, pc } */ + tcg_out32(s, (COND_AL << 28) | 0x08bd9ff0); } diff -Nru qemu-kvm-0.14.1+noroms/tcg/hppa/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/hppa/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/hppa/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/hppa/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -467,6 +467,14 @@ | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len)); } +static inline void tcg_out_depi(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len) +{ + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(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) { @@ -499,8 +507,7 @@ 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)); + tcg_out_depi(s, ret, -1, bs0, bs1 - bs0); } static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m) @@ -529,8 +536,7 @@ tcg_out_extr(s, ret, arg, 0, ls0, 0); } else { 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)); + tcg_out_depi(s, ret, 0, ls0, ls1 - ls0); } } @@ -646,14 +652,14 @@ int arg1, int arg2) { /* 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); + tcg_out_ldst(s, arg1, TCG_REG_CALL_STACK, STACK_TEMP_OFS, INSN_STW); + tcg_out_ldst(s, arg2, TCG_REG_CALL_STACK, 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) + tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_CALL_STACK) | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); /* xmpyu fr22r,fr22,fr22 */ @@ -661,15 +667,16 @@ /* Store the 64-bit result back into the stack. */ /* fstds stack_temp(sp),fr22 */ - tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_SP) + tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_CALL_STACK) | 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); + tcg_out_ldst(s, reth, TCG_REG_CALL_STACK, STACK_TEMP_OFS, INSN_LDW); } if (retl) { - tcg_out_ldst(s, retl, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_LDW); + tcg_out_ldst(s, retl, TCG_REG_CALL_STACK, STACK_TEMP_OFS + 4, + INSN_LDW); } } @@ -1198,7 +1205,7 @@ } 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_out_st(s, TCG_TYPE_I32, argreg, TCG_REG_CALL_STACK, TCG_TARGET_CALL_STACK_OFFSET - 4); break; default: @@ -1458,6 +1465,14 @@ args[4], args[5], const_args[2], const_args[4]); break; + case INDEX_op_deposit_i32: + if (const_args[2]) { + tcg_out_depi(s, args[0], args[2], args[3], args[4]); + } else { + tcg_out_dep(s, args[0], args[2], args[3], args[4]); + } + break; + case INDEX_op_qemu_ld8u: tcg_out_qemu_ld(s, args, 0); break; @@ -1551,6 +1566,8 @@ { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rI", "rZ" } }, { INDEX_op_sub2_i32, { "r", "r", "rI", "rZ", "rK", "rZ" } }, + { INDEX_op_deposit_i32, { "r", "0", "rJ" } }, + #if TARGET_LONG_BITS == 32 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, @@ -1596,7 +1613,7 @@ TCG_REG_R14, TCG_REG_R15, TCG_REG_R16, - /* R17 is the global env, so no need to save. */ + TCG_REG_R17, /* R17 is the global env. */ TCG_REG_R18 }; @@ -1611,23 +1628,30 @@ /* Allocate space for the saved registers. */ frame_size += ARRAY_SIZE(tcg_target_callee_save_regs) * 4; + /* Allocate space for the TCG temps. */ + frame_size += CPU_TEMP_BUF_NLONGS * sizeof(long); + /* 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); + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_CALL_STACK, -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); + TCG_REG_CALL_STACK, 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); + TCG_REG_CALL_STACK, -frame_size + i * 4); } + /* Record the location of the TCG temps. */ + tcg_set_frame(s, TCG_REG_CALL_STACK, -frame_size + i * 4, + TCG_TEMP_BUF_NLONGS * sizeof(long)); + #ifdef CONFIG_USE_GUEST_BASE if (GUEST_BASE != 0) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); @@ -1635,21 +1659,24 @@ } #endif + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + /* Jump to TB, and adjust R18 to be the return address. */ - tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R26)); + tcg_out32(s, INSN_BLE_SR4 | INSN_R2(tcg_target_call_iarg_regs[1])); 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); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_CALL_STACK, + -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); + TCG_REG_CALL_STACK, -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); + TCG_REG_CALL_STACK, -frame_size, INSN_LDWM); } static void tcg_target_init(TCGContext *s) @@ -1676,7 +1703,7 @@ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R19); /* clobbered w/o pic */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R20); /* reserved */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_DP); /* data pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); /* stack pointer */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R31); /* ble link reg */ tcg_add_target_add_op_defs(hppa_op_defs); diff -Nru qemu-kvm-0.14.1+noroms/tcg/hppa/tcg-target.h qemu-kvm-0.15.0+noroms/tcg/hppa/tcg-target.h --- qemu-kvm-0.14.1+noroms/tcg/hppa/tcg-target.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/hppa/tcg-target.h 2011-08-09 07:40:29.000000000 -0500 @@ -94,6 +94,7 @@ #define TCG_TARGET_HAS_not_i32 #define TCG_TARGET_HAS_andc_i32 // #define TCG_TARGET_HAS_orc_i32 +#define TCG_TARGET_HAS_deposit_i32 /* optional instructions automatically implemented */ #undef TCG_TARGET_HAS_neg_i32 /* sub rd, 0, rs */ diff -Nru qemu-kvm-0.14.1+noroms/tcg/i386/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/i386/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/i386/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/i386/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -1398,7 +1398,7 @@ /* 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); + tcg_out_addi(s, TCG_REG_CALL_STACK, stack_adjust); } /* label2: */ @@ -1901,10 +1901,10 @@ TCG_REG_RBX, TCG_REG_R12, TCG_REG_R13, - /* TCG_REG_R14, */ /* Currently used for the global env. */ + TCG_REG_R14, /* Currently used for the global env. */ TCG_REG_R15, #else - /* TCG_REG_EBP, */ /* Currently used for the global env. */ + TCG_REG_EBP, /* Currently used for the global env. */ TCG_REG_EBX, TCG_REG_ESI, TCG_REG_EDI, @@ -1918,28 +1918,34 @@ /* 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. */ + /* Reserve some stack space, also for TCG temps. */ 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 = push_size + TCG_STATIC_CALL_ARGS_SIZE + + CPU_TEMP_BUF_NLONGS * sizeof(long); frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & ~(TCG_TARGET_STACK_ALIGN - 1); stack_addend = frame_size - push_size; + tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, + CPU_TEMP_BUF_NLONGS * sizeof(long)); + + /* 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]); + } + tcg_out_addi(s, TCG_REG_ESP, -stack_addend); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + /* jmp *tb. */ - tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[0]); + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[1]); /* TB epilogue */ tb_ret_addr = s->code_ptr; - tcg_out_addi(s, TCG_REG_ESP, stack_addend); + tcg_out_addi(s, TCG_REG_CALL_STACK, 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]); @@ -1976,7 +1982,7 @@ } tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_ESP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); tcg_add_target_add_op_defs(x86_op_defs); } diff -Nru qemu-kvm-0.14.1+noroms/tcg/ia64/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/ia64/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/ia64/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/ia64/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -2292,7 +2292,7 @@ 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_REG_B6, TCG_REG_R33, 0), tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, TCG_REG_R32, TCG_REG_B0)); @@ -2308,7 +2308,8 @@ } tcg_out_bundle(s, miB, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_AREG0, TCG_REG_R32, 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)); @@ -2387,4 +2388,6 @@ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R6); tcg_add_target_add_op_defs(ia64_op_defs); + tcg_set_frame(s, TCG_AREG0, offsetof(CPUState, temp_buf), + CPU_TEMP_BUF_NLONGS * sizeof(long)); } diff -Nru qemu-kvm-0.14.1+noroms/tcg/mips/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/mips/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/mips/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/mips/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -1452,9 +1452,7 @@ }; static int tcg_target_callee_save_regs[] = { -#if 0 /* used for the global env (TCG_AREG0), so no need to save */ - TCG_REG_S0, -#endif + TCG_REG_S0, /* used for the global env (TCG_AREG0) */ TCG_REG_S1, TCG_REG_S2, TCG_REG_S3, @@ -1486,8 +1484,8 @@ } /* Call generated code */ - tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_A0, 0); - tcg_out_nop(s); + tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); tb_ret_addr = s->code_ptr; /* TB epilogue */ @@ -1530,4 +1528,6 @@ tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ tcg_add_target_add_op_defs(mips_op_defs); + tcg_set_frame(s, TCG_AREG0, offsetof(CPUState, temp_buf), + CPU_TEMP_BUF_NLONGS * sizeof(long)); } diff -Nru qemu-kvm-0.14.1+noroms/tcg/mips/tcg-target.h qemu-kvm-0.15.0+noroms/tcg/mips/tcg-target.h --- qemu-kvm-0.14.1+noroms/tcg/mips/tcg-target.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/mips/tcg-target.h 2011-08-09 07:40:29.000000000 -0500 @@ -102,7 +102,11 @@ /* guest base is supported */ #define TCG_TARGET_HAS_GUEST_BASE +#ifdef __OpenBSD__ +#include +#else #include +#endif static inline void flush_icache_range(unsigned long start, unsigned long stop) { diff -Nru qemu-kvm-0.14.1+noroms/tcg/ppc/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/ppc/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/ppc/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/ppc/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -160,8 +160,7 @@ TCG_REG_R24, TCG_REG_R25, TCG_REG_R26, - /* TCG_REG_R27, */ /* currently used for the global env, so no - need to save */ + TCG_REG_R27, /* currently used for the global env */ TCG_REG_R28, TCG_REG_R29, TCG_REG_R30, @@ -908,9 +907,14 @@ + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE + ARRAY_SIZE (tcg_target_callee_save_regs) * 4 + + CPU_TEMP_BUF_NLONGS * sizeof(long) ; frame_size = (frame_size + 15) & ~15; + tcg_set_frame(s, TCG_REG_CALL_STACK, frame_size + - CPU_TEMP_BUF_NLONGS * sizeof(long), + CPU_TEMP_BUF_NLONGS * sizeof(long)); + #ifdef _CALL_AIX { uint32_t addr; @@ -939,7 +943,8 @@ } #endif - tcg_out32 (s, MTSPR | RS (3) | CTR); + tcg_out_mov (s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out32 (s, MTSPR | RS (tcg_target_call_iarg_regs[1]) | CTR); tcg_out32 (s, BCCTR | BO_ALWAYS); tb_ret_addr = s->code_ptr; @@ -982,11 +987,6 @@ } } -static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) -{ - ppc_addi (s, reg, reg, val); -} - static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, int const_arg2, int cr) { diff -Nru qemu-kvm-0.14.1+noroms/tcg/ppc64/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/ppc64/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/ppc64/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/ppc64/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -151,8 +151,7 @@ TCG_REG_R24, TCG_REG_R25, TCG_REG_R26, - /* TCG_REG_R27, */ /* currently used for the global env, so no - need to save */ + TCG_REG_R27, /* currently used for the global env */ TCG_REG_R28, TCG_REG_R29, TCG_REG_R30, @@ -876,9 +875,14 @@ + 8 /* TOC save area */ + TCG_STATIC_CALL_ARGS_SIZE + ARRAY_SIZE (tcg_target_callee_save_regs) * 8 + + CPU_TEMP_BUF_NLONGS * sizeof(long) ; frame_size = (frame_size + 15) & ~15; + tcg_set_frame(s, TCG_REG_CALL_STACK, frame_size + - CPU_TEMP_BUF_NLONGS * sizeof(long), + CPU_TEMP_BUF_NLONGS * sizeof(long)); + #ifndef __APPLE__ /* First emit adhoc function descriptor */ addr = (uint64_t) s->code_ptr + 24; @@ -905,7 +909,8 @@ } #endif - tcg_out32 (s, MTSPR | RS (3) | CTR); + tcg_out_mov (s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out32 (s, MTSPR | RS (tcg_target_call_iarg_regs[1]) | CTR); tcg_out32 (s, BCCTR | BO_ALWAYS); /* Epilogue */ @@ -968,11 +973,6 @@ } } -static void tcg_out_addi (TCGContext *s, int reg, tcg_target_long val) -{ - ppc_addi64 (s, reg, reg, val); -} - static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, int const_arg2, int cr, int arch64) { diff -Nru qemu-kvm-0.14.1+noroms/tcg/README qemu-kvm-0.15.0+noroms/tcg/README --- qemu-kvm-0.14.1+noroms/tcg/README 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/README 2011-08-09 07:40:29.000000000 -0500 @@ -504,7 +504,15 @@ - Don't hesitate to use helpers for complicated or seldom used target instructions. There is little performance advantage in using TCG to implement target instructions taking more than about twenty TCG - instructions. + instructions. Note that this rule of thumb is more applicable to + helpers doing complex logic or arithmetic, where the C compiler has + scope to do a good job of optimisation; it is less relevant where + the instruction is mostly doing loads and stores, and in those cases + inline TCG may still be faster for longer sequences. + +- The hard limit on the number of TCG instructions you can generate + per target instruction is set by MAX_OP_PER_INSTR in exec-all.h -- + you cannot exceed this without risking a buffer overrun. - Use the 'discard' instruction if you know that TCG won't be able to prove that a given global is "dead" at a given program point. The diff -Nru qemu-kvm-0.14.1+noroms/tcg/s390/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/s390/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/s390/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/s390/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -2291,6 +2291,8 @@ tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); tcg_add_target_add_op_defs(s390_op_defs); + tcg_set_frame(s, TCG_AREG0, offsetof(CPUState, temp_buf), + CPU_TEMP_BUF_NLONGS * sizeof(long)); } static void tcg_target_qemu_prologue(TCGContext *s) @@ -2306,8 +2308,9 @@ 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); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + /* br %r3 (go to TB) */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, tcg_target_call_iarg_regs[1]); tb_ret_addr = s->code_ptr; diff -Nru qemu-kvm-0.14.1+noroms/tcg/sparc/tcg-target.c qemu-kvm-0.15.0+noroms/tcg/sparc/tcg-target.c --- qemu-kvm-0.14.1+noroms/tcg/sparc/tcg-target.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/sparc/tcg-target.c 2011-08-09 07:40:29.000000000 -0500 @@ -693,11 +693,14 @@ /* Generate global QEMU prologue and epilogue code */ static void tcg_target_qemu_prologue(TCGContext *s) { + tcg_set_frame(s, TCG_REG_I6, TCG_TARGET_CALL_STACK_OFFSET, + CPU_TEMP_BUF_NLONGS * (int)sizeof(long)); tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) | - INSN_IMM13(-TCG_TARGET_STACK_MINFRAME)); - tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I0) | + INSN_IMM13(-(TCG_TARGET_STACK_MINFRAME + + CPU_TEMP_BUF_NLONGS * (int)sizeof(long)))); + tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I1) | INSN_RS2(TCG_REG_G0)); - tcg_out_nop(s); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_I0); } #if defined(CONFIG_SOFTMMU) diff -Nru qemu-kvm-0.14.1+noroms/tcg/tcg.c qemu-kvm-0.15.0+noroms/tcg/tcg.c --- qemu-kvm-0.14.1+noroms/tcg/tcg.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/tcg.c 2011-08-09 07:40:29.000000000 -0500 @@ -54,7 +54,6 @@ instructions */ #define NO_CPU_IO_DEFS #include "cpu.h" -#include "exec-all.h" #include "tcg-op.h" #include "elf.h" @@ -450,6 +449,10 @@ s->nb_temps++; } } + +#if defined(CONFIG_DEBUG_TCG) + s->temps_in_use++; +#endif return idx; } @@ -475,6 +478,13 @@ TCGTemp *ts; int k; +#if defined(CONFIG_DEBUG_TCG) + s->temps_in_use--; + if (s->temps_in_use < 0) { + fprintf(stderr, "More temporaries freed than allocated!\n"); + } +#endif + assert(idx >= s->nb_globals && idx < s->nb_temps); ts = &s->temps[idx]; assert(ts->temp_allocated != 0); @@ -528,6 +538,27 @@ return t0; } +#if defined(CONFIG_DEBUG_TCG) +void tcg_clear_temp_count(void) +{ + TCGContext *s = &tcg_ctx; + s->temps_in_use = 0; +} + +int tcg_check_temp_count(void) +{ + TCGContext *s = &tcg_ctx; + if (s->temps_in_use) { + /* Clear the count so that we don't give another + * warning immediately next time around. + */ + s->temps_in_use = 0; + return 1; + } + return 0; +} +#endif + void tcg_register_helper(void *func, const char *name) { TCGContext *s = &tcg_ctx; @@ -553,7 +584,7 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, int sizemask, TCGArg ret, int nargs, TCGArg *args) { -#ifdef TCG_TARGET_I386 +#if defined(TCG_TARGET_I386) && TCG_TARGET_REG_BITS < 64 int call_type; #endif int i; @@ -580,7 +611,7 @@ *gen_opc_ptr++ = INDEX_op_call; nparam = gen_opparam_ptr++; -#ifdef TCG_TARGET_I386 +#if defined(TCG_TARGET_I386) && TCG_TARGET_REG_BITS < 64 call_type = (flags & TCG_CALL_TYPE_MASK); #endif if (ret != TCG_CALL_DUMMY_ARG) { @@ -1161,7 +1192,7 @@ } } -/* Liveness analysis : update the opc_dead_iargs array to tell if a +/* Liveness analysis : update the opc_dead_args array to tell if a given input arguments is dead. Instructions updating dead temporaries are removed. */ static void tcg_liveness_analysis(TCGContext *s) @@ -1171,13 +1202,13 @@ TCGArg *args; const TCGOpDef *def; uint8_t *dead_temps; - unsigned int dead_iargs; + unsigned int dead_args; gen_opc_ptr++; /* skip end */ nb_ops = gen_opc_ptr - gen_opc_buf; - s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t)); + s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t)); dead_temps = tcg_malloc(s->nb_temps); memset(dead_temps, 1, s->nb_temps); @@ -1213,8 +1244,12 @@ do_not_remove_call: /* output args are dead */ + dead_args = 0; for(i = 0; i < nb_oargs; i++) { arg = args[i]; + if (dead_temps[arg]) { + dead_args |= (1 << i); + } dead_temps[arg] = 1; } @@ -1224,17 +1259,16 @@ } /* input args are live */ - dead_iargs = 0; - for(i = 0; i < nb_iargs; i++) { - arg = args[i + nb_oargs]; + for(i = nb_oargs; i < nb_iargs + nb_oargs; i++) { + arg = args[i]; if (arg != TCG_CALL_DUMMY_ARG) { if (dead_temps[arg]) { - dead_iargs |= (1 << i); + dead_args |= (1 << i); } dead_temps[arg] = 0; } } - s->op_dead_iargs[op_index] = dead_iargs; + s->op_dead_args[op_index] = dead_args; } args--; } @@ -1281,8 +1315,12 @@ do_not_remove: /* output args are dead */ + dead_args = 0; for(i = 0; i < nb_oargs; i++) { arg = args[i]; + if (dead_temps[arg]) { + dead_args |= (1 << i); + } dead_temps[arg] = 1; } @@ -1295,15 +1333,14 @@ } /* input args are live */ - dead_iargs = 0; - for(i = 0; i < nb_iargs; i++) { - arg = args[i + nb_oargs]; + for(i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + arg = args[i]; if (dead_temps[arg]) { - dead_iargs |= (1 << i); + dead_args |= (1 << i); } dead_temps[arg] = 0; } - s->op_dead_iargs[op_index] = dead_iargs; + s->op_dead_args[op_index] = dead_args; } break; } @@ -1320,8 +1357,8 @@ int nb_ops; nb_ops = gen_opc_ptr - gen_opc_buf; - s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t)); - memset(s->op_dead_iargs, 0, nb_ops * sizeof(uint16_t)); + s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t)); + memset(s->op_dead_args, 0, nb_ops * sizeof(uint16_t)); } #endif @@ -1402,13 +1439,19 @@ { TCGTemp *ts; ts = &s->temps[temp]; - s->current_frame_offset = (s->current_frame_offset + sizeof(tcg_target_long) - 1) & ~(sizeof(tcg_target_long) - 1); - if (s->current_frame_offset + sizeof(tcg_target_long) > s->frame_end) +#ifndef __sparc_v9__ /* Sparc64 stack is accessed with offset of 2047 */ + s->current_frame_offset = (s->current_frame_offset + + (tcg_target_long)sizeof(tcg_target_long) - 1) & + ~(sizeof(tcg_target_long) - 1); +#endif + if (s->current_frame_offset + (tcg_target_long)sizeof(tcg_target_long) > + s->frame_end) { tcg_abort(); + } ts->mem_offset = s->current_frame_offset; ts->mem_reg = s->frame_reg; ts->mem_allocated = 1; - s->current_frame_offset += sizeof(tcg_target_long); + s->current_frame_offset += (tcg_target_long)sizeof(tcg_target_long); } /* free register 'reg' by spilling the corresponding temporary if necessary */ @@ -1525,7 +1568,7 @@ save_globals(s, allocated_regs); } -#define IS_DEAD_IARG(n) ((dead_iargs >> (n)) & 1) +#define IS_DEAD_ARG(n) ((dead_args >> (n)) & 1) static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args) { @@ -1550,7 +1593,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, const TCGArg *args, - unsigned int dead_iargs) + unsigned int dead_args) { TCGTemp *ts, *ots; int reg; @@ -1560,9 +1603,9 @@ ts = &s->temps[args[1]]; arg_ct = &def->args_ct[0]; - /* XXX: always mark arg dead if IS_DEAD_IARG(0) */ + /* XXX: always mark arg dead if IS_DEAD_ARG(1) */ if (ts->val_type == TEMP_VAL_REG) { - if (IS_DEAD_IARG(0) && !ts->fixed_reg && !ots->fixed_reg) { + if (IS_DEAD_ARG(1) && !ts->fixed_reg && !ots->fixed_reg) { /* the mov can be suppressed */ if (ots->val_type == TEMP_VAL_REG) s->reg_to_temp[ots->reg] = -1; @@ -1610,7 +1653,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOpDef *def, TCGOpcode opc, const TCGArg *args, - unsigned int dead_iargs) + unsigned int dead_args) { TCGRegSet allocated_regs; int i, k, nb_iargs, nb_oargs, reg; @@ -1669,8 +1712,9 @@ /* if the input is aliased to an output and if it is not dead after the instruction, we must allocate a new register and move it */ - if (!IS_DEAD_IARG(i - nb_oargs)) + if (!IS_DEAD_ARG(i)) { goto allocate_in_reg; + } } } reg = ts->reg; @@ -1693,9 +1737,9 @@ tcg_reg_alloc_bb_end(s, allocated_regs); } else { /* mark dead temporaries and free the associated registers */ - for(i = 0; i < nb_iargs; i++) { - arg = args[nb_oargs + i]; - if (IS_DEAD_IARG(i)) { + for(i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + arg = args[i]; + if (IS_DEAD_ARG(i)) { ts = &s->temps[arg]; if (!ts->fixed_reg) { if (ts->val_type == TEMP_VAL_REG) @@ -1743,12 +1787,16 @@ if (!ts->fixed_reg) { if (ts->val_type == TEMP_VAL_REG) s->reg_to_temp[ts->reg] = -1; - ts->val_type = TEMP_VAL_REG; - ts->reg = reg; - /* temp value is modified, so the value kept in memory is - potentially not the same */ - ts->mem_coherent = 0; - s->reg_to_temp[reg] = arg; + if (IS_DEAD_ARG(i)) { + ts->val_type = TEMP_VAL_DEAD; + } else { + ts->val_type = TEMP_VAL_REG; + ts->reg = reg; + /* temp value is modified, so the value kept in memory is + potentially not the same */ + ts->mem_coherent = 0; + s->reg_to_temp[reg] = arg; + } } oarg_end: new_args[i] = reg; @@ -1776,7 +1824,7 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, TCGOpcode opc, const TCGArg *args, - unsigned int dead_iargs) + unsigned int dead_args) { int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params; TCGArg arg, func_arg; @@ -1799,13 +1847,14 @@ nb_regs = nb_params; /* assign stack slots first */ - /* XXX: preallocate call stack */ call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long); call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & ~(TCG_TARGET_STACK_ALIGN - 1); allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE); if (allocate_args) { - tcg_out_addi(s, TCG_REG_CALL_STACK, -STACK_DIR(call_stack_size)); + /* XXX: if more than TCG_STATIC_CALL_ARGS_SIZE is needed, + preallocate call stack */ + tcg_abort(); } stack_offset = TCG_TARGET_CALL_STACK_OFFSET; @@ -1898,9 +1947,9 @@ /* mark dead temporaries and free the associated registers */ - for(i = 0; i < nb_iargs; i++) { - arg = args[nb_oargs + i]; - if (IS_DEAD_IARG(i)) { + for(i = nb_oargs; i < nb_iargs + nb_oargs; i++) { + arg = args[i]; + if (IS_DEAD_ARG(i)) { ts = &s->temps[arg]; if (!ts->fixed_reg) { if (ts->val_type == TEMP_VAL_REG) @@ -1924,10 +1973,6 @@ } tcg_out_op(s, opc, &func_arg, &const_func_arg); - - if (allocate_args) { - tcg_out_addi(s, TCG_REG_CALL_STACK, STACK_DIR(call_stack_size)); - } /* assign output registers and emit moves if needed */ for(i = 0; i < nb_oargs; i++) { @@ -1942,10 +1987,14 @@ } else { if (ts->val_type == TEMP_VAL_REG) s->reg_to_temp[ts->reg] = -1; - ts->val_type = TEMP_VAL_REG; - ts->reg = reg; - ts->mem_coherent = 0; - s->reg_to_temp[reg] = arg; + if (IS_DEAD_ARG(i)) { + ts->val_type = TEMP_VAL_DEAD; + } else { + ts->val_type = TEMP_VAL_REG; + ts->reg = reg; + ts->mem_coherent = 0; + s->reg_to_temp[reg] = arg; + } } } @@ -1975,7 +2024,7 @@ TCGOpcode opc; int op_index; const TCGOpDef *def; - unsigned int dead_iargs; + unsigned int dead_args; const TCGArg *args; #ifdef DEBUG_DISAS @@ -2026,8 +2075,8 @@ #if TCG_TARGET_REG_BITS == 64 case INDEX_op_mov_i64: #endif - dead_iargs = s->op_dead_iargs[op_index]; - tcg_reg_alloc_mov(s, def, args, dead_iargs); + dead_args = s->op_dead_args[op_index]; + tcg_reg_alloc_mov(s, def, args, dead_args); break; case INDEX_op_movi_i32: #if TCG_TARGET_REG_BITS == 64 @@ -2063,8 +2112,8 @@ tcg_out_label(s, args[0], (long)s->code_ptr); break; case INDEX_op_call: - dead_iargs = s->op_dead_iargs[op_index]; - args += tcg_reg_alloc_call(s, def, opc, args, dead_iargs); + dead_args = s->op_dead_args[op_index]; + args += tcg_reg_alloc_call(s, def, opc, args, dead_args); goto next; case INDEX_op_end: goto the_end; @@ -2072,8 +2121,8 @@ /* Note: in order to speed up the code, it would be much faster to have specialized register allocator functions for some common argument patterns */ - dead_iargs = s->op_dead_iargs[op_index]; - tcg_reg_alloc_op(s, def, opc, args, dead_iargs); + dead_args = s->op_dead_args[op_index]; + tcg_reg_alloc_op(s, def, opc, args, dead_args); break; } args += def->nb_args; diff -Nru qemu-kvm-0.14.1+noroms/tcg/tcg.h qemu-kvm-0.15.0+noroms/tcg/tcg.h --- qemu-kvm-0.14.1+noroms/tcg/tcg.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/tcg.h 2011-08-09 07:40:29.000000000 -0500 @@ -129,7 +129,7 @@ We use plain int by default to avoid this runtime overhead. Users of tcg_gen_* don't need to know about any of this, and should treat TCGv as an opaque type. - In additon we do typechecking for different types of variables. TCGv_i32 + In addition we do typechecking for different types of variables. TCGv_i32 and TCGv_i64 are 32/64-bit variables respectively. TCGv and TCGv_ptr are aliases for target_ulong and host pointer sized values respectively. */ @@ -150,12 +150,19 @@ int i64; } TCGv_i64; +typedef struct { + int iptr; +} TCGv_ptr; + #define MAKE_TCGV_I32(i) __extension__ \ ({ TCGv_i32 make_tcgv_tmp = {i}; make_tcgv_tmp;}) #define MAKE_TCGV_I64(i) __extension__ \ ({ TCGv_i64 make_tcgv_tmp = {i}; make_tcgv_tmp;}) +#define MAKE_TCGV_PTR(i) __extension__ \ + ({ TCGv_ptr make_tcgv_tmp = {i}; make_tcgv_tmp; }) #define GET_TCGV_I32(t) ((t).i32) #define GET_TCGV_I64(t) ((t).i64) +#define GET_TCGV_PTR(t) ((t).iptr) #if TCG_TARGET_REG_BITS == 32 #define TCGV_LOW(t) MAKE_TCGV_I32(GET_TCGV_I64(t)) #define TCGV_HIGH(t) MAKE_TCGV_I32(GET_TCGV_I64(t) + 1) @@ -165,10 +172,17 @@ typedef int TCGv_i32; typedef int TCGv_i64; +#if TCG_TARGET_REG_BITS == 32 +#define TCGv_ptr TCGv_i32 +#else +#define TCGv_ptr TCGv_i64 +#endif #define MAKE_TCGV_I32(x) (x) #define MAKE_TCGV_I64(x) (x) +#define MAKE_TCGV_PTR(x) (x) #define GET_TCGV_I32(t) (t) #define GET_TCGV_I64(t) (t) +#define GET_TCGV_PTR(t) (t) #if TCG_TARGET_REG_BITS == 32 #define TCGV_LOW(t) (t) @@ -252,9 +266,9 @@ unsigned int fixed_reg:1; unsigned int mem_coherent:1; unsigned int mem_allocated:1; - unsigned int temp_local:1; /* If true, the temp is saved accross + unsigned int temp_local:1; /* If true, the temp is saved across basic blocks. Otherwise, it is not - preserved accross basic blocks. */ + preserved across basic blocks. */ unsigned int temp_allocated:1; /* never used for code gen */ /* index of next free temp of same base type, -1 if end */ int next_free_temp; @@ -286,8 +300,8 @@ uint16_t *tb_jmp_offset; /* != NULL if USE_DIRECT_JUMP */ /* liveness analysis */ - uint16_t *op_dead_iargs; /* for each operation, each bit tells if the - corresponding input argument is dead */ + uint16_t *op_dead_args; /* for each operation, each bit tells if the + corresponding argument is dead */ /* tells in which temporary a given register is. It does not take into account fixed registers */ @@ -323,6 +337,10 @@ int64_t restore_count; int64_t restore_time; #endif + +#ifdef CONFIG_DEBUG_TCG + int temps_in_use; +#endif }; extern TCGContext tcg_ctx; @@ -392,6 +410,19 @@ void tcg_temp_free_i64(TCGv_i64 arg); char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg); +#if defined(CONFIG_DEBUG_TCG) +/* If you call tcg_clear_temp_count() at the start of a section of + * code which is not supposed to leak any TCG temporaries, then + * calling tcg_check_temp_count() at the end of the section will + * return 1 if the section did in fact leak a temporary. + */ +void tcg_clear_temp_count(void); +int tcg_check_temp_count(void); +#else +#define tcg_clear_temp_count() do { } while (0) +#define tcg_check_temp_count() 0 +#endif + void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf); #define TCG_CT_ALIAS 0x80 @@ -442,25 +473,27 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs); #if TCG_TARGET_REG_BITS == 32 -#define tcg_const_ptr tcg_const_i32 -#define tcg_add_ptr tcg_add_i32 -#define tcg_sub_ptr tcg_sub_i32 -#define TCGv_ptr TCGv_i32 -#define GET_TCGV_PTR GET_TCGV_I32 -#define tcg_global_reg_new_ptr tcg_global_reg_new_i32 -#define tcg_global_mem_new_ptr tcg_global_mem_new_i32 -#define tcg_temp_new_ptr tcg_temp_new_i32 -#define tcg_temp_free_ptr tcg_temp_free_i32 +#define TCGV_NAT_TO_PTR(n) MAKE_TCGV_PTR(GET_TCGV_I32(n)) +#define TCGV_PTR_TO_NAT(n) MAKE_TCGV_I32(GET_TCGV_PTR(n)) + +#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i32(V)) +#define tcg_global_reg_new_ptr(R, N) \ + TCGV_NAT_TO_PTR(tcg_global_reg_new_i32((R), (N))) +#define tcg_global_mem_new_ptr(R, O, N) \ + TCGV_NAT_TO_PTR(tcg_global_mem_new_i32((R), (O), (N))) +#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i32()) +#define tcg_temp_free_ptr(T) tcg_temp_free_i32(TCGV_PTR_TO_NAT(T)) #else -#define tcg_const_ptr tcg_const_i64 -#define tcg_add_ptr tcg_add_i64 -#define tcg_sub_ptr tcg_sub_i64 -#define TCGv_ptr TCGv_i64 -#define GET_TCGV_PTR GET_TCGV_I64 -#define tcg_global_reg_new_ptr tcg_global_reg_new_i64 -#define tcg_global_mem_new_ptr tcg_global_mem_new_i64 -#define tcg_temp_new_ptr tcg_temp_new_i64 -#define tcg_temp_free_ptr tcg_temp_free_i64 +#define TCGV_NAT_TO_PTR(n) MAKE_TCGV_PTR(GET_TCGV_I64(n)) +#define TCGV_PTR_TO_NAT(n) MAKE_TCGV_I64(GET_TCGV_PTR(n)) + +#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i64(V)) +#define tcg_global_reg_new_ptr(R, N) \ + TCGV_NAT_TO_PTR(tcg_global_reg_new_i64((R), (N))) +#define tcg_global_mem_new_ptr(R, O, N) \ + TCGV_NAT_TO_PTR(tcg_global_mem_new_i64((R), (O), (N))) +#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i64()) +#define tcg_temp_free_ptr(T) tcg_temp_free_i64(TCGV_PTR_TO_NAT(T)) #endif void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, @@ -482,8 +515,9 @@ extern uint8_t code_gen_prologue[]; #if defined(_ARCH_PPC) && !defined(_ARCH_PPC64) -#define tcg_qemu_tb_exec(tb_ptr) \ - ((long REGPARM __attribute__ ((longcall)) (*)(void *))code_gen_prologue)(tb_ptr) +#define tcg_qemu_tb_exec(env, tb_ptr) \ + ((long REGPARM __attribute__ ((longcall)) (*)(void *, void *))code_gen_prologue)(env, tb_ptr) #else -#define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM (*)(void *))code_gen_prologue)(tb_ptr) +#define tcg_qemu_tb_exec(env, tb_ptr) \ + ((long REGPARM (*)(void *, void *))code_gen_prologue)(env, tb_ptr) #endif diff -Nru qemu-kvm-0.14.1+noroms/tcg/tcg-op.h qemu-kvm-0.15.0+noroms/tcg/tcg-op.h --- qemu-kvm-0.14.1+noroms/tcg/tcg-op.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tcg/tcg-op.h 2011-08-09 07:40:29.000000000 -0500 @@ -1063,66 +1063,66 @@ tcg_gen_op2i_i64(INDEX_op_movi_i64, ret, arg); } -static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_i64 arg2, +static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); } -static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_i64 arg2, +static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); } -static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_i64 arg2, +static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); } -static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_i64 arg2, +static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); } -static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_i64 arg2, +static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); } -static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_i64 arg2, +static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); } -static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_i64 arg2, tcg_target_long offset) +static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); } -static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); } -static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); } -static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); } -static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_i64 arg2, tcg_target_long offset) +static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); } @@ -2304,8 +2304,8 @@ #endif } -#define tcg_gen_ld_ptr tcg_gen_ld_i32 -#define tcg_gen_discard_ptr tcg_gen_discard_i32 +#define tcg_gen_ld_ptr(R, A, O) tcg_gen_ld_i32(TCGV_PTR_TO_NAT(R), (A), (O)) +#define tcg_gen_discard_ptr(A) tcg_gen_discard_i32(TCGV_PTR_TO_NAT(A)) #else /* TCG_TARGET_REG_BITS == 32 */ @@ -2372,8 +2372,8 @@ tcg_gen_qemu_ldst_op_i64(INDEX_op_qemu_st64, arg, addr, mem_index); } -#define tcg_gen_ld_ptr tcg_gen_ld_i64 -#define tcg_gen_discard_ptr tcg_gen_discard_i64 +#define tcg_gen_ld_ptr(R, A, O) tcg_gen_ld_i64(TCGV_PTR_TO_NAT(R), (A), (O)) +#define tcg_gen_discard_ptr(A) tcg_gen_discard_i64(TCGV_PTR_TO_NAT(A)) #endif /* TCG_TARGET_REG_BITS != 32 */ @@ -2523,11 +2523,17 @@ #endif #if TCG_TARGET_REG_BITS == 32 -#define tcg_gen_add_ptr tcg_gen_add_i32 -#define tcg_gen_addi_ptr tcg_gen_addi_i32 -#define tcg_gen_ext_i32_ptr tcg_gen_mov_i32 +#define tcg_gen_add_ptr(R, A, B) tcg_gen_add_i32(TCGV_PTR_TO_NAT(R), \ + TCGV_PTR_TO_NAT(A), \ + TCGV_PTR_TO_NAT(B)) +#define tcg_gen_addi_ptr(R, A, B) tcg_gen_addi_i32(TCGV_PTR_TO_NAT(R), \ + TCGV_PTR_TO_NAT(A), (B)) +#define tcg_gen_ext_i32_ptr(R, A) tcg_gen_mov_i32(TCGV_PTR_TO_NAT(R), (A)) #else /* TCG_TARGET_REG_BITS == 32 */ -#define tcg_gen_add_ptr tcg_gen_add_i64 -#define tcg_gen_addi_ptr tcg_gen_addi_i64 -#define tcg_gen_ext_i32_ptr tcg_gen_ext_i32_i64 +#define tcg_gen_add_ptr(R, A, B) tcg_gen_add_i64(TCGV_PTR_TO_NAT(R), \ + TCGV_PTR_TO_NAT(A), \ + TCGV_PTR_TO_NAT(B)) +#define tcg_gen_addi_ptr(R, A, B) tcg_gen_addi_i64(TCGV_PTR_TO_NAT(R), \ + TCGV_PTR_TO_NAT(A), (B)) +#define tcg_gen_ext_i32_ptr(R, A) tcg_gen_ext_i32_i64(TCGV_PTR_TO_NAT(R), (A)) #endif /* TCG_TARGET_REG_BITS != 32 */ diff -Nru qemu-kvm-0.14.1+noroms/test-qmp-commands.c qemu-kvm-0.15.0+noroms/test-qmp-commands.c --- qemu-kvm-0.14.1+noroms/test-qmp-commands.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/test-qmp-commands.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,113 @@ +#include +#include "qemu-objects.h" +#include "test-qmp-commands.h" +#include "qapi/qmp-core.h" +#include "module.h" + +void qmp_user_def_cmd(Error **errp) +{ +} + +void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) +{ +} + +UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **errp) +{ + UserDefTwo *ret; + UserDefOne *ud1c = qemu_mallocz(sizeof(UserDefOne)); + UserDefOne *ud1d = qemu_mallocz(sizeof(UserDefOne)); + + ud1c->string = strdup(ud1a->string); + ud1c->integer = ud1a->integer; + ud1d->string = strdup(ud1b->string); + ud1d->integer = ud1b->integer; + + ret = qemu_mallocz(sizeof(UserDefTwo)); + ret->string = strdup("blah1"); + ret->dict.string = strdup("blah2"); + ret->dict.dict.userdef = ud1c; + ret->dict.dict.string = strdup("blah3"); + ret->dict.has_dict2 = true; + ret->dict.dict2.userdef = ud1d; + ret->dict.dict2.string = strdup("blah4"); + + return ret; +} + +/* test commands with no input and no return value */ +static void test_dispatch_cmd(void) +{ + QDict *req = qdict_new(); + QObject *resp; + + qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd"))); + + resp = qmp_dispatch(QOBJECT(req)); + assert(resp != NULL); + assert(!qdict_haskey(qobject_to_qdict(resp), "error")); + g_print("\nresp: %s\n", qstring_get_str(qobject_to_json(resp))); + + qobject_decref(resp); + QDECREF(req); +} + +/* test commands that return an error due to invalid parameters */ +static void test_dispatch_cmd_error(void) +{ + QDict *req = qdict_new(); + QObject *resp; + + qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2"))); + + resp = qmp_dispatch(QOBJECT(req)); + assert(resp != NULL); + assert(qdict_haskey(qobject_to_qdict(resp), "error")); + g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp))); + + qobject_decref(resp); + QDECREF(req); +} + +/* test commands that involve both input parameters and return values */ +static void test_dispatch_cmd_io(void) +{ + QDict *req = qdict_new(); + QDict *args = qdict_new(); + QDict *ud1a = qdict_new(); + QDict *ud1b = qdict_new(); + QObject *resp; + + qdict_put_obj(ud1a, "integer", QOBJECT(qint_from_int(42))); + qdict_put_obj(ud1a, "string", QOBJECT(qstring_from_str("hello"))); + qdict_put_obj(ud1b, "integer", QOBJECT(qint_from_int(422))); + qdict_put_obj(ud1b, "string", QOBJECT(qstring_from_str("hello2"))); + qdict_put_obj(args, "ud1a", QOBJECT(ud1a)); + qdict_put_obj(args, "ud1b", QOBJECT(ud1b)); + qdict_put_obj(req, "arguments", QOBJECT(args)); + + qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2"))); + + /* TODO: put in full payload and check for errors */ + resp = qmp_dispatch(QOBJECT(req)); + assert(resp != NULL); + assert(!qdict_haskey(qobject_to_qdict(resp), "error")); + g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp))); + + qobject_decref(resp); + QDECREF(req); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/0.15/dispatch_cmd", test_dispatch_cmd); + g_test_add_func("/0.15/dispatch_cmd_error", test_dispatch_cmd_error); + g_test_add_func("/0.15/dispatch_cmd_io", test_dispatch_cmd_io); + + module_call_init(MODULE_INIT_QAPI); + g_test_run(); + + return 0; +} diff -Nru qemu-kvm-0.14.1+noroms/tests/cris/check_openpf1.c qemu-kvm-0.15.0+noroms/tests/cris/check_openpf1.c --- qemu-kvm-0.14.1+noroms/tests/cris/check_openpf1.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/cris/check_openpf1.c 2011-08-09 07:40:29.000000000 -0500 @@ -27,7 +27,7 @@ f = fopen (fnam, "rb"); if (f == NULL) abort (); - close (f); + fclose(f); /* Cover another execution path. */ if (fopen ("/nonexistent", "rb") != NULL diff -Nru qemu-kvm-0.14.1+noroms/tests/cris/check_openpf2.c qemu-kvm-0.15.0+noroms/tests/cris/check_openpf2.c --- qemu-kvm-0.14.1+noroms/tests/cris/check_openpf2.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/cris/check_openpf2.c 2011-08-09 07:40:29.000000000 -0500 @@ -10,7 +10,7 @@ FILE *f = fopen ("check_openpf2.c", "rb"); if (f == NULL) abort (); - close (f); + fclose(f); printf ("pass\n"); return 0; } diff -Nru qemu-kvm-0.14.1+noroms/tests/cris/check_stat3.c qemu-kvm-0.15.0+noroms/tests/cris/check_stat3.c --- qemu-kvm-0.14.1+noroms/tests/cris/check_stat3.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/cris/check_stat3.c 2011-08-09 07:40:29.000000000 -0500 @@ -13,7 +13,7 @@ char path[1024] = "/"; struct stat buf; - strcat (path, argv[0]); + strncat(path, argv[0], sizeof(path) - 2); if (stat (".", &buf) != 0 || !S_ISDIR (buf.st_mode)) abort (); diff -Nru qemu-kvm-0.14.1+noroms/tests/cris/check_stat4.c qemu-kvm-0.15.0+noroms/tests/cris/check_stat4.c --- qemu-kvm-0.14.1+noroms/tests/cris/check_stat4.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/cris/check_stat4.c 2011-08-09 07:40:29.000000000 -0500 @@ -15,7 +15,7 @@ char path[1024] = "/"; struct stat buf; - strcat (path, argv[0]); + strncat(path, argv[0], sizeof(path) - 2); if (lstat (".", &buf) != 0 || !S_ISDIR (buf.st_mode)) abort (); diff -Nru qemu-kvm-0.14.1+noroms/tests/linux-test.c qemu-kvm-0.15.0+noroms/tests/linux-test.c --- qemu-kvm-0.14.1+noroms/tests/linux-test.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/linux-test.c 2011-08-09 07:40:29.000000000 -0500 @@ -426,7 +426,9 @@ CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2")); while (waitpid(pid1, &status1, 0) != pid1); + free(stack1); while (waitpid(pid2, &status2, 0) != pid2); + free(stack2); if (thread1_res != 5 || thread2_res != 6) error("clone"); diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/crt.S qemu-kvm-0.15.0+noroms/tests/lm32/crt.S --- qemu-kvm-0.14.1+noroms/tests/lm32/crt.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/crt.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,84 @@ +.text +.global _start + +_start: +_reset_handler: + xor r0, r0, r0 + mvhi r1, hi(_start) + ori r1, r1, lo(_start) + wcsr eba, r1 + wcsr deba, r1 + bi _main + nop + nop + +_breakpoint_handler: + ori r25, r25, 1 + addi ra, ba, 4 + ret + nop + nop + nop + nop + nop + +_instruction_bus_error_handler: + ori r25, r25, 2 + addi ra, ea, 4 + ret + nop + nop + nop + nop + nop + +_watchpoint_handler: + ori r25, r25, 4 + addi ra, ba, 4 + ret + nop + nop + nop + nop + nop + +_data_bus_error_handler: + ori r25, r25, 8 + addi ra, ea, 4 + ret + nop + nop + nop + nop + nop + +_divide_by_zero_handler: + ori r25, r25, 16 + addi ra, ea, 4 + ret + nop + nop + nop + nop + nop + +_interrupt_handler: + ori r25, r25, 32 + addi ra, ea, 4 + ret + nop + nop + nop + nop + nop + +_system_call_handler: + ori r25, r25, 64 + addi ra, ea, 4 + ret + nop + nop + nop + nop + nop + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/linker.ld qemu-kvm-0.15.0+noroms/tests/lm32/linker.ld --- qemu-kvm-0.14.1+noroms/tests/lm32/linker.ld 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/linker.ld 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,55 @@ +OUTPUT_FORMAT("elf32-lm32") +ENTRY(_start) + +__DYNAMIC = 0; + +MEMORY { + ram : ORIGIN = 0x08000000, LENGTH = 0x04000000 /* 64M */ +} + +SECTIONS +{ + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + } > ram + + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + _erodata = .; + } > ram + + .data : + { + . = ALIGN(4); + _fdata = .; + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + _gp = ALIGN(16); + *(.sdata .sdata.* .gnu.linkonce.s.*) + _edata = .; + } > ram + + .bss : + { + . = ALIGN(4); + _fbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + _ebss = .; + _end = .; + } > ram +} + +PROVIDE(_fstack = ORIGIN(ram) + LENGTH(ram) - 4); + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/macros.inc qemu-kvm-0.15.0+noroms/tests/lm32/macros.inc --- qemu-kvm-0.14.1+noroms/tests/lm32/macros.inc 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/macros.inc 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,79 @@ + +.macro test_name name + .data +tn_\name: + .asciz "\name" + .text + mvhi r13, hi(tn_\name) + ori r13, r13, lo(tn_\name) + sw (r12+8), r13 +.endm + +.macro load reg val + mvhi \reg, hi(\val) + ori \reg, \reg, lo(\val) +.endm + +.macro tc_pass + mvi r13, 0 + sw (r12+4), r13 +.endm + +.macro tc_fail + mvi r13, 1 + sw (r12+4), r13 +.endm + +.macro check_r3 val + mvhi r13, hi(\val) + ori r13, r13, lo(\val) + be r3, r13, 1f + tc_fail + bi 2f +1: + tc_pass +2: +.endm + +.macro check_mem adr val + mvhi r13, hi(\adr) + ori r13, r13, lo(\adr) + mvhi r14, hi(\val) + ori r14, r14, lo(\val) + lw r13, (r13+0) + be r13, r14, 1f + tc_fail + bi 2f +1: + tc_pass +2: +.endm + +.macro check_excp excp + andi r13, r25, \excp + bne r13, r0, 1f + tc_fail + bi 2f +1: + tc_pass +2: +.endm + +.macro start + .global _main + .text +_main: + mvhi r12, hi(0xffff0000) # base address of test block + ori r12, r12, lo(0xffff0000) +.endm + +.macro end + sw (r12+0), r0 +1: + bi 1b +.endm + +# base + +# 0 ctrl +# 4 pass/fail +# 8 ptr to test name diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/Makefile qemu-kvm-0.15.0+noroms/tests/lm32/Makefile --- qemu-kvm-0.14.1+noroms/tests/lm32/Makefile 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/Makefile 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,102 @@ +-include ../../config-host.mak + +CROSS=lm32-elf- + +SIM = qemu-system-lm32 +SIMFLAGS = -M lm32-evr -nographic -device lm32-sys -net none -kernel + +CC = $(CROSS)gcc +AS = $(CROSS)as +AS = $(CC) -x assembler +SIZE = $(CROSS)size +LD = $(CC) +OBJCOPY = $(CROSS)objcopy + +LDFLAGS = -Tlinker.ld + +CRT = crt.o +TESTCASES += test_add.tst +TESTCASES += test_addi.tst +TESTCASES += test_and.tst +TESTCASES += test_andhi.tst +TESTCASES += test_andi.tst +TESTCASES += test_b.tst +TESTCASES += test_be.tst +TESTCASES += test_bg.tst +TESTCASES += test_bge.tst +TESTCASES += test_bgeu.tst +TESTCASES += test_bgu.tst +TESTCASES += test_bi.tst +TESTCASES += test_bne.tst +TESTCASES += test_break.tst +TESTCASES += test_bret.tst +TESTCASES += test_call.tst +TESTCASES += test_calli.tst +TESTCASES += test_cmpe.tst +TESTCASES += test_cmpei.tst +TESTCASES += test_cmpg.tst +TESTCASES += test_cmpgi.tst +TESTCASES += test_cmpge.tst +TESTCASES += test_cmpgei.tst +TESTCASES += test_cmpgeu.tst +TESTCASES += test_cmpgeui.tst +TESTCASES += test_cmpgu.tst +TESTCASES += test_cmpgui.tst +TESTCASES += test_cmpne.tst +TESTCASES += test_cmpnei.tst +TESTCASES += test_divu.tst +TESTCASES += test_eret.tst +TESTCASES += test_lb.tst +TESTCASES += test_lbu.tst +TESTCASES += test_lh.tst +TESTCASES += test_lhu.tst +TESTCASES += test_lw.tst +TESTCASES += test_modu.tst +TESTCASES += test_mul.tst +TESTCASES += test_muli.tst +TESTCASES += test_nor.tst +TESTCASES += test_nori.tst +TESTCASES += test_or.tst +TESTCASES += test_ori.tst +TESTCASES += test_orhi.tst +#TESTCASES += test_rcsr.tst +TESTCASES += test_ret.tst +TESTCASES += test_sb.tst +TESTCASES += test_scall.tst +TESTCASES += test_sextb.tst +TESTCASES += test_sexth.tst +TESTCASES += test_sh.tst +TESTCASES += test_sl.tst +TESTCASES += test_sli.tst +TESTCASES += test_sr.tst +TESTCASES += test_sri.tst +TESTCASES += test_sru.tst +TESTCASES += test_srui.tst +TESTCASES += test_sub.tst +TESTCASES += test_sw.tst +#TESTCASES += test_wcsr.tst +TESTCASES += test_xnor.tst +TESTCASES += test_xnori.tst +TESTCASES += test_xor.tst +TESTCASES += test_xori.tst + +all: build + +%.o: $(SRC_PATH)/tests/lm32/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: $(SRC_PATH)/tests/lm32/%.S + $(AS) $(ASFLAGS) -c $< -o $@ + +%.tst: %.o macros.inc $(CRT) + $(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@ + +build: $(CRT) $(TESTCASES) + +check: $(CRT) $(SYS) $(TESTCASES) + @for case in $(TESTCASES); do \ + $(SIM) $(SIMFLAGS) ./$$case; \ + done + +clean: + $(RM) -fr $(TESTCASES) $(CRT) diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_addi.S qemu-kvm-0.15.0+noroms/tests/lm32/test_addi.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_addi.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_addi.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,56 @@ +.include "macros.inc" + +start + +test_name ADDI_1 +mvi r1, 0 +addi r3, r1, 0 +check_r3 0 + +test_name ADDI_2 +mvi r1, 0 +addi r3, r1, 1 +check_r3 1 + +test_name ADDI_3 +mvi r1, 1 +addi r3, r1, 0 +check_r3 1 + +test_name ADDI_4 +mvi r1, 1 +addi r3, r1, -1 +check_r3 0 + +test_name ADDI_5 +mvi r1, -1 +addi r3, r1, 1 +check_r3 0 + +test_name ADDI_6 +mvi r1, -1 +addi r3, r1, 0 +check_r3 -1 + +test_name ADDI_7 +mvi r1, 0 +addi r3, r1, -1 +check_r3 -1 + +test_name ADDI_8 +mvi r3, 4 +addi r3, r3, 4 +check_r3 8 + +test_name ADDI_9 +mvi r3, 4 +addi r3, r3, -4 +check_r3 0 + +test_name ADDI_10 +mvi r3, 4 +addi r3, r3, -5 +check_r3 -1 + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_add.S qemu-kvm-0.15.0+noroms/tests/lm32/test_add.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_add.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_add.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,75 @@ +.include "macros.inc" + +start + +test_name ADD_1 +mvi r1, 0 +mvi r2, 0 +add r3, r1, r2 +check_r3 0 + +test_name ADD_2 +mvi r1, 0 +mvi r2, 1 +add r3, r1, r2 +check_r3 1 + +test_name ADD_3 +mvi r1, 1 +mvi r2, 0 +add r3, r1, r2 +check_r3 1 + +test_name ADD_4 +mvi r1, 1 +mvi r2, -1 +add r3, r1, r2 +check_r3 0 + +test_name ADD_5 +mvi r1, -1 +mvi r2, 1 +add r3, r1, r2 +check_r3 0 + +test_name ADD_6 +mvi r1, -1 +mvi r2, 0 +add r3, r1, r2 +check_r3 -1 + +test_name ADD_7 +mvi r1, 0 +mvi r2, -1 +add r3, r1, r2 +check_r3 -1 + +test_name ADD_8 +mvi r3, 2 +add r3, r3, r3 +check_r3 4 + +test_name ADD_9 +mvi r1, 4 +mvi r3, 2 +add r3, r1, r3 +check_r3 6 + +test_name ADD_10 +mvi r1, 4 +mvi r3, 2 +add r3, r3, r1 +check_r3 6 + +test_name ADD_11 +mvi r1, 4 +add r3, r1, r1 +check_r3 8 + +test_name ADD_12 +load r1 0x12345678 +load r2 0xabcdef97 +add r3, r1, r2 +check_r3 0xbe02460f + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_andhi.S qemu-kvm-0.15.0+noroms/tests/lm32/test_andhi.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_andhi.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_andhi.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name ANDHI_1 +mvi r1, 0 +andhi r3, r1, 0 +check_r3 0 + +test_name ANDHI_2 +mvi r1, 1 +andhi r3, r1, 1 +check_r3 0 + +test_name ANDHI_3 +load r1 0x000f0000 +andhi r3, r1, 1 +check_r3 0x00010000 + +test_name ANDHI_4 +load r1 0xffffffff +andhi r3, r1, 0xffff +check_r3 0xffff0000 + +test_name ANDHI_5 +load r1 0xffffffff +andhi r3, r1, 0 +check_r3 0 + +test_name ANDHI_6 +load r3 0x55aaffff +andhi r3, r3, 0xaaaa +check_r3 0x00aa0000 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_andi.S qemu-kvm-0.15.0+noroms/tests/lm32/test_andi.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_andi.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_andi.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name ANDI_1 +mvi r1, 0 +andi r3, r1, 0 +check_r3 0 + +test_name ANDI_2 +mvi r1, 1 +andi r3, r1, 1 +check_r3 1 + +test_name ANDI_3 +load r1 0x000f0000 +andi r3, r1, 1 +check_r3 0 + +test_name ANDI_4 +load r1 0xffffffff +andi r3, r1, 0xffff +check_r3 0xffff + +test_name ANDI_5 +load r1 0xffffffff +andi r3, r1, 0 +check_r3 0 + +test_name ANDI_6 +load r3 0xffff55aa +andi r3, r3, 0xaaaa +check_r3 0x000000aa + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_and.S qemu-kvm-0.15.0+noroms/tests/lm32/test_and.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_and.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_and.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +.include "macros.inc" + +start + +test_name AND_1 +mvi r1, 0 +mvi r2, 0 +and r3, r1, r2 +check_r3 0 + +test_name AND_2 +mvi r1, 0 +mvi r2, 1 +and r3, r1, r2 +check_r3 0 + +test_name AND_3 +mvi r1, 1 +mvi r2, 1 +and r3, r1, r2 +check_r3 1 + +test_name AND_4 +mvi r3, 7 +and r3, r3, r3 +check_r3 7 + +test_name AND_5 +mvi r1, 7 +and r3, r1, r1 +check_r3 7 + +test_name AND_6 +mvi r1, 7 +mvi r3, 0 +and r3, r1, r3 +check_r3 0 + +test_name AND_7 +load r1 0xaa55aa55 +load r2 0x55aa55aa +and r3, r1, r2 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_be.S qemu-kvm-0.15.0+noroms/tests/lm32/test_be.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_be.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_be.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,48 @@ +.include "macros.inc" + +start + +test_name BE_1 +mvi r1, 0 +mvi r2, 0 +be r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BE_2 +mvi r1, 1 +mvi r2, 0 +be r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BE_3 +mvi r1, 0 +mvi r2, 1 +be r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +bi 2f +1: +tc_pass +bi 3f +2: +test_name BE_4 +mvi r1, 1 +mvi r2, 1 +be r1, r2, 1b +tc_fail +3: + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_bge.S qemu-kvm-0.15.0+noroms/tests/lm32/test_bge.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_bge.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_bge.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,78 @@ +.include "macros.inc" + +start + +test_name BGE_1 +mvi r1, 0 +mvi r2, 0 +bge r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGE_2 +mvi r1, 1 +mvi r2, 0 +bge r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGE_3 +mvi r1, 0 +mvi r2, 1 +bge r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BGE_4 +mvi r1, 0 +mvi r2, -1 +bge r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGE_5 +mvi r1, -1 +mvi r2, 0 +bge r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BGE_6 +mvi r1, -1 +mvi r2, -1 +bge r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +bi 2f +1: +tc_pass +bi 3f +2: +test_name BGE_7 +mvi r1, 1 +mvi r2, 0 +bge r1, r2, 1b +tc_fail +3: + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_bgeu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_bgeu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_bgeu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_bgeu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,78 @@ +.include "macros.inc" + +start + +test_name BGEU_1 +mvi r1, 0 +mvi r2, 0 +bgeu r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGEU_2 +mvi r1, 1 +mvi r2, 0 +bgeu r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGEU_3 +mvi r1, 0 +mvi r2, 1 +bgeu r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BGEU_4 +mvi r1, 0 +mvi r2, -1 +bgeu r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BGEU_5 +mvi r1, -1 +mvi r2, 0 +bgeu r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGEU_6 +mvi r1, -1 +mvi r2, -1 +bgeu r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +bi 2f +1: +tc_pass +bi 3f +2: +test_name BGEU_7 +mvi r1, 1 +mvi r2, 0 +bgeu r1, r2, 1b +tc_fail +3: + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_bg.S qemu-kvm-0.15.0+noroms/tests/lm32/test_bg.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_bg.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_bg.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,78 @@ +.include "macros.inc" + +start + +test_name BG_1 +mvi r1, 0 +mvi r2, 0 +bg r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BG_2 +mvi r1, 1 +mvi r2, 0 +bg r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BG_3 +mvi r1, 0 +mvi r2, 1 +bg r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BG_4 +mvi r1, 0 +mvi r2, -1 +bg r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BG_5 +mvi r1, -1 +mvi r2, 0 +bg r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BG_6 +mvi r1, -1 +mvi r2, -1 +bg r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +bi 2f +1: +tc_pass +bi 3f +2: +test_name BG_7 +mvi r1, 1 +mvi r2, 0 +bg r1, r2, 1b +tc_fail +3: + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_bgu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_bgu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_bgu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_bgu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,78 @@ +.include "macros.inc" + +start + +test_name BGU_1 +mvi r1, 0 +mvi r2, 0 +bgu r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BGU_2 +mvi r1, 1 +mvi r2, 0 +bgu r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGU_3 +mvi r1, 0 +mvi r2, 1 +bgu r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BGU_4 +mvi r1, 0 +mvi r2, -1 +bgu r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BGU_5 +mvi r1, -1 +mvi r2, 0 +bgu r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BGU_6 +mvi r1, -1 +mvi r2, -1 +bgu r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +bi 2f +1: +tc_pass +bi 3f +2: +test_name BGU_7 +mvi r1, 1 +mvi r2, 0 +bgu r1, r2, 1b +tc_fail +3: + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_bi.S qemu-kvm-0.15.0+noroms/tests/lm32/test_bi.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_bi.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_bi.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,23 @@ +.include "macros.inc" + +start + +test_name BI_1 +bi jump +tc_fail +end + +jump_back: +tc_pass +end + +jump: +tc_pass + +test_name BI_2 +bi jump_back +tc_fail + +end + + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_bne.S qemu-kvm-0.15.0+noroms/tests/lm32/test_bne.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_bne.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_bne.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,48 @@ +.include "macros.inc" + +start + +test_name BNE_1 +mvi r1, 0 +mvi r2, 0 +bne r1, r2, 1f +tc_pass +bi 2f +1: +tc_fail +2: + +test_name BNE_2 +mvi r1, 1 +mvi r2, 0 +bne r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +test_name BNE_3 +mvi r1, 0 +mvi r2, 1 +bne r1, r2, 1f +tc_fail +bi 2f +1: +tc_pass +2: + +bi 2f +1: +tc_fail +bi 3f +2: +test_name BNE_4 +mvi r1, 1 +mvi r2, 1 +bne r1, r2, 1b +tc_pass +3: + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_break.S qemu-kvm-0.15.0+noroms/tests/lm32/test_break.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_break.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_break.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,20 @@ +.include "macros.inc" + +start + +test_name BREAK_1 +mvi r1, 1 +wcsr IE, r1 +insn: +break +check_excp 1 + +test_name BREAK_2 +mv r3, ba +check_r3 insn + +test_name BREAK_3 +rcsr r3, IE +check_r3 4 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_bret.S qemu-kvm-0.15.0+noroms/tests/lm32/test_bret.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_bret.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_bret.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,38 @@ +.include "macros.inc" + +start + +test_name BRET_1 +mvi r1, 4 +wcsr IE, r1 +load ba mark +bret +tc_fail +bi 1f + +mark: +tc_pass + +1: +test_name BRET_2 +rcsr r3, IE +check_r3 5 + +test_name BRET_3 +mvi r1, 0 +wcsr IE, r1 +load ba mark2 +bret +tc_fail +bi 1f + +mark2: +tc_pass + +1: +test_name BRET_4 +rcsr r3, IE +check_r3 0 + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_b.S qemu-kvm-0.15.0+noroms/tests/lm32/test_b.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_b.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_b.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,13 @@ +.include "macros.inc" + +start + +test_name B_1 +load r1 jump +b r1 +tc_fail +end + +jump: +tc_pass +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_calli.S qemu-kvm-0.15.0+noroms/tests/lm32/test_calli.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_calli.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_calli.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,15 @@ +.include "macros.inc" + +start + +test_name CALLI_1 +calli mark +return: + +tc_fail +end + +mark: +mv r3, ra +check_r3 return +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_call.S qemu-kvm-0.15.0+noroms/tests/lm32/test_call.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_call.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_call.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,16 @@ +.include "macros.inc" + +start + +test_name CALL_1 +load r1 mark +call r1 +return: + +tc_fail +end + +mark: +mv r3, ra +check_r3 return +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpei.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpei.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpei.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpei.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name CMPEI_1 +mvi r1, 0 +cmpei r3, r1, 0 +check_r3 1 + +test_name CMPEI_2 +mvi r1, 0 +cmpei r3, r1, 1 +check_r3 0 + +test_name CMPEI_3 +mvi r1, 1 +cmpei r3, r1, 0 +check_r3 0 + +test_name CMPEI_4 +load r1 0xffffffff +cmpei r3, r1, -1 +check_r3 1 + +test_name CMPEI_5 +mvi r3, 0 +cmpei r3, r3, 0 +check_r3 1 + +test_name CMPEI_6 +mvi r3, 0 +cmpei r3, r3, 1 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpe.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpe.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpe.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpe.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,40 @@ +.include "macros.inc" + +start + +test_name CMPE_1 +mvi r1, 0 +mvi r2, 0 +cmpe r3, r1, r2 +check_r3 1 + +test_name CMPE_2 +mvi r1, 0 +mvi r2, 1 +cmpe r3, r1, r2 +check_r3 0 + +test_name CMPE_3 +mvi r1, 1 +mvi r2, 0 +cmpe r3, r1, r2 +check_r3 0 + +test_name CMPE_4 +mvi r3, 0 +mvi r2, 1 +cmpe r3, r3, r2 +check_r3 0 + +test_name CMPE_5 +mvi r3, 0 +mvi r2, 0 +cmpe r3, r3, r2 +check_r3 1 + +test_name CMPE_6 +mvi r3, 0 +cmpe r3, r3, r3 +check_r3 1 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgei.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgei.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgei.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgei.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,55 @@ +.include "macros.inc" + +start + +test_name CMPGEI_1 +mvi r1, 0 +cmpgei r3, r1, 0 +check_r3 1 + +test_name CMPGEI_2 +mvi r1, 0 +cmpgei r3, r1, 1 +check_r3 0 + +test_name CMPGEI_3 +mvi r1, 1 +cmpgei r3, r1, 0 +check_r3 1 + +test_name CMPGEI_4 +mvi r1, 1 +cmpgei r3, r1, 1 +check_r3 1 + +test_name CMPGEI_5 +mvi r1, 0 +cmpgei r3, r1, -1 +check_r3 1 + +test_name CMPGEI_6 +mvi r1, -1 +cmpgei r3, r1, 0 +check_r3 0 + +test_name CMPGEI_7 +mvi r1, -1 +cmpgei r3, r1, -1 +check_r3 1 + +test_name CMPGEI_8 +mvi r3, 0 +cmpgei r3, r3, 1 +check_r3 0 + +test_name CMPGEI_9 +mvi r3, 1 +cmpgei r3, r3, 0 +check_r3 1 + +test_name CMPGEI_10 +mvi r3, 0 +cmpgei r3, r3, 0 +check_r3 1 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpge.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpge.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpge.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpge.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,64 @@ +.include "macros.inc" + +start + +test_name CMPGE_1 +mvi r1, 0 +mvi r2, 0 +cmpge r3, r1, r2 +check_r3 1 + +test_name CMPGE_2 +mvi r1, 0 +mvi r2, 1 +cmpge r3, r1, r2 +check_r3 0 + +test_name CMPGE_3 +mvi r1, 1 +mvi r2, 0 +cmpge r3, r1, r2 +check_r3 1 + +test_name CMPGE_4 +mvi r1, 1 +mvi r2, 1 +cmpge r3, r1, r2 +check_r3 1 + +test_name CMPGE_5 +mvi r1, 0 +mvi r2, -1 +cmpge r3, r1, r2 +check_r3 1 + +test_name CMPGE_6 +mvi r1, -1 +mvi r2, 0 +cmpge r3, r1, r2 +check_r3 0 + +test_name CMPGE_7 +mvi r1, -1 +mvi r2, -1 +cmpge r3, r1, r2 +check_r3 1 + +test_name CMPGE_8 +mvi r3, 0 +mvi r2, 1 +cmpge r3, r3, r2 +check_r3 0 + +test_name CMPGE_9 +mvi r3, 1 +mvi r2, 0 +cmpge r3, r3, r2 +check_r3 1 + +test_name CMPGE_10 +mvi r3, 0 +cmpge r3, r3, r3 +check_r3 1 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgeui.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgeui.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgeui.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgeui.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,55 @@ +.include "macros.inc" + +start + +test_name CMPGEUI_1 +mvi r1, 0 +cmpgeui r3, r1, 0 +check_r3 1 + +test_name CMPGEUI_2 +mvi r1, 0 +cmpgeui r3, r1, 1 +check_r3 0 + +test_name CMPGEUI_3 +mvi r1, 1 +cmpgeui r3, r1, 0 +check_r3 1 + +test_name CMPGEUI_4 +mvi r1, 1 +cmpgeui r3, r1, 1 +check_r3 1 + +test_name CMPGEUI_5 +mvi r1, 0 +cmpgeui r3, r1, 0xffff +check_r3 0 + +test_name CMPGEUI_6 +mvi r1, -1 +cmpgeui r3, r1, 0 +check_r3 1 + +test_name CMPGEUI_7 +mvi r1, -1 +cmpgeui r3, r1, 0xffff +check_r3 1 + +test_name CMPGEUI_8 +mvi r3, 0 +cmpgeui r3, r3, 1 +check_r3 0 + +test_name CMPGEUI_9 +mvi r3, 1 +cmpgeui r3, r3, 0 +check_r3 1 + +test_name CMPGEUI_10 +mvi r3, 0 +cmpgeui r3, r3, 0 +check_r3 1 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgeu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgeu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgeu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgeu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,64 @@ +.include "macros.inc" + +start + +test_name CMPGEU_1 +mvi r1, 0 +mvi r2, 0 +cmpgeu r3, r1, r2 +check_r3 1 + +test_name CMPGEU_2 +mvi r1, 0 +mvi r2, 1 +cmpgeu r3, r1, r2 +check_r3 0 + +test_name CMPGEU_3 +mvi r1, 1 +mvi r2, 0 +cmpgeu r3, r1, r2 +check_r3 1 + +test_name CMPGEU_4 +mvi r1, 1 +mvi r2, 1 +cmpgeu r3, r1, r2 +check_r3 1 + +test_name CMPGEU_5 +mvi r1, 0 +mvi r2, -1 +cmpgeu r3, r1, r2 +check_r3 0 + +test_name CMPGEU_6 +mvi r1, -1 +mvi r2, 0 +cmpgeu r3, r1, r2 +check_r3 1 + +test_name CMPGEU_7 +mvi r1, -1 +mvi r2, -1 +cmpgeu r3, r1, r2 +check_r3 1 + +test_name CMPGEU_8 +mvi r3, 0 +mvi r2, 1 +cmpgeu r3, r3, r2 +check_r3 0 + +test_name CMPGEU_9 +mvi r3, 1 +mvi r2, 0 +cmpgeu r3, r3, r2 +check_r3 1 + +test_name CMPGEU_10 +mvi r3, 0 +cmpgeu r3, r3, r3 +check_r3 1 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgi.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgi.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgi.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgi.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,55 @@ +.include "macros.inc" + +start + +test_name CMPGI_1 +mvi r1, 0 +cmpgi r3, r1, 0 +check_r3 0 + +test_name CMPGI_2 +mvi r1, 0 +cmpgi r3, r1, 1 +check_r3 0 + +test_name CMPGI_3 +mvi r1, 1 +cmpgi r3, r1, 0 +check_r3 1 + +test_name CMPGI_4 +mvi r1, 1 +cmpgi r3, r1, 1 +check_r3 0 + +test_name CMPGI_5 +mvi r1, 0 +cmpgi r3, r1, -1 +check_r3 1 + +test_name CMPGI_6 +mvi r1, -1 +cmpgi r3, r1, 0 +check_r3 0 + +test_name CMPGI_7 +mvi r1, -1 +cmpgi r3, r1, -1 +check_r3 0 + +test_name CMPGI_8 +mvi r3, 0 +cmpgi r3, r3, 1 +check_r3 0 + +test_name CMPGI_9 +mvi r3, 1 +cmpgi r3, r3, 0 +check_r3 1 + +test_name CMPGI_10 +mvi r3, 0 +cmpgi r3, r3, 0 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpg.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpg.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpg.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpg.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,64 @@ +.include "macros.inc" + +start + +test_name CMPG_1 +mvi r1, 0 +mvi r2, 0 +cmpg r3, r1, r2 +check_r3 0 + +test_name CMPG_2 +mvi r1, 0 +mvi r2, 1 +cmpg r3, r1, r2 +check_r3 0 + +test_name CMPG_3 +mvi r1, 1 +mvi r2, 0 +cmpg r3, r1, r2 +check_r3 1 + +test_name CMPG_4 +mvi r1, 1 +mvi r2, 1 +cmpg r3, r1, r2 +check_r3 0 + +test_name CMPG_5 +mvi r1, 0 +mvi r2, -1 +cmpg r3, r1, r2 +check_r3 1 + +test_name CMPG_6 +mvi r1, -1 +mvi r2, 0 +cmpg r3, r1, r2 +check_r3 0 + +test_name CMPG_7 +mvi r1, -1 +mvi r2, -1 +cmpg r3, r1, r2 +check_r3 0 + +test_name CMPG_8 +mvi r3, 0 +mvi r2, 1 +cmpg r3, r3, r2 +check_r3 0 + +test_name CMPG_9 +mvi r3, 1 +mvi r2, 0 +cmpg r3, r3, r2 +check_r3 1 + +test_name CMPG_10 +mvi r3, 0 +cmpg r3, r3, r3 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgui.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgui.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgui.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgui.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,55 @@ +.include "macros.inc" + +start + +test_name CMPGUI_1 +mvi r1, 0 +cmpgui r3, r1, 0 +check_r3 0 + +test_name CMPGUI_2 +mvi r1, 0 +cmpgui r3, r1, 1 +check_r3 0 + +test_name CMPGUI_3 +mvi r1, 1 +cmpgui r3, r1, 0 +check_r3 1 + +test_name CMPGUI_4 +mvi r1, 1 +cmpgui r3, r1, 1 +check_r3 0 + +test_name CMPGUI_5 +mvi r1, 0 +cmpgui r3, r1, 0xffff +check_r3 0 + +test_name CMPGUI_6 +mvi r1, -1 +cmpgui r3, r1, 0 +check_r3 1 + +test_name CMPGUI_7 +mvi r1, -1 +cmpgui r3, r1, 0xffff +check_r3 0 + +test_name CMPGUI_8 +mvi r3, 0 +cmpgui r3, r3, 1 +check_r3 0 + +test_name CMPGUI_9 +mvi r3, 1 +cmpgui r3, r3, 0 +check_r3 1 + +test_name CMPGUI_10 +mvi r3, 0 +cmpgui r3, r3, 0 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpgu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpgu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,64 @@ +.include "macros.inc" + +start + +test_name CMPGU_1 +mvi r1, 0 +mvi r2, 0 +cmpgu r3, r1, r2 +check_r3 0 + +test_name CMPGU_2 +mvi r1, 0 +mvi r2, 1 +cmpgu r3, r1, r2 +check_r3 0 + +test_name CMPGU_3 +mvi r1, 1 +mvi r2, 0 +cmpgu r3, r1, r2 +check_r3 1 + +test_name CMPGU_4 +mvi r1, 1 +mvi r2, 1 +cmpgu r3, r1, r2 +check_r3 0 + +test_name CMPGU_5 +mvi r1, 0 +mvi r2, -1 +cmpgu r3, r1, r2 +check_r3 0 + +test_name CMPGU_6 +mvi r1, -1 +mvi r2, 0 +cmpgu r3, r1, r2 +check_r3 1 + +test_name CMPGU_7 +mvi r1, -1 +mvi r2, -1 +cmpgu r3, r1, r2 +check_r3 0 + +test_name CMPGU_8 +mvi r3, 0 +mvi r2, 1 +cmpgu r3, r3, r2 +check_r3 0 + +test_name CMPGU_9 +mvi r3, 1 +mvi r2, 0 +cmpgu r3, r3, r2 +check_r3 1 + +test_name CMPGU_10 +mvi r3, 0 +cmpgu r3, r3, r3 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpnei.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpnei.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpnei.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpnei.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name CMPNEI_1 +mvi r1, 0 +cmpnei r3, r1, 0 +check_r3 0 + +test_name CMPNEI_2 +mvi r1, 0 +cmpnei r3, r1, 1 +check_r3 1 + +test_name CMPNEI_3 +mvi r1, 1 +cmpnei r3, r1, 0 +check_r3 1 + +test_name CMPNEI_4 +load r1 0xffffffff +cmpnei r3, r1, -1 +check_r3 0 + +test_name CMPNEI_5 +mvi r3, 0 +cmpnei r3, r3, 0 +check_r3 0 + +test_name CMPNEI_6 +mvi r3, 0 +cmpnei r3, r3, 1 +check_r3 1 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpne.S qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpne.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_cmpne.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_cmpne.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,40 @@ +.include "macros.inc" + +start + +test_name CMPNE_1 +mvi r1, 0 +mvi r2, 0 +cmpne r3, r1, r2 +check_r3 0 + +test_name CMPNE_2 +mvi r1, 0 +mvi r2, 1 +cmpne r3, r1, r2 +check_r3 1 + +test_name CMPNE_3 +mvi r1, 1 +mvi r2, 0 +cmpne r3, r1, r2 +check_r3 1 + +test_name CMPNE_4 +mvi r3, 0 +mvi r2, 1 +cmpne r3, r3, r2 +check_r3 1 + +test_name CMPNE_5 +mvi r3, 0 +mvi r2, 0 +cmpne r3, r3, r2 +check_r3 0 + +test_name CMPNE_6 +mvi r3, 0 +cmpne r3, r3, r3 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_divu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_divu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_divu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_divu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,29 @@ +.include "macros.inc" + +start + +test_name DIVU_1 +mvi r1, 0 +mvi r2, 1 +divu r3, r1, r2 +check_r3 0 + +test_name DIVU_2 +mvi r1, 1 +mvi r2, 1 +divu r3, r1, r2 +check_r3 1 + +test_name DIVU_3 +mvi r1, 0 +mvi r2, 0 +divu r3, r1, r2 +check_excp 16 + +test_name DIVU_4 +load r1 0xabcdef12 +load r2 0x12345 +divu r3, r1, r2 +check_r3 0x9700 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_eret.S qemu-kvm-0.15.0+noroms/tests/lm32/test_eret.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_eret.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_eret.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,38 @@ +.include "macros.inc" + +start + +test_name ERET_1 +mvi r1, 2 +wcsr IE, r1 +load ea mark +eret +tc_fail +bi 1f + +mark: +tc_pass + +1: +test_name ERET_2 +rcsr r3, IE +check_r3 3 + +test_name ERET_3 +mvi r1, 0 +wcsr IE, r1 +load ea mark2 +eret +tc_fail +bi 1f + +mark2: +tc_pass + +1: +test_name ERET_4 +rcsr r3, IE +check_r3 0 + +end + diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_lb.S qemu-kvm-0.15.0+noroms/tests/lm32/test_lb.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_lb.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_lb.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +.include "macros.inc" + +start + +test_name LB_1 +load r1 data +lb r3, (r1+0) +check_r3 0x7e + +test_name LB_2 +lb r3, (r1+1) +check_r3 0x7f + +test_name LB_3 +lb r3, (r1+-1) +check_r3 0x7d + +test_name LB_4 +load r1 data_msb +lb r3, (r1+0) +check_r3 0xfffffffe + +test_name LB_5 +lb r3, (r1+1) +check_r3 0xffffffff + +test_name LB_6 +lb r3, (r1+-1) +check_r3 0xfffffffd + +test_name LB_7 +load r3 data +lb r3, (r3+0) +check_r3 0x7e + +end + +.data + .align 4 + .byte 0x7a, 0x7b, 0x7c, 0x7d +data: + .byte 0x7e, 0x7f, 0x70, 0x71 + .byte 0xfa, 0xfb, 0xfc, 0xfd +data_msb: + .byte 0xfe, 0xff, 0xf0, 0xf1 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_lbu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_lbu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_lbu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_lbu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +.include "macros.inc" + +start + +test_name LBU_1 +load r1 data +lbu r3, (r1+0) +check_r3 0x7e + +test_name LBU_2 +lbu r3, (r1+1) +check_r3 0x7f + +test_name LBU_3 +lbu r3, (r1+-1) +check_r3 0x7d + +test_name LBU_4 +load r1 data_msb +lbu r3, (r1+0) +check_r3 0xfe + +test_name LBU_5 +lbu r3, (r1+1) +check_r3 0xff + +test_name LBU_6 +lbu r3, (r1+-1) +check_r3 0xfd + +test_name LBU_7 +load r3 data +lbu r3, (r3+0) +check_r3 0x7e + +end + +.data + .align 4 + .byte 0x7a, 0x7b, 0x7c, 0x7d +data: + .byte 0x7e, 0x7f, 0x70, 0x71 + .byte 0xfa, 0xfb, 0xfc, 0xfd +data_msb: + .byte 0xfe, 0xff, 0xf0, 0xf1 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_lh.S qemu-kvm-0.15.0+noroms/tests/lm32/test_lh.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_lh.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_lh.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +.include "macros.inc" + +start + +test_name LH_1 +load r1 data +lh r3, (r1+0) +check_r3 0x7e7f + +test_name LH_2 +lh r3, (r1+2) +check_r3 0x7071 + +test_name LH_3 +lh r3, (r1+-2) +check_r3 0x7c7d + +test_name LH_4 +load r1 data_msb +lh r3, (r1+0) +check_r3 0xfffffeff + +test_name LH_5 +lh r3, (r1+2) +check_r3 0xfffff0f1 + +test_name LH_6 +lh r3, (r1+-2) +check_r3 0xfffffcfd + +test_name LH_7 +load r3 data +lh r3, (r3+0) +check_r3 0x7e7f + +end + +.data + .align 4 + .byte 0x7a, 0x7b, 0x7c, 0x7d +data: + .byte 0x7e, 0x7f, 0x70, 0x71 + .byte 0xfa, 0xfb, 0xfc, 0xfd +data_msb: + .byte 0xfe, 0xff, 0xf0, 0xf1 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_lhu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_lhu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_lhu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_lhu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +.include "macros.inc" + +start + +test_name LHU_1 +load r1 data +lhu r3, (r1+0) +check_r3 0x7e7f + +test_name LHU_2 +lhu r3, (r1+2) +check_r3 0x7071 + +test_name LHU_3 +lhu r3, (r1+-2) +check_r3 0x7c7d + +test_name LHU_4 +load r1 data_msb +lhu r3, (r1+0) +check_r3 0xfeff + +test_name LHU_5 +lhu r3, (r1+2) +check_r3 0xf0f1 + +test_name LHU_6 +lhu r3, (r1+-2) +check_r3 0xfcfd + +test_name LHU_7 +load r3 data +lhu r3, (r3+0) +check_r3 0x7e7f + +end + +.data + .align 4 + .byte 0x7a, 0x7b, 0x7c, 0x7d +data: + .byte 0x7e, 0x7f, 0x70, 0x71 + .byte 0xfa, 0xfb, 0xfc, 0xfd +data_msb: + .byte 0xfe, 0xff, 0xf0, 0xf1 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_lw.S qemu-kvm-0.15.0+noroms/tests/lm32/test_lw.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_lw.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_lw.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,30 @@ +.include "macros.inc" + +start + +test_name LW_1 +load r1 data +lw r3, (r1+0) +check_r3 0x7e7f7071 + +test_name LW_2 +lw r3, (r1+4) +check_r3 0x72737475 + +test_name LW_3 +lw r3, (r1+-4) +check_r3 0x7a7b7c7d + +test_name LW_4 +load r3 data +lw r3, (r3+0) +check_r3 0x7e7f7071 + +end + +.data + .align 4 + .byte 0x7a, 0x7b, 0x7c, 0x7d +data: + .byte 0x7e, 0x7f, 0x70, 0x71 + .byte 0x72, 0x73, 0x74, 0x75 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_modu.S qemu-kvm-0.15.0+noroms/tests/lm32/test_modu.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_modu.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_modu.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name MODU_1 +mvi r1, 0 +mvi r2, 1 +modu r3, r1, r2 +check_r3 0 + +test_name MODU_2 +mvi r1, 1 +mvi r2, 1 +modu r3, r1, r2 +check_r3 0 + +test_name MODU_3 +mvi r1, 3 +mvi r2, 2 +modu r3, r1, r2 +check_r3 1 + +test_name MODU_4 +mvi r1, 0 +mvi r2, 0 +modu r3, r1, r2 +check_excp 16 + +test_name MODU_5 +load r1 0xabcdef12 +load r2 0x12345 +modu r3, r1, r2 +check_r3 0x3c12 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_muli.S qemu-kvm-0.15.0+noroms/tests/lm32/test_muli.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_muli.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_muli.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +.include "macros.inc" + +start + +test_name MULI_1 +mvi r1, 0 +muli r3, r1, 0 +check_r3 0 + +test_name MULI_2 +mvi r1, 1 +muli r3, r1, 0 +check_r3 0 + +test_name MULI_3 +mvi r1, 0 +muli r3, r1, 1 +check_r3 0 + +test_name MULI_4 +mvi r1, 1 +muli r3, r1, 1 +check_r3 1 + +test_name MULI_5 +mvi r1, 2 +muli r3, r1, -1 +check_r3 -2 + +test_name MULI_6 +mvi r1, -2 +muli r3, r1, -1 +check_r3 2 + +test_name MULI_7 +mvi r1, 0x1234 +muli r3, r1, 0x789 +check_r3 0x8929d4 + +test_name MULI_8 +mvi r3, 4 +muli r3, r3, 4 +check_r3 16 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_mul.S qemu-kvm-0.15.0+noroms/tests/lm32/test_mul.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_mul.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_mul.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,70 @@ +.include "macros.inc" + +start + +test_name MUL_1 +mvi r1, 0 +mvi r2, 0 +mul r3, r1, r2 +check_r3 0 + +test_name MUL_2 +mvi r1, 1 +mvi r2, 0 +mul r3, r1, r2 +check_r3 0 + +test_name MUL_3 +mvi r1, 0 +mvi r2, 1 +mul r3, r1, r2 +check_r3 0 + +test_name MUL_4 +mvi r1, 1 +mvi r2, 1 +mul r3, r1, r2 +check_r3 1 + +test_name MUL_5 +mvi r1, 2 +mvi r2, -1 +mul r3, r1, r2 +check_r3 -2 + +test_name MUL_6 +mvi r1, -2 +mvi r2, -1 +mul r3, r1, r2 +check_r3 2 + +test_name MUL_7 +mvi r1, 0x1234 +mvi r2, 0x789 +mul r3, r1, r2 +check_r3 0x8929d4 + +test_name MUL_8 +mvi r3, 4 +mul r3, r3, r3 +check_r3 16 + +test_name MUL_9 +mvi r2, 2 +mvi r3, 4 +mul r3, r3, r2 +check_r3 8 + +test_name MUL_10 +load r1 0x12345678 +load r2 0x7bcdef12 +mul r3, r1, r2 +check_r3 0xa801c70 + +test_name MUL_11 +load r1 0x12345678 +load r2 0xabcdef12 +mul r3, r1, r2 +check_r3 0x8a801c70 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_nori.S qemu-kvm-0.15.0+noroms/tests/lm32/test_nori.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_nori.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_nori.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name NORI_1 +mvi r1, 0 +nori r3, r1, 0 +check_r3 0xffffffff + +test_name NORI_2 +mvi r1, 0 +nori r3, r1, 1 +check_r3 0xfffffffe + +test_name NORI_3 +mvi r1, 1 +nori r3, r1, 1 +check_r3 0xfffffffe + +test_name NORI_4 +mvi r1, 1 +nori r3, r1, 0 +check_r3 0xfffffffe + +test_name NORI_5 +load r1 0xaa55aa55 +nori r3, r1, 0x55aa +check_r3 0x55aa0000 + +test_name NORI_6 +load r3 0xaa55aa55 +nori r3, r3, 0x55aa +check_r3 0x55aa0000 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_nor.S qemu-kvm-0.15.0+noroms/tests/lm32/test_nor.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_nor.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_nor.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,51 @@ +.include "macros.inc" + +start + +test_name NOR_1 +mvi r1, 0 +mvi r2, 0 +nor r3, r1, r2 +check_r3 0xffffffff + +test_name NOR_2 +mvi r1, 0 +mvi r2, 1 +nor r3, r1, r2 +check_r3 0xfffffffe + +test_name NOR_3 +mvi r1, 1 +mvi r2, 1 +nor r3, r1, r2 +check_r3 0xfffffffe + +test_name NOR_4 +mvi r1, 1 +mvi r2, 0 +nor r3, r1, r2 +check_r3 0xfffffffe + +test_name NOR_5 +load r1 0xaa55aa55 +load r2 0x55aa55aa +nor r3, r1, r2 +check_r3 0 + +test_name NOR_6 +load r1 0xaa550000 +load r2 0x0000aa55 +nor r3, r1, r2 +check_r3 0x55aa55aa + +test_name NOR_7 +load r1 0xaa55aa55 +nor r3, r1, r1 +check_r3 0x55aa55aa + +test_name NOR_8 +load r3 0xaa55aa55 +nor r3, r3, r3 +check_r3 0x55aa55aa + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_orhi.S qemu-kvm-0.15.0+noroms/tests/lm32/test_orhi.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_orhi.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_orhi.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name ORHI_1 +mvi r1, 0 +orhi r3, r1, 0 +check_r3 0 + +test_name ORHI_2 +mvi r1, 0 +orhi r3, r1, 1 +check_r3 0x00010000 + +test_name ORHI_3 +load r1 0x00010000 +orhi r3, r1, 1 +check_r3 0x00010000 + +test_name ORHI_4 +mvi r1, 1 +orhi r3, r1, 0 +check_r3 1 + +test_name ORHI_5 +load r1 0xaa55aa55 +orhi r3, r1, 0x55aa +check_r3 0xffffaa55 + +test_name ORHI_6 +load r3 0xaa55aa55 +orhi r3, r3, 0x55aa +check_r3 0xffffaa55 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_ori.S qemu-kvm-0.15.0+noroms/tests/lm32/test_ori.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_ori.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_ori.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name ORI_1 +mvi r1, 0 +ori r3, r1, 0 +check_r3 0 + +test_name ORI_2 +mvi r1, 0 +ori r3, r1, 1 +check_r3 1 + +test_name ORI_3 +mvi r1, 1 +ori r3, r1, 1 +check_r3 1 + +test_name ORI_4 +mvi r1, 1 +ori r3, r1, 0 +check_r3 1 + +test_name ORI_5 +load r1 0xaa55aa55 +ori r3, r1, 0x55aa +check_r3 0xaa55ffff + +test_name ORI_6 +load r3 0xaa55aa55 +ori r3, r3, 0x55aa +check_r3 0xaa55ffff + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_or.S qemu-kvm-0.15.0+noroms/tests/lm32/test_or.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_or.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_or.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,51 @@ +.include "macros.inc" + +start + +test_name OR_1 +mvi r1, 0 +mvi r2, 0 +or r3, r1, r2 +check_r3 0 + +test_name OR_2 +mvi r1, 0 +mvi r2, 1 +or r3, r1, r2 +check_r3 1 + +test_name OR_3 +mvi r1, 1 +mvi r2, 1 +or r3, r1, r2 +check_r3 1 + +test_name OR_4 +mvi r1, 1 +mvi r2, 0 +or r3, r1, r2 +check_r3 1 + +test_name OR_5 +load r1 0xaa55aa55 +load r2 0x55aa55aa +or r3, r1, r2 +check_r3 0xffffffff + +test_name OR_6 +load r1 0xaa550000 +load r2 0x0000aa55 +or r3, r1, r2 +check_r3 0xaa55aa55 + +test_name OR_7 +load r1 0xaa55aa55 +or r3, r1, r1 +check_r3 0xaa55aa55 + +test_name OR_8 +load r3 0xaa55aa55 +or r3, r3, r3 +check_r3 0xaa55aa55 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_ret.S qemu-kvm-0.15.0+noroms/tests/lm32/test_ret.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_ret.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_ret.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,14 @@ +.include "macros.inc" + +start + +test_name RET_1 +load ra mark +ret + +tc_fail +end + +mark: +tc_pass +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sb.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sb.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sb.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sb.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,30 @@ +.include "macros.inc" + +start + +test_name SB_1 +load r1 data +load r2 0xf0f1f2aa +sb (r1+0), r2 +check_mem data 0xaa000000 + +test_name SB_2 +load r2 0xf0f1f2bb +sb (r1+1), r2 +check_mem data 0xaabb0000 + +test_name SB_3 +load r2 0xf0f1f2cc +sb (r1+-1), r2 +check_mem data0 0x000000cc + +end + +.data + .align 4 +data0: + .byte 0, 0, 0, 0 +data: + .byte 0, 0, 0, 0 +data1: + .byte 0, 0, 0, 0 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_scall.S qemu-kvm-0.15.0+noroms/tests/lm32/test_scall.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_scall.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_scall.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,20 @@ +.include "macros.inc" + +start + +test_name SCALL_1 +mvi r1, 1 +wcsr IE, r1 +insn: +scall +check_excp 64 + +test_name SCALL_2 +mv r3, ea +check_r3 insn + +test_name SCALL_3 +rcsr r3, IE +check_r3 2 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sextb.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sextb.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sextb.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sextb.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,20 @@ +.include "macros.inc" + +start + +test_name SEXTB_1 +mvi r1, 0 +sextb r3, r1 +check_r3 0 + +test_name SEXTB_2 +mvi r1, 0x7f +sextb r3, r1 +check_r3 0x0000007f + +test_name SEXTB_3 +mvi r1, 0x80 +sextb r3, r1 +check_r3 0xffffff80 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sexth.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sexth.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sexth.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sexth.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,20 @@ +.include "macros.inc" + +start + +test_name SEXTH_1 +mvi r1, 0 +sexth r3, r1 +check_r3 0 + +test_name SEXTH_2 +load r1 0x7fff +sexth r3, r1 +check_r3 0x00007fff + +test_name SEXTH_3 +load r1 0x8000 +sexth r3, r1 +check_r3 0xffff8000 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sh.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sh.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sh.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sh.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,30 @@ +.include "macros.inc" + +start + +test_name SH_1 +load r1 data +load r2 0xf0f1aaaa +sh (r1+0), r2 +check_mem data 0xaaaa0000 + +test_name SH_2 +load r2 0xf0f1bbbb +sh (r1+2), r2 +check_mem data 0xaaaabbbb + +test_name SH_3 +load r2 0xf0f1cccc +sh (r1+-2), r2 +check_mem data0 0x0000cccc + +end + +.data + .align 4 +data0: + .byte 0, 0, 0, 0 +data: + .byte 0, 0, 0, 0 +data1: + .byte 0, 0, 0, 0 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sli.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sli.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sli.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sli.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,30 @@ +.include "macros.inc" + +start + +test_name SLI_1 +mvi r1, 1 +sli r3, r1, 0 +check_r3 1 + +test_name SLI_2 +mvi r1, 0 +sli r3, r1, 1 +check_r3 0 + +test_name SLI_3 +mvi r1, 1 +sli r3, r1, 31 +check_r3 0x80000000 + +test_name SLI_4 +mvi r1, 16 +sli r3, r1, 31 +check_r3 0 + +test_name SLI_7 +mvi r3, 2 +sli r3, r3, 2 +check_r3 8 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sl.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sl.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sl.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sl.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +.include "macros.inc" + +start + +test_name SL_1 +mvi r1, 1 +mvi r2, 0 +sl r3, r1, r2 +check_r3 1 + +test_name SL_2 +mvi r1, 0 +mvi r2, 1 +sl r3, r1, r2 +check_r3 0 + +test_name SL_3 +mvi r1, 1 +mvi r2, 31 +sl r3, r1, r2 +check_r3 0x80000000 + +test_name SL_4 +mvi r1, 16 +mvi r2, 31 +sl r3, r1, r2 +check_r3 0 + +test_name SL_5 +mvi r1, 1 +mvi r2, 34 +sl r3, r1, r2 +check_r3 4 + +test_name SL_6 +mvi r1, 2 +sl r3, r1, r1 +check_r3 8 + +test_name SL_7 +mvi r3, 2 +sl r3, r3, r3 +check_r3 8 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sri.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sri.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sri.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sri.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,40 @@ +.include "macros.inc" + +start + +test_name SRI_1 +mvi r1, 1 +sri r3, r1, 0 +check_r3 1 + +test_name SRI_2 +mvi r1, 0 +sri r3, r1, 1 +check_r3 0 + +test_name SRI_3 +load r1 0x40000000 +sri r3, r1, 30 +check_r3 1 + +test_name SRI_4 +load r1 0x40000000 +sri r3, r1, 31 +check_r3 0 + +test_name SRI_5 +mvi r3, 2 +sri r3, r3, 2 +check_r3 0 + +test_name SRI_6 +mvi r1, 0xfffffff0 +sri r3, r1, 2 +check_r3 0xfffffffc + +test_name SRI_7 +mvi r1, 0xfffffff0 +sri r3, r1, 4 +check_r3 0xffffffff + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sr.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sr.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sr.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sr.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,57 @@ +.include "macros.inc" + +start + +test_name SR_1 +mvi r1, 1 +mvi r2, 0 +sr r3, r1, r2 +check_r3 1 + +test_name SR_2 +mvi r1, 0 +mvi r2, 1 +sr r3, r1, r2 +check_r3 0 + +test_name SR_3 +load r1 0x40000000 +mvi r2, 30 +sr r3, r1, r2 +check_r3 1 + +test_name SR_4 +load r1 0x40000000 +mvi r2, 31 +sr r3, r1, r2 +check_r3 0 + +test_name SR_5 +mvi r1, 16 +mvi r2, 34 +sr r3, r1, r2 +check_r3 4 + +test_name SR_6 +mvi r1, 2 +sr r3, r1, r1 +check_r3 0 + +test_name SR_7 +mvi r3, 2 +sr r3, r3, r3 +check_r3 0 + +test_name SR_8 +mvi r1, 0xfffffff0 +mvi r2, 2 +sr r3, r1, r2 +check_r3 0xfffffffc + +test_name SR_9 +mvi r1, 0xfffffff0 +mvi r2, 4 +sr r3, r1, r2 +check_r3 0xffffffff + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_srui.S qemu-kvm-0.15.0+noroms/tests/lm32/test_srui.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_srui.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_srui.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,40 @@ +.include "macros.inc" + +start + +test_name SRUI_1 +mvi r1, 1 +srui r3, r1, 0 +check_r3 1 + +test_name SRUI_2 +mvi r1, 0 +srui r3, r1, 1 +check_r3 0 + +test_name SRUI_3 +load r1 0x40000000 +srui r3, r1, 30 +check_r3 1 + +test_name SRUI_4 +load r1 0x40000000 +srui r3, r1, 31 +check_r3 0 + +test_name SRUI_5 +mvi r3, 2 +srui r3, r3, 2 +check_r3 0 + +test_name SRUI_6 +mvi r1, 0xfffffff0 +srui r3, r1, 2 +check_r3 0x3ffffffc + +test_name SRUI_7 +mvi r1, 0xfffffff0 +srui r3, r1, 4 +check_r3 0x0fffffff + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sru.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sru.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sru.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sru.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,57 @@ +.include "macros.inc" + +start + +test_name SRU_1 +mvi r1, 1 +mvi r2, 0 +sru r3, r1, r2 +check_r3 1 + +test_name SRU_2 +mvi r1, 0 +mvi r2, 1 +sru r3, r1, r2 +check_r3 0 + +test_name SRU_3 +load r1 0x40000000 +mvi r2, 30 +sru r3, r1, r2 +check_r3 1 + +test_name SRU_4 +load r1 0x40000000 +mvi r2, 31 +sru r3, r1, r2 +check_r3 0 + +test_name SRU_5 +mvi r1, 16 +mvi r2, 34 +sru r3, r1, r2 +check_r3 4 + +test_name SRU_6 +mvi r1, 2 +sru r3, r1, r1 +check_r3 0 + +test_name SRU_7 +mvi r3, 2 +sru r3, r3, r3 +check_r3 0 + +test_name SRU_8 +mvi r1, 0xfffffff0 +mvi r2, 2 +sru r3, r1, r2 +check_r3 0x3ffffffc + +test_name SRU_9 +mvi r1, 0xfffffff0 +mvi r2, 4 +sru r3, r1, r2 +check_r3 0x0fffffff + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sub.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sub.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sub.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sub.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,75 @@ +.include "macros.inc" + +start + +test_name SUB_1 +mvi r1, 0 +mvi r2, 0 +sub r3, r1, r2 +check_r3 0 + +test_name SUB_2 +mvi r1, 0 +mvi r2, 1 +sub r3, r1, r2 +check_r3 -1 + +test_name SUB_3 +mvi r1, 1 +mvi r2, 0 +sub r3, r1, r2 +check_r3 1 + +test_name SUB_4 +mvi r1, 1 +mvi r2, -1 +sub r3, r1, r2 +check_r3 2 + +test_name SUB_5 +mvi r1, -1 +mvi r2, 1 +sub r3, r1, r2 +check_r3 -2 + +test_name SUB_6 +mvi r1, -1 +mvi r2, 0 +sub r3, r1, r2 +check_r3 -1 + +test_name SUB_7 +mvi r1, 0 +mvi r2, -1 +sub r3, r1, r2 +check_r3 1 + +test_name SUB_8 +mvi r3, 2 +sub r3, r3, r3 +check_r3 0 + +test_name SUB_9 +mvi r1, 4 +mvi r3, 2 +sub r3, r1, r3 +check_r3 2 + +test_name SUB_10 +mvi r1, 4 +mvi r3, 2 +sub r3, r3, r1 +check_r3 -2 + +test_name SUB_11 +mvi r1, 4 +sub r3, r1, r1 +check_r3 0 + +test_name SUB_12 +load r1 0x12345678 +load r2 0xabcdef97 +sub r3, r1, r2 +check_r3 0x666666e1 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_sw.S qemu-kvm-0.15.0+noroms/tests/lm32/test_sw.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_sw.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_sw.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name SW_1 +load r1 data +load r2 0xaabbccdd +sw (r1+0), r2 +check_mem data 0xaabbccdd + +test_name SW_2 +load r2 0x00112233 +sw (r1+4), r2 +check_mem data1 0x00112233 + +test_name SW_3 +load r2 0x44556677 +sw (r1+-4), r2 +check_mem data0 0x44556677 + +test_name SW_4 +sw (r1+0), r1 +lw r3, (r1+0) +check_r3 data + +end + +.data + .align 4 +data0: + .byte 0, 0, 0, 0 +data: + .byte 0, 0, 0, 0 +data1: + .byte 0, 0, 0, 0 diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_xnori.S qemu-kvm-0.15.0+noroms/tests/lm32/test_xnori.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_xnori.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_xnori.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name XNORI_1 +mvi r1, 0 +xnori r3, r1, 0 +check_r3 0xffffffff + +test_name XNORI_2 +mvi r1, 0 +xnori r3, r1, 1 +check_r3 0xfffffffe + +test_name XNORI_3 +mvi r1, 1 +xnori r3, r1, 1 +check_r3 0xffffffff + +test_name XNORI_4 +mvi r1, 1 +xnori r3, r1, 0 +check_r3 0xfffffffe + +test_name XNORI_5 +load r1 0xaa55aa55 +xnori r3, r1, 0x5555 +check_r3 0x55aa00ff + +test_name XNORI_6 +load r3 0xaa55aa55 +xnori r3, r3, 0x5555 +check_r3 0x55aa00ff + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_xnor.S qemu-kvm-0.15.0+noroms/tests/lm32/test_xnor.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_xnor.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_xnor.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,51 @@ +.include "macros.inc" + +start + +test_name XNOR_1 +mvi r1, 0 +mvi r2, 0 +xnor r3, r1, r2 +check_r3 0xffffffff + +test_name XNOR_2 +mvi r1, 0 +mvi r2, 1 +xnor r3, r1, r2 +check_r3 0xfffffffe + +test_name XNOR_3 +mvi r1, 1 +mvi r2, 1 +xnor r3, r1, r2 +check_r3 0xffffffff + +test_name XNOR_4 +mvi r1, 1 +mvi r2, 0 +xnor r3, r1, r2 +check_r3 0xfffffffe + +test_name XNOR_5 +load r1 0xaa55aa55 +load r2 0x55aa55aa +xnor r3, r1, r2 +check_r3 0 + +test_name XNOR_6 +load r1 0xaa550000 +load r2 0x0000aa55 +xnor r3, r1, r2 +check_r3 0x55aa55aa + +test_name XNOR_7 +load r1 0xaa55aa55 +xnor r3, r1, r1 +check_r3 0xffffffff + +test_name XNOR_8 +load r3 0xaa55aa55 +xnor r3, r3, r3 +check_r3 0xffffffff + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_xori.S qemu-kvm-0.15.0+noroms/tests/lm32/test_xori.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_xori.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_xori.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,35 @@ +.include "macros.inc" + +start + +test_name XORI_1 +mvi r1, 0 +xori r3, r1, 0 +check_r3 0 + +test_name XORI_2 +mvi r1, 0 +xori r3, r1, 1 +check_r3 1 + +test_name XORI_3 +mvi r1, 1 +xori r3, r1, 1 +check_r3 0 + +test_name XORI_4 +mvi r1, 1 +xori r3, r1, 0 +check_r3 1 + +test_name XORI_5 +load r1 0xaa55aa55 +xori r3, r1, 0x5555 +check_r3 0xaa55ff00 + +test_name XORI_6 +load r3 0xaa55aa55 +xori r3, r3, 0x5555 +check_r3 0xaa55ff00 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/lm32/test_xor.S qemu-kvm-0.15.0+noroms/tests/lm32/test_xor.S --- qemu-kvm-0.14.1+noroms/tests/lm32/test_xor.S 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/tests/lm32/test_xor.S 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,51 @@ +.include "macros.inc" + +start + +test_name XOR_1 +mvi r1, 0 +mvi r2, 0 +xor r3, r1, r2 +check_r3 0 + +test_name XOR_2 +mvi r1, 0 +mvi r2, 1 +xor r3, r1, r2 +check_r3 1 + +test_name XOR_3 +mvi r1, 1 +mvi r2, 1 +xor r3, r1, r2 +check_r3 0 + +test_name XOR_4 +mvi r1, 1 +mvi r2, 0 +xor r3, r1, r2 +check_r3 1 + +test_name XOR_5 +load r1 0xaa55aa55 +load r2 0x55aa55aa +xor r3, r1, r2 +check_r3 0xffffffff + +test_name XOR_6 +load r1 0xaa550000 +load r2 0x0000aa55 +xor r3, r1, r2 +check_r3 0xaa55aa55 + +test_name XOR_7 +load r1 0xaa55aa55 +xor r3, r1, r1 +check_r3 0 + +test_name XOR_8 +load r3 0xaa55aa55 +xor r3, r3, r3 +check_r3 0 + +end diff -Nru qemu-kvm-0.14.1+noroms/tests/Makefile qemu-kvm-0.15.0+noroms/tests/Makefile --- qemu-kvm-0.14.1+noroms/tests/Makefile 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/Makefile 2011-08-09 07:40:29.000000000 -0500 @@ -142,6 +142,10 @@ test-cris: $(MAKE) -C cris check +# testsuite for the LM32 port. +test-lm32: + $(MAKE) -C lm32 check + clean: rm -f *~ *.o test-i386.out test-i386.ref \ test-x86_64.log test-x86_64.ref qruncom $(TESTS) diff -Nru qemu-kvm-0.14.1+noroms/tests/test-i386.c qemu-kvm-0.15.0+noroms/tests/test-i386.c --- qemu-kvm-0.14.1+noroms/tests/test-i386.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/test-i386.c 2011-08-09 07:40:29.000000000 -0500 @@ -2281,7 +2281,7 @@ } /* Force %xmm0 usage to avoid the case where both register index are 0 - to test intruction decoding more extensively */ + to test instruction decoding more extensively */ #define CVT_OP_XMM2MMX(op)\ {\ asm volatile (#op " %1, %0" : "=y" (r.q[0]) : "x" (a.dq) \ diff -Nru qemu-kvm-0.14.1+noroms/tests/test-mmap.c qemu-kvm-0.15.0+noroms/tests/test-mmap.c --- qemu-kvm-0.14.1+noroms/tests/test-mmap.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/tests/test-mmap.c 2011-08-09 07:40:29.000000000 -0500 @@ -164,6 +164,7 @@ nlen = pagesize * 8; p3 = mmap(NULL, nlen, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fail_unless (p3 != MAP_FAILED); /* Check if the mmaped areas collide. */ if (p3 < p2 @@ -174,7 +175,6 @@ /* Make sure we get pages aligned with the pagesize. The target expects this. */ - fail_unless (p3 != MAP_FAILED); p = (uintptr_t) p3; fail_unless ((p & pagemask) == 0); munmap (p2, pagesize); @@ -322,7 +322,7 @@ fail_unless (p1[(test_fsize & pagemask) / sizeof *p1 - 1] == ((test_fsize - sizeof *p1) / sizeof *p1)); - /* Verify that the end of page is accessable and zeroed. */ + /* Verify that the end of page is accessible and zeroed. */ cp = (void *) p1; fail_unless (cp[pagesize - 4] == 0); munmap (p1, pagesize); @@ -365,7 +365,7 @@ fail_unless (p1[(test_fsize & pagemask) / sizeof *p1 - 1] == ((test_fsize - sizeof *p1) / sizeof *p1)); - /* Verify that the end of page is accessable and zeroed. */ + /* Verify that the end of page is accessible and zeroed. */ cp = (void *)p1; fail_unless (cp[pagesize - 4] == 0); munmap (p1, pagesize); diff -Nru qemu-kvm-0.14.1+noroms/test-visitor.c qemu-kvm-0.15.0+noroms/test-visitor.c --- qemu-kvm-0.14.1+noroms/test-visitor.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/test-visitor.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,306 @@ +#include +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-input-visitor.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "qemu-objects.h" + +typedef struct TestStruct +{ + int64_t x; + int64_t y; +} TestStruct; + +typedef struct TestStructList +{ + TestStruct *value; + struct TestStructList *next; +} TestStructList; + +static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) +{ + visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), errp); + visit_type_int(v, &(*obj)->x, "x", errp); + visit_type_int(v, &(*obj)->y, "y", errp); + visit_end_struct(v, errp); +} + +static void visit_type_TestStructList(Visitor *m, TestStructList ** obj, const char *name, Error **errp) +{ + GenericList *i; + + visit_start_list(m, name, errp); + + for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) { + TestStructList *native_i = (TestStructList *)i; + visit_type_TestStruct(m, &native_i->value, NULL, errp); + } + + visit_end_list(m, errp); +} + +/* test core visitor methods */ +static void test_visitor_core(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + TestStruct ts = { 42, 82 }; + TestStruct *pts = &ts; + TestStructList *lts = NULL; + Error *err = NULL; + QObject *obj; + QString *str; + int64_t value = 0; + + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + + visit_type_TestStruct(v, &pts, NULL, &err); + + obj = qmp_output_get_qobject(mo); + + str = qobject_to_json(obj); + + printf("%s\n", qstring_get_str(str)); + + QDECREF(str); + + obj = QOBJECT(qint_from_int(0x42)); + + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + + visit_type_int(v, &value, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(value == 0x42); + + qobject_decref(obj); + + obj = qobject_from_json("{'x': 42, 'y': 84}"); + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + + pts = NULL; + + visit_type_TestStruct(v, &pts, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(pts != NULL); + g_assert(pts->x == 42); + g_assert(pts->y == 84); + + qobject_decref(obj); + + obj = qobject_from_json("[{'x': 42, 'y': 84}, {'x': 12, 'y': 24}]"); + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + + visit_type_TestStructList(v, <s, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(lts != NULL); + g_assert(lts->value->x == 42); + g_assert(lts->value->y == 84); + + lts = lts->next; + g_assert(lts != NULL); + g_assert(lts->value->x == 12); + g_assert(lts->value->y == 24); + + g_assert(lts->next == NULL); + + qobject_decref(obj); +} + +/* test deep nesting with refs to other user-defined types */ +static void test_nested_structs(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + UserDefOne ud1; + UserDefOne *ud1_p = &ud1, *ud1c_p = NULL; + UserDefTwo ud2; + UserDefTwo *ud2_p = &ud2, *ud2c_p = NULL; + Error *err = NULL; + QObject *obj; + QString *str; + + ud1.integer = 42; + ud1.string = strdup("fourty two"); + + /* sanity check */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_UserDefOne(v, &ud1_p, "o_O", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + qobject_decref(obj); + + ud2.string = strdup("fourty three"); + ud2.dict.string = strdup("fourty four"); + ud2.dict.dict.userdef = ud1_p; + ud2.dict.dict.string = strdup("fourty five"); + ud2.dict.has_dict2 = true; + ud2.dict.dict2.userdef = ud1_p; + ud2.dict.dict2.string = strdup("fourty six"); + + /* c type -> qobject */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_UserDefTwo(v, &ud2_p, "unused", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + str = qobject_to_json_pretty(obj); + g_print("%s\n", qstring_get_str(str)); + QDECREF(str); + + /* qobject -> c type, should match original struct */ + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + visit_type_UserDefTwo(v, &ud2c_p, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(!g_strcmp0(ud2c_p->string, ud2.string)); + g_assert(!g_strcmp0(ud2c_p->dict.string, ud2.dict.string)); + + ud1c_p = ud2c_p->dict.dict.userdef; + g_assert(ud1c_p->integer == ud1_p->integer); + g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string)); + + g_assert(!g_strcmp0(ud2c_p->dict.dict.string, ud2.dict.dict.string)); + + ud1c_p = ud2c_p->dict.dict2.userdef; + g_assert(ud1c_p->integer == ud1_p->integer); + g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string)); + + g_assert(!g_strcmp0(ud2c_p->dict.dict2.string, ud2.dict.dict2.string)); + qemu_free(ud1.string); + qemu_free(ud2.string); + qemu_free(ud2.dict.string); + qemu_free(ud2.dict.dict.string); + qemu_free(ud2.dict.dict2.string); + + qapi_free_UserDefTwo(ud2c_p); + + qobject_decref(obj); +} + +/* test enum values */ +static void test_enums(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + EnumOne enum1 = ENUM_ONE_VALUE2, enum1_cpy = ENUM_ONE_VALUE1; + Error *err = NULL; + QObject *obj; + QString *str; + + /* C type -> QObject */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_EnumOne(v, &enum1, "unused", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + str = qobject_to_json_pretty(obj); + g_print("%s\n", qstring_get_str(str)); + QDECREF(str); + g_assert(g_strcmp0(qstring_get_str(qobject_to_qstring(obj)), "value2") == 0); + + /* QObject -> C type */ + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + visit_type_EnumOne(v, &enum1_cpy, "unused", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + g_debug("enum1_cpy, enum1: %d, %d", enum1_cpy, enum1); + g_assert(enum1_cpy == enum1); + + qobject_decref(obj); +} + +/* test enum values nested in schema-defined structs */ +static void test_nested_enums(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + NestedEnumsOne *nested_enums, *nested_enums_cpy = NULL; + Error *err = NULL; + QObject *obj; + QString *str; + + nested_enums = qemu_mallocz(sizeof(NestedEnumsOne)); + nested_enums->enum1 = ENUM_ONE_VALUE1; + nested_enums->enum2 = ENUM_ONE_VALUE2; + nested_enums->enum3 = ENUM_ONE_VALUE3; + nested_enums->enum4 = ENUM_ONE_VALUE3; + nested_enums->has_enum2 = false; + nested_enums->has_enum4 = true; + + /* C type -> QObject */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_NestedEnumsOne(v, &nested_enums, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + str = qobject_to_json_pretty(obj); + g_print("%s\n", qstring_get_str(str)); + QDECREF(str); + + /* QObject -> C type */ + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + visit_type_NestedEnumsOne(v, &nested_enums_cpy, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + g_assert(nested_enums_cpy); + g_assert(nested_enums_cpy->enum1 == nested_enums->enum1); + g_assert(nested_enums_cpy->enum3 == nested_enums->enum3); + g_assert(nested_enums_cpy->enum4 == nested_enums->enum4); + g_assert(nested_enums_cpy->has_enum2 == false); + g_assert(nested_enums_cpy->has_enum4 == true); + + qobject_decref(obj); + qapi_free_NestedEnumsOne(nested_enums); + qapi_free_NestedEnumsOne(nested_enums_cpy); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/0.15/visitor_core", test_visitor_core); + g_test_add_func("/0.15/nested_structs", test_nested_structs); + g_test_add_func("/0.15/enums", test_enums); + g_test_add_func("/0.15/nested_enums", test_nested_enums); + + g_test_run(); + + return 0; +} diff -Nru qemu-kvm-0.14.1+noroms/trace-events qemu-kvm-0.15.0+noroms/trace-events --- qemu-kvm-0.14.1+noroms/trace-events 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/trace-events 2011-08-09 07:40:29.000000000 -0500 @@ -46,13 +46,26 @@ disable virtio_irq(void *vq) "vq %p" disable virtio_notify(void *vdev, void *vq) "vdev %p vq %p" +# hw/virtio-serial-bus.c +disable virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t value) "port %u, event %u, value %u" +disable virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, throttle %d" +disable virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event %u, value %u" +disable virtio_serial_handle_control_message_port(unsigned int port) "port %u" + +# hw/virtio-console.c +disable virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, in_len %zu, out_len %zd" +disable virtio_console_chr_read(unsigned int port, int size) "port %u, size %d" +disable virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" + # 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_flush(void *bs, void *opaque) "bs %p opaque %p" 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" +disable bdrv_set_locked(void *bs, int locked) "bs %p locked %d" # hw/virtio-blk.c disable virtio_blk_req_complete(void *req, int status) "req %p status %d" @@ -61,6 +74,8 @@ # 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" +disable paio_complete(void *acb, void *opaque, int ret) "acb %p opaque %p ret %d" +disable paio_cancel(void *acb, void *opaque) "acb %p opaque %p" # ioport.c disable cpu_in(unsigned int addr, unsigned int val) "addr %#x value %u" @@ -88,6 +103,10 @@ 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/ds1225y.c +disable nvram_read(uint32_t addr, uint32_t ret) "read addr %d: 0x%02x" +disable nvram_write(uint32_t addr, uint32_t old, uint32_t val) "write addr %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" @@ -190,6 +209,26 @@ 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-ehci.c +disable usb_ehci_reset(void) "=== RESET ===" +disable usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" +disable usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x" +disable usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)" +disable usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" +disable usb_ehci_state(const char *schedule, const char *state) "%s schedule %s" +disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x" +disable usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ %08x - rl %d, mplen %d, eps %d, ep %d, dev %d" +disable usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ %08x - c %d, h %d, dtc %d, i %d" +disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x" +disable usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d" +disable usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d" +disable usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d" +disable usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s" +disable usb_ehci_port_detach(uint32_t port) "detach port #%d" +disable usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" +disable usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d" +disable usb_ehci_queue_action(void *q, const char *action) "q %p: %s" + # 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" @@ -201,6 +240,15 @@ 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" +# hw/scsi-bus.c +disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d" +disable scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" +disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d" +disable scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d" +disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d" +disable scsi_req_parsed_lba(int target, int lun, int tag, int cmd, uint64_t lba) "target %d lun %d tag %d command %d lba %"PRIu64"" +disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d" + # vl.c disable vm_state_notify(int running, int reason) "running %d reason %d" @@ -216,6 +264,9 @@ 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_need_check_timer_cb(void *s) "s %p" +disable qed_start_need_check_timer(void *s) "s %p" +disable qed_cancel_need_check_timer(void *s) "s %p" 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"" @@ -231,26 +282,146 @@ 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"" +disable grlib_gptimer_readl(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" +disable grlib_gptimer_writel(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" # 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"" +disable grlib_irqmp_readl_unknown(uint64_t addr) "addr 0x%"PRIx64"" +disable grlib_irqmp_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x" # 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"" +disable grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x" # 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_write(ssize_t out, int len) "spice wrottn %zd of requested %d" +disable spice_vmc_read(int bytes, int len) "spice read %d of requested %d" disable spice_vmc_register_interface(void *scd) "spice vmc registered interface %p" disable spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p" + +# hw/lm32_pic.c +disable lm32_pic_raise_irq(void) "Raise CPU interrupt" +disable lm32_pic_lower_irq(void) "Lower CPU interrupt" +disable lm32_pic_interrupt(int irq, int level) "Set IRQ%d %d" +disable lm32_pic_set_im(uint32_t im) "im 0x%08x" +disable lm32_pic_set_ip(uint32_t ip) "ip 0x%08x" +disable lm32_pic_get_im(uint32_t im) "im 0x%08x" +disable lm32_pic_get_ip(uint32_t ip) "ip 0x%08x" + +# hw/lm32_juart.c +disable lm32_juart_get_jtx(uint32_t value) "jtx 0x%08x" +disable lm32_juart_set_jtx(uint32_t value) "jtx 0x%08x" +disable lm32_juart_get_jrx(uint32_t value) "jrx 0x%08x" +disable lm32_juart_set_jrx(uint32_t value) "jrx 0x%08x" + +# hw/lm32_timer.c +disable lm32_timer_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +disable lm32_timer_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +disable lm32_timer_hit(void) "timer hit" +disable lm32_timer_irq_state(int level) "irq state %d" + +# hw/lm32_uart.c +disable lm32_uart_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +disable lm32_uart_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +disable lm32_uart_irq_state(int level) "irq state %d" + +# hw/lm32_sys.c +disable lm32_sys_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" + +# hw/milkymist-ac97.c +disable milkymist_ac97_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_ac97_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_ac97_pulse_irq_crrequest(void) "Pulse IRQ CR request" +disable milkymist_ac97_pulse_irq_crreply(void) "Pulse IRQ CR reply" +disable milkymist_ac97_pulse_irq_dmaw(void) "Pulse IRQ DMA write" +disable milkymist_ac97_pulse_irq_dmar(void) "Pulse IRQ DMA read" +disable milkymist_ac97_in_cb(int avail, uint32_t remaining) "avail %d remaining %u" +disable milkymist_ac97_in_cb_transferred(int transferred) "transferred %d" +disable milkymist_ac97_out_cb(int free, uint32_t remaining) "free %d remaining %u" +disable milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" + +# hw/milkymist-hpdmc.c +disable milkymist_hpdmc_memory_read(uint32_t addr, uint32_t value) "addr=%08x value=%08x" +disable milkymist_hpdmc_memory_write(uint32_t addr, uint32_t value) "addr=%08x value=%08x" + +# hw/milkymist-memcard.c +disable milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_memcard_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" + +# hw/milkymist-minimac2.c +disable milkymist_minimac2_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_minimac2_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_minimac2_mdio_write(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x" +disable milkymist_minimac2_mdio_read(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x" +disable milkymist_minimac2_tx_frame(uint32_t length) "length %u" +disable milkymist_minimac2_rx_frame(const void *buf, uint32_t length) "buf %p length %u" +disable milkymist_minimac2_drop_rx_frame(const void *buf) "buf %p" +disable milkymist_minimac2_rx_transfer(const void *buf, uint32_t length) "buf %p length %d" +disable milkymist_minimac2_raise_irq_rx(void) "Raise IRQ RX" +disable milkymist_minimac2_lower_irq_rx(void) "Lower IRQ RX" +disable milkymist_minimac2_pulse_irq_tx(void) "Pulse IRQ TX" + +# hw/milkymist-pfpu.c +disable milkymist_pfpu_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_pfpu_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_pfpu_vectout(uint32_t a, uint32_t b, uint32_t dma_ptr) "a %08x b %08x dma_ptr %08x" +disable milkymist_pfpu_pulse_irq(void) "Pulse IRQ" + +# hw/milkymist-softusb.c +disable milkymist_softusb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_softusb_mevt(uint8_t m) "m %d" +disable milkymist_softusb_kevt(uint8_t m) "m %d" +disable milkymist_softusb_mouse_event(int dx, int dy, int dz, int bs) "dx %d dy %d dz %d bs %02x" +disable milkymist_softusb_pulse_irq(void) "Pulse IRQ" + +# hw/milkymist-sysctl.c +disable milkymist_sysctl_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_sysctl_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_sysctl_icap_write(uint32_t value) "value %08x" +disable milkymist_sysctl_start_timer0(void) "Start timer0" +disable milkymist_sysctl_stop_timer0(void) "Stop timer0" +disable milkymist_sysctl_start_timer1(void) "Start timer1" +disable milkymist_sysctl_stop_timer1(void) "Stop timer1" +disable milkymist_sysctl_pulse_irq_timer0(void) "Pulse IRQ Timer0" +disable milkymist_sysctl_pulse_irq_timer1(void) "Pulse IRQ Timer1" + +# hw/milkymist-tmu2.c +disable milkymist_tmu2_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_tmu2_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_tmu2_start(void) "Start TMU" +disable milkymist_tmu2_pulse_irq(void) "Pulse IRQ" + +# hw/milkymist-uart.c +disable milkymist_uart_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_uart_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_uart_pulse_irq_rx(void) "Pulse IRQ RX" +disable milkymist_uart_pulse_irq_tx(void) "Pulse IRQ TX" + +# hw/milkymist-vgafb.c +disable milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +disable milkymist_vgafb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" + +# xen-all.c +disable xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx" +disable xen_client_set_memory(uint64_t start_addr, unsigned long size, unsigned long phys_offset, bool log_dirty) "%#"PRIx64" size %#lx, offset %#lx, log_dirty %i" + +# xen-mapcache.c +disable xen_map_cache(uint64_t phys_addr) "want %#"PRIx64"" +disable xen_remap_bucket(uint64_t index) "index %#"PRIx64"" +disable xen_map_cache_return(void* ptr) "%p" +disable xen_map_block(uint64_t phys_addr, uint64_t size) "%#"PRIx64", size %#"PRIx64"" +disable xen_unmap_block(void* addr, unsigned long size) "%p, size %#lx" + +# exec.c +disable qemu_put_ram_ptr(void* addr) "%p" + +# hw/xen_platform.c +disable xen_platform_log(char *s) "xen platform: %s" diff -Nru qemu-kvm-0.14.1+noroms/translate-all.c qemu-kvm-0.15.0+noroms/translate-all.c --- qemu-kvm-0.14.1+noroms/translate-all.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/translate-all.c 2011-08-09 07:40:29.000000000 -0500 @@ -26,7 +26,6 @@ #define NO_CPU_IO_DEFS #include "cpu.h" -#include "exec-all.h" #include "disas.h" #include "tcg.h" #include "qemu-timer.h" @@ -44,8 +43,6 @@ void cpu_gen_init(void) { tcg_context_init(&tcg_ctx); - tcg_set_frame(&tcg_ctx, TCG_AREG0, offsetof(CPUState, temp_buf), - CPU_TEMP_BUF_NLONGS * sizeof(long)); } /* return non zero if the very first instruction is invalid so that @@ -112,8 +109,7 @@ /* The cpu state corresponding to 'searched_pc' is restored. */ int cpu_restore_state(TranslationBlock *tb, - CPUState *env, unsigned long searched_pc, - void *puc) + CPUState *env, unsigned long searched_pc) { TCGContext *s = &tcg_ctx; int j; @@ -157,7 +153,7 @@ j--; env->icount_decr.u16.low -= gen_opc_icount[j]; - gen_pc_load(env, tb, searched_pc, j, puc); + restore_state_to_opc(env, tb, j); #ifdef CONFIG_PROFILER s->restore_time += profile_getclock() - ti; diff -Nru qemu-kvm-0.14.1+noroms/ui/cocoa.m qemu-kvm-0.15.0+noroms/ui/cocoa.m --- qemu-kvm-0.14.1+noroms/ui/cocoa.m 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/cocoa.m 2011-08-09 07:40:29.000000000 -0500 @@ -23,6 +23,7 @@ */ #import +#include #include "qemu-common.h" #include "console.h" @@ -61,9 +62,7 @@ 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; @@ -278,6 +277,8 @@ - (QEMUScreen) gscreen; @end +QemuCocoaView *cocoaView; + @implementation QemuCocoaView - (id)initWithFrame:(NSRect)frameRect { @@ -794,7 +795,7 @@ COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); int status; - status = qemu_main(argc, argv); + status = qemu_main(argc, argv, *_NSGetEnviron()); exit(status); } @@ -865,10 +866,20 @@ /* 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); + const char *opt = argv[i]; + + if (opt[0] == '-') { + /* Treat --foo the same as -foo. */ + if (opt[1] == '-') { + opt++; + } + if (!strcmp(opt, "-h") || !strcmp(opt, "-help") || + !strcmp(opt, "-vnc") || + !strcmp(opt, "-nographic") || + !strcmp(opt, "-version") || + !strcmp(opt, "-curses")) { + return qemu_main(gArgc, gArgv, *_NSGetEnviron()); + } } } diff -Nru qemu-kvm-0.14.1+noroms/ui/curses.c qemu-kvm-0.15.0+noroms/ui/curses.c --- qemu-kvm-0.14.1+noroms/ui/curses.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/curses.c 2011-08-09 07:40:29.000000000 -0500 @@ -24,7 +24,6 @@ #include #ifndef _WIN32 -#include #include #include #endif diff -Nru qemu-kvm-0.14.1+noroms/ui/qemu-spice.h qemu-kvm-0.15.0+noroms/ui/qemu-spice.h --- qemu-kvm-0.14.1+noroms/ui/qemu-spice.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/qemu-spice.h 2011-08-09 07:40:29.000000000 -0500 @@ -42,13 +42,21 @@ 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); +int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr); #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_set_passwd(const char *passwd, + bool fail_if_connected, + bool disconnect_if_connected) +{ + return -1; +} +static inline int qemu_spice_set_pw_expire(time_t expires) +{ + return -1; +} static inline int qemu_spice_migrate_info(const char *h, int p, int t, const char *s) { return -1; } diff -Nru qemu-kvm-0.14.1+noroms/ui/sdl.c qemu-kvm-0.15.0+noroms/ui/sdl.c --- qemu-kvm-0.14.1+noroms/ui/sdl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/sdl.c 2011-08-09 07:40:29.000000000 -0500 @@ -28,10 +28,6 @@ #include #include -#ifndef _WIN32 -#include -#endif - #include "qemu-common.h" #include "console.h" #include "sysemu.h" @@ -176,22 +172,18 @@ surface->width = width; surface->height = height; - + if (scaling_active) { + int linesize; + PixelFormat pf; if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) { - surface->linesize = width * 4; - surface->pf = qemu_default_pixelformat(32); + linesize = width * 4; + pf = qemu_default_pixelformat(32); } else { - surface->linesize = width * host_format.BytesPerPixel; - surface->pf = sdl_to_qemu_pixelformat(&host_format); + linesize = width * host_format.BytesPerPixel; + 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); - + qemu_alloc_display(surface, width, height, linesize, pf, 0); return surface; } @@ -489,7 +481,7 @@ sdl_update_caption(); } -static void sdl_mouse_mode_change(Notifier *notify) +static void sdl_mouse_mode_change(Notifier *notify, void *data) { if (kbd_mouse_is_absolute()) { if (!absolute_enabled) { @@ -816,6 +808,7 @@ uint8_t data = 0; DisplayAllocator *da; const SDL_VideoInfo *vi; + char *filename; #if defined(__APPLE__) /* always use generic keymaps */ @@ -834,6 +827,18 @@ if (!full_screen) { setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0); } +#ifdef __linux__ + /* on Linux, SDL may use fbcon|directfb|svgalib when run without + * accessible $DISPLAY to open X11 window. This is often the case + * when qemu is run using sudo. But in this case, and when actually + * run in X11 environment, SDL fights with X11 for the video card, + * making current display unavailable, often until reboot. + * So make x11 the default SDL video driver if this variable is unset. + * This is a bit hackish but saves us from bigger problem. + * Maybe it's a good idea to fix this in SDL instead. + */ + setenv("SDL_VIDEODRIVER", "x11", 0); +#endif /* Enable normal up/down events for Caps-Lock and Num-Lock keys. * This requires SDL >= 1.2.14. */ @@ -848,6 +853,18 @@ vi = SDL_GetVideoInfo(); host_format = *(vi->vfmt); + /* Load a 32x32x4 image. White pixels are transparent. */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); + if (filename) { + SDL_Surface *image = SDL_LoadBMP(filename); + if (image) { + uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); + SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey); + SDL_WM_SetIcon(image, NULL); + } + qemu_free(filename); + } + dcl = qemu_mallocz(sizeof(DisplayChangeListener)); dcl->dpy_update = sdl_update; dcl->dpy_resize = sdl_resize; diff -Nru qemu-kvm-0.14.1+noroms/ui/spice-core.c qemu-kvm-0.15.0+noroms/ui/spice-core.c --- qemu-kvm-0.14.1+noroms/ui/spice-core.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/spice-core.c 2011-08-09 07:40:29.000000000 -0500 @@ -55,14 +55,14 @@ SpiceTimer *timer; timer = qemu_mallocz(sizeof(*timer)); - timer->timer = qemu_new_timer(rt_clock, func, opaque); + timer->timer = qemu_new_timer_ms(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); + qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms); } static void timer_cancel(SpiceTimer *timer) @@ -299,8 +299,6 @@ 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", @@ -309,8 +307,6 @@ #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", @@ -420,7 +416,7 @@ *ret_data = QOBJECT(server); } -static void migration_state_notifier(Notifier *notifier) +static void migration_state_notifier(Notifier *notifier, void *data) { int state = get_migration_state(); @@ -484,7 +480,16 @@ port = qemu_opt_get_number(opts, "port", 0); tls_port = qemu_opt_get_number(opts, "tls-port", 0); if (!port && !tls_port) { - return; + fprintf(stderr, "neither port nor tls-port specified for spice."); + exit(1); + } + if (port < 0 || port > 65535) { + fprintf(stderr, "spice port is out of range"); + exit(1); + } + if (tls_port < 0 || tls_port > 65535) { + fprintf(stderr, "spice tls-port is out of range"); + exit(1); } password = qemu_opt_get(opts, "password"); @@ -549,11 +554,29 @@ if (password) { spice_server_set_ticket(spice_server, password, 0, 0, 0); } + if (qemu_opt_get_bool(opts, "sasl", 0)) { +#if SPICE_SERVER_VERSION >= 0x000900 /* 0.9.0 */ + if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 || + spice_server_set_sasl(spice_server, 1) == -1) { + fprintf(stderr, "spice: failed to enable sasl\n"); + exit(1); + } +#else + fprintf(stderr, "spice: sasl is not available (spice >= 0.9 required)\n"); + exit(1); +#endif + } if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) { auth = "none"; spice_server_set_noauth(spice_server); } +#if SPICE_SERVER_VERSION >= 0x000801 + if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) { + spice_server_set_agent_copypaste(spice_server, false); + } +#endif + compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; str = qemu_opt_get(opts, "image-compression"); if (str) { @@ -575,8 +598,6 @@ } 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); @@ -588,11 +609,12 @@ 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); + if (0 != spice_server_init(spice_server, &core_interface)) { + fprintf(stderr, "failed to initialize spice server"); + exit(1); + }; using_spice = 1; migration_state.notify = migration_state_notifier; diff -Nru qemu-kvm-0.14.1+noroms/ui/spice-display.c qemu-kvm-0.15.0+noroms/ui/spice-display.c --- qemu-kvm-0.14.1+noroms/ui/spice-display.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/spice-display.c 2011-08-09 07:40:29.000000000 -0500 @@ -62,14 +62,7 @@ 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) +static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) { SimpleSpiceUpdate *update; QXLDrawable *drawable; @@ -77,10 +70,9 @@ QXLCommand *cmd; uint8_t *src, *dst; int by, bw, bh; + struct timespec time_space; - qemu_mutex_lock_iothread(); if (qemu_spice_rect_is_empty(&ssd->dirty)) { - qemu_mutex_unlock_iothread(); return NULL; }; @@ -105,6 +97,10 @@ drawable->surfaces_dest[0] = -1; drawable->surfaces_dest[1] = -1; drawable->surfaces_dest[2] = -1; + clock_gettime(CLOCK_MONOTONIC, &time_space); + /* time in milliseconds from epoch. */ + drawable->mm_time = time_space.tv_sec * 1000 + + time_space.tv_nsec / 1000 / 1000; drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; drawable->u.copy.src_bitmap = (intptr_t)image; @@ -141,7 +137,6 @@ cmd->data = (intptr_t)drawable; memset(&ssd->dirty, 0, sizeof(ssd->dirty)); - qemu_mutex_unlock_iothread(); return update; } @@ -186,18 +181,14 @@ 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) @@ -207,9 +198,7 @@ if (running) { ssd->worker->start(ssd->worker); } else { - qemu_mutex_unlock_iothread(); ssd->worker->stop(ssd->worker); - qemu_mutex_lock_iothread(); } ssd->running = running; } @@ -241,6 +230,12 @@ qemu_pf_conv_put(ssd->conv); ssd->conv = NULL; + qemu_mutex_lock(&ssd->lock); + if (ssd->update != NULL) { + qemu_spice_destroy_update(ssd, ssd->update); + ssd->update = NULL; + } + qemu_mutex_unlock(&ssd->lock); qemu_spice_destroy_host_primary(ssd); qemu_spice_create_host_primary(ssd); @@ -252,6 +247,24 @@ { dprint(3, "%s:\n", __FUNCTION__); vga_hw_update(); + + qemu_mutex_lock(&ssd->lock); + if (ssd->update == NULL) { + ssd->update = qemu_spice_create_update(ssd); + ssd->notify++; + } + if (ssd->cursor) { + ssd->ds->cursor_define(ssd->cursor); + cursor_put(ssd->cursor); + ssd->cursor = NULL; + } + if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { + ssd->ds->mouse_set(ssd->mouse_x, ssd->mouse_y, 1); + ssd->mouse_x = -1; + ssd->mouse_y = -1; + } + qemu_mutex_unlock(&ssd->lock); + if (ssd->notify) { ssd->notify = 0; ssd->worker->wakeup(ssd->worker); @@ -298,14 +311,20 @@ { SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); SimpleSpiceUpdate *update; + int ret = false; dprint(3, "%s:\n", __FUNCTION__); - update = qemu_spice_create_update(ssd); - if (update == NULL) { - return false; + + qemu_mutex_lock(&ssd->lock); + if (ssd->update != NULL) { + update = ssd->update; + ssd->update = NULL; + *ext = update->ext; + ret = true; } - *ext = update->ext; - return true; + qemu_mutex_unlock(&ssd->lock); + + return ret; } static int interface_req_cmd_notification(QXLInstance *sin) @@ -398,6 +417,9 @@ { assert(sdpy.ds == NULL); sdpy.ds = ds; + qemu_mutex_init(&sdpy.lock); + sdpy.mouse_x = -1; + sdpy.mouse_y = -1; sdpy.bufsize = (16 * 1024 * 1024); sdpy.buf = qemu_malloc(sdpy.bufsize); register_displaychangelistener(ds, &display_listener); diff -Nru qemu-kvm-0.14.1+noroms/ui/spice-display.h qemu-kvm-0.15.0+noroms/ui/spice-display.h --- qemu-kvm-0.14.1+noroms/ui/spice-display.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/spice-display.h 2011-08-09 07:40:29.000000000 -0500 @@ -19,6 +19,8 @@ #include #include +#include "qemu-thread.h" +#include "console.h" #include "pflib.h" #define NUM_MEMSLOTS 8 @@ -31,7 +33,10 @@ #define NUM_SURFACES 1024 -typedef struct SimpleSpiceDisplay { +typedef struct SimpleSpiceDisplay SimpleSpiceDisplay; +typedef struct SimpleSpiceUpdate SimpleSpiceUpdate; + +struct SimpleSpiceDisplay { DisplayState *ds; void *buf; int bufsize; @@ -43,19 +48,28 @@ QXLRect dirty; int notify; int running; -} SimpleSpiceDisplay; -typedef struct SimpleSpiceUpdate { + /* + * All struct members below this comment can be accessed from + * both spice server and qemu (iothread) context and any access + * to them must be protected by the lock. + */ + QemuMutex lock; + SimpleSpiceUpdate *update; + QEMUCursor *cursor; + int mouse_x, mouse_y; +}; + +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); diff -Nru qemu-kvm-0.14.1+noroms/ui/spice-input.c qemu-kvm-0.15.0+noroms/ui/spice-input.c --- qemu-kvm-0.14.1+noroms/ui/spice-input.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/spice-input.c 2011-08-09 07:40:29.000000000 -0500 @@ -178,7 +178,7 @@ .buttons = tablet_buttons, }; -static void mouse_mode_notifier(Notifier *notifier) +static void mouse_mode_notifier(Notifier *notifier, void *data) { QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); bool is_absolute = kbd_mouse_is_absolute(); @@ -213,5 +213,5 @@ 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); + mouse_mode_notifier(&pointer->mouse_mode, NULL); } diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-auth-sasl.c qemu-kvm-0.15.0+noroms/ui/vnc-auth-sasl.c --- qemu-kvm-0.14.1+noroms/ui/vnc-auth-sasl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc-auth-sasl.c 2011-08-09 07:40:29.000000000 -0500 @@ -491,13 +491,6 @@ 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; @@ -538,8 +531,8 @@ #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) { + if (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { gnutls_cipher_algorithm_t cipher; sasl_ssf_t ssf; @@ -570,8 +563,8 @@ #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) + || (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) #endif /* CONFIG_VNC_TLS */ ) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-auth-vencrypt.c qemu-kvm-0.15.0+noroms/ui/vnc-auth-vencrypt.c --- qemu-kvm-0.14.1+noroms/ui/vnc-auth-vencrypt.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc-auth-vencrypt.c 2011-08-09 07:40:29.000000000 -0500 @@ -29,7 +29,7 @@ static void start_auth_vencrypt_subauth(VncState *vs) { - switch (vs->vd->subauth) { + switch (vs->subauth) { case VNC_AUTH_VENCRYPT_TLSNONE: case VNC_AUTH_VENCRYPT_X509NONE: VNC_DEBUG("Accept TLS auth none\n"); @@ -51,7 +51,7 @@ #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_DEBUG("Reject subauth %d server bug\n", vs->auth); vnc_write_u8(vs, 1); if (vs->minor >= 8) { static const char err[] = "Unsupported authentication type"; @@ -110,17 +110,17 @@ #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) + ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ + (vs)->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) { + if (auth != vs->subauth) { VNC_DEBUG("Rejecting auth %d\n", auth); vnc_write_u8(vs, 0); /* Reject auth */ vnc_flush(vs); @@ -153,10 +153,10 @@ vnc_flush(vs); vnc_client_error(vs); } else { - VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth); + VNC_DEBUG("Sending allowed auth %d\n", vs->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_write_u32(vs, vs->subauth); /* The supported auth */ vnc_flush(vs); vnc_read_when(vs, protocol_client_vencrypt_auth, 4); } diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc.c qemu-kvm-0.15.0+noroms/ui/vnc.c --- qemu-kvm-0.14.1+noroms/ui/vnc.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc.c 2011-08-09 07:40:29.000000000 -0500 @@ -35,17 +35,12 @@ #define VNC_REFRESH_INTERVAL_BASE 30 #define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_MAX 2000 +static const struct timeval VNC_REFRESH_STATS = { 0, 500000 }; +static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; #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; @@ -376,47 +371,6 @@ 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; @@ -439,7 +393,7 @@ for (; y < h; y++) for (i = 0; i < w; i += 16) - vnc_set_bit(s->dirty[y], (x + i) / 16); + set_bit((x + i) / 16, s->dirty[y]); } void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, @@ -694,6 +648,12 @@ case VNC_ENCODING_TIGHT_PNG: n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h); break; + case VNC_ENCODING_ZRLE: + n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h); + break; + case VNC_ENCODING_ZYWRLE: + n = vnc_zywrle_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); @@ -772,7 +732,7 @@ 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)); + set_bit(((x + dst_x) / 16), vs->dirty[y]); } } } @@ -835,17 +795,18 @@ } static int find_and_clear_dirty_height(struct VncState *vs, - int y, int last_x, int x) + int y, int last_x, int x, int height) { int h; - VncDisplay *vd = vs->vd; - for (h = 1; h < (vd->server->height - y); h++) { + for (h = 1; h < (height - y); h++) { int tmp_x; - if (!vnc_get_bit(vs->dirty[y + h], last_x)) + if (!test_bit(last_x, vs->dirty[y + h])) { break; - for (tmp_x = last_x; tmp_x < x; tmp_x++) - vnc_clear_bit(vs->dirty[y + h], tmp_x); + } + for (tmp_x = last_x; tmp_x < x; tmp_x++) { + clear_bit(tmp_x, vs->dirty[y + h]); + } } return h; @@ -897,14 +858,14 @@ int x; int last_x = -1; for (x = 0; x < width / 16; x++) { - if (vnc_get_bit(vs->dirty[y], x)) { + if (test_and_clear_bit(x, vs->dirty[y])) { 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); + int h = find_and_clear_dirty_height(vs, y, last_x, x, + height); n += vnc_job_add_rect(job, last_x * 16, y, (x - last_x) * 16, h); @@ -913,7 +874,7 @@ } } if (last_x != -1) { - int h = find_and_clear_dirty_height(vs, y, last_x, x); + int h = find_and_clear_dirty_height(vs, y, last_x, x, height); n += vnc_job_add_rect(job, last_x * 16, y, (x - last_x) * 16, h); } @@ -1012,6 +973,8 @@ static void vnc_disconnect_finish(VncState *vs) { + int i; + vnc_jobs_join(vs); /* Wait encoding jobs */ vnc_lock_output(vs); @@ -1024,6 +987,7 @@ vnc_zlib_clear(vs); vnc_tight_clear(vs); + vnc_zrle_clear(vs); #ifdef CONFIG_VNC_TLS vnc_tls_client_cleanup(vs); @@ -1048,6 +1012,10 @@ #ifdef CONFIG_VNC_THREAD qemu_mutex_destroy(&vs->output_mutex); #endif + for (i = 0; i < VNC_STAT_ROWS; ++i) { + qemu_free(vs->lossy_rect[i]); + } + qemu_free(vs->lossy_rect); qemu_free(vs); } @@ -1231,7 +1199,7 @@ } } else #endif /* CONFIG_VNC_TLS */ - ret = recv(vs->csock, (void *)data, datalen, 0); + ret = qemu_recv(vs->csock, data, datalen, 0); VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); return vnc_client_io_error(vs, ret, socket_error()); } @@ -1378,7 +1346,7 @@ { } -static void check_pointer_type_change(Notifier *notifier) +static void check_pointer_type_change(Notifier *notifier, void *data) { VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); int absolute = kbd_mouse_is_absolute(); @@ -1677,18 +1645,21 @@ int x_position, int y_position, int w, int h) { + int i; + const size_t width = ds_get_width(vs->ds) / 16; + 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); + bitmap_set(vs->dirty[y_position + i], 0, width); + bitmap_clear(vs->dirty[y_position + i], width, + VNC_DIRTY_BITS - width); } } } @@ -1758,6 +1729,14 @@ vs->features |= VNC_FEATURE_ZLIB_MASK; vs->vnc_encoding = enc; break; + case VNC_ENCODING_ZRLE: + vs->features |= VNC_FEATURE_ZRLE_MASK; + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_ZYWRLE: + vs->features |= VNC_FEATURE_ZYWRLE_MASK; + vs->vnc_encoding = enc; + break; case VNC_ENCODING_DESKTOPRESIZE: vs->features |= VNC_FEATURE_RESIZE_MASK; break; @@ -1780,7 +1759,9 @@ vs->tight.compression = (enc & 0x0F); break; case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: - vs->tight.quality = (enc & 0x0F); + if (vs->vd->lossy) { + vs->tight.quality = (enc & 0x0F); + } break; default: VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc); @@ -1788,7 +1769,7 @@ } } vnc_desktop_resize(vs); - check_pointer_type_change(&vs->mouse_mode_notifier); + check_pointer_type_change(&vs->mouse_mode_notifier, NULL); } static void set_pixel_conversion(VncState *vs) @@ -1817,15 +1798,15 @@ vs->clientds = *(vs->vd->guest.ds); vs->clientds.pf.rmax = red_max; - count_bits(vs->clientds.pf.rbits, red_max); + vs->clientds.pf.rbits = hweight_long(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.gbits = hweight_long(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.bbits = hweight_long(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; @@ -1898,8 +1879,8 @@ 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); + if (!qemu_timer_expired(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval)) + qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval); } switch (data[0]) { @@ -2143,7 +2124,7 @@ { /* 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 */ + if (data[0] != vs->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) { @@ -2154,7 +2135,7 @@ vnc_client_error(vs); } else { /* Accept requested auth */ VNC_DEBUG("Client requested auth %d\n", (int)data[0]); - switch (vs->vd->auth) { + switch (vs->auth) { case VNC_AUTH_NONE: VNC_DEBUG("Accept auth none\n"); if (vs->minor >= 8) { @@ -2184,7 +2165,7 @@ #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_DEBUG("Reject auth %d server code bug\n", vs->auth); vnc_write_u8(vs, 1); if (vs->minor >= 8) { static const char err[] = "Authentication failed"; @@ -2229,26 +2210,26 @@ vs->minor = 3; if (vs->minor == 3) { - if (vs->vd->auth == VNC_AUTH_NONE) { + if (vs->auth == VNC_AUTH_NONE) { VNC_DEBUG("Tell client auth none\n"); - vnc_write_u32(vs, vs->vd->auth); + vnc_write_u32(vs, vs->auth); vnc_flush(vs); start_client_init(vs); - } else if (vs->vd->auth == VNC_AUTH_VNC) { + } else if (vs->auth == VNC_AUTH_VNC) { VNC_DEBUG("Tell client VNC auth\n"); - vnc_write_u32(vs, vs->vd->auth); + vnc_write_u32(vs, vs->auth); vnc_flush(vs); start_auth_vnc(vs); } else { - VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth); + VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->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_DEBUG("Telling client we support auth %d\n", vs->auth); vnc_write_u8(vs, 1); /* num auth */ - vnc_write_u8(vs, vs->vd->auth); + vnc_write_u8(vs, vs->auth); vnc_read_when(vs, protocol_client_auth, 1); vnc_flush(vs); } @@ -2256,27 +2237,176 @@ return 0; } +static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y) +{ + struct VncSurface *vs = &vd->guest; + + return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT]; +} + +void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h) +{ + int i, j; + + w = (x + w) / VNC_STAT_RECT; + h = (y + h) / VNC_STAT_RECT; + x /= VNC_STAT_RECT; + y /= VNC_STAT_RECT; + + for (j = y; j <= h; j++) { + for (i = x; i <= w; i++) { + vs->lossy_rect[j][i] = 1; + } + } +} + +static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) +{ + VncState *vs; + int sty = y / VNC_STAT_RECT; + int stx = x / VNC_STAT_RECT; + int has_dirty = 0; + + y = y / VNC_STAT_RECT * VNC_STAT_RECT; + x = x / VNC_STAT_RECT * VNC_STAT_RECT; + + QTAILQ_FOREACH(vs, &vd->clients, next) { + int j; + + /* kernel send buffers are full -> refresh later */ + if (vs->output.offset) { + continue; + } + + if (!vs->lossy_rect[sty][stx]) { + continue; + } + + vs->lossy_rect[sty][stx] = 0; + for (j = 0; j < VNC_STAT_RECT; ++j) { + bitmap_set(vs->dirty[y + j], x / 16, VNC_STAT_RECT / 16); + } + has_dirty++; + } + + return has_dirty; +} + +static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) +{ + int x, y; + struct timeval res; + int has_dirty = 0; + + for (y = 0; y < vd->guest.ds->height; y += VNC_STAT_RECT) { + for (x = 0; x < vd->guest.ds->width; x += VNC_STAT_RECT) { + VncRectStat *rect = vnc_stat_rect(vd, x, y); + + rect->updated = false; + } + } + + qemu_timersub(tv, &VNC_REFRESH_STATS, &res); + + if (timercmp(&vd->guest.last_freq_check, &res, >)) { + return has_dirty; + } + vd->guest.last_freq_check = *tv; + + for (y = 0; y < vd->guest.ds->height; y += VNC_STAT_RECT) { + for (x = 0; x < vd->guest.ds->width; x += VNC_STAT_RECT) { + VncRectStat *rect= vnc_stat_rect(vd, x, y); + int count = ARRAY_SIZE(rect->times); + struct timeval min, max; + + if (!timerisset(&rect->times[count - 1])) { + continue ; + } + + max = rect->times[(rect->idx + count - 1) % count]; + qemu_timersub(tv, &max, &res); + + if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) { + rect->freq = 0; + has_dirty += vnc_refresh_lossy_rect(vd, x, y); + memset(rect->times, 0, sizeof (rect->times)); + continue ; + } + + min = rect->times[rect->idx]; + max = rect->times[(rect->idx + count - 1) % count]; + qemu_timersub(&max, &min, &res); + + rect->freq = res.tv_sec + res.tv_usec / 1000000.; + rect->freq /= count; + rect->freq = 1. / rect->freq; + } + } + return has_dirty; +} + +double vnc_update_freq(VncState *vs, int x, int y, int w, int h) +{ + int i, j; + double total = 0; + int num = 0; + + x = (x / VNC_STAT_RECT) * VNC_STAT_RECT; + y = (y / VNC_STAT_RECT) * VNC_STAT_RECT; + + for (j = y; j <= y + h; j += VNC_STAT_RECT) { + for (i = x; i <= x + w; i += VNC_STAT_RECT) { + total += vnc_stat_rect(vs->vd, i, j)->freq; + num++; + } + } + + if (num) { + return total / num; + } else { + return 0; + } +} + +static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv) +{ + VncRectStat *rect; + + rect = vnc_stat_rect(vd, x, y); + if (rect->updated) { + return ; + } + rect->times[rect->idx] = *tv; + rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times); + rect->updated = true; +} + 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; + struct timeval tv = { 0, 0 }; + + if (!vd->non_adaptive) { + gettimeofday(&tv, NULL); + has_dirty = vnc_update_stats(vd, &tv); + } + /* * 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)) { + if (!bitmap_empty(vd->guest.dirty[y], VNC_DIRTY_BITS)) { int x; uint8_t *guest_ptr; uint8_t *server_ptr; @@ -2286,14 +2416,15 @@ 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))) + if (!test_and_clear_bit((x / 16), vd->guest.dirty[y])) 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); + if (!vd->non_adaptive) + vnc_rect_updated(vd, x, y, &tv); QTAILQ_FOREACH(vs, &vd->clients, next) { - vnc_set_bit(vs->dirty[y], (x / 16)); + set_bit((x / 16), vs->dirty[y]); } has_dirty++; } @@ -2314,7 +2445,7 @@ if (vnc_trylock_display(vd)) { vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; - qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + + qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval); return; } @@ -2341,14 +2472,14 @@ 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); + qemu_mod_timer(vd->timer, qemu_get_clock_ms(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); + vd->timer = qemu_new_timer_ms(rt_clock, vnc_refresh, vd); vnc_dpy_resize(vd->ds); vnc_refresh(vd); } @@ -2363,11 +2494,30 @@ } } -static void vnc_connect(VncDisplay *vd, int csock) +static void vnc_connect(VncDisplay *vd, int csock, int skipauth) { VncState *vs = qemu_mallocz(sizeof(VncState)); + int i; + vs->csock = csock; + if (skipauth) { + vs->auth = VNC_AUTH_NONE; +#ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; +#endif + } else { + vs->auth = vd->auth; +#ifdef CONFIG_VNC_TLS + vs->subauth = vd->subauth; +#endif + } + + vs->lossy_rect = qemu_mallocz(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); + for (i = 0; i < VNC_STAT_ROWS; ++i) { + vs->lossy_rect[i] = qemu_mallocz(VNC_STAT_COLS * sizeof (uint8_t)); + } + VNC_DEBUG("New client on socket %d\n", csock); dcl->idle = 0; socket_set_nonblock(vs->csock); @@ -2420,7 +2570,7 @@ int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { - vnc_connect(vs, csock); + vnc_connect(vs, csock, 0); } } @@ -2505,16 +2655,19 @@ int vnc_display_password(DisplayState *ds, const char *password) { + int ret = 0; VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; if (!vs) { - return -1; + ret = -EINVAL; + goto out; } if (!password) { /* This is not the intention of this interface but err on the side of being safe */ - return vnc_display_disable_login(ds); + ret = vnc_display_disable_login(ds); + goto out; } if (vs->password) { @@ -2523,8 +2676,11 @@ } vs->password = qemu_strdup(password); vs->auth = VNC_AUTH_VNC; - - return 0; +out: + if (ret != 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + } + return ret; } int vnc_display_pw_expire(DisplayState *ds, time_t expires) @@ -2621,6 +2777,8 @@ #endif } else if (strncmp(options, "lossy", 5) == 0) { vs->lossy = true; + } else if (strncmp(options, "non-adapative", 13) == 0) { + vs->non_adaptive = true; } } @@ -2742,7 +2900,7 @@ } else { int csock = vs->lsock; vs->lsock = -1; - vnc_connect(vs, csock); + vnc_connect(vs, csock, 0); } return 0; @@ -2766,3 +2924,10 @@ } return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); } + +void vnc_display_add_client(DisplayState *ds, int csock, int skipauth) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + return vnc_connect(vs, csock, skipauth); +} diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-enc-tight.c qemu-kvm-0.15.0+noroms/ui/vnc-enc-tight.c --- qemu-kvm-0.14.1+noroms/ui/vnc-enc-tight.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc-enc-tight.c 2011-08-09 07:40:29.000000000 -0500 @@ -28,7 +28,15 @@ #include "config-host.h" +/* This needs to be before jpeglib.h line because of conflict with + INT32 definitions between jmorecfg.h (included by jpeglib.h) and + Win32 basetsd.h (included by windows.h). */ +#include "qemu-common.h" + #ifdef CONFIG_VNC_PNG +/* The following define is needed by pngconf.h. Otherwise it won't compile, + because setjmp.h was already included by qemu-common.h. */ +#define PNG_SKIP_SETJMP_CHECK #include #endif #ifdef CONFIG_VNC_JPEG @@ -36,8 +44,6 @@ #include #endif -#include "qemu-common.h" - #include "bswap.h" #include "qint.h" #include "vnc.h" @@ -72,6 +78,26 @@ static int tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +#ifdef CONFIG_VNC_JPEG +static const struct { + double jpeg_freq_min; /* Don't send JPEG if the freq is bellow */ + double jpeg_freq_threshold; /* Always send JPEG if the freq is above */ + int jpeg_idx; /* Allow indexed JPEG */ + int jpeg_full; /* Allow full color JPEG */ +} tight_jpeg_conf[] = { + { 0, 8, 1, 1 }, + { 0, 8, 1, 1 }, + { 0, 8, 1, 1 }, + { 0, 8, 1, 1 }, + { 0, 10, 1, 1 }, + { 0.1, 10, 1, 1 }, + { 0.2, 10, 1, 1 }, + { 0.3, 12, 0, 0 }, + { 0.4, 14, 0, 0 }, + { 0.5, 16, 0, 0 }, +}; +#endif + #ifdef CONFIG_VNC_PNG static const struct { int png_zlib_level, png_filters; @@ -1477,12 +1503,13 @@ #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) + VncPalette *palette, bool force) { int ret; if (colors == 0) { - if (tight_detect_smooth_image(vs, w, h)) { + if (force || (tight_jpeg_conf[vs->tight.quality].jpeg_full && + 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); @@ -1494,8 +1521,9 @@ } 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)) { + if (force || (colors > 96 && + tight_jpeg_conf[vs->tight.quality].jpeg_idx && + 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); @@ -1515,6 +1543,10 @@ uint32_t bg = 0, fg = 0; int colors; int ret = 0; +#ifdef CONFIG_VNC_JPEG + bool force_jpeg = false; + bool allow_jpeg = true; +#endif vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type); @@ -1522,11 +1554,26 @@ vnc_raw_send_framebuffer_update(vs, x, y, w, h); vnc_tight_stop(vs); +#ifdef CONFIG_VNC_JPEG + if (!vs->vd->non_adaptive && vs->tight.quality != (uint8_t)-1) { + double freq = vnc_update_freq(vs, x, y, w, h); + + if (freq < tight_jpeg_conf[vs->tight.quality].jpeg_freq_min) { + allow_jpeg = false; + } + if (freq >= tight_jpeg_conf[vs->tight.quality].jpeg_freq_threshold) { + force_jpeg = true; + vnc_sent_lossy_rect(vs, x, y, w, h); + } + } +#endif + 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); + if (allow_jpeg && vs->tight.quality != (uint8_t)-1) { + ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, palette, + force_jpeg); } else { ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette); } @@ -1549,7 +1596,8 @@ return send_solid_rect(vs); } -static int send_rect_simple(VncState *vs, int x, int y, int w, int h) +static int send_rect_simple(VncState *vs, int x, int y, int w, int h, + bool split) { int max_size, max_width; int max_sub_width, max_sub_height; @@ -1560,7 +1608,7 @@ 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) { + if (split && (w > max_width || w * h > max_size)) { max_sub_width = (w > max_width) ? max_width : w; max_sub_height = max_size / max_sub_width; @@ -1591,7 +1639,7 @@ /* 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); + n += send_rect_simple(vs, x, y, w, max_rows, true); y += max_rows; h -= max_rows; } @@ -1630,7 +1678,7 @@ /* 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); + n += send_rect_simple(vs, x, y, w, y_best-y, true); } if (x_best != x) { n += tight_send_framebuffer_update(vs, x, y_best, @@ -1657,7 +1705,7 @@ return n; } } - return n + send_rect_simple(vs, x, y, w, h); + return n + send_rect_simple(vs, x, y, w, h, true); } static int tight_send_framebuffer_update(VncState *vs, int x, int y, @@ -1672,8 +1720,19 @@ vs->tight.pixel24 = false; } - if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE) - return send_rect_simple(vs, x, y, w, h); +#ifdef CONFIG_VNC_JPEG + if (vs->tight.quality != (uint8_t)-1) { + double freq = vnc_update_freq(vs, x, y, w, h); + + if (freq > tight_jpeg_conf[vs->tight.quality].jpeg_freq_threshold) { + return send_rect_simple(vs, x, y, w, h, false); + } + } +#endif + + if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE) { + return send_rect_simple(vs, x, y, w, h, true); + } /* Calculate maximum number of rows in one non-solid rectangle. */ diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-enc-zrle.c qemu-kvm-0.15.0+noroms/ui/vnc-enc-zrle.c --- qemu-kvm-0.14.1+noroms/ui/vnc-enc-zrle.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/ui/vnc-enc-zrle.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,366 @@ +/* + * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE) + * + * From libvncserver/libvncserver/zrle.c + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, 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-enc-zrle.h" + +static const int bits_per_packed_pixel[] = { + 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + + +static void vnc_zrle_start(VncState *vs) +{ + buffer_reset(&vs->zrle.zrle); + + /* make the output buffer be the zlib buffer, so we can compress it later */ + vs->zrle.tmp = vs->output; + vs->output = vs->zrle.zrle; +} + +static void vnc_zrle_stop(VncState *vs) +{ + /* switch back to normal output/zlib buffers */ + vs->zrle.zrle = vs->output; + vs->output = vs->zrle.tmp; +} + +static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h, + int bpp) +{ + Buffer tmp; + + buffer_reset(&vs->zrle.fb); + buffer_reserve(&vs->zrle.fb, w * h * bpp + bpp); + + tmp = vs->output; + vs->output = vs->zrle.fb; + + vnc_raw_send_framebuffer_update(vs, x, y, w, h); + + vs->zrle.fb = vs->output; + vs->output = tmp; + return vs->zrle.fb.buffer; +} + +static int zrle_compress_data(VncState *vs, int level) +{ + z_streamp zstream = &vs->zrle.stream; + + buffer_reset(&vs->zrle.zlib); + + if (zstream->opaque != vs) { + int err; + + zstream->zalloc = vnc_zlib_zalloc; + zstream->zfree = vnc_zlib_zfree; + + err = deflateInit2(zstream, level, 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; + } + + /* reserve memory in output buffer */ + buffer_reserve(&vs->zrle.zlib, vs->zrle.zrle.offset + 64); + + /* set pointers */ + zstream->next_in = vs->zrle.zrle.buffer; + zstream->avail_in = vs->zrle.zrle.offset; + zstream->next_out = vs->zrle.zlib.buffer + vs->zrle.zlib.offset; + zstream->avail_out = vs->zrle.zlib.capacity - vs->zrle.zlib.offset; + zstream->data_type = Z_BINARY; + + /* start encoding */ + if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) { + fprintf(stderr, "VNC: error during zrle compression\n"); + return -1; + } + + vs->zrle.zlib.offset = vs->zrle.zlib.capacity - zstream->avail_out; + return vs->zrle.zlib.offset; +} + +/* Try to work out whether to use RLE and/or a palette. We do this by + * estimating the number of bytes which will be generated and picking the + * method which results in the fewest bytes. Of course this may not result + * in the fewest bytes after compression... */ +static void zrle_choose_palette_rle(VncState *vs, int w, int h, + VncPalette *palette, int bpp_out, + int runs, int single_pixels, + int zywrle_level, + bool *use_rle, bool *use_palette) +{ + size_t estimated_bytes; + size_t plain_rle_bytes; + + *use_palette = *use_rle = false; + + estimated_bytes = w * h * (bpp_out / 8); /* start assuming raw */ + + if (bpp_out != 8) { + if (zywrle_level > 0 && !(zywrle_level & 0x80)) + estimated_bytes >>= zywrle_level; + } + + plain_rle_bytes = ((bpp_out / 8) + 1) * (runs + single_pixels); + + if (plain_rle_bytes < estimated_bytes) { + *use_rle = true; + estimated_bytes = plain_rle_bytes; + } + + if (palette_size(palette) < 128) { + int palette_rle_bytes; + + palette_rle_bytes = (bpp_out / 8) * palette_size(palette); + palette_rle_bytes += 2 * runs + single_pixels; + + if (palette_rle_bytes < estimated_bytes) { + *use_rle = true; + *use_palette = true; + estimated_bytes = palette_rle_bytes; + } + + if (palette_size(palette) < 17) { + int packed_bytes; + + packed_bytes = (bpp_out / 8) * palette_size(palette); + packed_bytes += w * h * + bits_per_packed_pixel[palette_size(palette)-1] / 8; + + if (packed_bytes < estimated_bytes) { + *use_rle = false; + *use_palette = true; + estimated_bytes = packed_bytes; + } + } + } +} + +static void zrle_write_u32(VncState *vs, uint32_t value) +{ + vnc_write(vs, (uint8_t *)&value, 4); +} + +static void zrle_write_u24a(VncState *vs, uint32_t value) +{ + vnc_write(vs, (uint8_t *)&value, 3); +} + +static void zrle_write_u24b(VncState *vs, uint32_t value) +{ + vnc_write(vs, ((uint8_t *)&value) + 1, 3); +} + +static void zrle_write_u16(VncState *vs, uint16_t value) +{ + vnc_write(vs, (uint8_t *)&value, 2); +} + +static void zrle_write_u8(VncState *vs, uint8_t value) +{ + vnc_write_u8(vs, value); +} + +#define ENDIAN_LITTLE 0 +#define ENDIAN_BIG 1 +#define ENDIAN_NO 2 + +#define ZRLE_BPP 8 +#define ZYWRLE_ENDIAN ENDIAN_NO +#include "vnc-enc-zrle-template.c" +#undef ZRLE_BPP + +#define ZRLE_BPP 15 +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#undef ZRLE_BPP +#define ZRLE_BPP 16 +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#undef ZRLE_BPP +#define ZRLE_BPP 32 +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#define ZRLE_COMPACT_PIXEL 24a +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" + +#undef ZRLE_COMPACT_PIXEL +#define ZRLE_COMPACT_PIXEL 24b +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_LITTLE +#include "vnc-enc-zrle-template.c" + +#undef ZYWRLE_ENDIAN +#define ZYWRLE_ENDIAN ENDIAN_BIG +#include "vnc-enc-zrle-template.c" +#undef ZRLE_COMPACT_PIXEL +#undef ZRLE_BPP + +static int zrle_send_framebuffer_update(VncState *vs, int x, int y, + int w, int h) +{ + bool be = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG); + size_t bytes; + int zywrle_level; + + if (vs->zrle.type == VNC_ENCODING_ZYWRLE) { + if (!vs->vd->lossy || vs->tight.quality == (uint8_t)-1 + || vs->tight.quality == 9) { + zywrle_level = 0; + vs->zrle.type = VNC_ENCODING_ZRLE; + } else if (vs->tight.quality < 3) { + zywrle_level = 3; + } else if (vs->tight.quality < 6) { + zywrle_level = 2; + } else { + zywrle_level = 1; + } + } else { + zywrle_level = 0; + } + + vnc_zrle_start(vs); + + switch(vs->clientds.pf.bytes_per_pixel) { + case 1: + zrle_encode_8ne(vs, x, y, w, h, zywrle_level); + break; + + case 2: + if (vs->clientds.pf.gmax > 0x1F) { + if (be) { + zrle_encode_16be(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_16le(vs, x, y, w, h, zywrle_level); + } + } else { + if (be) { + zrle_encode_15be(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_15le(vs, x, y, w, h, zywrle_level); + } + } + break; + + case 4: + { + bool fits_in_ls3bytes; + bool fits_in_ms3bytes; + + fits_in_ls3bytes = + ((vs->clientds.pf.rmax << vs->clientds.pf.rshift) < (1 << 24) && + (vs->clientds.pf.gmax << vs->clientds.pf.gshift) < (1 << 24) && + (vs->clientds.pf.bmax << vs->clientds.pf.bshift) < (1 << 24)); + + fits_in_ms3bytes = (vs->clientds.pf.rshift > 7 && + vs->clientds.pf.gshift > 7 && + vs->clientds.pf.bshift > 7); + + if ((fits_in_ls3bytes && !be) || (fits_in_ms3bytes && be)) { + if (be) { + zrle_encode_24abe(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_24ale(vs, x, y, w, h, zywrle_level); + } + } else if ((fits_in_ls3bytes && be) || (fits_in_ms3bytes && !be)) { + if (be) { + zrle_encode_24bbe(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_24ble(vs, x, y, w, h, zywrle_level); + } + } else { + if (be) { + zrle_encode_32be(vs, x, y, w, h, zywrle_level); + } else { + zrle_encode_32le(vs, x, y, w, h, zywrle_level); + } + } + } + break; + } + + vnc_zrle_stop(vs); + bytes = zrle_compress_data(vs, Z_DEFAULT_COMPRESSION); + vnc_framebuffer_update(vs, x, y, w, h, vs->zrle.type); + vnc_write_u32(vs, bytes); + vnc_write(vs, vs->zrle.zlib.buffer, vs->zrle.zlib.offset); + return 1; +} + +int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + vs->zrle.type = VNC_ENCODING_ZRLE; + return zrle_send_framebuffer_update(vs, x, y, w, h); +} + +int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + vs->zrle.type = VNC_ENCODING_ZYWRLE; + return zrle_send_framebuffer_update(vs, x, y, w, h); +} + +void vnc_zrle_clear(VncState *vs) +{ + if (vs->zrle.stream.opaque) { + deflateEnd(&vs->zrle.stream); + } + buffer_free(&vs->zrle.zrle); + buffer_free(&vs->zrle.fb); + buffer_free(&vs->zrle.zlib); +} diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-enc-zrle.h qemu-kvm-0.15.0+noroms/ui/vnc-enc-zrle.h --- qemu-kvm-0.14.1+noroms/ui/vnc-enc-zrle.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/ui/vnc-enc-zrle.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,40 @@ +/* + * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE) + * + * From libvncserver/libvncserver/zrle.c + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, 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. + */ + +#ifndef VNC_ENCODING_ZRLE_H +#define VNC_ENCODING_ZRLE_H + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ZRLE - encoding combining Zlib compression, tiling, palettisation and + * run-length encoding. + */ + +#define VNC_ZRLE_TILE_WIDTH 64 +#define VNC_ZRLE_TILE_HEIGHT 64 + +#endif diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-enc-zrle-template.c qemu-kvm-0.15.0+noroms/ui/vnc-enc-zrle-template.c --- qemu-kvm-0.14.1+noroms/ui/vnc-enc-zrle-template.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/ui/vnc-enc-zrle-template.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,263 @@ +/* + * QEMU VNC display driver: Zlib Run-length Encoding (ZRLE) + * + * From libvncserver/libvncserver/zrleencodetemplate.c + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * Copyright (C) 2010 Corentin Chary + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Before including this file, you must define a number of CPP macros. + * + * ZRLE_BPP should be 8, 16 or 32 depending on the bits per pixel. + * + * Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel + * bigger than the largest tile of pixel data, since the ZRLE encoding + * algorithm writes to the position one past the end of the pixel data. + */ + + +#include + +#undef ZRLE_ENDIAN_SUFFIX + +#if ZYWRLE_ENDIAN == ENDIAN_LITTLE +#define ZRLE_ENDIAN_SUFFIX le +#elif ZYWRLE_ENDIAN == ENDIAN_BIG +#define ZRLE_ENDIAN_SUFFIX be +#else +#define ZRLE_ENDIAN_SUFFIX ne +#endif + +#ifndef ZRLE_CONCAT +#define ZRLE_CONCAT_I(a, b) a##b +#define ZRLE_CONCAT2(a, b) ZRLE_CONCAT_I(a, b) +#define ZRLE_CONCAT3(a, b, c) ZRLE_CONCAT2(a, ZRLE_CONCAT2(b, c)) +#endif + +#ifdef ZRLE_COMPACT_PIXEL +#define ZRLE_ENCODE_SUFFIX ZRLE_CONCAT2(ZRLE_COMPACT_PIXEL,ZRLE_ENDIAN_SUFFIX) +#define ZRLE_WRITE_SUFFIX ZRLE_COMPACT_PIXEL +#define ZRLE_PIXEL ZRLE_CONCAT3(uint,ZRLE_BPP,_t) +#define ZRLE_BPP_OUT 24 +#elif ZRLE_BPP == 15 +#define ZRLE_ENCODE_SUFFIX ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX) +#define ZRLE_WRITE_SUFFIX 16 +#define ZRLE_PIXEL uint16_t +#define ZRLE_BPP_OUT 16 +#else +#define ZRLE_ENCODE_SUFFIX ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX) +#define ZRLE_WRITE_SUFFIX ZRLE_BPP +#define ZRLE_BPP_OUT ZRLE_BPP +#define ZRLE_PIXEL ZRLE_CONCAT3(uint,ZRLE_BPP,_t) +#endif + +#define ZRLE_WRITE_PIXEL ZRLE_CONCAT2(zrle_write_u, ZRLE_WRITE_SUFFIX) +#define ZRLE_ENCODE ZRLE_CONCAT2(zrle_encode_, ZRLE_ENCODE_SUFFIX) +#define ZRLE_ENCODE_TILE ZRLE_CONCAT2(zrle_encode_tile, ZRLE_ENCODE_SUFFIX) +#define ZRLE_WRITE_PALETTE ZRLE_CONCAT2(zrle_write_palette,ZRLE_ENCODE_SUFFIX) + +static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, + int zywrle_level); + +#if ZRLE_BPP != 8 +#include "vnc-enc-zywrle-template.c" +#endif + + +static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h, + int zywrle_level) +{ + int ty; + + for (ty = y; ty < y + h; ty += VNC_ZRLE_TILE_HEIGHT) { + + int tx, th; + + th = MIN(VNC_ZRLE_TILE_HEIGHT, y + h - ty); + + for (tx = x; tx < x + w; tx += VNC_ZRLE_TILE_WIDTH) { + int tw; + ZRLE_PIXEL *buf; + + tw = MIN(VNC_ZRLE_TILE_WIDTH, x + w - tx); + + buf = zrle_convert_fb(vs, tx, ty, tw, th, ZRLE_BPP); + ZRLE_ENCODE_TILE(vs, buf, tw, th, zywrle_level); + } + } +} + +static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, + int zywrle_level) +{ + VncPalette *palette = &vs->zrle.palette; + + int runs = 0; + int single_pixels = 0; + + bool use_rle; + bool use_palette; + + int i; + + ZRLE_PIXEL *ptr = data; + ZRLE_PIXEL *end = ptr + h * w; + *end = ~*(end-1); /* one past the end is different so the while loop ends */ + + /* Real limit is 127 but we wan't a way to know if there is more than 127 */ + palette_init(palette, 256, ZRLE_BPP); + + while (ptr < end) { + ZRLE_PIXEL pix = *ptr; + if (*++ptr != pix) { /* FIXME */ + single_pixels++; + } else { + while (*++ptr == pix) ; + runs++; + } + palette_put(palette, pix); + } + + /* Solid tile is a special case */ + + if (palette_size(palette) == 1) { + bool found; + + vnc_write_u8(vs, 1); + ZRLE_WRITE_PIXEL(vs, palette_color(palette, 0, &found)); + return; + } + + zrle_choose_palette_rle(vs, w, h, palette, ZRLE_BPP_OUT, + runs, single_pixels, zywrle_level, + &use_rle, &use_palette); + + if (!use_palette) { + vnc_write_u8(vs, (use_rle ? 128 : 0)); + } else { + uint32_t colors[VNC_PALETTE_MAX_SIZE]; + size_t size = palette_size(palette); + + vnc_write_u8(vs, (use_rle ? 128 : 0) | size); + palette_fill(palette, colors); + + for (i = 0; i < size; i++) { + ZRLE_WRITE_PIXEL(vs, colors[i]); + } + } + + if (use_rle) { + ZRLE_PIXEL *ptr = data; + ZRLE_PIXEL *end = ptr + w * h; + ZRLE_PIXEL *run_start; + ZRLE_PIXEL pix; + + while (ptr < end) { + int len; + int index = 0; + + run_start = ptr; + pix = *ptr++; + + while (*ptr == pix && ptr < end) { + ptr++; + } + + len = ptr - run_start; + + if (use_palette) + index = palette_idx(palette, pix); + + if (len <= 2 && use_palette) { + if (len == 2) { + vnc_write_u8(vs, index); + } + vnc_write_u8(vs, index); + continue; + } + if (use_palette) { + vnc_write_u8(vs, index | 128); + } else { + ZRLE_WRITE_PIXEL(vs, pix); + } + + len -= 1; + + while (len >= 255) { + vnc_write_u8(vs, 255); + len -= 255; + } + + vnc_write_u8(vs, len); + } + } else if (use_palette) { /* no RLE */ + int bppp; + ZRLE_PIXEL *ptr = data; + + /* packed pixels */ + + assert (palette_size(palette) < 17); + + bppp = bits_per_packed_pixel[palette_size(palette)-1]; + + for (i = 0; i < h; i++) { + uint8_t nbits = 0; + uint8_t byte = 0; + + ZRLE_PIXEL *eol = ptr + w; + + while (ptr < eol) { + ZRLE_PIXEL pix = *ptr++; + uint8_t index = palette_idx(palette, pix); + + byte = (byte << bppp) | index; + nbits += bppp; + if (nbits >= 8) { + vnc_write_u8(vs, byte); + nbits = 0; + } + } + if (nbits > 0) { + byte <<= 8 - nbits; + vnc_write_u8(vs, byte); + } + } + } else { + + /* raw */ + +#if ZRLE_BPP != 8 + if (zywrle_level > 0 && !(zywrle_level & 0x80)) { + ZYWRLE_ANALYZE(data, data, w, h, w, zywrle_level, vs->zywrle.buf); + ZRLE_ENCODE_TILE(vs, data, w, h, zywrle_level | 0x80); + } + else +#endif + { +#ifdef ZRLE_COMPACT_PIXEL + ZRLE_PIXEL *ptr; + + for (ptr = data; ptr < data + w * h; ptr++) { + ZRLE_WRITE_PIXEL(vs, *ptr); + } +#else + vnc_write(vs, data, w * h * (ZRLE_BPP / 8)); +#endif + } + } +} + +#undef ZRLE_PIXEL +#undef ZRLE_WRITE_PIXEL +#undef ZRLE_ENCODE +#undef ZRLE_ENCODE_TILE +#undef ZYWRLE_ENCODE_TILE +#undef ZRLE_BPP_OUT +#undef ZRLE_WRITE_SUFFIX +#undef ZRLE_ENCODE_SUFFIX diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-enc-zywrle.h qemu-kvm-0.15.0+noroms/ui/vnc-enc-zywrle.h --- qemu-kvm-0.14.1+noroms/ui/vnc-enc-zywrle.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/ui/vnc-enc-zywrle.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,659 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE. * + * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006 * + * BY Hitachi Systems & Services, Ltd. * + * (Noriaki Yamazaki, Research & Developement Center) * + * * + * * + ******************************************************************** +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- 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. + +- Neither the name of the Hitachi Systems & Services, Ltd. 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 COPYRIGHT HOLDERS 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 FOUNDATION +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. + ********************************************************************/ + +#ifndef VNC_ENCODING_ZYWRLE_H +#define VNC_ENCODING_ZYWRLE_H + +/* Tables for Coefficients filtering. */ +#ifndef ZYWRLE_QUANTIZE +/* Type A:lower bit omitting of EZW style. */ +static const unsigned int zywrle_param[3][3]={ + {0x0000F000, 0x00000000, 0x00000000}, + {0x0000C000, 0x00F0F0F0, 0x00000000}, + {0x0000C000, 0x00C0C0C0, 0x00F0F0F0}, +/* {0x0000FF00, 0x00000000, 0x00000000}, + {0x0000FF00, 0x00FFFFFF, 0x00000000}, + {0x0000FF00, 0x00FFFFFF, 0x00FFFFFF}, */ +}; +#else +/* Type B:Non liner quantization filter. */ +static const int8_t zywrle_conv[4][256]={ +{ /* bi=5, bo=5 r=0.0:PSNR=24.849 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=5 r=2.0:PSNR=74.031 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 64, 64, 64, 64, + 64, 64, 64, 64, 72, 72, 72, 72, + 72, 72, 72, 72, 80, 80, 80, 80, + 80, 80, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 96, 96, + 96, 96, 96, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -104, -104, -104, -104, + -104, -104, -104, -104, -104, -104, -96, -96, + -96, -96, -96, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -80, + -80, -80, -80, -80, -80, -72, -72, -72, + -72, -72, -72, -72, -72, -64, -64, -64, + -64, -64, -64, -64, -64, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=4 r=2.0:PSNR=64.441 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -104, -104, + -104, -104, -104, -104, -104, -104, -104, -104, + -104, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, + -80, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, + -64, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}, +{ /* bi=5, bo=2 r=2.0:PSNR=43.175 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 0, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +} +}; + +static const int8_t *zywrle_param[3][3][3]={ + {{zywrle_conv[0], zywrle_conv[2], zywrle_conv[0]}, + {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}, + {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}}, + {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, + {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]}, + {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}}, + {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, + {zywrle_conv[2], zywrle_conv[2], zywrle_conv[2]}, + {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]}}, +}; +#endif + +/* Load/Save pixel stuffs. */ +#define ZYWRLE_YMASK15 0xFFFFFFF8 +#define ZYWRLE_UVMASK15 0xFFFFFFF8 +#define ZYWRLE_LOAD_PIXEL15(src, r, g, b) \ + do { \ + r = (((uint8_t*)src)[S_1]<< 1)& 0xF8; \ + g = (((uint8_t*)src)[S_1]<< 6) | (((uint8_t*)src)[S_0]>> 2); \ + g &= 0xF8; \ + b = (((uint8_t*)src)[S_0]<< 3)& 0xF8; \ + } while (0) + +#define ZYWRLE_SAVE_PIXEL15(dst, r, g, b) \ + do { \ + r &= 0xF8; \ + g &= 0xF8; \ + b &= 0xF8; \ + ((uint8_t*)dst)[S_1] = (uint8_t)((r >> 1)|(g >> 6)); \ + ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 2))& 0xFF); \ + } while (0) + +#define ZYWRLE_YMASK16 0xFFFFFFFC +#define ZYWRLE_UVMASK16 0xFFFFFFF8 +#define ZYWRLE_LOAD_PIXEL16(src, r, g, b) \ + do { \ + r = ((uint8_t*)src)[S_1] & 0xF8; \ + g = (((uint8_t*)src)[S_1]<< 5) | (((uint8_t*)src)[S_0] >> 3); \ + g &= 0xFC; \ + b = (((uint8_t*)src)[S_0]<< 3) & 0xF8; \ + } while (0) + +#define ZYWRLE_SAVE_PIXEL16(dst, r, g,b) \ + do { \ + r &= 0xF8; \ + g &= 0xFC; \ + b &= 0xF8; \ + ((uint8_t*)dst)[S_1] = (uint8_t)(r | (g >> 5)); \ + ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 3)) & 0xFF); \ + } while (0) + +#define ZYWRLE_YMASK32 0xFFFFFFFF +#define ZYWRLE_UVMASK32 0xFFFFFFFF +#define ZYWRLE_LOAD_PIXEL32(src, r, g, b) \ + do { \ + r = ((uint8_t*)src)[L_2]; \ + g = ((uint8_t*)src)[L_1]; \ + b = ((uint8_t*)src)[L_0]; \ + } while (0) +#define ZYWRLE_SAVE_PIXEL32(dst, r, g, b) \ + do { \ + ((uint8_t*)dst)[L_2] = (uint8_t)r; \ + ((uint8_t*)dst)[L_1] = (uint8_t)g; \ + ((uint8_t*)dst)[L_0] = (uint8_t)b; \ + } while (0) + +static inline void harr(int8_t *px0, int8_t *px1) +{ + /* Piecewise-Linear Harr(PLHarr) */ + int x0 = (int)*px0, x1 = (int)*px1; + int orgx0 = x0, orgx1 = x1; + + if ((x0 ^ x1) & 0x80) { + /* differ sign */ + x1 += x0; + if (((x1 ^ orgx1) & 0x80) == 0) { + /* |x1| > |x0| */ + x0 -= x1; /* H = -B */ + } + } else { + /* same sign */ + x0 -= x1; + if (((x0 ^ orgx0) & 0x80) == 0) { + /* |x0| > |x1| */ + x1 += x0; /* L = A */ + } + } + *px0 = (int8_t)x1; + *px1 = (int8_t)x0; +} + +/* + 1D-Wavelet transform. + + In coefficients array, the famous 'pyramid' decomposition is well used. + + 1D Model: + |L0L0L0L0|L0L0L0L0|H0H0H0H0|H0H0H0H0| : level 0 + |L1L1L1L1|H1H1H1H1|H0H0H0H0|H0H0H0H0| : level 1 + + But this method needs line buffer because H/L is different position from X0/X1. + So, I used 'interleave' decomposition instead of it. + + 1D Model: + |L0H0L0H0|L0H0L0H0|L0H0L0H0|L0H0L0H0| : level 0 + |L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1 + + In this method, H/L and X0/X1 is always same position. + This lead us to more speed and less memory. + Of cause, the result of both method is quite same + because it's only difference that coefficient position. +*/ +static inline void wavelet_level(int *data, int size, int l, int skip_pixel) +{ + int s, ofs; + int8_t *px0; + int8_t *end; + + px0 = (int8_t*)data; + s = (8 << l) * skip_pixel; + end = px0 + (size >> (l + 1)) * s; + s -= 2; + ofs = (4 << l) * skip_pixel; + + while (px0 < end) { + harr(px0, px0 + ofs); + px0++; + harr(px0, px0 + ofs); + px0++; + harr(px0, px0 + ofs); + px0 += s; + } +} + +#ifndef ZYWRLE_QUANTIZE +/* Type A:lower bit omitting of EZW style. */ +static inline void filter_wavelet_square(int *buf, int width, int height, + int level, int l) +{ + int r, s; + int x, y; + int *h; + const unsigned int *m; + + m = &(zywrle_param[level - 1][l]); + s = 2 << l; + + for (r = 1; r < 4; r++) { + h = buf; + if (r & 0x01) { + h += s >> 1; + } + if (r & 0x02) { + h += (s >> 1) * width; + } + for (y = 0; y < height / s; y++) { + for (x = 0; x < width / s; x++) { + /* + these are same following code. + h[x] = h[x] / (~m[x]+1) * (~m[x]+1); + ( round h[x] with m[x] bit ) + '&' operator isn't 'round' but is 'floor'. + So, we must offset when h[x] is negative. + */ + if (((int8_t*)h)[0] & 0x80) { + ((int8_t*)h)[0] += ~((int8_t*)m)[0]; + } + if (((int8_t*)h)[1] & 0x80) { + ((int8_t*)h)[1] += ~((int8_t*)m)[1]; + } + if (((int8_t*)h)[2] & 0x80) { + ((int8_t*)h)[2] += ~((int8_t*)m)[2]; + } + *h &= *m; + h += s; + } + h += (s-1)*width; + } + } +} +#else +/* + Type B:Non liner quantization filter. + + Coefficients have Gaussian curve and smaller value which is + large part of coefficients isn't more important than larger value. + So, I use filter of Non liner quantize/dequantize table. + In general, Non liner quantize formula is explained as following. + + y=f(x) = sign(x)*round( ((abs(x)/(2^7))^ r )* 2^(bo-1) )*2^(8-bo) + x=f-1(y) = sign(y)*round( ((abs(y)/(2^7))^(1/r))* 2^(bi-1) )*2^(8-bi) + ( r:power coefficient bi:effective MSB in input bo:effective MSB in output ) + + r < 1.0 : Smaller value is more important than larger value. + r > 1.0 : Larger value is more important than smaller value. + r = 1.0 : Liner quantization which is same with EZW style. + + r = 0.75 is famous non liner quantization used in MP3 audio codec. + In contrast to audio data, larger value is important in wavelet coefficients. + So, I select r = 2.0 table( quantize is x^2, dequantize sqrt(x) ). + + As compared with EZW style liner quantization, this filter tended to be + more sharp edge and be more compression rate but be more blocking noise and be + less quality. Especially, the surface of graphic objects has distinguishable + noise in middle quality mode. + + We need only quantized-dequantized(filtered) value rather than quantized value + itself because all values are packed or palette-lized in later ZRLE section. + This lead us not to need to modify client decoder when we change + the filtering procedure in future. + Client only decodes coefficients given by encoder. +*/ +static inline void filter_wavelet_square(int *buf, int width, int height, + int level, int l) +{ + int r, s; + int x, y; + int *h; + const int8_t **m; + + m = zywrle_param[level - 1][l]; + s = 2 << l; + + for (r = 1; r < 4; r++) { + h = buf; + if (r & 0x01) { + h += s >> 1; + } + if (r & 0x02) { + h += (s >> 1) * width; + } + for (y = 0; y < height / s; y++) { + for (x = 0; x < width / s; x++) { + ((int8_t*)h)[0] = m[0][((uint8_t*)h)[0]]; + ((int8_t*)h)[1] = m[1][((uint8_t*)h)[1]]; + ((int8_t*)h)[2] = m[2][((uint8_t*)h)[2]]; + h += s; + } + h += (s - 1) * width; + } + } +} +#endif + +static inline void wavelet(int *buf, int width, int height, int level) +{ + int l, s; + int *top; + int *end; + + for (l = 0; l < level; l++) { + top = buf; + end = buf + height * width; + s = width << l; + while (top < end) { + wavelet_level(top, width, l, 1); + top += s; + } + top = buf; + end = buf + width; + s = 1< YUV conversion stuffs. + YUV coversion is explained as following formula in strict meaning: + Y = 0.299R + 0.587G + 0.114B ( 0<=Y<=255) + U = -0.169R - 0.331G + 0.500B (-128<=U<=127) + V = 0.500R - 0.419G - 0.081B (-128<=V<=127) + + I use simple conversion RCT(reversible color transform) which is described + in JPEG-2000 specification. + Y = (R + 2G + B)/4 ( 0<=Y<=255) + U = B-G (-256<=U<=255) + V = R-G (-256<=V<=255) +*/ + +/* RCT is N-bit RGB to N-bit Y and N+1-bit UV. + For make Same N-bit, UV is lossy. + More exact PLHarr, we reduce to odd range(-127<=x<=127). */ +#define ZYWRLE_RGBYUV_(r, g, b, y, u, v, ymask, uvmask) \ + do { \ + y = (r + (g << 1) + b) >> 2; \ + u = b - g; \ + v = r - g; \ + y -= 128; \ + u >>= 1; \ + v >>= 1; \ + y &= ymask; \ + u &= uvmask; \ + v &= uvmask; \ + if (y == -128) { \ + y += (0xFFFFFFFF - ymask + 1); \ + } \ + if (u == -128) { \ + u += (0xFFFFFFFF - uvmask + 1); \ + } \ + if (v == -128) { \ + v += (0xFFFFFFFF - uvmask + 1); \ + } \ + } while (0) + + +/* + coefficient packing/unpacking stuffs. + Wavelet transform makes 4 sub coefficient image from 1 original image. + + model with pyramid decomposition: + +------+------+ + | | | + | L | Hx | + | | | + +------+------+ + | | | + | H | Hxy | + | | | + +------+------+ + + So, we must transfer each sub images individually in strict meaning. + But at least ZRLE meaning, following one decompositon image is same as + avobe individual sub image. I use this format. + (Strictly saying, transfer order is reverse(Hxy->Hy->Hx->L) + for simplified procedure for any wavelet level.) + + +------+------+ + | L | + +------+------+ + | Hx | + +------+------+ + | Hy | + +------+------+ + | Hxy | + +------+------+ +*/ +#define ZYWRLE_INC_PTR(data) \ + do { \ + data++; \ + if( data - p >= (w + uw) ) { \ + data += scanline-(w + uw); \ + p = data; \ + } \ + } while (0) + +#define ZYWRLE_TRANSFER_COEFF(buf, data, t, w, h, scanline, level, TRANS) \ + do { \ + ph = buf; \ + s = 2 << level; \ + if (t & 0x01) { \ + ph += s >> 1; \ + } \ + if (t & 0x02) { \ + ph += (s >> 1) * w; \ + } \ + end = ph + h * w; \ + while (ph < end) { \ + line = ph + w; \ + while (ph < line) { \ + TRANS \ + ZYWRLE_INC_PTR(data); \ + ph += s; \ + } \ + ph += (s - 1) * w; \ + } \ + } while (0) + +#define ZYWRLE_PACK_COEFF(buf, data, t, width, height, scanline, level) \ + ZYWRLE_TRANSFER_COEFF(buf, data, t, width, height, scanline, level, \ + ZYWRLE_LOAD_COEFF(ph, r, g, b); \ + ZYWRLE_SAVE_PIXEL(data, r, g, b);) + +#define ZYWRLE_UNPACK_COEFF(buf, data, t, width, height, scanline, level) \ + ZYWRLE_TRANSFER_COEFF(buf, data, t, width, height, scanline, level, \ + ZYWRLE_LOAD_PIXEL(data, r, g, b); \ + ZYWRLE_SAVE_COEFF(ph, r, g, b);) + +#define ZYWRLE_SAVE_UNALIGN(data, TRANS) \ + do { \ + top = buf + w * h; \ + end = buf + (w + uw) * (h + uh); \ + while (top < end) { \ + TRANS \ + ZYWRLE_INC_PTR(data); \ + top++; \ + } \ + } while (0) + +#define ZYWRLE_LOAD_UNALIGN(data,TRANS) \ + do { \ + top = buf + w * h; \ + if (uw) { \ + p = data + w; \ + end = (int*)(p + h * scanline); \ + while (p < (ZRLE_PIXEL*)end) { \ + line = (int*)(p + uw); \ + while (p < (ZRLE_PIXEL*)line) { \ + TRANS \ + p++; \ + top++; \ + } \ + p += scanline - uw; \ + } \ + } \ + if (uh) { \ + p = data + h * scanline; \ + end = (int*)(p + uh * scanline); \ + while (p < (ZRLE_PIXEL*)end) { \ + line = (int*)(p + w); \ + while (p < (ZRLE_PIXEL*)line) { \ + TRANS \ + p++; \ + top++; \ + } \ + p += scanline - w; \ + } \ + } \ + if (uw && uh) { \ + p= data + w + h * scanline; \ + end = (int*)(p + uh * scanline); \ + while (p < (ZRLE_PIXEL*)end) { \ + line = (int*)(p + uw); \ + while (p < (ZRLE_PIXEL*)line) { \ + TRANS \ + p++; \ + top++; \ + } \ + p += scanline-uw; \ + } \ + } \ + } while (0) + +static inline void zywrle_calc_size(int *w, int *h, int level) +{ + *w &= ~((1 << level) - 1); + *h &= ~((1 << level) - 1); +} + +#endif diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-enc-zywrle-template.c qemu-kvm-0.15.0+noroms/ui/vnc-enc-zywrle-template.c --- qemu-kvm-0.14.1+noroms/ui/vnc-enc-zywrle-template.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/ui/vnc-enc-zywrle-template.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,170 @@ + +/******************************************************************** + * * + * THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE. * + * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006 * + * BY Hitachi Systems & Services, Ltd. * + * (Noriaki Yamazaki, Research & Developement Center) * + * * + * * + ******************************************************************** +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- 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. + +- Neither the name of the Hitachi Systems & Services, Ltd. 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 COPYRIGHT HOLDERS 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 FOUNDATION +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. + ********************************************************************/ + +/* Change Log: + V0.02 : 2008/02/04 : Fix mis encode/decode when width != scanline + (Thanks Johannes Schindelin, author of LibVNC + Server/Client) + V0.01 : 2007/02/06 : Initial release +*/ + +/* +[References] + PLHarr: + Senecal, J. G., P. Lindstrom, M. A. Duchaineau, and K. I. Joy, + "An Improved N-Bit to N-Bit Reversible Haar-Like Transform," + Pacific Graphics 2004, October 2004, pp. 371-380. + EZW: + Shapiro, JM: Embedded Image Coding Using Zerotrees of Wavelet Coefficients, + IEEE Trans. Signal. Process., Vol.41, pp.3445-3462 (1993). +*/ + + +/* Template Macro stuffs. */ +#undef ZYWRLE_ANALYZE +#undef ZYWRLE_SYNTHESIZE + +#define ZYWRLE_SUFFIX ZRLE_CONCAT2(ZRLE_BPP,ZRLE_ENDIAN_SUFFIX) + +#define ZYWRLE_ANALYZE ZRLE_CONCAT2(zywrle_analyze_, ZYWRLE_SUFFIX) +#define ZYWRLE_SYNTHESIZE ZRLE_CONCAT2(zywrle_synthesize_,ZYWRLE_SUFFIX) + +#define ZYWRLE_RGBYUV ZRLE_CONCAT2(zywrle_rgbyuv_, ZYWRLE_SUFFIX) +#define ZYWRLE_YUVRGB ZRLE_CONCAT2(zywrle_yuvrgb_, ZYWRLE_SUFFIX) +#define ZYWRLE_YMASK ZRLE_CONCAT2(ZYWRLE_YMASK, ZRLE_BPP) +#define ZYWRLE_UVMASK ZRLE_CONCAT2(ZYWRLE_UVMASK, ZRLE_BPP) +#define ZYWRLE_LOAD_PIXEL ZRLE_CONCAT2(ZYWRLE_LOAD_PIXEL, ZRLE_BPP) +#define ZYWRLE_SAVE_PIXEL ZRLE_CONCAT2(ZYWRLE_SAVE_PIXEL, ZRLE_BPP) + +/* Packing/Unpacking pixel stuffs. + Endian conversion stuffs. */ +#undef S_0 +#undef S_1 +#undef L_0 +#undef L_1 +#undef L_2 + +#if ZYWRLE_ENDIAN == ENDIAN_BIG +# define S_0 1 +# define S_1 0 +# define L_0 3 +# define L_1 2 +# define L_2 1 +#else +# define S_0 0 +# define S_1 1 +# define L_0 0 +# define L_1 1 +# define L_2 2 +#endif + +#define ZYWRLE_QUANTIZE +#include "vnc-enc-zywrle.h" + +#ifndef ZRLE_COMPACT_PIXEL +static inline void ZYWRLE_RGBYUV(int *buf, ZRLE_PIXEL *data, + int width, int height, int scanline) +{ + int r, g, b; + int y, u, v; + int *line; + int *end; + + end = buf + height * width; + while (buf < end) { + line = buf + width; + while (buf < line) { + ZYWRLE_LOAD_PIXEL(data, r, g, b); + ZYWRLE_RGBYUV_(r, g, b, y, u, v, ZYWRLE_YMASK, ZYWRLE_UVMASK); + ZYWRLE_SAVE_COEFF(buf, v, y, u); + buf++; + data++; + } + data += scanline - width; + } +} + +static ZRLE_PIXEL *ZYWRLE_ANALYZE(ZRLE_PIXEL *dst, ZRLE_PIXEL *src, + int w, int h, int scanline, int level, + int *buf) { + int l; + int uw = w; + int uh = h; + int *top; + int *end; + int *line; + ZRLE_PIXEL *p; + int r, g, b; + int s; + int *ph; + + zywrle_calc_size(&w, &h, level); + + if (w == 0 || h == 0) { + return NULL; + } + uw -= w; + uh -= h; + + p = dst; + ZYWRLE_LOAD_UNALIGN(src,*(ZRLE_PIXEL*)top = *p;); + ZYWRLE_RGBYUV(buf, src, w, h, scanline); + wavelet(buf, w, h, level); + for (l = 0; l < level; l++) { + ZYWRLE_PACK_COEFF(buf, dst, 3, w, h, scanline, l); + ZYWRLE_PACK_COEFF(buf, dst, 2, w, h, scanline, l); + ZYWRLE_PACK_COEFF(buf, dst, 1, w, h, scanline, l); + if (l == level - 1) { + ZYWRLE_PACK_COEFF(buf, dst, 0, w, h, scanline, l); + } + } + ZYWRLE_SAVE_UNALIGN(dst,*dst = *(ZRLE_PIXEL*)top;); + return dst; +} +#endif /* ZRLE_COMPACT_PIXEL */ + +#undef ZYWRLE_RGBYUV +#undef ZYWRLE_YUVRGB +#undef ZYWRLE_LOAD_PIXEL +#undef ZYWRLE_SAVE_PIXEL diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc.h qemu-kvm-0.15.0+noroms/ui/vnc.h --- qemu-kvm-0.14.1+noroms/ui/vnc.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc.h 2011-08-09 07:40:29.000000000 -0500 @@ -35,10 +35,13 @@ #include "console.h" #include "monitor.h" #include "audio/audio.h" +#include "bitmap.h" #include #include #include "keymaps.h" +#include "vnc-palette.h" +#include "vnc-enc-zrle.h" // #define _VNC_DEBUG 1 @@ -76,9 +79,16 @@ void *last_fg, int *has_bg, int *has_fg); +/* VNC_MAX_WIDTH must be a multiple of 16. */ #define VNC_MAX_WIDTH 2560 #define VNC_MAX_HEIGHT 2048 -#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) + +/* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */ +#define VNC_DIRTY_BITS (VNC_MAX_WIDTH / 16) + +#define VNC_STAT_RECT 64 +#define VNC_STAT_COLS (VNC_MAX_WIDTH / VNC_STAT_RECT) +#define VNC_STAT_ROWS (VNC_MAX_HEIGHT / VNC_STAT_RECT) #define VNC_AUTH_CHALLENGE_SIZE 16 @@ -92,9 +102,23 @@ #include "vnc-auth-sasl.h" #endif +struct VncRectStat +{ + /* time of last 10 updates, to find update frequency */ + struct timeval times[10]; + int idx; + + double freq; /* Update frequency (in Hz) */ + bool updated; /* Already updated during this refresh */ +}; + +typedef struct VncRectStat VncRectStat; + struct VncSurface { - uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; + struct timeval last_freq_check; + DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_MAX_WIDTH / 16); + VncRectStat stats[VNC_STAT_ROWS][VNC_STAT_COLS]; DisplaySurface *ds; }; @@ -123,6 +147,7 @@ time_t expires; int auth; bool lossy; + bool non_adaptive; #ifdef CONFIG_VNC_TLS int subauth; /* Used by VeNCrypt */ VncDisplayTLS tls; @@ -162,6 +187,20 @@ int level; } VncZlib; +typedef struct VncZrle { + int type; + Buffer fb; + Buffer zrle; + Buffer tmp; + Buffer zlib; + z_stream stream; + VncPalette palette; +} VncZrle; + +typedef struct VncZywrle { + int buf[VNC_ZRLE_TILE_WIDTH * VNC_ZRLE_TILE_HEIGHT]; +} VncZywrle; + #ifdef CONFIG_VNC_THREAD struct VncRect { @@ -198,7 +237,9 @@ int csock; DisplayState *ds; - uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; + DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS); + uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in + * vnc-jobs-async.c */ VncDisplay *vd; int need_update; @@ -215,8 +256,10 @@ int major; int minor; + int auth; char challenge[VNC_AUTH_CHALLENGE_SIZE]; #ifdef CONFIG_VNC_TLS + int subauth; /* Used by VeNCrypt */ VncStateTLS tls; #endif #ifdef CONFIG_VNC_SASL @@ -253,7 +296,8 @@ VncTight tight; VncZlib zlib; VncHextile hextile; - + VncZrle zrle; + VncZywrle zywrle; Notifier mouse_mode_notifier; @@ -357,6 +401,8 @@ #define VNC_FEATURE_COPYRECT 6 #define VNC_FEATURE_RICH_CURSOR 7 #define VNC_FEATURE_TIGHT_PNG 8 +#define VNC_FEATURE_ZRLE 9 +#define VNC_FEATURE_ZYWRLE 10 #define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE) #define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE) @@ -367,6 +413,8 @@ #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) +#define VNC_FEATURE_ZRLE_MASK (1 << VNC_FEATURE_ZRLE) +#define VNC_FEATURE_ZYWRLE_MASK (1 << VNC_FEATURE_ZYWRLE) /* Client -> Server message IDs */ @@ -479,6 +527,8 @@ int32_t encoding); void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v); +double vnc_update_freq(VncState *vs, int x, int y, int w, int h); +void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h); /* Encodings */ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); @@ -499,4 +549,8 @@ int w, int h); void vnc_tight_clear(VncState *vs); +int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +void vnc_zrle_clear(VncState *vs); + #endif /* __QEMU_VNC_H */ diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-jobs-async.c qemu-kvm-0.15.0+noroms/ui/vnc-jobs-async.c --- qemu-kvm-0.14.1+noroms/ui/vnc-jobs-async.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc-jobs-async.c 2011-08-09 07:40:29.000000000 -0500 @@ -166,11 +166,13 @@ local->features = orig->features; local->ds = orig->ds; local->vd = orig->vd; + local->lossy_rect = orig->lossy_rect; local->write_pixels = orig->write_pixels; local->clientds = orig->clientds; local->tight = orig->tight; local->zlib = orig->zlib; local->hextile = orig->hextile; + local->zrle = orig->zrle; local->output = queue->buffer; local->csock = -1; /* Don't do any network work on this thread */ @@ -182,6 +184,10 @@ orig->tight = local->tight; orig->zlib = local->zlib; orig->hextile = local->hextile; + orig->zrle = local->zrle; + orig->lossy_rect = local->lossy_rect; + + queue->buffer = local->output; } static int vnc_worker_thread_loop(VncJobQueue *queue) @@ -297,7 +303,7 @@ { VncJobQueue *queue = arg; - qemu_thread_self(&queue->thread); + qemu_thread_get_self(&queue->thread); while (!vnc_worker_thread_loop(queue)) ; vnc_queue_clear(queue); diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc_keysym.h qemu-kvm-0.15.0+noroms/ui/vnc_keysym.h --- qemu-kvm-0.14.1+noroms/ui/vnc_keysym.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc_keysym.h 2011-08-09 07:40:29.000000000 -0500 @@ -202,6 +202,24 @@ { "ydiaeresis", 0x0ff}, {"EuroSign", 0x20ac}, /* XK_EuroSign */ +/* latin 2 - Polish national characters */ +{ "eogonek", 0x1ea}, +{ "Eogonek", 0x1ca}, +{ "aogonek", 0x1b1}, +{ "Aogonek", 0x1a1}, +{ "sacute", 0x1b6}, +{ "Sacute", 0x1a6}, +{ "lstroke", 0x1b3}, +{ "Lstroke", 0x1a3}, +{ "zabovedot", 0x1bf}, +{ "Zabovedot", 0x1af}, +{ "zacute", 0x1bc}, +{ "Zacute", 0x1ac}, +{ "cacute", 0x1e6}, +{ "Cacute", 0x1c6}, +{ "nacute", 0x1f1}, +{ "Nacute", 0x1d1}, + /* modifiers */ {"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */ {"Control_L", 0xffe3}, /* XK_Control_L */ diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-palette.c qemu-kvm-0.15.0+noroms/ui/vnc-palette.c --- qemu-kvm-0.14.1+noroms/ui/vnc-palette.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc-palette.c 2011-08-09 07:40:29.000000000 -0500 @@ -56,29 +56,19 @@ VncPalette *palette; palette = qemu_mallocz(sizeof(*palette)); + palette_init(palette, max, bpp); + return palette; +} + +void palette_init(VncPalette *palette, size_t max, int bpp) +{ + memset(palette, 0, 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); } @@ -97,7 +87,7 @@ if (!entry) { VncPaletteEntry *entry; - entry = qemu_mallocz(sizeof(*entry)); + entry = &palette->pool[palette->size]; entry->color = color; entry->idx = idx; QLIST_INSERT_HEAD(&palette->table[hash], entry, next); @@ -134,3 +124,35 @@ } } } + +uint32_t palette_color(const VncPalette *palette, int idx, bool *found) +{ + int i; + VncPaletteEntry *entry; + + for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) { + QLIST_FOREACH(entry, &palette->table[i], next) { + if (entry->idx == idx) { + *found = true; + return entry->color; + } + } + } + + *found = false; + return -1; +} + +static void palette_fill_cb(int idx, uint32_t color, void *opaque) +{ + uint32_t *colors = opaque; + + colors[idx] = color; +} + +size_t palette_fill(const VncPalette *palette, + uint32_t colors[VNC_PALETTE_MAX_SIZE]) +{ + palette_iter(palette, palette_fill_cb, colors); + return palette_size(palette); +} diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-palette.h qemu-kvm-0.15.0+noroms/ui/vnc-palette.h --- qemu-kvm-0.14.1+noroms/ui/vnc-palette.h 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc-palette.h 2011-08-09 07:40:29.000000000 -0500 @@ -34,6 +34,7 @@ #include #define VNC_PALETTE_HASH_SIZE 256 +#define VNC_PALETTE_MAX_SIZE 256 typedef struct VncPaletteEntry { int idx; @@ -42,7 +43,7 @@ } VncPaletteEntry; typedef struct VncPalette { - QObject_HEAD; + VncPaletteEntry pool[VNC_PALETTE_MAX_SIZE]; size_t size; size_t max; int bpp; @@ -50,6 +51,7 @@ } VncPalette; VncPalette *palette_new(size_t max, int bpp); +void palette_init(VncPalette *palette, size_t max, int bpp); void palette_destroy(VncPalette *palette); int palette_put(VncPalette *palette, uint32_t color); @@ -59,5 +61,8 @@ void palette_iter(const VncPalette *palette, void (*iter)(int idx, uint32_t color, void *opaque), void *opaque); +uint32_t palette_color(const VncPalette *palette, int idx, bool *found); +size_t palette_fill(const VncPalette *palette, + uint32_t colors[VNC_PALETTE_MAX_SIZE]); #endif /* VNC_PALETTE_H */ diff -Nru qemu-kvm-0.14.1+noroms/ui/vnc-tls.c qemu-kvm-0.15.0+noroms/ui/vnc-tls.c --- qemu-kvm-0.14.1+noroms/ui/vnc-tls.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/ui/vnc-tls.c 2011-08-09 07:40:29.000000000 -0500 @@ -89,7 +89,7 @@ int ret; retry: - ret = recv(vs->csock, data, len, 0); + ret = qemu_recv(vs->csock, data, len, 0); if (ret < 0) { if (errno == EINTR) goto retry; diff -Nru qemu-kvm-0.14.1+noroms/usb-bsd.c qemu-kvm-0.15.0+noroms/usb-bsd.c --- qemu-kvm-0.14.1+noroms/usb-bsd.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/usb-bsd.c 2011-08-09 07:40:29.000000000 -0500 @@ -39,7 +39,6 @@ #else #include #endif -#include /* This value has maximum potential at 16. * You should also set hw.usb.debug to gain @@ -126,6 +125,7 @@ * and return appropriate response */ static int usb_host_handle_control(USBDevice *dev, + USBPacket *p, int request, int value, int index, @@ -367,8 +367,10 @@ if (dev_info.udi_speed == 1) { dev->dev.speed = USB_SPEED_LOW - 1; + dev->dev.speedmask = USB_SPEED_MASK_LOW; } else { dev->dev.speed = USB_SPEED_FULL - 1; + dev->dev.speedmask = USB_SPEED_MASK_FULL; } if (strncmp(dev_info.udi_product, "product", 7) != 0) { @@ -464,7 +466,7 @@ printf("usb_host_scan: couldn't get device information for %s - %s\n", devbuf, strerror(errno)); - // XXX: might need to fixup endianess of word values before copying over + /* XXX: might need to fixup endianness of word values before copying over */ vendor_id = dev_info.udi_vendorNo; product_id = dev_info.udi_productNo; diff -Nru qemu-kvm-0.14.1+noroms/usb-linux.c qemu-kvm-0.15.0+noroms/usb-linux.c --- qemu-kvm-0.14.1+noroms/usb-linux.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/usb-linux.c 2011-08-09 07:40:29.000000000 -0500 @@ -37,7 +37,6 @@ #include #include -#include #include #include @@ -54,15 +53,7 @@ void *data; }; -struct usb_ctrlrequest { - uint8_t bRequestType; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; -}; - -typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath, +typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port, int class_id, int vendor_id, int product_id, const char *product_name, int speed); @@ -78,7 +69,8 @@ #define USBPROCBUS_PATH "/proc/bus/usb" #define PRODUCT_NAME_SZ 32 -#define MAX_ENDPOINTS 16 +#define MAX_ENDPOINTS 15 +#define MAX_PORTLEN 16 #define USBDEVBUS_PATH "/dev/bus/usb" #define USBSYSBUS_PATH "/sys/bus/usb" @@ -92,34 +84,29 @@ static int usb_fs_type; /* endpoint association data */ +#define ISO_FRAME_DESC_PER_URB 32 +#define INVALID_EP_TYPE 255 + +/* devio.c limits single requests to 16k */ +#define MAX_USBFS_BUFFER_SIZE 16384 + +typedef struct AsyncURB AsyncURB; + struct endp_data { uint8_t type; uint8_t halted; -}; - -enum { - CTRL_STATE_IDLE = 0, - CTRL_STATE_SETUP, - CTRL_STATE_DATA, - CTRL_STATE_ACK -}; - -/* - * Control transfer state. - * 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[8192]; + uint8_t iso_started; + AsyncURB *iso_urb; + int iso_urb_idx; + int iso_buffer_used; + int max_packet_size; + int inflight; }; struct USBAutoFilter { uint32_t bus_num; uint32_t addr; + char *port; uint32_t vendor_id; uint32_t product_id; }; @@ -128,20 +115,21 @@ USBDevice dev; int fd; - uint8_t descr[1024]; + uint8_t descr[8192]; int descr_len; int configuration; int ninterfaces; int closing; + uint32_t iso_urb_count; Notifier exit; - struct ctrl_struct ctrl; struct endp_data endp_table[MAX_ENDPOINTS]; + QLIST_HEAD(, AsyncURB) aurbs; /* Host side address */ int bus_num; int addr; - int devpath; + char port[MAX_PORTLEN]; struct USBAutoFilter match; QTAILQ_ENTRY(USBHostDevice) next; @@ -155,74 +143,159 @@ static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name); +static struct endp_data *get_endp(USBHostDevice *s, int ep) +{ + return s->endp_table + ep - 1; +} + static int is_isoc(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO; + return get_endp(s, ep)->type == USBDEVFS_URB_TYPE_ISO; +} + +static int is_valid(USBHostDevice *s, int ep) +{ + return get_endp(s, ep)->type != INVALID_EP_TYPE; } static int is_halted(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].halted; + return get_endp(s, ep)->halted; } static void clear_halt(USBHostDevice *s, int ep) { - s->endp_table[ep - 1].halted = 0; + get_endp(s, ep)->halted = 0; } static void set_halt(USBHostDevice *s, int ep) { - s->endp_table[ep - 1].halted = 1; + get_endp(s, ep)->halted = 1; +} + +static int is_iso_started(USBHostDevice *s, int ep) +{ + return get_endp(s, ep)->iso_started; +} + +static void clear_iso_started(USBHostDevice *s, int ep) +{ + get_endp(s, ep)->iso_started = 0; +} + +static void set_iso_started(USBHostDevice *s, int ep) +{ + struct endp_data *e = get_endp(s, ep); + if (!e->iso_started) { + e->iso_started = 1; + e->inflight = 0; + } +} + +static int change_iso_inflight(USBHostDevice *s, int ep, int value) +{ + struct endp_data *e = get_endp(s, ep); + + e->inflight += value; + return e->inflight; +} + +static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) +{ + get_endp(s, ep)->iso_urb = iso_urb; +} + +static AsyncURB *get_iso_urb(USBHostDevice *s, int ep) +{ + return get_endp(s, ep)->iso_urb; +} + +static void set_iso_urb_idx(USBHostDevice *s, int ep, int i) +{ + get_endp(s, ep)->iso_urb_idx = i; +} + +static int get_iso_urb_idx(USBHostDevice *s, int ep) +{ + return get_endp(s, ep)->iso_urb_idx; +} + +static void set_iso_buffer_used(USBHostDevice *s, int ep, int i) +{ + get_endp(s, ep)->iso_buffer_used = i; +} + +static int get_iso_buffer_used(USBHostDevice *s, int ep) +{ + return get_endp(s, ep)->iso_buffer_used; +} + +static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) +{ + int raw = descriptor[4] + (descriptor[5] << 8); + int size, microframes; + + size = raw & 0x7ff; + switch ((raw >> 11) & 3) { + case 1: microframes = 2; break; + case 2: microframes = 3; break; + default: microframes = 1; break; + } + get_endp(s, ep)->max_packet_size = size * microframes; +} + +static int get_max_packet_size(USBHostDevice *s, int ep) +{ + return get_endp(s, ep)->max_packet_size; } /* * Async URB state. - * We always allocate one isoc descriptor even for bulk transfers + * We always allocate iso packet descriptors even for bulk transfers * to simplify allocation and casts. */ -typedef struct AsyncURB +struct AsyncURB { struct usbdevfs_urb urb; - struct usbdevfs_iso_packet_desc isocpd; + struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB]; + USBHostDevice *hdev; + QLIST_ENTRY(AsyncURB) next; + /* For regular async urbs */ USBPacket *packet; - USBHostDevice *hdev; -} AsyncURB; + int more; /* large transfer, more urbs follow */ + + /* For buffered iso handling */ + int iso_frame_idx; /* -1 means in flight */ +}; -static AsyncURB *async_alloc(void) +static AsyncURB *async_alloc(USBHostDevice *s) { - return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB)); + AsyncURB *aurb = qemu_mallocz(sizeof(AsyncURB)); + aurb->hdev = s; + QLIST_INSERT_HEAD(&s->aurbs, aurb, next); + return aurb; } static void async_free(AsyncURB *aurb) { + QLIST_REMOVE(aurb, next); qemu_free(aurb); } -static void async_complete_ctrl(USBHostDevice *s, USBPacket *p) +static void do_disconnect(USBHostDevice *s) { - switch(s->ctrl.state) { - case CTRL_STATE_SETUP: - if (p->len < s->ctrl.len) - s->ctrl.len = p->len; - s->ctrl.state = CTRL_STATE_DATA; - p->len = 8; - break; - - case CTRL_STATE_ACK: - s->ctrl.state = CTRL_STATE_IDLE; - p->len = 0; - break; - - default: - break; - } + printf("husb: device %d.%d disconnected\n", + s->bus_num, s->addr); + usb_host_close(s); + usb_host_auto_check(NULL); } static void async_complete(void *opaque) { USBHostDevice *s = opaque; AsyncURB *aurb; + int urbs = 0; while (1) { USBPacket *p; @@ -230,13 +303,13 @@ int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb); if (r < 0) { if (errno == EAGAIN) { + if (urbs > 2) { + fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs); + } return; } if (errno == ENODEV && !s->closing) { - printf("husb: device %d.%d disconnected\n", - s->bus_num, s->addr); - usb_host_close(s); - usb_host_auto_check(NULL); + do_disconnect(s); return; } @@ -244,18 +317,31 @@ return; } - p = aurb->packet; - DPRINTF("husb: async completed. aurb %p status %d alen %d\n", aurb, aurb->urb.status, aurb->urb.actual_length); + /* If this is a buffered iso urb mark it as complete and don't do + anything else (it is handled further in usb_host_handle_iso_data) */ + if (aurb->iso_frame_idx == -1) { + int inflight; + if (aurb->urb.status == -EPIPE) { + set_halt(s, aurb->urb.endpoint & 0xf); + } + aurb->iso_frame_idx = 0; + urbs++; + inflight = change_iso_inflight(s, aurb->urb.endpoint & 0xf, -1); + if (inflight == 0 && is_iso_started(s, aurb->urb.endpoint & 0xf)) { + fprintf(stderr, "husb: out of buffers for iso stream\n"); + } + continue; + } + + p = aurb->packet; + if (p) { switch (aurb->urb.status) { case 0: - p->len = aurb->urb.actual_length; - if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { - async_complete_ctrl(s, p); - } + p->len += aurb->urb.actual_length; break; case -EPIPE: @@ -268,31 +354,42 @@ break; } - usb_packet_complete(p); + if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { + usb_generic_async_ctrl_complete(&s->dev, p); + } else if (!aurb->more) { + usb_packet_complete(&s->dev, p); + } } async_free(aurb); } } -static void async_cancel(USBPacket *unused, void *opaque) +static void usb_host_async_cancel(USBDevice *dev, USBPacket *p) { - AsyncURB *aurb = opaque; - USBHostDevice *s = aurb->hdev; + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + AsyncURB *aurb; + + QLIST_FOREACH(aurb, &s->aurbs, next) { + if (p != aurb->packet) { + continue; + } - DPRINTF("husb: async cancel. aurb %p\n", aurb); + DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb); - /* Mark it as dead (see async_complete above) */ - aurb->packet = NULL; + /* 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); + int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb); + if (r < 0) { + DPRINTF("husb: async. discard urb failed errno %d\n", errno); + } } } static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) { + const char *op = NULL; int dev_descr_len, config_descr_len; int interface, nb_interfaces; int ret, i; @@ -305,7 +402,8 @@ i = 0; dev_descr_len = dev->descr[0]; if (dev_descr_len > dev->descr_len) { - goto fail; + fprintf(stderr, "husb: update iface failed. descr too short\n"); + return 0; } i += dev_descr_len; @@ -333,7 +431,7 @@ if (i >= dev->descr_len) { fprintf(stderr, "husb: update iface failed. no matching configuration\n"); - goto fail; + return 0; } nb_interfaces = dev->descr[i + 4]; @@ -344,9 +442,10 @@ for (interface = 0; interface < nb_interfaces; interface++) { ctrl.ioctl_code = USBDEVFS_DISCONNECT; ctrl.ifno = interface; + ctrl.data = 0; + op = "USBDEVFS_DISCONNECT"; ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl); if (ret < 0 && errno != ENODATA) { - perror("USBDEVFS_DISCONNECT"); goto fail; } } @@ -355,6 +454,7 @@ /* XXX: only grab if all interfaces are free */ for (interface = 0; interface < nb_interfaces; interface++) { + op = "USBDEVFS_CLAIMINTERFACE"; ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface); if (ret < 0) { if (errno == EBUSY) { @@ -362,8 +462,7 @@ } else { perror("husb: failed to claim interface"); } - fail: - return 0; + goto fail; } } @@ -373,6 +472,13 @@ dev->ninterfaces = nb_interfaces; dev->configuration = configuration; return 1; + +fail: + if (errno == ENODEV) { + do_disconnect(dev); + } + perror(op); + return 0; } static int usb_host_release_interfaces(USBHostDevice *s) @@ -414,69 +520,262 @@ static int usb_linux_update_endp_table(USBHostDevice *s); -static int usb_host_handle_data(USBHostDevice *s, USBPacket *p) +/* iso data is special, we need to keep enough urbs in flight to make sure + that the controller never runs out of them, otherwise the device will + likely suffer a buffer underrun / overrun. */ +static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in) { - struct usbdevfs_urb *urb; AsyncURB *aurb; - int ret; + int i, j, len = get_max_packet_size(s, ep); - aurb = async_alloc(); - aurb->hdev = s; - aurb->packet = p; + aurb = qemu_mallocz(s->iso_urb_count * sizeof(*aurb)); + for (i = 0; i < s->iso_urb_count; i++) { + aurb[i].urb.endpoint = ep; + aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len; + aurb[i].urb.buffer = qemu_malloc(aurb[i].urb.buffer_length); + aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO; + aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP; + aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB; + for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++) + aurb[i].urb.iso_frame_desc[j].length = len; + if (in) { + aurb[i].urb.endpoint |= 0x80; + /* Mark as fully consumed (idle) */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB; + } + } + set_iso_urb(s, ep, aurb); - urb = &aurb->urb; + return aurb; +} + +static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) +{ + AsyncURB *aurb; + int i, ret, killed = 0, free = 1; + + aurb = get_iso_urb(s, ep); + if (!aurb) { + return; + } + + for (i = 0; i < s->iso_urb_count; i++) { + /* in flight? */ + if (aurb[i].iso_frame_idx == -1) { + ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]); + if (ret < 0) { + printf("husb: discard isoc in urb failed errno %d\n", errno); + free = 0; + continue; + } + killed++; + } + } + + /* Make sure any urbs we've killed are reaped before we free them */ + if (killed) { + async_complete(s); + } + + for (i = 0; i < s->iso_urb_count; i++) { + qemu_free(aurb[i].urb.buffer); + } + + if (free) + qemu_free(aurb); + else + printf("husb: leaking iso urbs because of discard failure\n"); + set_iso_urb(s, ep, NULL); + set_iso_urb_idx(s, ep, 0); + clear_iso_started(s, ep); +} + +static int urb_status_to_usb_ret(int status) +{ + switch (status) { + case -EPIPE: + return USB_RET_STALL; + default: + return USB_RET_NAK; + } +} + +static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) +{ + AsyncURB *aurb; + int i, j, ret, max_packet_size, offset, len = 0; + + max_packet_size = get_max_packet_size(s, p->devep); + if (max_packet_size == 0) + return USB_RET_NAK; + + aurb = get_iso_urb(s, p->devep); + if (!aurb) { + aurb = usb_host_alloc_iso(s, p->devep, in); + } + + i = get_iso_urb_idx(s, p->devep); + j = aurb[i].iso_frame_idx; + if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { + if (in) { + /* Check urb status */ + if (aurb[i].urb.status) { + len = urb_status_to_usb_ret(aurb[i].urb.status); + /* Move to the next urb */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; + /* Check frame status */ + } else if (aurb[i].urb.iso_frame_desc[j].status) { + len = urb_status_to_usb_ret( + aurb[i].urb.iso_frame_desc[j].status); + /* Check the frame fits */ + } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { + printf("husb: received iso data is larger then packet\n"); + len = USB_RET_NAK; + /* All good copy data over */ + } else { + len = aurb[i].urb.iso_frame_desc[j].actual_length; + memcpy(p->data, + aurb[i].urb.buffer + + j * aurb[i].urb.iso_frame_desc[0].length, + len); + } + } else { + len = p->len; + offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); + + /* Check the frame fits */ + if (len > max_packet_size) { + printf("husb: send iso data is larger then max packet size\n"); + return USB_RET_NAK; + } + + /* All good copy data over */ + memcpy(aurb[i].urb.buffer + offset, p->data, len); + aurb[i].urb.iso_frame_desc[j].length = len; + offset += len; + set_iso_buffer_used(s, p->devep, offset); + + /* Start the stream once we have buffered enough data */ + if (!is_iso_started(s, p->devep) && i == 1 && j == 8) { + set_iso_started(s, p->devep); + } + } + aurb[i].iso_frame_idx++; + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + i = (i + 1) % s->iso_urb_count; + set_iso_urb_idx(s, p->devep, i); + } + } else { + if (in) { + set_iso_started(s, p->devep); + } else { + DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); + } + } + + if (is_iso_started(s, p->devep)) { + /* (Re)-submit all fully consumed / filled urbs */ + for (i = 0; i < s->iso_urb_count; i++) { + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); + if (ret < 0) { + printf("husb error submitting iso urb %d: %d\n", i, errno); + if (!in || len == 0) { + switch(errno) { + case ETIMEDOUT: + len = USB_RET_NAK; + break; + case EPIPE: + default: + len = USB_RET_STALL; + } + } + break; + } + aurb[i].iso_frame_idx = -1; + change_iso_inflight(s, p->devep, +1); + } + } + } + + return len; +} + +static int usb_host_handle_data(USBDevice *dev, USBPacket *p) +{ + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); + struct usbdevfs_urb *urb; + AsyncURB *aurb; + int ret, rem; + uint8_t *pbuf; + uint8_t ep; + + if (!is_valid(s, p->devep)) { + return USB_RET_NAK; + } if (p->pid == USB_TOKEN_IN) { - urb->endpoint = p->devep | 0x80; + ep = p->devep | 0x80; } else { - urb->endpoint = p->devep; + ep = p->devep; } if (is_halted(s, p->devep)) { - ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint); + ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &ep); if (ret < 0) { DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n", - urb->endpoint, errno); + ep, errno); return USB_RET_NAK; } clear_halt(s, p->devep); } - urb->buffer = p->data; - urb->buffer_length = p->len; - if (is_isoc(s, p->devep)) { - /* Setup ISOC transfer */ - urb->type = USBDEVFS_URB_TYPE_ISO; - urb->flags = USBDEVFS_URB_ISO_ASAP; - urb->number_of_packets = 1; - urb->iso_frame_desc[0].length = p->len; - } else { - /* Setup bulk transfer */ - urb->type = USBDEVFS_URB_TYPE_BULK; + return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } - urb->usercontext = s; + rem = p->len; + pbuf = p->data; + p->len = 0; + while (rem) { + aurb = async_alloc(s); + aurb->packet = p; + + urb = &aurb->urb; + urb->endpoint = ep; + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->usercontext = s; + urb->buffer = pbuf; + + if (rem > MAX_USBFS_BUFFER_SIZE) { + urb->buffer_length = MAX_USBFS_BUFFER_SIZE; + aurb->more = 1; + } else { + urb->buffer_length = rem; + aurb->more = 0; + } + pbuf += urb->buffer_length; + rem -= urb->buffer_length; - ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); + 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, more %d, packet %p, aurb %p\n", + urb->endpoint, urb->buffer_length, aurb->more, p, aurb); - if (ret < 0) { - DPRINTF("husb: submit failed. errno %d\n", errno); - async_free(aurb); + if (ret < 0) { + DPRINTF("husb: submit failed. errno %d\n", errno); + async_free(aurb); - switch(errno) { - case ETIMEDOUT: - return USB_RET_NAK; - case EPIPE: - default: - return USB_RET_STALL; + switch(errno) { + case ETIMEDOUT: + return USB_RET_NAK; + case EPIPE: + default: + return USB_RET_STALL; + } } } - usb_defer_packet(p, async_cancel, aurb); return USB_RET_ASYNC; } @@ -514,7 +813,13 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) { struct usbdevfs_setinterface si; - int ret; + int i, ret; + + for (i = 1; i <= MAX_ENDPOINTS; i++) { + if (is_isoc(s, i)) { + usb_host_stop_n_free_iso(s, i); + } + } si.interface = iface; si.altsetting = alt; @@ -530,56 +835,49 @@ return 0; } -static int usb_host_handle_control(USBHostDevice *s, USBPacket *p) +static int usb_host_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { + USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); struct usbdevfs_urb *urb; AsyncURB *aurb; - int ret, value, index; - int buffer_len; + int ret; /* * 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); + /* Note request is (bRequestType << 8) | bRequest */ 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); + request >> 8, request & 0xff, value, index, length); - if (s->ctrl.req.bRequestType == 0) { - switch (s->ctrl.req.bRequest) { - case USB_REQ_SET_ADDRESS: - return usb_host_set_address(s, value); + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + return usb_host_set_address(s, value); - case USB_REQ_SET_CONFIGURATION: - return usb_host_set_config(s, value & 0xff); - } - } + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + return usb_host_set_config(s, value & 0xff); - if (s->ctrl.req.bRequestType == 1 && - s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE) { + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: return usb_host_set_interface(s, index, value); } /* The rest are asynchronous */ - buffer_len = 8 + s->ctrl.len; - if (buffer_len > sizeof(s->ctrl.buffer)) { - fprintf(stderr, "husb: ctrl buffer too small (%u > %zu)\n", - buffer_len, sizeof(s->ctrl.buffer)); + if (length > sizeof(dev->data_buf)) { + fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", + length, sizeof(dev->data_buf)); return USB_RET_STALL; } - aurb = async_alloc(); - aurb->hdev = s; + aurb = async_alloc(s); aurb->packet = p; /* * Setup ctrl transfer. * - * s->ctrl is layed out such that data buffer immediately follows + * s->ctrl is laid out such that data buffer immediately follows * 'req' struct which is exactly what usbdevfs expects. */ urb = &aurb->urb; @@ -587,8 +885,8 @@ urb->type = USBDEVFS_URB_TYPE_CONTROL; urb->endpoint = p->devep; - urb->buffer = &s->ctrl.req; - urb->buffer_length = buffer_len; + urb->buffer = &dev->setup_buf; + urb->buffer_length = length + 8; urb->usercontext = s; @@ -609,174 +907,9 @@ } } - usb_defer_packet(p, async_cancel, aurb); return USB_RET_ASYNC; } -static int do_token_setup(USBDevice *dev, USBPacket *p) -{ - USBHostDevice *s = (USBHostDevice *) dev; - int ret = 0; - - 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; - s->ctrl.state = CTRL_STATE_SETUP; - - if (s->ctrl.req.bRequestType & USB_DIR_IN) { - ret = usb_host_handle_control(s, p); - if (ret < 0) { - return ret; - } - - if (ret < s->ctrl.len) { - s->ctrl.len = ret; - } - s->ctrl.state = CTRL_STATE_DATA; - } else { - if (s->ctrl.len == 0) { - s->ctrl.state = CTRL_STATE_ACK; - } else { - s->ctrl.state = CTRL_STATE_DATA; - } - } - - return ret; -} - -static int do_token_in(USBDevice *dev, USBPacket *p) -{ - USBHostDevice *s = (USBHostDevice *) dev; - int ret = 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) { - return USB_RET_ASYNC; - } - s->ctrl.state = CTRL_STATE_IDLE; - return ret > 0 ? 0 : ret; - } - - return 0; - - case CTRL_STATE_DATA: - if (s->ctrl.req.bRequestType & USB_DIR_IN) { - int len = s->ctrl.len - s->ctrl.offset; - 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) { - s->ctrl.state = CTRL_STATE_ACK; - } - return len; - } - - s->ctrl.state = CTRL_STATE_IDLE; - return USB_RET_STALL; - - default: - return USB_RET_STALL; - } -} - -static int do_token_out(USBDevice *dev, USBPacket *p) -{ - USBHostDevice *s = (USBHostDevice *) dev; - - 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) { - s->ctrl.state = CTRL_STATE_IDLE; - /* transfer OK */ - } else { - /* ignore additional output */ - } - return 0; - - case CTRL_STATE_DATA: - if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) { - int len = s->ctrl.len - s->ctrl.offset; - 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) { - s->ctrl.state = CTRL_STATE_ACK; - } - return len; - } - - s->ctrl.state = CTRL_STATE_IDLE; - return USB_RET_STALL; - - default: - return USB_RET_STALL; - } -} - -/* - * Packet handler. - * Called by the HC (host controller). - * - * Returns length of the transaction or one of the USB_RET_XXX codes. - */ -static int usb_host_handle_packet(USBDevice *s, USBPacket *p) -{ - switch(p->pid) { - case USB_MSG_ATTACH: - s->state = USB_STATE_ATTACHED; - return 0; - - case USB_MSG_DETACH: - s->state = USB_STATE_NOTATTACHED; - return 0; - - case USB_MSG_RESET: - s->remote_wakeup = 0; - s->addr = 0; - s->state = USB_STATE_DEFAULT; - s->info->handle_reset(s); - return 0; - } - - /* Rest of the PIDs must match our address */ - if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) { - return USB_RET_NODEV; - } - - switch (p->pid) { - case USB_TOKEN_SETUP: - return do_token_setup(s, p); - - case USB_TOKEN_IN: - return do_token_in(s, p); - - case USB_TOKEN_OUT: - return do_token_out(s, p); - - default: - return USB_RET_STALL; - } -} - static int usb_linux_get_configuration(USBHostDevice *s) { uint8_t configuration; @@ -787,7 +920,7 @@ char device_name[32], line[1024]; int configuration; - sprintf(device_name, "%d-%d", s->bus_num, s->devpath); + sprintf(device_name, "%d-%s", s->bus_num, s->port); if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue", device_name)) { @@ -822,13 +955,56 @@ return configuration; } +static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, + uint8_t configuration, uint8_t interface) +{ + uint8_t alt_setting; + struct usb_ctrltransfer ct; + int ret; + + if (usb_fs_type == USB_FS_SYS) { + char device_name[64], line[1024]; + int alt_setting; + + sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port, + (int)configuration, (int)interface); + + if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting", + device_name)) { + goto usbdevfs; + } + if (sscanf(line, "%d", &alt_setting) != 1) { + goto usbdevfs; + } + return alt_setting; + } + +usbdevfs: + ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE; + ct.bRequest = USB_REQ_GET_INTERFACE; + ct.wValue = 0; + ct.wIndex = interface; + ct.wLength = 1; + ct.data = &alt_setting; + ct.timeout = 50; + ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); + if (ret < 0) { + /* Assume alt 0 on error */ + return 0; + } + + return alt_setting; +} + /* 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; + int interface, length, i; + + for (i = 0; i < MAX_ENDPOINTS; i++) + s->endp_table[i].type = INVALID_EP_TYPE; i = usb_linux_get_configuration(s); if (i < 0) @@ -857,19 +1033,7 @@ } interface = descriptors[i + 2]; - - ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE; - ct.bRequest = USB_REQ_GET_INTERFACE; - ct.wValue = 0; - ct.wIndex = interface; - ct.wLength = 1; - ct.data = &alt_interface; - ct.timeout = 50; - - ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); - if (ret < 0) { - alt_interface = interface; - } + alt_interface = usb_linux_get_alt_setting(s, configuration, interface); /* the current interface descriptor is the active interface * and has endpoints */ @@ -892,12 +1056,18 @@ } devep = descriptors[i + 2]; + if ((devep & 0x0f) == 0) { + fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n"); + return 1; + } + switch (descriptors[i + 3] & 0x3) { case 0x00: type = USBDEVFS_URB_TYPE_CONTROL; break; case 0x01: type = USBDEVFS_URB_TYPE_ISO; + set_max_packet_size(s, (devep & 0xf), descriptors + i); break; case 0x02: type = USBDEVFS_URB_TYPE_BULK; @@ -918,11 +1088,46 @@ return 0; } +/* + * Check if we can safely redirect a usb2 device to a usb1 virtual controller, + * this function assumes this is safe, if: + * 1) There are no isoc endpoints + * 2) There are no interrupt endpoints with a max_packet_size > 64 + * Note bulk endpoints with a max_packet_size > 64 in theory also are not + * usb1 compatible, but in practice this seems to work fine. + */ +static int usb_linux_full_speed_compat(USBHostDevice *dev) +{ + int i, packet_size; + + /* + * usb_linux_update_endp_table only registers info about ep in the current + * interface altsettings, so we need to parse the descriptors again. + */ + for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) { + if (dev->descr[i + 1] == USB_DT_ENDPOINT) { + switch (dev->descr[i + 3] & 0x3) { + case 0x00: /* CONTROL */ + break; + case 0x01: /* ISO */ + return 0; + case 0x02: /* BULK */ + break; + case 0x03: /* INTERRUPT */ + packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8); + if (packet_size > 64) + return 0; + break; + } + } + } + return 1; +} + static int usb_host_open(USBHostDevice *dev, int bus_num, - int addr, int devpath, const char *prod_name) + int addr, char *port, const char *prod_name, int speed) { int fd = -1, ret; - struct usbdevfs_connectinfo ci; char buf[1024]; if (dev->fd != -1) { @@ -945,7 +1150,7 @@ dev->bus_num = bus_num; dev->addr = addr; - dev->devpath = devpath; + strcpy(dev->port, port); dev->fd = fd; /* read the device description */ @@ -977,25 +1182,34 @@ goto fail; } - ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); - if (ret < 0) { - perror("usb_host_device_open: USBDEVFS_CONNECTINFO"); - goto fail; - } - - printf("husb: grabbed usb device %d.%d\n", bus_num, addr); - ret = usb_linux_update_endp_table(dev); if (ret) { goto fail; } - if (ci.slow) { - dev->dev.speed = USB_SPEED_LOW; - } else { - dev->dev.speed = USB_SPEED_HIGH; + if (speed == -1) { + struct usbdevfs_connectinfo ci; + + ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); + if (ret < 0) { + perror("usb_host_device_open: USBDEVFS_CONNECTINFO"); + goto fail; + } + + if (ci.slow) { + speed = USB_SPEED_LOW; + } else { + speed = USB_SPEED_HIGH; + } + } + dev->dev.speed = speed; + dev->dev.speedmask = (1 << speed); + if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) { + dev->dev.speedmask |= USB_SPEED_MASK_FULL; } + printf("husb: grabbed usb device %d.%d\n", bus_num, addr); + if (!prod_name || prod_name[0] == '\0') { snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), "host:%d.%d", bus_num, addr); @@ -1004,28 +1218,39 @@ prod_name); } + ret = usb_device_attach(&dev->dev); + if (ret) { + goto fail; + } + /* USB devio uses 'write' flag to check for async completions */ qemu_set_fd_handler(dev->fd, NULL, async_complete, dev); - usb_device_attach(&dev->dev); return 0; fail: - dev->fd = -1; - if (fd != -1) { - close(fd); + if (dev->fd != -1) { + close(dev->fd); + dev->fd = -1; } return -1; } static int usb_host_close(USBHostDevice *dev) { - if (dev->fd == -1) { + int i; + + if (dev->fd == -1 || !dev->dev.attached) { return -1; } qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); dev->closing = 1; + for (i = 1; i <= MAX_ENDPOINTS; i++) { + if (is_isoc(dev, i)) { + usb_host_stop_n_free_iso(dev, i); + } + } async_complete(dev); dev->closing = 0; usb_device_detach(&dev->dev); @@ -1035,7 +1260,7 @@ return 0; } -static void usb_host_exit_notifier(struct Notifier* n) +static void usb_host_exit_notifier(struct Notifier *n, void *data) { USBHostDevice *s = container_of(n, USBHostDevice, exit); @@ -1062,7 +1287,10 @@ .qdev.name = "usb-host", .qdev.size = sizeof(USBHostDevice), .init = usb_host_initfn, - .handle_packet = usb_host_handle_packet, + .handle_packet = usb_generic_handle_packet, + .cancel_packet = usb_host_async_cancel, + .handle_data = usb_host_handle_data, + .handle_control = usb_host_handle_control, .handle_reset = usb_host_handle_reset, .handle_destroy = usb_host_handle_destroy, .usbdevice_name = "host", @@ -1070,8 +1298,10 @@ .qdev.props = (Property[]) { DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0), DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0), + DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0), DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0), + DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), DEFINE_PROP_END_OF_LIST(), }, }; @@ -1197,7 +1427,8 @@ } device_count = 0; - bus_num = addr = speed = class_id = product_id = vendor_id = 0; + bus_num = addr = class_id = product_id = vendor_id = 0; + speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */ for(;;) { if (fgets(line, sizeof(line), f) == NULL) { break; @@ -1225,7 +1456,9 @@ if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) { goto fail; } - if (!strcmp(buf, "480")) { + if (!strcmp(buf, "5000")) { + speed = USB_SPEED_SUPER; + } else if (!strcmp(buf, "480")) { speed = USB_SPEED_HIGH; } else if (!strcmp(buf, "1.5")) { speed = USB_SPEED_LOW; @@ -1310,8 +1543,9 @@ { DIR *dir = NULL; char line[1024]; - int bus_num, addr, devpath, speed, class_id, product_id, vendor_id; + int bus_num, addr, speed, class_id, product_id, vendor_id; int ret = 0; + char port[MAX_PORTLEN]; char product_name[512]; struct dirent *de; @@ -1323,12 +1557,8 @@ 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)) { - tmpstr += 3; - } - if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) { - goto the_end; + if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) { + continue; } if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) { @@ -1372,7 +1602,9 @@ if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) { goto the_end; } - if (!strcmp(line, "480\n")) { + if (!strcmp(line, "5000\n")) { + speed = USB_SPEED_SUPER; + } else if (!strcmp(line, "480\n")) { speed = USB_SPEED_HIGH; } else if (!strcmp(line, "1.5\n")) { speed = USB_SPEED_LOW; @@ -1380,7 +1612,7 @@ speed = USB_SPEED_FULL; } - ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id, + ret = func(opaque, bus_num, addr, port, class_id, vendor_id, product_id, product_name, speed); if (ret) { goto the_end; @@ -1471,7 +1703,7 @@ static QEMUTimer *usb_auto_timer; -static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath, +static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port, int class_id, int vendor_id, int product_id, const char *product_name, int speed) { @@ -1491,6 +1723,9 @@ if (f->addr > 0 && f->addr != addr) { continue; } + if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) { + continue; + } if (f->vendor_id > 0 && f->vendor_id != vendor_id) { continue; @@ -1507,7 +1742,8 @@ } DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); - usb_host_open(s, bus_num, addr, devpath, product_name); + usb_host_open(s, bus_num, addr, port, product_name, speed); + break; } return 0; @@ -1535,12 +1771,12 @@ } if (!usb_auto_timer) { - usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_check, NULL); + usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL); if (!usb_auto_timer) { return; } } - qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000); + qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); } /* @@ -1629,8 +1865,8 @@ return p->class_name; } -static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id, - int vendor_id, int product_id, +static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port, + int class_id, int vendor_id, int product_id, const char *product_name, int speed) { @@ -1646,13 +1882,16 @@ case USB_SPEED_HIGH: speed_str = "480"; break; + case USB_SPEED_SUPER: + speed_str = "5000"; + break; default: speed_str = "?"; break; } - monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n", - bus_num, addr, speed_str); + monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n", + bus_num, addr, port, speed_str); class_str = usb_class_str(class_id); if (class_str) { monitor_printf(mon, " %s:", class_str); @@ -1667,14 +1906,14 @@ } static int usb_host_info_device(void *opaque, int bus_num, int addr, - int devpath, int class_id, + char *path, int class_id, int vendor_id, int product_id, const char *product_name, int speed) { Monitor *mon = opaque; - usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id, + usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id, product_name, speed); return 0; } @@ -1716,7 +1955,7 @@ dec2str(f->addr, addr, sizeof(addr)); hex2str(f->vendor_id, vid, sizeof(vid)); hex2str(f->product_id, pid, sizeof(pid)); - monitor_printf(mon, " Device %s.%s ID %s:%s\n", - bus, addr, vid, pid); + monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n", + bus, addr, f->port ? f->port : "*", vid, pid); } } diff -Nru qemu-kvm-0.14.1+noroms/usb-redir.c qemu-kvm-0.15.0+noroms/usb-redir.c --- qemu-kvm-0.14.1+noroms/usb-redir.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/usb-redir.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,1218 @@ +/* + * USB redirector usb-guest + * + * Copyright (c) 2011 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede + * + * 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 "qemu-timer.h" +#include "monitor.h" +#include "sysemu.h" + +#include +#include +#include +#include + +#include "hw/usb.h" + +#define MAX_ENDPOINTS 32 +#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) +#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) + +typedef struct AsyncURB AsyncURB; +typedef struct USBRedirDevice USBRedirDevice; + +/* Struct to hold buffered packets (iso or int input packets) */ +struct buf_packet { + uint8_t *data; + int len; + int status; + QTAILQ_ENTRY(buf_packet)next; +}; + +struct endp_data { + uint8_t type; + uint8_t interval; + uint8_t interface; /* bInterfaceNumber this ep belongs to */ + uint8_t iso_started; + uint8_t iso_error; /* For reporting iso errors to the HC */ + uint8_t interrupt_started; + uint8_t interrupt_error; + QTAILQ_HEAD(, buf_packet) bufpq; +}; + +struct USBRedirDevice { + USBDevice dev; + /* Properties */ + CharDriverState *cs; + uint8_t debug; + /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ + const uint8_t *read_buf; + int read_buf_size; + /* For async handling of open/close */ + QEMUBH *open_close_bh; + /* To delay the usb attach in case of quick chardev close + open */ + QEMUTimer *attach_timer; + int64_t next_attach_time; + struct usbredirparser *parser; + struct endp_data endpoint[MAX_ENDPOINTS]; + uint32_t packet_id; + QTAILQ_HEAD(, AsyncURB) asyncq; +}; + +struct AsyncURB { + USBRedirDevice *dev; + USBPacket *packet; + uint32_t packet_id; + int get; + union { + struct usb_redir_control_packet_header control_packet; + struct usb_redir_bulk_packet_header bulk_packet; + struct usb_redir_interrupt_packet_header interrupt_packet; + }; + QTAILQ_ENTRY(AsyncURB)next; +}; + +static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect); +static void usbredir_device_disconnect(void *priv); +static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info); +static void usbredir_ep_info(void *priv, + struct usb_redir_ep_info_header *ep_info); +static void usbredir_configuration_status(void *priv, uint32_t id, + struct usb_redir_configuration_status_header *configuration_status); +static void usbredir_alt_setting_status(void *priv, uint32_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status); +static void usbredir_iso_stream_status(void *priv, uint32_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status); +static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status); +static void usbredir_bulk_streams_status(void *priv, uint32_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status); +static void usbredir_control_packet(void *priv, uint32_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len); +static void usbredir_bulk_packet(void *priv, uint32_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len); +static void usbredir_iso_packet(void *priv, uint32_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len); +static void usbredir_interrupt_packet(void *priv, uint32_t id, + struct usb_redir_interrupt_packet_header *interrupt_header, + uint8_t *data, int data_len); + +static int usbredir_handle_status(USBRedirDevice *dev, + int status, int actual_len); + +#define VERSION "qemu usb-redir guest " QEMU_VERSION + +/* + * Logging stuff + */ + +#define ERROR(...) \ + do { \ + if (dev->debug >= usbredirparser_error) { \ + error_report("usb-redir error: " __VA_ARGS__); \ + } \ + } while (0) +#define WARNING(...) \ + do { \ + if (dev->debug >= usbredirparser_warning) { \ + error_report("usb-redir warning: " __VA_ARGS__); \ + } \ + } while (0) +#define INFO(...) \ + do { \ + if (dev->debug >= usbredirparser_info) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) +#define DPRINTF(...) \ + do { \ + if (dev->debug >= usbredirparser_debug) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) +#define DPRINTF2(...) \ + do { \ + if (dev->debug >= usbredirparser_debug_data) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) + +static void usbredir_log(void *priv, int level, const char *msg) +{ + USBRedirDevice *dev = priv; + + if (dev->debug < level) { + return; + } + + error_report("%s\n", msg); +} + +static void usbredir_log_data(USBRedirDevice *dev, const char *desc, + const uint8_t *data, int len) +{ + int i, j, n; + + if (dev->debug < usbredirparser_debug_data) { + return; + } + + for (i = 0; i < len; i += j) { + char buf[128]; + + n = sprintf(buf, "%s", desc); + for (j = 0; j < 8 && i + j < len; j++) { + n += sprintf(buf + n, " %02X", data[i + j]); + } + error_report("%s\n", buf); + } +} + +/* + * usbredirparser io functions + */ + +static int usbredir_read(void *priv, uint8_t *data, int count) +{ + USBRedirDevice *dev = priv; + + if (dev->read_buf_size < count) { + count = dev->read_buf_size; + } + + memcpy(data, dev->read_buf, count); + + dev->read_buf_size -= count; + if (dev->read_buf_size) { + dev->read_buf += count; + } else { + dev->read_buf = NULL; + } + + return count; +} + +static int usbredir_write(void *priv, uint8_t *data, int count) +{ + USBRedirDevice *dev = priv; + + return qemu_chr_write(dev->cs, data, count); +} + +/* + * Async and buffered packets helpers + */ + +static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) +{ + AsyncURB *aurb = (AsyncURB *) qemu_mallocz(sizeof(AsyncURB)); + aurb->dev = dev; + aurb->packet = p; + aurb->packet_id = dev->packet_id; + QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); + dev->packet_id++; + + return aurb; +} + +static void async_free(USBRedirDevice *dev, AsyncURB *aurb) +{ + QTAILQ_REMOVE(&dev->asyncq, aurb, next); + qemu_free(aurb); +} + +static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id) +{ + AsyncURB *aurb; + + QTAILQ_FOREACH(aurb, &dev->asyncq, next) { + if (aurb->packet_id == packet_id) { + return aurb; + } + } + ERROR("could not find async urb for packet_id %u\n", packet_id); + return NULL; +} + +static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + AsyncURB *aurb; + + QTAILQ_FOREACH(aurb, &dev->asyncq, next) { + if (p != aurb->packet) { + continue; + } + + DPRINTF("async cancel id %u\n", aurb->packet_id); + usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id); + usbredirparser_do_write(dev->parser); + + /* Mark it as dead */ + aurb->packet = NULL; + break; + } +} + +static struct buf_packet *bufp_alloc(USBRedirDevice *dev, + uint8_t *data, int len, int status, uint8_t ep) +{ + struct buf_packet *bufp = qemu_malloc(sizeof(struct buf_packet)); + bufp->data = data; + bufp->len = len; + bufp->status = status; + QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); + return bufp; +} + +static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, + uint8_t ep) +{ + QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); + free(bufp->data); + qemu_free(bufp); +} + +static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep) +{ + struct buf_packet *buf, *buf_next; + + QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) { + bufp_free(dev, buf, ep); + } +} + +/* + * USBDevice callbacks + */ + +static void usbredir_handle_reset(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + DPRINTF("reset device\n"); + usbredirparser_send_reset(dev->parser); + usbredirparser_do_write(dev->parser); +} + +static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + uint8_t ep) +{ + int status, len; + + if (!dev->endpoint[EP2I(ep)].iso_started && + !dev->endpoint[EP2I(ep)].iso_error) { + struct usb_redir_start_iso_stream_header start_iso = { + .endpoint = ep, + /* TODO maybe do something with these depending on ep interval? */ + .pkts_per_urb = 32, + .no_urbs = 3, + }; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso); + usbredirparser_do_write(dev->parser); + DPRINTF("iso stream started ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 1; + } + + if (ep & USB_DIR_IN) { + struct buf_packet *isop; + + isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); + if (isop == NULL) { + DPRINTF2("iso-token-in ep %02X, no isop\n", ep); + /* Check iso_error for stream errors, otherwise its an underrun */ + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; + return usbredir_handle_status(dev, status, 0); + } + DPRINTF2("iso-token-in ep %02X status %d len %d\n", ep, isop->status, + isop->len); + + status = isop->status; + if (status != usb_redir_success) { + bufp_free(dev, isop, ep); + return usbredir_handle_status(dev, status, 0); + } + + len = isop->len; + if (len > p->len) { + ERROR("received iso data is larger then packet ep %02X\n", ep); + bufp_free(dev, isop, ep); + return USB_RET_NAK; + } + memcpy(p->data, isop->data, len); + bufp_free(dev, isop, ep); + return len; + } else { + /* If the stream was not started because of a pending error don't + send the packet to the usb-host */ + if (dev->endpoint[EP2I(ep)].iso_started) { + struct usb_redir_iso_packet_header iso_packet = { + .endpoint = ep, + .length = p->len + }; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, + p->data, p->len); + usbredirparser_do_write(dev->parser); + } + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; + DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status, + p->len); + return usbredir_handle_status(dev, status, p->len); + } +} + +static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) +{ + struct usb_redir_stop_iso_stream_header stop_iso_stream = { + .endpoint = ep + }; + if (dev->endpoint[EP2I(ep)].iso_started) { + usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream); + DPRINTF("iso stream stopped ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 0; + } + usbredir_free_bufpq(dev, ep); +} + +static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, + uint8_t ep) +{ + AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_bulk_packet_header bulk_packet; + + DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id); + + bulk_packet.endpoint = ep; + bulk_packet.length = p->len; + bulk_packet.stream_id = 0; + aurb->bulk_packet = bulk_packet; + + if (ep & USB_DIR_IN) { + usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + &bulk_packet, NULL, 0); + } else { + usbredir_log_data(dev, "bulk data out:", p->data, p->len); + usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + &bulk_packet, p->data, p->len); + } + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + USBPacket *p, uint8_t ep) +{ + if (ep & USB_DIR_IN) { + /* Input interrupt endpoint, buffered packet input */ + struct buf_packet *intp; + int status, len; + + if (!dev->endpoint[EP2I(ep)].interrupt_started && + !dev->endpoint[EP2I(ep)].interrupt_error) { + struct usb_redir_start_interrupt_receiving_header start_int = { + .endpoint = ep, + }; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_start_interrupt_receiving(dev->parser, 0, + &start_int); + usbredirparser_do_write(dev->parser); + DPRINTF("interrupt recv started ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 1; + } + + intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); + if (intp == NULL) { + DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep); + /* Check interrupt_error for stream errors */ + status = dev->endpoint[EP2I(ep)].interrupt_error; + dev->endpoint[EP2I(ep)].interrupt_error = 0; + return usbredir_handle_status(dev, status, 0); + } + DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, + intp->status, intp->len); + + status = intp->status; + if (status != usb_redir_success) { + bufp_free(dev, intp, ep); + return usbredir_handle_status(dev, status, 0); + } + + len = intp->len; + if (len > p->len) { + ERROR("received int data is larger then packet ep %02X\n", ep); + bufp_free(dev, intp, ep); + return USB_RET_NAK; + } + memcpy(p->data, intp->data, len); + bufp_free(dev, intp, ep); + return len; + } else { + /* Output interrupt endpoint, normal async operation */ + AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_interrupt_packet_header interrupt_packet; + + DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len, + aurb->packet_id); + + interrupt_packet.endpoint = ep; + interrupt_packet.length = p->len; + aurb->interrupt_packet = interrupt_packet; + + usbredir_log_data(dev, "interrupt data out:", p->data, p->len); + usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, + &interrupt_packet, p->data, p->len); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; + } +} + +static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev, + uint8_t ep) +{ + struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = { + .endpoint = ep + }; + if (dev->endpoint[EP2I(ep)].interrupt_started) { + usbredirparser_send_stop_interrupt_receiving(dev->parser, 0, + &stop_interrupt_recv); + DPRINTF("interrupt recv stopped ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 0; + } + usbredir_free_bufpq(dev, ep); +} + +static int usbredir_handle_data(USBDevice *udev, USBPacket *p) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + uint8_t ep; + + ep = p->devep; + if (p->pid == USB_TOKEN_IN) { + ep |= USB_DIR_IN; + } + + switch (dev->endpoint[EP2I(ep)].type) { + case USB_ENDPOINT_XFER_CONTROL: + ERROR("handle_data called for control transfer on ep %02X\n", ep); + return USB_RET_NAK; + case USB_ENDPOINT_XFER_ISOC: + return usbredir_handle_iso_data(dev, p, ep); + case USB_ENDPOINT_XFER_BULK: + return usbredir_handle_bulk_data(dev, p, ep);; + case USB_ENDPOINT_XFER_INT: + return usbredir_handle_interrupt_data(dev, p, ep);; + default: + ERROR("handle_data ep %02X has unknown type %d\n", ep, + dev->endpoint[EP2I(ep)].type); + return USB_RET_NAK; + } +} + +static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, + int config) +{ + struct usb_redir_set_configuration_header set_config; + AsyncURB *aurb = async_alloc(dev, p); + int i; + + DPRINTF("set config %d id %u\n", config, aurb->packet_id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + switch (dev->endpoint[i].type) { + case USB_ENDPOINT_XFER_ISOC: + usbredir_stop_iso_stream(dev, I2EP(i)); + break; + case USB_ENDPOINT_XFER_INT: + if (i & 0x10) { + usbredir_stop_interrupt_receiving(dev, I2EP(i)); + } + break; + } + usbredir_free_bufpq(dev, I2EP(i)); + } + + set_config.configuration = config; + usbredirparser_send_set_configuration(dev->parser, aurb->packet_id, + &set_config); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) +{ + AsyncURB *aurb = async_alloc(dev, p); + + DPRINTF("get config id %u\n", aurb->packet_id); + + aurb->get = 1; + usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, + int interface, int alt) +{ + struct usb_redir_set_alt_setting_header set_alt; + AsyncURB *aurb = async_alloc(dev, p); + int i; + + DPRINTF("set interface %d alt %d id %u\n", interface, alt, + aurb->packet_id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + if (dev->endpoint[i].interface == interface) { + switch (dev->endpoint[i].type) { + case USB_ENDPOINT_XFER_ISOC: + usbredir_stop_iso_stream(dev, I2EP(i)); + break; + case USB_ENDPOINT_XFER_INT: + if (i & 0x10) { + usbredir_stop_interrupt_receiving(dev, I2EP(i)); + } + break; + } + usbredir_free_bufpq(dev, I2EP(i)); + } + } + + set_alt.interface = interface; + set_alt.alt = alt; + usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id, + &set_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, + int interface) +{ + struct usb_redir_get_alt_setting_header get_alt; + AsyncURB *aurb = async_alloc(dev, p); + + DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); + + get_alt.interface = interface; + aurb->get = 1; + usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, + &get_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + struct usb_redir_control_packet_header control_packet; + AsyncURB *aurb; + + /* Special cases for certain standard device requests */ + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + DPRINTF("set address %d\n", value); + dev->dev.addr = value; + return 0; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + return usbredir_set_config(dev, p, value & 0xff); + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + return usbredir_get_config(dev, p); + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + return usbredir_set_interface(dev, p, index, value); + case InterfaceRequest | USB_REQ_GET_INTERFACE: + return usbredir_get_interface(dev, p, index); + } + + /* "Normal" ctrl requests */ + aurb = async_alloc(dev, p); + + /* Note request is (bRequestType << 8) | bRequest */ + DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n", + request >> 8, request & 0xff, value, index, length, + aurb->packet_id); + + control_packet.request = request & 0xFF; + control_packet.requesttype = request >> 8; + control_packet.endpoint = control_packet.requesttype & USB_DIR_IN; + control_packet.value = value; + control_packet.index = index; + control_packet.length = length; + aurb->control_packet = control_packet; + + if (control_packet.requesttype & USB_DIR_IN) { + usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + &control_packet, NULL, 0); + } else { + usbredir_log_data(dev, "ctrl data out:", data, length); + usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + &control_packet, data, length); + } + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +/* + * Close events can be triggered by usbredirparser_do_write which gets called + * from within the USBDevice data / control packet callbacks and doing a + * usb_detach from within these callbacks is not a good idea. + * + * So we use a bh handler to take care of close events. We also handle + * open events from this callback to make sure that a close directly followed + * by an open gets handled in the right order. + */ +static void usbredir_open_close_bh(void *opaque) +{ + USBRedirDevice *dev = opaque; + + usbredir_device_disconnect(dev); + + if (dev->parser) { + usbredirparser_destroy(dev->parser); + dev->parser = NULL; + } + + if (dev->cs->opened) { + dev->parser = qemu_oom_check(usbredirparser_create()); + dev->parser->priv = dev; + dev->parser->log_func = usbredir_log; + dev->parser->read_func = usbredir_read; + dev->parser->write_func = usbredir_write; + dev->parser->device_connect_func = usbredir_device_connect; + dev->parser->device_disconnect_func = usbredir_device_disconnect; + dev->parser->interface_info_func = usbredir_interface_info; + dev->parser->ep_info_func = usbredir_ep_info; + dev->parser->configuration_status_func = usbredir_configuration_status; + dev->parser->alt_setting_status_func = usbredir_alt_setting_status; + dev->parser->iso_stream_status_func = usbredir_iso_stream_status; + dev->parser->interrupt_receiving_status_func = + usbredir_interrupt_receiving_status; + dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; + dev->parser->control_packet_func = usbredir_control_packet; + dev->parser->bulk_packet_func = usbredir_bulk_packet; + dev->parser->iso_packet_func = usbredir_iso_packet; + dev->parser->interrupt_packet_func = usbredir_interrupt_packet; + dev->read_buf = NULL; + dev->read_buf_size = 0; + usbredirparser_init(dev->parser, VERSION, NULL, 0, 0); + usbredirparser_do_write(dev->parser); + } +} + +static void usbredir_do_attach(void *opaque) +{ + USBRedirDevice *dev = opaque; + + usb_device_attach(&dev->dev); +} + +/* + * chardev callbacks + */ + +static int usbredir_chardev_can_read(void *opaque) +{ + USBRedirDevice *dev = opaque; + + if (dev->parser) { + /* usbredir_parser_do_read will consume *all* data we give it */ + return 1024 * 1024; + } else { + /* usbredir_open_close_bh hasn't handled the open event yet */ + return 0; + } +} + +static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) +{ + USBRedirDevice *dev = opaque; + + /* No recursion allowed! */ + assert(dev->read_buf == NULL); + + dev->read_buf = buf; + dev->read_buf_size = size; + + usbredirparser_do_read(dev->parser); + /* Send any acks, etc. which may be queued now */ + usbredirparser_do_write(dev->parser); +} + +static void usbredir_chardev_event(void *opaque, int event) +{ + USBRedirDevice *dev = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + case CHR_EVENT_CLOSED: + qemu_bh_schedule(dev->open_close_bh); + break; + } +} + +/* + * init + destroy + */ + +static int usbredir_initfn(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + int i; + + if (dev->cs == NULL) { + qerror_report(QERR_MISSING_PARAMETER, "chardev"); + return -1; + } + + dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + + QTAILQ_INIT(&dev->asyncq); + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } + + /* We'll do the attach once we receive the speed from the usb-host */ + udev->auto_attach = 0; + + qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, + usbredir_chardev_read, usbredir_chardev_event, dev); + + return 0; +} + +static void usbredir_cleanup_device_queues(USBRedirDevice *dev) +{ + AsyncURB *aurb, *next_aurb; + int i; + + QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) { + async_free(dev, aurb); + } + for (i = 0; i < MAX_ENDPOINTS; i++) { + usbredir_free_bufpq(dev, I2EP(i)); + } +} + +static void usbredir_handle_destroy(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + qemu_chr_close(dev->cs); + /* Note must be done after qemu_chr_close, as that causes a close event */ + qemu_bh_delete(dev->open_close_bh); + + qemu_del_timer(dev->attach_timer); + qemu_free_timer(dev->attach_timer); + + usbredir_cleanup_device_queues(dev); + + if (dev->parser) { + usbredirparser_destroy(dev->parser); + } +} + +/* + * usbredirparser packet complete callbacks + */ + +static int usbredir_handle_status(USBRedirDevice *dev, + int status, int actual_len) +{ + switch (status) { + case usb_redir_success: + return actual_len; + case usb_redir_stall: + return USB_RET_STALL; + case usb_redir_cancelled: + WARNING("returning cancelled packet to HC?\n"); + case usb_redir_inval: + case usb_redir_ioerror: + case usb_redir_timeout: + default: + return USB_RET_NAK; + } +} + +static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect) +{ + USBRedirDevice *dev = priv; + + switch (device_connect->speed) { + case usb_redir_speed_low: + DPRINTF("attaching low speed device\n"); + dev->dev.speed = USB_SPEED_LOW; + break; + case usb_redir_speed_full: + DPRINTF("attaching full speed device\n"); + dev->dev.speed = USB_SPEED_FULL; + break; + case usb_redir_speed_high: + DPRINTF("attaching high speed device\n"); + dev->dev.speed = USB_SPEED_HIGH; + break; + case usb_redir_speed_super: + DPRINTF("attaching super speed device\n"); + dev->dev.speed = USB_SPEED_SUPER; + break; + default: + DPRINTF("attaching unknown speed device, assuming full speed\n"); + dev->dev.speed = USB_SPEED_FULL; + } + dev->dev.speedmask = (1 << dev->dev.speed); + qemu_mod_timer(dev->attach_timer, dev->next_attach_time); +} + +static void usbredir_device_disconnect(void *priv) +{ + USBRedirDevice *dev = priv; + + /* Stop any pending attaches */ + qemu_del_timer(dev->attach_timer); + + if (dev->dev.attached) { + usb_device_detach(&dev->dev); + usbredir_cleanup_device_queues(dev); + /* + * Delay next usb device attach to give the guest a chance to see + * see the detach / attach in case of quick close / open succession + */ + dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200; + } +} + +static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info) +{ + /* The intention is to allow specifying acceptable interface classes + for redirection on the cmdline and in the future verify this here, + and disconnect (or never connect) the device if a not accepted + interface class is detected */ +} + +static void usbredir_ep_info(void *priv, + struct usb_redir_ep_info_header *ep_info) +{ + USBRedirDevice *dev = priv; + int i; + + for (i = 0; i < MAX_ENDPOINTS; i++) { + dev->endpoint[i].type = ep_info->type[i]; + dev->endpoint[i].interval = ep_info->interval[i]; + dev->endpoint[i].interface = ep_info->interface[i]; + if (dev->endpoint[i].type != usb_redir_type_invalid) { + DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i), + dev->endpoint[i].type, dev->endpoint[i].interface); + } + } +} + +static void usbredir_configuration_status(void *priv, uint32_t id, + struct usb_redir_configuration_status_header *config_status) +{ + USBRedirDevice *dev = priv; + AsyncURB *aurb; + int len = 0; + + DPRINTF("set config status %d config %d id %u\n", config_status->status, + config_status->configuration, id); + + aurb = async_find(dev, id); + if (!aurb) { + return; + } + if (aurb->packet) { + if (aurb->get) { + dev->dev.data_buf[0] = config_status->configuration; + len = 1; + } + aurb->packet->len = + usbredir_handle_status(dev, config_status->status, len); + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); +} + +static void usbredir_alt_setting_status(void *priv, uint32_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status) +{ + USBRedirDevice *dev = priv; + AsyncURB *aurb; + int len = 0; + + DPRINTF("alt status %d intf %d alt %d id: %u\n", + alt_setting_status->status, + alt_setting_status->interface, + alt_setting_status->alt, id); + + aurb = async_find(dev, id); + if (!aurb) { + return; + } + if (aurb->packet) { + if (aurb->get) { + dev->dev.data_buf[0] = alt_setting_status->alt; + len = 1; + } + aurb->packet->len = + usbredir_handle_status(dev, alt_setting_status->status, len); + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); +} + +static void usbredir_iso_stream_status(void *priv, uint32_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status) +{ + USBRedirDevice *dev = priv; + uint8_t ep = iso_stream_status->endpoint; + + DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status, + ep, id); + + dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status; + if (iso_stream_status->status == usb_redir_stall) { + DPRINTF("iso stream stopped by peer ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 0; + } +} + +static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status) +{ + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_receiving_status->endpoint; + + DPRINTF("interrupt recv status %d ep %02X id %u\n", + interrupt_receiving_status->status, ep, id); + + dev->endpoint[EP2I(ep)].interrupt_error = + interrupt_receiving_status->status; + if (interrupt_receiving_status->status == usb_redir_stall) { + DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 0; + } +} + +static void usbredir_bulk_streams_status(void *priv, uint32_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status) +{ +} + +static void usbredir_control_packet(void *priv, uint32_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + int len = control_packet->length; + AsyncURB *aurb; + + DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, + len, id); + + aurb = async_find(dev, id); + if (!aurb) { + free(data); + return; + } + + aurb->control_packet.status = control_packet->status; + aurb->control_packet.length = control_packet->length; + if (memcmp(&aurb->control_packet, control_packet, + sizeof(*control_packet))) { + ERROR("return control packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + len = usbredir_handle_status(dev, control_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "ctrl data in:", data, data_len); + if (data_len <= sizeof(dev->dev.data_buf)) { + memcpy(dev->dev.data_buf, data, data_len); + } else { + ERROR("ctrl buffer too small (%d > %zu)\n", + data_len, sizeof(dev->dev.data_buf)); + len = USB_RET_STALL; + } + } + aurb->packet->len = len; + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + free(data); +} + +static void usbredir_bulk_packet(void *priv, uint32_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = bulk_packet->endpoint; + int len = bulk_packet->length; + AsyncURB *aurb; + + DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, + ep, len, id); + + aurb = async_find(dev, id); + if (!aurb) { + free(data); + return; + } + + if (aurb->bulk_packet.endpoint != bulk_packet->endpoint || + aurb->bulk_packet.stream_id != bulk_packet->stream_id) { + ERROR("return bulk packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + len = usbredir_handle_status(dev, bulk_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "bulk data in:", data, data_len); + if (data_len <= aurb->packet->len) { + memcpy(aurb->packet->data, data, data_len); + } else { + ERROR("bulk buffer too small (%d > %d)\n", data_len, + aurb->packet->len); + len = USB_RET_STALL; + } + } + aurb->packet->len = len; + usb_packet_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + free(data); +} + +static void usbredir_iso_packet(void *priv, uint32_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = iso_packet->endpoint; + + DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep, + data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { + ERROR("received iso packet for non iso endpoint %02X\n", ep); + free(data); + return; + } + + if (dev->endpoint[EP2I(ep)].iso_started == 0) { + DPRINTF("received iso packet for non started stream ep %02X\n", ep); + free(data); + return; + } + + /* bufp_alloc also adds the packet to the ep queue */ + bufp_alloc(dev, data, data_len, iso_packet->status, ep); +} + +static void usbredir_interrupt_packet(void *priv, uint32_t id, + struct usb_redir_interrupt_packet_header *interrupt_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_packet->endpoint; + + DPRINTF("interrupt-in status %d ep %02X len %d id %u\n", + interrupt_packet->status, ep, data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { + ERROR("received int packet for non interrupt endpoint %02X\n", ep); + free(data); + return; + } + + if (ep & USB_DIR_IN) { + if (dev->endpoint[EP2I(ep)].interrupt_started == 0) { + DPRINTF("received int packet while not started ep %02X\n", ep); + free(data); + return; + } + + /* bufp_alloc also adds the packet to the ep queue */ + bufp_alloc(dev, data, data_len, interrupt_packet->status, ep); + } else { + int len = interrupt_packet->length; + + AsyncURB *aurb = async_find(dev, id); + if (!aurb) { + return; + } + + if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) { + ERROR("return int packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + aurb->packet->len = usbredir_handle_status(dev, + interrupt_packet->status, len); + usb_packet_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + } +} + +static struct USBDeviceInfo usbredir_dev_info = { + .product_desc = "USB Redirection Device", + .qdev.name = "usb-redir", + .qdev.size = sizeof(USBRedirDevice), + .init = usbredir_initfn, + .handle_destroy = usbredir_handle_destroy, + .handle_packet = usb_generic_handle_packet, + .cancel_packet = usbredir_cancel_packet, + .handle_reset = usbredir_handle_reset, + .handle_data = usbredir_handle_data, + .handle_control = usbredir_handle_control, + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), + DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void usbredir_register_devices(void) +{ + usb_qdev_register(&usbredir_dev_info); +} +device_init(usbredir_register_devices); diff -Nru qemu-kvm-0.14.1+noroms/user-exec.c qemu-kvm-0.15.0+noroms/user-exec.c --- qemu-kvm-0.14.1+noroms/user-exec.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/user-exec.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,674 @@ +/* + * User emulator execution + * + * Copyright (c) 2003-2005 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 "config.h" +#include "exec.h" +#include "disas.h" +#include "tcg.h" + +#undef EAX +#undef ECX +#undef EDX +#undef EBX +#undef ESP +#undef EBP +#undef ESI +#undef EDI +#undef EIP +#include +#ifdef __linux__ +#include +#endif + +//#define DEBUG_SIGNAL + +static void exception_action(CPUState *env1) +{ +#if defined(TARGET_I386) + raise_exception_err(env1->exception_index, env1->error_code); +#else + cpu_loop_exit(env1); +#endif +} + +/* exit the current TB from a signal handler. The host registers are + restored in a state compatible with the CPU emulator + */ +void cpu_resume_from_signal(CPUState *env1, void *puc) +{ +#ifdef __linux__ + struct ucontext *uc = puc; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; +#endif + + env = env1; + + /* XXX: restore cpu registers saved in host registers */ + + 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 + } + env->exception_index = -1; + longjmp(env->jmp_env, 1); +} + +/* 'pc' is the host PC at which the exception was raised. 'address' is + the effective address of the memory exception. 'is_write' is 1 if a + write caused the exception and otherwise 0'. 'old_set' is the + signal set which should be restored */ +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) { + env = cpu_single_env; /* XXX: find a correct solution for multithread */ + } +#if defined(DEBUG_SIGNAL) + qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + + /* see if it is an MMU fault */ + ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0); + if (ret < 0) { + return 0; /* not an MMU fault */ + } + if (ret == 0) { + return 1; /* the MMU fault was handled without causing real CPU fault */ + } + /* now we have a real cpu fault */ + 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); + } + + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + exception_action(env); + + /* never comes here */ + return 1; +} + +#if defined(__i386__) + +#if defined(__APPLE__) +#include + +#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext->ss.eip)) +#define TRAP_sig(context) ((context)->uc_mcontext->es.trapno) +#define ERROR_sig(context) ((context)->uc_mcontext->es.err) +#define MASK_sig(context) ((context)->uc_sigmask) +#elif defined(__NetBSD__) +#include + +#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP]) +#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) +#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) +#define MASK_sig(context) ((context)->uc_sigmask) +#elif defined(__FreeBSD__) || defined(__DragonFly__) +#include + +#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip)) +#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) +#define ERROR_sig(context) ((context)->uc_mcontext.mc_err) +#define MASK_sig(context) ((context)->uc_sigmask) +#elif defined(__OpenBSD__) +#define EIP_sig(context) ((context)->sc_eip) +#define TRAP_sig(context) ((context)->sc_trapno) +#define ERROR_sig(context) ((context)->sc_err) +#define MASK_sig(context) ((context)->sc_mask) +#else +#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP]) +#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) +#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) +#define MASK_sig(context) ((context)->uc_sigmask) +#endif + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + ucontext_t *uc = puc; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; +#else + struct ucontext *uc = puc; +#endif + unsigned long pc; + int trapno; + +#ifndef REG_EIP +/* for glibc 2.1 */ +#define REG_EIP EIP +#define REG_ERR ERR +#define REG_TRAPNO TRAPNO +#endif + pc = EIP_sig(uc); + trapno = TRAP_sig(uc); + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + trapno == 0xe ? + (ERROR_sig(uc) >> 1) & 1 : 0, + &MASK_sig(uc), puc); +} + +#elif defined(__x86_64__) + +#ifdef __NetBSD__ +#define PC_sig(context) _UC_MACHINE_PC(context) +#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) +#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) +#define MASK_sig(context) ((context)->uc_sigmask) +#elif defined(__OpenBSD__) +#define PC_sig(context) ((context)->sc_rip) +#define TRAP_sig(context) ((context)->sc_trapno) +#define ERROR_sig(context) ((context)->sc_err) +#define MASK_sig(context) ((context)->sc_mask) +#elif defined(__FreeBSD__) || defined(__DragonFly__) +#include + +#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip)) +#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) +#define ERROR_sig(context) ((context)->uc_mcontext.mc_err) +#define MASK_sig(context) ((context)->uc_sigmask) +#else +#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP]) +#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) +#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR]) +#define MASK_sig(context) ((context)->uc_sigmask) +#endif + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + unsigned long pc; +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + ucontext_t *uc = puc; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; +#else + struct ucontext *uc = puc; +#endif + + pc = PC_sig(uc); + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + TRAP_sig(uc) == 0xe ? + (ERROR_sig(uc) >> 1) & 1 : 0, + &MASK_sig(uc), puc); +} + +#elif defined(_ARCH_PPC) + +/*********************************************************************** + * signal context platform-specific definitions + * From Wine + */ +#ifdef linux +/* All Registers access - only for local access */ +#define REG_sig(reg_name, context) \ + ((context)->uc_mcontext.regs->reg_name) +/* Gpr Registers access */ +#define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context) +/* Program counter */ +#define IAR_sig(context) REG_sig(nip, context) +/* Machine State Register (Supervisor) */ +#define MSR_sig(context) REG_sig(msr, context) +/* Count register */ +#define CTR_sig(context) REG_sig(ctr, context) +/* User's integer exception register */ +#define XER_sig(context) REG_sig(xer, context) +/* Link register */ +#define LR_sig(context) REG_sig(link, context) +/* Condition register */ +#define CR_sig(context) REG_sig(ccr, context) + +/* Float Registers access */ +#define FLOAT_sig(reg_num, context) \ + (((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num]) +#define FPSCR_sig(context) \ + (*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4))) +/* Exception Registers access */ +#define DAR_sig(context) REG_sig(dar, context) +#define DSISR_sig(context) REG_sig(dsisr, context) +#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; +/* All Registers access - only for local access */ +#define REG_sig(reg_name, context) \ + ((context)->uc_mcontext->ss.reg_name) +#define FLOATREG_sig(reg_name, context) \ + ((context)->uc_mcontext->fs.reg_name) +#define EXCEPREG_sig(reg_name, context) \ + ((context)->uc_mcontext->es.reg_name) +#define VECREG_sig(reg_name, context) \ + ((context)->uc_mcontext->vs.reg_name) +/* Gpr Registers access */ +#define GPR_sig(reg_num, context) REG_sig(r##reg_num, context) +/* Program counter */ +#define IAR_sig(context) REG_sig(srr0, context) +/* Machine State Register (Supervisor) */ +#define MSR_sig(context) REG_sig(srr1, context) +#define CTR_sig(context) REG_sig(ctr, context) +/* Link register */ +#define XER_sig(context) REG_sig(xer, context) +/* User's integer exception register */ +#define LR_sig(context) REG_sig(lr, context) +/* Condition register */ +#define CR_sig(context) REG_sig(cr, context) +/* Float Registers access */ +#define FLOAT_sig(reg_num, context) \ + FLOATREG_sig(fpregs[reg_num], context) +#define FPSCR_sig(context) \ + ((double)FLOATREG_sig(fpscr, context)) +/* Exception Registers access */ +/* Fault registers for coredump */ +#define DAR_sig(context) EXCEPREG_sig(dar, context) +#define DSISR_sig(context) EXCEPREG_sig(dsisr, context) +/* number of powerpc exception taken */ +#define TRAP_sig(context) EXCEPREG_sig(exception, context) +#endif /* __APPLE__ */ + +int cpu_signal_handler(int host_signum, void *pinfo, + 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; + + pc = IAR_sig(uc); + is_write = 0; +#if 0 + /* ppc 4xx case */ + if (DSISR_sig(uc) & 0x00800000) { + is_write = 1; + } +#else + if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) { + is_write = 1; + } +#endif + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); +} + +#elif defined(__alpha__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + struct ucontext *uc = puc; + uint32_t *pc = uc->uc_mcontext.sc_pc; + uint32_t insn = *pc; + int is_write = 0; + + /* XXX: need kernel patch to get write flag faster */ + switch (insn >> 26) { + case 0x0d: /* stw */ + case 0x0e: /* stb */ + case 0x0f: /* stq_u */ + case 0x24: /* stf */ + case 0x25: /* stg */ + case 0x26: /* sts */ + case 0x27: /* stt */ + case 0x2c: /* stl */ + case 0x2d: /* stq */ + case 0x2e: /* stl_c */ + case 0x2f: /* stq_c */ + is_write = 1; + } + + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); +} +#elif defined(__sparc__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + int is_write; + uint32_t insn; +#if !defined(__arch64__) || defined(CONFIG_SOLARIS) + uint32_t *regs = (uint32_t *)(info + 1); + void *sigmask = (regs + 20); + /* XXX: is there a standard glibc define ? */ + unsigned long pc = regs[1]; +#else +#ifdef __linux__ + struct sigcontext *sc = puc; + unsigned long pc = sc->sigc_regs.tpc; + void *sigmask = (void *)sc->sigc_mask; +#elif defined(__OpenBSD__) + struct sigcontext *uc = puc; + unsigned long pc = uc->sc_pc; + void *sigmask = (void *)(long)uc->sc_mask; +#endif +#endif + + /* XXX: need kernel patch to get write flag faster */ + is_write = 0; + insn = *(uint32_t *)pc; + if ((insn >> 30) == 3) { + switch ((insn >> 19) & 0x3f) { + case 0x05: /* stb */ + case 0x15: /* stba */ + case 0x06: /* sth */ + case 0x16: /* stha */ + case 0x04: /* st */ + case 0x14: /* sta */ + case 0x07: /* std */ + case 0x17: /* stda */ + case 0x0e: /* stx */ + case 0x1e: /* stxa */ + case 0x24: /* stf */ + case 0x34: /* stfa */ + case 0x27: /* stdf */ + case 0x37: /* stdfa */ + case 0x26: /* stqf */ + case 0x36: /* stqfa */ + case 0x25: /* stfsr */ + case 0x3c: /* casa */ + case 0x3e: /* casxa */ + is_write = 1; + break; + } + } + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, sigmask, NULL); +} + +#elif defined(__arm__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + struct ucontext *uc = puc; + unsigned long pc; + int is_write; + +#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) + pc = uc->uc_mcontext.gregs[R15]; +#else + pc = uc->uc_mcontext.arm_pc; +#endif + /* XXX: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask, puc); +} + +#elif defined(__mc68000) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + struct ucontext *uc = puc; + unsigned long pc; + int is_write; + + pc = uc->uc_mcontext.gregs[16]; + /* XXX: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask, puc); +} + +#elif defined(__ia64) + +#ifndef __ISR_VALID + /* This ought to be in ... */ +# define __ISR_VALID 1 +#endif + +int cpu_signal_handler(int host_signum, void *pinfo, void *puc) +{ + siginfo_t *info = pinfo; + struct ucontext *uc = puc; + unsigned long ip; + int is_write = 0; + + ip = uc->uc_mcontext.sc_ip; + switch (host_signum) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + case SIGTRAP: + if (info->si_code && (info->si_segvflags & __ISR_VALID)) { + /* ISR.W (write-access) is bit 33: */ + is_write = (info->si_isr >> 33) & 1; + } + break; + + default: + break; + } + return handle_cpu_signal(ip, (unsigned long)info->si_addr, + is_write, + (sigset_t *)&uc->uc_sigmask, puc); +} + +#elif defined(__s390__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + struct ucontext *uc = puc; + unsigned long pc; + uint16_t *pinsn; + int is_write = 0; + + pc = uc->uc_mcontext.psw.addr; + + /* ??? 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); +} + +#elif defined(__mips__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + struct ucontext *uc = puc; + greg_t pc = uc->uc_mcontext.pc; + int is_write; + + /* XXX: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); +} + +#elif defined(__hppa__) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + struct siginfo *info = pinfo; + struct ucontext *uc = puc; + 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; + } + + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); +} + +#else + +#error host CPU specific signal handler needed + +#endif + +#if defined(TARGET_I386) + +void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { + selector &= 0xffff; + cpu_x86_load_seg_cache(env, seg_reg, selector, + (selector << 4), 0xffff, 0); + } else { + helper_load_seg(seg_reg, selector); + } + env = saved_env; +} + +void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_fsave(ptr, data32); + + env = saved_env; +} + +void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_frstor(ptr, data32); + + env = saved_env; +} + +#endif /* TARGET_I386 */ diff -Nru qemu-kvm-0.14.1+noroms/VERSION qemu-kvm-0.15.0+noroms/VERSION --- qemu-kvm-0.14.1+noroms/VERSION 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/VERSION 2011-08-09 07:40:29.000000000 -0500 @@ -1 +1 @@ -0.14.1 +0.15.0 diff -Nru qemu-kvm-0.14.1+noroms/vl.c qemu-kvm-0.15.0+noroms/vl.c --- qemu-kvm-0.14.1+noroms/vl.c 2011-05-11 08:29:46.000000000 -0500 +++ qemu-kvm-0.15.0+noroms/vl.c 2011-08-09 07:40:29.000000000 -0500 @@ -47,9 +47,6 @@ #include #include #include -#ifdef CONFIG_SIMPLE_TRACE -#include "trace.h" -#endif #ifdef CONFIG_BSD #include @@ -148,7 +145,7 @@ #include "qemu-config.h" #include "qemu-objects.h" #include "qemu-options.h" -#include "hw/device-assignment.h" +#include "kvm.h" #ifdef CONFIG_VIRTFS #include "fsdev/qemu-fsdev.h" #endif @@ -160,6 +157,7 @@ #include "slirp/libslirp.h" #include "trace.h" +#include "simpletrace.h" #include "qemu-queue.h" #include "cpus.h" #include "arch_init.h" @@ -209,7 +207,9 @@ int max_cpus = 0; int smp_cores = 1; int smp_threads = 1; +#ifdef CONFIG_VNC const char *vnc_display; +#endif int acpi_enabled = 1; int no_hpet = 0; int fd_bootchk = 1; @@ -217,7 +217,6 @@ int no_shutdown = 0; int cursor_hide = 1; int graphic_rotate = 0; -uint8_t irq0override = 1; const char *watchdog; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; @@ -261,7 +260,9 @@ static NotifierList machine_init_done_notifiers = NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers); -int kvm_allowed = -1; +static int tcg_allowed = 1; +int kvm_allowed = 0; +int xen_allowed = 0; uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; @@ -281,13 +282,18 @@ { .driver = "isa-serial", .flag = &default_serial }, { .driver = "isa-parallel", .flag = &default_parallel }, { .driver = "isa-fdc", .flag = &default_floppy }, + { .driver = "ide-cd", .flag = &default_cdrom }, + { .driver = "ide-hd", .flag = &default_cdrom }, { .driver = "ide-drive", .flag = &default_cdrom }, + { .driver = "scsi-cd", .flag = &default_cdrom }, { .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 }, + { .driver = "isa-vga", .flag = &default_vga }, + { .driver = "qxl-vga", .flag = &default_vga }, }; static int default_driver_check(QemuOpts *opts, void *opaque) @@ -760,7 +766,7 @@ /* * This function returns null terminated string that consist of new line - * separated device pathes. + * separated device paths. * * memory pointed by "size" is assigned total length of the array in bytes * @@ -922,9 +928,13 @@ goto done; /* the other ones */ +#ifndef CONFIG_LINUX + /* only the linux version is qdev-ified, usb-bsd still needs this */ if (strstart(devname, "host:", &p)) { dev = usb_host_device_open(p); - } else if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) { + } else +#endif + 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 { @@ -1027,69 +1037,6 @@ } /***********************************************************/ -/* I/O handling */ - -typedef struct IOHandlerRecord { - int fd; - IOCanReadHandler *fd_read_poll; - IOHandler *fd_read; - IOHandler *fd_write; - int deleted; - void *opaque; - /* temporary data */ - struct pollfd *ufd; - QLIST_ENTRY(IOHandlerRecord) next; -} IOHandlerRecord; - -static QLIST_HEAD(, IOHandlerRecord) io_handlers = - QLIST_HEAD_INITIALIZER(io_handlers); - - -/* 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; - - 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; -} - -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); -} - -/***********************************************************/ /* machine registration */ static QEMUMachine *first_machine = NULL; @@ -1149,7 +1096,7 @@ interval = dcl->gui_timer_interval; dcl = dcl->next; } - qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock)); + qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock)); } static void nographic_update(void *opaque) @@ -1157,7 +1104,7 @@ uint64_t interval = GUI_REFRESH_INTERVAL; qemu_flush_coalesced_mmio_buffer(); - qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock)); + qemu_mod_timer(nographic_timer, interval + qemu_get_clock_ms(rt_clock)); } struct vm_change_state_entry { @@ -1220,16 +1167,20 @@ static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers = QTAILQ_HEAD_INITIALIZER(reset_handlers); static int reset_requested; -static int shutdown_requested; +static int shutdown_requested, shutdown_signal = -1; +static pid_t shutdown_pid; static int powerdown_requested; -int debug_requested; -int vmstop_requested; +static int debug_requested; +static int vmstop_requested; -int qemu_no_shutdown(void) +int qemu_shutdown_requested_get(void) { - int r = no_shutdown; - no_shutdown = 0; - return r; + return shutdown_requested; +} + +int qemu_reset_requested_get(void) +{ + return reset_requested; } int qemu_shutdown_requested(void) @@ -1239,6 +1190,22 @@ return r; } +void qemu_kill_report(void) +{ + if (shutdown_signal != -1) { + fprintf(stderr, "qemu: terminating on signal %d", shutdown_signal); + if (shutdown_pid == 0) { + /* This happens for eg ^C at the terminal, so it's worth + * avoiding printing an odd message in that case. + */ + fputc('\n', stderr); + } else { + fprintf(stderr, " from pid " FMT_pid "\n", shutdown_pid); + } + shutdown_signal = -1; + } +} + int qemu_reset_requested(void) { int r = reset_requested; @@ -1289,7 +1256,7 @@ } } -void qemu_system_reset(void) +void qemu_system_reset(bool report) { QEMUResetEntry *re, *nre; @@ -1297,7 +1264,9 @@ QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) { re->func(re->opaque); } - monitor_protocol_event(QEVENT_RESET, NULL); + if (report) { + monitor_protocol_event(QEVENT_RESET, NULL); + } cpu_synchronize_all_post_reset(); } @@ -1308,13 +1277,17 @@ } else { reset_requested = 1; } - if (cpu_single_env) { - cpu_single_env->stopped = 1; - cpu_exit(cpu_single_env); - } + cpu_stop_current(); qemu_notify_event(); } +void qemu_system_killed(int signal, pid_t pid) +{ + shutdown_signal = signal; + shutdown_pid = pid; + qemu_system_shutdown_request(); +} + void qemu_system_shutdown_request(void) { shutdown_requested = 1; @@ -1327,9 +1300,20 @@ qemu_notify_event(); } +void qemu_system_debug_request(void) +{ + debug_requested = 1; + qemu_notify_event(); +} + +void qemu_system_vmstop_request(int reason) +{ + vmstop_requested = reason; + qemu_notify_event(); +} + void main_loop_wait(int nonblocking) { - IOHandlerRecord *ioh; fd_set rfds, wfds, xfds; int ret, nfds; struct timeval tv; @@ -1344,58 +1328,23 @@ os_host_main_loop_wait(&timeout); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + /* poll any events */ /* XXX: separate device handlers from system ones */ nfds = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&xfds); - 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 (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; - + qemu_iohandler_fill(&nfds, &rfds, &wfds, &xfds); 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; - - 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); - } - - /* Do this last in case read/write handlers marked it for deletion */ - if (ioh->deleted) { - QLIST_REMOVE(ioh, next); - qemu_free(ioh); - } - } - } + qemu_iohandler_poll(&rfds, &wfds, &xfds, ret); slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0)); qemu_run_all_timers(); @@ -1406,65 +1355,59 @@ } -static int vm_can_run(void) +#ifndef CONFIG_IOTHREAD +static int vm_request_pending(void) { - if (powerdown_requested) - return 0; - if (reset_requested) - return 0; - if (shutdown_requested) - return 0; - if (debug_requested) - return 0; - return 1; + return powerdown_requested || + reset_requested || + shutdown_requested || + debug_requested || + vmstop_requested; } +#endif qemu_irq qemu_system_powerdown; static void main_loop(void) { + bool nonblocking = false; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif int r; - if (kvm_enabled()) { - kvm_main_loop(); - cpu_disable_ticks(); - return; - } - qemu_main_loop_start(); for (;;) { - do { - bool nonblocking = false; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif #ifndef CONFIG_IOTHREAD - nonblocking = cpu_exec_all(); + nonblocking = cpu_exec_all(); + if (vm_request_pending()) { + nonblocking = true; + } #endif #ifdef CONFIG_PROFILER - ti = profile_getclock(); + ti = profile_getclock(); #endif - main_loop_wait(nonblocking); + main_loop_wait(nonblocking); #ifdef CONFIG_PROFILER - dev_time += profile_getclock() - ti; + dev_time += profile_getclock() - ti; #endif - } while (vm_can_run()); - if ((r = qemu_debug_requested())) { - vm_stop(r); + if (qemu_debug_requested()) { + vm_stop(VMSTOP_DEBUG); } if (qemu_shutdown_requested()) { + qemu_kill_report(); monitor_protocol_event(QEVENT_SHUTDOWN, NULL); if (no_shutdown) { - vm_stop(0); - no_shutdown = 0; + vm_stop(VMSTOP_SHUTDOWN); } else break; } if (qemu_reset_requested()) { pause_all_vcpus(); - qemu_system_reset(); + cpu_synchronize_all_states(); + qemu_system_reset(VMRESET_REPORT); resume_all_vcpus(); } if (qemu_powerdown_requested()) { @@ -1568,6 +1511,100 @@ } } +static DisplayType select_display(const char *p) +{ + const char *opts; + DisplayType display = DT_DEFAULT; + + if (strstart(p, "sdl", &opts)) { +#ifdef CONFIG_SDL + display = DT_SDL; + while (*opts) { + const char *nextopt; + + if (strstart(opts, ",frame=", &nextopt)) { + opts = nextopt; + if (strstart(opts, "on", &nextopt)) { + no_frame = 0; + } else if (strstart(opts, "off", &nextopt)) { + no_frame = 1; + } else { + goto invalid_sdl_args; + } + } else if (strstart(opts, ",alt_grab=", &nextopt)) { + opts = nextopt; + if (strstart(opts, "on", &nextopt)) { + alt_grab = 1; + } else if (strstart(opts, "off", &nextopt)) { + alt_grab = 0; + } else { + goto invalid_sdl_args; + } + } else if (strstart(opts, ",ctrl_grab=", &nextopt)) { + opts = nextopt; + if (strstart(opts, "on", &nextopt)) { + ctrl_grab = 1; + } else if (strstart(opts, "off", &nextopt)) { + ctrl_grab = 0; + } else { + goto invalid_sdl_args; + } + } else if (strstart(opts, ",window_close=", &nextopt)) { + opts = nextopt; + if (strstart(opts, "on", &nextopt)) { + no_quit = 0; + } else if (strstart(opts, "off", &nextopt)) { + no_quit = 1; + } else { + goto invalid_sdl_args; + } + } else { + invalid_sdl_args: + fprintf(stderr, "Invalid SDL option string: %s\n", p); + exit(1); + } + opts = nextopt; + } +#else + fprintf(stderr, "SDL support is disabled\n"); + exit(1); +#endif + } else if (strstart(p, "vnc", &opts)) { +#ifdef CONFIG_VNC + display_remote++; + + if (*opts) { + const char *nextopt; + + if (strstart(opts, "=", &nextopt)) { + vnc_display = nextopt; + } + } + if (!vnc_display) { + fprintf(stderr, "VNC requires a display argument vnc=\n"); + exit(1); + } +#else + fprintf(stderr, "VNC support is disabled\n"); + exit(1); +#endif + } else if (strstart(p, "curses", &opts)) { +#ifdef CONFIG_CURSES + display = DT_CURSES; +#else + fprintf(stderr, "Curses support is disabled\n"); + exit(1); +#endif + } else if (strstart(p, "none", &opts)) { + display = DT_NONE; + } else { + fprintf(stderr, "Unknown display type: %s\n", p); + exit(1); + } + + return display; +} + static int balloon_parse(const char *arg) { QemuOpts *opts; @@ -1586,7 +1623,7 @@ /* create empty opts */ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); } - qemu_opt_set(opts, "driver", "virtio-balloon-pci"); + qemu_opt_set(opts, "driver", "virtio-balloon"); return 0; } @@ -1864,6 +1901,110 @@ return 0; } +static QEMUMachine *machine_parse(const char *name) +{ + QEMUMachine *m, *machine = NULL; + + if (name) { + machine = find_machine(name); + } + if (machine) { + return machine; + } + printf("Supported machines are:\n"); + for (m = first_machine; m != NULL; m = m->next) { + if (m->alias) { + printf("%-10s %s (alias of %s)\n", m->alias, m->desc, m->name); + } + printf("%-10s %s%s\n", m->name, m->desc, + m->is_default ? " (default)" : ""); + } + exit(!name || *name != '?'); +} + +static int tcg_init(void) +{ + return 0; +} + +static struct { + const char *opt_name; + const char *name; + int (*available)(void); + int (*init)(void); + int *allowed; +} accel_list[] = { + { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, + { "xen", "Xen", xen_available, xen_init, &xen_allowed }, + { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed }, +}; + +static int configure_accelerator(void) +{ + const char *p = NULL; + char buf[10]; + int i, ret; + bool accel_initalised = 0; + bool init_failed = 0; + + QemuOptsList *list = qemu_find_opts("machine"); + if (!QTAILQ_EMPTY(&list->head)) { + p = qemu_opt_get(QTAILQ_FIRST(&list->head), "accel"); + } + + if (p == NULL) { +#ifdef CONFIG_KVM + /* Use the default "accelerator", kvm */ + p = "kvm"; +#else + /* Use the default "accelerator", tcg */ + p = "tcg"; +#endif + } + + while (!accel_initalised && *p != '\0') { + if (*p == ':') { + p++; + } + p = get_opt_name(buf, sizeof (buf), p, ':'); + for (i = 0; i < ARRAY_SIZE(accel_list); i++) { + if (strcmp(accel_list[i].opt_name, buf) == 0) { + *(accel_list[i].allowed) = 1; + ret = accel_list[i].init(); + if (ret < 0) { + init_failed = 1; + if (!accel_list[i].available()) { + printf("%s not supported for this target\n", + accel_list[i].name); + } else { + fprintf(stderr, "failed to initialize %s: %s\n", + accel_list[i].name, + strerror(-ret)); + } + *(accel_list[i].allowed) = 0; + } else { + accel_initalised = 1; + } + break; + } + } + if (i == ARRAY_SIZE(accel_list)) { + fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf); + } + } + + if (!accel_initalised) { + fprintf(stderr, "No accelerator found!\n"); + exit(1); + } + + if (init_failed) { + fprintf(stderr, "Back to %s accelerator.\n", accel_list[i].name); + } + + return !accel_initalised; +} + void qemu_add_exit_notifier(Notifier *notify) { notifier_list_add(&exit_notifiers, notify); @@ -1876,7 +2017,7 @@ static void qemu_run_exit_notifiers(void) { - notifier_list_notify(&exit_notifiers); + notifier_list_notify(&exit_notifiers, NULL); } void qemu_add_machine_init_done_notifier(Notifier *notify) @@ -1886,7 +2027,7 @@ static void qemu_run_machine_init_done_notifiers(void) { - notifier_list_notify(&machine_init_done_notifiers); + notifier_list_notify(&machine_init_done_notifiers, NULL); } static const QEMUOption *lookup_opt(int argc, char **argv, @@ -1951,12 +2092,14 @@ int tb_size; const char *pid_file = NULL; const char *incoming = NULL; +#ifdef CONFIG_VNC int show_vnc_port = 0; +#endif int defconfig = 1; - -#ifdef CONFIG_SIMPLE_TRACE const char *trace_file = NULL; -#endif + const char *log_mask = NULL; + const char *log_file = NULL; + atexit(qemu_run_exit_notifiers); error_set_progname(argv[0]); @@ -2040,20 +2183,7 @@ } switch(popt->index) { case QEMU_OPTION_M: - machine = find_machine(optarg); - if (!machine) { - QEMUMachine *m; - printf("Supported machines are:\n"); - for(m = first_machine; m != NULL; m = m->next) { - if (m->alias) - printf("%-10s %s (alias of %s)\n", - m->alias, m->desc, m->name); - printf("%-10s %s%s\n", - m->name, m->desc, - m->is_default ? " (default)" : ""); - } - exit(*optarg != '?'); - } + machine = machine_parse(optarg); break; case QEMU_OPTION_cpu: /* hw initialization will check this */ @@ -2170,16 +2300,31 @@ } numa_add(optarg); break; + case QEMU_OPTION_display: + display_type = select_display(optarg); + break; case QEMU_OPTION_nographic: display_type = DT_NOGRAPHIC; break; -#ifdef CONFIG_CURSES case QEMU_OPTION_curses: +#ifdef CONFIG_CURSES display_type = DT_CURSES; - break; +#else + fprintf(stderr, "Curses support is disabled\n"); + exit(1); #endif + break; case QEMU_OPTION_portrait: - graphic_rotate = 1; + graphic_rotate = 90; + break; + case QEMU_OPTION_rotate: + graphic_rotate = strtol(optarg, (char **) &optarg, 10); + if (graphic_rotate != 0 && graphic_rotate != 90 && + graphic_rotate != 180 && graphic_rotate != 270) { + fprintf(stderr, + "qemu: only 90, 180, 270 deg rotation is available\n"); + exit(1); + } break; case QEMU_OPTION_kernel: kernel_filename = optarg; @@ -2324,7 +2469,10 @@ break; #endif case QEMU_OPTION_d: - set_cpu_log(optarg); + log_mask = optarg; + break; + case QEMU_OPTION_D: + log_file = optarg; break; case QEMU_OPTION_s: gdbstub_dev = "tcp::" DEFAULT_GDBSTUB_PORT; @@ -2429,9 +2577,8 @@ } break; case QEMU_OPTION_virtfs: { - char *arg_fsdev = NULL; - char *arg_9p = NULL; - int len = 0; + QemuOpts *fsdev; + QemuOpts *device; olist = qemu_find_opts("virtfs"); if (!olist) { @@ -2450,45 +2597,28 @@ qemu_opt_get(opts, "security_model") == NULL) { fprintf(stderr, "Usage: -virtfs fstype,path=/share_path/," "security_model=[mapped|passthrough|none]," - "mnt_tag=tag.\n"); + "mount_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); + fsdev = qemu_opts_create(qemu_find_opts("fsdev"), + qemu_opt_get(opts, "mount_tag"), 1); + if (!fsdev) { + fprintf(stderr, "duplicate fsdev id: %s\n", + qemu_opt_get(opts, "mount_tag")); exit(1); } + qemu_opt_set(fsdev, "fstype", qemu_opt_get(opts, "fstype")); + qemu_opt_set(fsdev, "path", qemu_opt_get(opts, "path")); + qemu_opt_set(fsdev, "security_model", + qemu_opt_get(opts, "security_model")); - qemu_free(arg_fsdev); - qemu_free(arg_9p); + device = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + qemu_opt_set(device, "driver", "virtio-9p-pci"); + qemu_opt_set(device, "fsdev", + qemu_opt_get(opts, "mount_tag")); + qemu_opt_set(device, "mount_tag", + qemu_opt_get(opts, "mount_tag")); break; } case QEMU_OPTION_serial: @@ -2551,6 +2681,14 @@ case QEMU_OPTION_sdl: display_type = DT_SDL; break; +#else + case QEMU_OPTION_no_frame: + case QEMU_OPTION_alt_grab: + case QEMU_OPTION_ctrl_grab: + case QEMU_OPTION_no_quit: + case QEMU_OPTION_sdl: + fprintf(stderr, "SDL support is disabled\n"); + exit(1); #endif case QEMU_OPTION_pidfile: pid_file = optarg; @@ -2568,15 +2706,25 @@ do_smbios_option(optarg); break; case QEMU_OPTION_enable_kvm: - kvm_allowed = 1; + olist = qemu_find_opts("machine"); + qemu_opts_reset(olist); + qemu_opts_parse(olist, "accel=kvm", 0); + break; + case QEMU_OPTION_machine: + olist = qemu_find_opts("machine"); + qemu_opts_reset(olist); + opts = qemu_opts_parse(olist, optarg, 1); + if (!opts) { + fprintf(stderr, "parse error: %s\n", optarg); + exit(1); + } + machine = machine_parse(qemu_opt_get(opts, "type")); 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; + olist = qemu_find_opts("machine"); + qemu_opts_reset(olist); + qemu_opts_parse(olist, "accel=tcg", 0); + break; #ifdef CONFIG_KVM case QEMU_OPTION_no_kvm_irqchip: { kvm_irqchip = 0; @@ -2591,10 +2739,6 @@ kvm_pit_reinject = 0; break; } - case QEMU_OPTION_enable_nesting: { - kvm_nested = 1; - break; - } #endif case QEMU_OPTION_usb: usb_enabled = 1; @@ -2625,9 +2769,14 @@ } break; case QEMU_OPTION_vnc: +#ifdef CONFIG_VNC display_remote++; - vnc_display = optarg; - break; + vnc_display = optarg; +#else + fprintf(stderr, "VNC support is disabled\n"); + exit(1); +#endif + break; case QEMU_OPTION_no_acpi: acpi_enabled = 0; break; @@ -2821,6 +2970,22 @@ } loc_set_none(); + /* Open the logfile at this point, if necessary. We can't open the logfile + * when encountering either of the logging options (-d or -D) because the + * other one may be encountered later on the command line, changing the + * location or level of logging. + */ + if (log_mask) { + if (log_file) { + set_cpu_log_filename(log_file); + } + set_cpu_log(log_mask); + } + + if (!st_init(trace_file)) { + fprintf(stderr, "warning: unable to initialize simple trace backend\n"); + } + /* If no data_dir is specified then try to find it relative to the executable path. */ if (!data_dir) { @@ -2831,12 +2996,6 @@ 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. @@ -2852,6 +3011,28 @@ exit(1); } + /* + * Get the default machine options from the machine if it is not already + * specified either by the configuration file or by the command line. + */ + if (machine->default_machine_opts) { + QemuOptsList *list = qemu_find_opts("machine"); + const char *p = NULL; + + if (!QTAILQ_EMPTY(&list->head)) { + p = qemu_opt_get(QTAILQ_FIRST(&list->head), "accel"); + } + if (p == NULL) { + qemu_opts_reset(list); + opts = qemu_opts_parse(list, machine->default_machine_opts, 0); + if (!opts) { + fprintf(stderr, "parse error for machine %s: %s\n", + machine->name, machine->default_machine_opts); + exit(1); + } + } + } + qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, 0); qemu_opts_foreach(qemu_find_opts("global"), default_driver_check, NULL, 0); @@ -2922,21 +3103,7 @@ exit(1); } - if (kvm_allowed) { - int ret = kvm_init(); - if (ret < 0) { - 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 = ret >= 0; - } + configure_accelerator(); if (qemu_init_main_loop()) { fprintf(stderr, "qemu_init_main_loop failed\n"); @@ -3000,8 +3167,8 @@ if (nb_numa_nodes > 0) { int i; - if (nb_numa_nodes > smp_cpus) { - nb_numa_nodes = smp_cpus; + if (nb_numa_nodes > MAX_NODES) { + nb_numa_nodes = MAX_NODES; } /* If no memory size if given for any node, assume the default case @@ -3073,9 +3240,6 @@ cpu_synchronize_all_post_init(); - /* must be after terminal init, SDL library changes signal handlers */ - os_setup_signal_handling(); - set_numa_modes(); current_machine = machine; @@ -3100,12 +3264,14 @@ if (display_type == DT_DEFAULT && !display_remote) { #if defined(CONFIG_SDL) || defined(CONFIG_COCOA) display_type = DT_SDL; -#else +#elif defined(CONFIG_VNC) vnc_display = "localhost:0,to=99"; show_vnc_port = 1; +#else + display_type = DT_NONE; #endif } - + /* init local displays */ switch (display_type) { @@ -3129,6 +3295,10 @@ break; } + /* must be after terminal init, SDL library changes signal handlers */ + os_setup_signal_handling(); + +#ifdef CONFIG_VNC /* init remote displays */ if (vnc_display) { vnc_display_init(ds); @@ -3139,6 +3309,7 @@ printf("VNC server running on `%s'\n", vnc_display_local_addr(ds)); } } +#endif #ifdef CONFIG_SPICE if (using_spice && !qxl_enabled) { qemu_spice_display_init(ds); @@ -3150,15 +3321,15 @@ 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)); + ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds); + qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock)); break; } dcl = dcl->next; } 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)); + nographic_timer = qemu_new_timer_ms(rt_clock, nographic_update, NULL); + qemu_mod_timer(nographic_timer, qemu_get_clock_ms(rt_clock)); } text_consoles_set_display(ds); @@ -3180,7 +3351,7 @@ qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); qemu_run_machine_init_done_notifiers(); - qemu_system_reset(); + qemu_system_reset(VMRESET_SILENT); if (loadvm) { if (load_vmstate(loadvm) < 0) { autostart = 0; diff -Nru qemu-kvm-0.14.1+noroms/xen-all.c qemu-kvm-0.15.0+noroms/xen-all.c --- qemu-kvm-0.14.1+noroms/xen-all.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/xen-all.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,955 @@ +/* + * Copyright (C) 2010 Citrix Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include + +#include "hw/pci.h" +#include "hw/pc.h" +#include "hw/xen_common.h" +#include "hw/xen_backend.h" + +#include "range.h" +#include "xen-mapcache.h" +#include "trace.h" + +#include +#include + +//#define DEBUG_XEN + +#ifdef DEBUG_XEN +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "xen: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/* Compatibility with older version */ +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x0003020a +static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) +{ + return shared_page->vcpu_iodata[i].vp_eport; +} +static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) +{ + return &shared_page->vcpu_iodata[vcpu].vp_ioreq; +} +# define FMT_ioreq_size PRIx64 +#else +static inline uint32_t xen_vcpu_eport(shared_iopage_t *shared_page, int i) +{ + return shared_page->vcpu_ioreq[i].vp_eport; +} +static inline ioreq_t *xen_vcpu_ioreq(shared_iopage_t *shared_page, int vcpu) +{ + return &shared_page->vcpu_ioreq[vcpu]; +} +# define FMT_ioreq_size "u" +#endif + +#define BUFFER_IO_MAX_DELAY 100 + +typedef struct XenPhysmap { + target_phys_addr_t start_addr; + ram_addr_t size; + target_phys_addr_t phys_offset; + + QLIST_ENTRY(XenPhysmap) list; +} XenPhysmap; + +typedef struct XenIOState { + shared_iopage_t *shared_page; + buffered_iopage_t *buffered_io_page; + QEMUTimer *buffered_io_timer; + /* the evtchn port for polling the notification, */ + evtchn_port_t *ioreq_local_port; + /* the evtchn fd for polling */ + XenEvtchn xce_handle; + /* which vcpu we are serving */ + int send_vcpu; + + struct xs_handle *xenstore; + CPUPhysMemoryClient client; + QLIST_HEAD(, XenPhysmap) physmap; + const XenPhysmap *log_for_dirtybit; + + Notifier exit; +} XenIOState; + +/* Xen specific function for piix pci */ + +int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +{ + return irq_num + ((pci_dev->devfn >> 3) << 2); +} + +void xen_piix3_set_irq(void *opaque, int irq_num, int level) +{ + xc_hvm_set_pci_intx_level(xen_xc, xen_domid, 0, 0, irq_num >> 2, + irq_num & 3, level); +} + +void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) +{ + int i; + + /* Scan for updates to PCI link routes (0x60-0x63). */ + for (i = 0; i < len; i++) { + uint8_t v = (val >> (8 * i)) & 0xff; + if (v & 0x80) { + v = 0; + } + v &= 0xf; + if (((address + i) >= 0x60) && ((address + i) <= 0x63)) { + xc_hvm_set_pci_link_route(xen_xc, xen_domid, address + i - 0x60, v); + } + } +} + +void xen_cmos_set_s3_resume(void *opaque, int irq, int level) +{ + pc_cmos_set_s3_resume(opaque, irq, level); + if (level) { + xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 3); + } +} + +/* Xen Interrupt Controller */ + +static void xen_set_irq(void *opaque, int irq, int level) +{ + xc_hvm_set_isa_irq_level(xen_xc, xen_domid, irq, level); +} + +qemu_irq *xen_interrupt_controller_init(void) +{ + return qemu_allocate_irqs(xen_set_irq, NULL, 16); +} + +/* Memory Ops */ + +static void xen_ram_init(ram_addr_t ram_size) +{ + RAMBlock *new_block; + ram_addr_t below_4g_mem_size, above_4g_mem_size = 0; + + new_block = qemu_mallocz(sizeof (*new_block)); + pstrcpy(new_block->idstr, sizeof (new_block->idstr), "xen.ram"); + new_block->host = NULL; + new_block->offset = 0; + new_block->length = ram_size; + + QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next); + + ram_list.phys_dirty = qemu_realloc(ram_list.phys_dirty, + new_block->length >> TARGET_PAGE_BITS); + memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), + 0xff, new_block->length >> TARGET_PAGE_BITS); + + if (ram_size >= 0xe0000000 ) { + above_4g_mem_size = ram_size - 0xe0000000; + below_4g_mem_size = 0xe0000000; + } else { + below_4g_mem_size = ram_size; + } + + cpu_register_physical_memory(0, below_4g_mem_size, new_block->offset); +#if TARGET_PHYS_ADDR_BITS > 32 + if (above_4g_mem_size > 0) { + cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, + new_block->offset + below_4g_mem_size); + } +#endif +} + +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size) +{ + unsigned long nr_pfn; + xen_pfn_t *pfn_list; + int i; + + trace_xen_ram_alloc(ram_addr, size); + + nr_pfn = size >> TARGET_PAGE_BITS; + pfn_list = qemu_malloc(sizeof (*pfn_list) * nr_pfn); + + for (i = 0; i < nr_pfn; i++) { + pfn_list[i] = (ram_addr >> TARGET_PAGE_BITS) + i; + } + + if (xc_domain_populate_physmap_exact(xen_xc, xen_domid, nr_pfn, 0, 0, pfn_list)) { + hw_error("xen: failed to populate ram at %lx", ram_addr); + } + + qemu_free(pfn_list); +} + +static XenPhysmap *get_physmapping(XenIOState *state, + target_phys_addr_t start_addr, ram_addr_t size) +{ + XenPhysmap *physmap = NULL; + + start_addr &= TARGET_PAGE_MASK; + + QLIST_FOREACH(physmap, &state->physmap, list) { + if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { + return physmap; + } + } + return NULL; +} + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 340 +static int xen_add_to_physmap(XenIOState *state, + target_phys_addr_t start_addr, + ram_addr_t size, + target_phys_addr_t phys_offset) +{ + unsigned long i = 0; + int rc = 0; + XenPhysmap *physmap = NULL; + target_phys_addr_t pfn, start_gpfn; + RAMBlock *block; + + if (get_physmapping(state, start_addr, size)) { + return 0; + } + if (size <= 0) { + return -1; + } + + /* Xen can only handle a single dirty log region for now and we want + * the linear framebuffer to be that region. + * Avoid tracking any regions that is not videoram and avoid tracking + * the legacy vga region. */ + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (!strcmp(block->idstr, "vga.vram") && block->offset == phys_offset + && start_addr > 0xbffff) { + goto go_physmap; + } + } + return -1; + +go_physmap: + DPRINTF("mapping vram to %llx - %llx, from %llx\n", + start_addr, start_addr + size, phys_offset); + + pfn = phys_offset >> TARGET_PAGE_BITS; + start_gpfn = start_addr >> TARGET_PAGE_BITS; + for (i = 0; i < size >> TARGET_PAGE_BITS; i++) { + unsigned long idx = pfn + i; + xen_pfn_t gpfn = start_gpfn + i; + + rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); + if (rc) { + DPRINTF("add_to_physmap MFN %"PRI_xen_pfn" to PFN %" + PRI_xen_pfn" failed: %d\n", idx, gpfn, rc); + return -rc; + } + } + + physmap = qemu_malloc(sizeof (XenPhysmap)); + + physmap->start_addr = start_addr; + physmap->size = size; + physmap->phys_offset = phys_offset; + + QLIST_INSERT_HEAD(&state->physmap, physmap, list); + + xc_domain_pin_memory_cacheattr(xen_xc, xen_domid, + start_addr >> TARGET_PAGE_BITS, + (start_addr + size) >> TARGET_PAGE_BITS, + XEN_DOMCTL_MEM_CACHEATTR_WB); + return 0; +} + +static int xen_remove_from_physmap(XenIOState *state, + target_phys_addr_t start_addr, + ram_addr_t size) +{ + unsigned long i = 0; + int rc = 0; + XenPhysmap *physmap = NULL; + target_phys_addr_t phys_offset = 0; + + physmap = get_physmapping(state, start_addr, size); + if (physmap == NULL) { + return -1; + } + + phys_offset = physmap->phys_offset; + size = physmap->size; + + DPRINTF("unmapping vram to %llx - %llx, from %llx\n", + phys_offset, phys_offset + size, start_addr); + + size >>= TARGET_PAGE_BITS; + start_addr >>= TARGET_PAGE_BITS; + phys_offset >>= TARGET_PAGE_BITS; + for (i = 0; i < size; i++) { + unsigned long idx = start_addr + i; + xen_pfn_t gpfn = phys_offset + i; + + rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); + if (rc) { + fprintf(stderr, "add_to_physmap MFN %"PRI_xen_pfn" to PFN %" + PRI_xen_pfn" failed: %d\n", idx, gpfn, rc); + return -rc; + } + } + + QLIST_REMOVE(physmap, list); + if (state->log_for_dirtybit == physmap) { + state->log_for_dirtybit = NULL; + } + free(physmap); + + return 0; +} + +#else +static int xen_add_to_physmap(XenIOState *state, + target_phys_addr_t start_addr, + ram_addr_t size, + target_phys_addr_t phys_offset) +{ + return -ENOSYS; +} + +static int xen_remove_from_physmap(XenIOState *state, + target_phys_addr_t start_addr, + ram_addr_t size) +{ + return -ENOSYS; +} +#endif + +static void xen_client_set_memory(struct CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset, + bool log_dirty) +{ + XenIOState *state = container_of(client, XenIOState, client); + ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; + hvmmem_type_t mem_type; + + if (!(start_addr != phys_offset + && ( (log_dirty && flags < IO_MEM_UNASSIGNED) + || (!log_dirty && flags == IO_MEM_UNASSIGNED)))) { + return; + } + + trace_xen_client_set_memory(start_addr, size, phys_offset, log_dirty); + + start_addr &= TARGET_PAGE_MASK; + size = TARGET_PAGE_ALIGN(size); + phys_offset &= TARGET_PAGE_MASK; + + switch (flags) { + case IO_MEM_RAM: + xen_add_to_physmap(state, start_addr, size, phys_offset); + break; + case IO_MEM_ROM: + mem_type = HVMMEM_ram_ro; + if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type, + start_addr >> TARGET_PAGE_BITS, + size >> TARGET_PAGE_BITS)) { + DPRINTF("xc_hvm_set_mem_type error, addr: "TARGET_FMT_plx"\n", + start_addr); + } + break; + case IO_MEM_UNASSIGNED: + if (xen_remove_from_physmap(state, start_addr, size) < 0) { + DPRINTF("physmapping does not exist at "TARGET_FMT_plx"\n", start_addr); + } + break; + } +} + +static int xen_sync_dirty_bitmap(XenIOState *state, + target_phys_addr_t start_addr, + ram_addr_t size) +{ + target_phys_addr_t npages = size >> TARGET_PAGE_BITS; + target_phys_addr_t vram_offset = 0; + const int width = sizeof(unsigned long) * 8; + unsigned long bitmap[(npages + width - 1) / width]; + int rc, i, j; + const XenPhysmap *physmap = NULL; + + physmap = get_physmapping(state, start_addr, size); + if (physmap == NULL) { + /* not handled */ + return -1; + } + + if (state->log_for_dirtybit == NULL) { + state->log_for_dirtybit = physmap; + } else if (state->log_for_dirtybit != physmap) { + return -1; + } + vram_offset = physmap->phys_offset; + + rc = xc_hvm_track_dirty_vram(xen_xc, xen_domid, + start_addr >> TARGET_PAGE_BITS, npages, + bitmap); + if (rc) { + return rc; + } + + for (i = 0; i < ARRAY_SIZE(bitmap); i++) { + unsigned long map = bitmap[i]; + while (map != 0) { + j = ffsl(map) - 1; + map &= ~(1ul << j); + cpu_physical_memory_set_dirty(vram_offset + (i * width + j) * TARGET_PAGE_SIZE); + }; + } + + return 0; +} + +static int xen_log_start(CPUPhysMemoryClient *client, target_phys_addr_t phys_addr, ram_addr_t size) +{ + XenIOState *state = container_of(client, XenIOState, client); + + return xen_sync_dirty_bitmap(state, phys_addr, size); +} + +static int xen_log_stop(CPUPhysMemoryClient *client, target_phys_addr_t phys_addr, ram_addr_t size) +{ + XenIOState *state = container_of(client, XenIOState, client); + + state->log_for_dirtybit = NULL; + /* Disable dirty bit tracking */ + return xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL); +} + +static int xen_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + target_phys_addr_t end_addr) +{ + XenIOState *state = container_of(client, XenIOState, client); + + return xen_sync_dirty_bitmap(state, start_addr, end_addr - start_addr); +} + +static int xen_client_migration_log(struct CPUPhysMemoryClient *client, + int enable) +{ + return 0; +} + +static CPUPhysMemoryClient xen_cpu_phys_memory_client = { + .set_memory = xen_client_set_memory, + .sync_dirty_bitmap = xen_client_sync_dirty_bitmap, + .migration_log = xen_client_migration_log, + .log_start = xen_log_start, + .log_stop = xen_log_stop, +}; + +/* VCPU Operations, MMIO, IO ring ... */ + +static void xen_reset_vcpu(void *opaque) +{ + CPUState *env = opaque; + + env->halted = 1; +} + +void xen_vcpu_init(void) +{ + CPUState *first_cpu; + + if ((first_cpu = qemu_get_cpu(0))) { + qemu_register_reset(xen_reset_vcpu, first_cpu); + xen_reset_vcpu(first_cpu); + } +} + +/* get the ioreq packets from share mem */ +static ioreq_t *cpu_get_ioreq_from_shared_memory(XenIOState *state, int vcpu) +{ + ioreq_t *req = xen_vcpu_ioreq(state->shared_page, vcpu); + + if (req->state != STATE_IOREQ_READY) { + DPRINTF("I/O request not ready: " + "%x, ptr: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %" FMT_ioreq_size ", size: %" FMT_ioreq_size "\n", + req->state, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + return NULL; + } + + xen_rmb(); /* see IOREQ_READY /then/ read contents of ioreq */ + + req->state = STATE_IOREQ_INPROCESS; + return req; +} + +/* use poll to get the port notification */ +/* ioreq_vec--out,the */ +/* retval--the number of ioreq packet */ +static ioreq_t *cpu_get_ioreq(XenIOState *state) +{ + int i; + evtchn_port_t port; + + port = xc_evtchn_pending(state->xce_handle); + if (port != -1) { + for (i = 0; i < smp_cpus; i++) { + if (state->ioreq_local_port[i] == port) { + break; + } + } + + if (i == smp_cpus) { + hw_error("Fatal error while trying to get io event!\n"); + } + + /* unmask the wanted port again */ + xc_evtchn_unmask(state->xce_handle, port); + + /* get the io packet from shared memory */ + state->send_vcpu = i; + return cpu_get_ioreq_from_shared_memory(state, i); + } + + /* read error or read nothing */ + return NULL; +} + +static uint32_t do_inp(pio_addr_t addr, unsigned long size) +{ + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + default: + hw_error("inp: bad size: %04"FMT_pioaddr" %lx", addr, size); + } +} + +static void do_outp(pio_addr_t addr, + unsigned long size, uint32_t val) +{ + switch (size) { + case 1: + return cpu_outb(addr, val); + case 2: + return cpu_outw(addr, val); + case 4: + return cpu_outl(addr, val); + default: + hw_error("outp: bad size: %04"FMT_pioaddr" %lx", addr, size); + } +} + +static void cpu_ioreq_pio(ioreq_t *req) +{ + int i, sign; + + sign = req->df ? -1 : 1; + + if (req->dir == IOREQ_READ) { + if (!req->data_is_ptr) { + req->data = do_inp(req->addr, req->size); + } else { + uint32_t tmp; + + for (i = 0; i < req->count; i++) { + tmp = do_inp(req->addr, req->size); + cpu_physical_memory_write(req->data + (sign * i * req->size), + (uint8_t *) &tmp, req->size); + } + } + } else if (req->dir == IOREQ_WRITE) { + if (!req->data_is_ptr) { + do_outp(req->addr, req->size, req->data); + } else { + for (i = 0; i < req->count; i++) { + uint32_t tmp = 0; + + cpu_physical_memory_read(req->data + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + do_outp(req->addr, req->size, tmp); + } + } + } +} + +static void cpu_ioreq_move(ioreq_t *req) +{ + int i, sign; + + sign = req->df ? -1 : 1; + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_read(req->addr + (sign * i * req->size), + (uint8_t *) &req->data, req->size); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_write(req->addr + (sign * i * req->size), + (uint8_t *) &req->data, req->size); + } + } + } else { + target_ulong tmp; + + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_read(req->addr + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + cpu_physical_memory_write(req->data + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + cpu_physical_memory_read(req->data + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + cpu_physical_memory_write(req->addr + (sign * i * req->size), + (uint8_t*) &tmp, req->size); + } + } + } +} + +static void handle_ioreq(ioreq_t *req) +{ + if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) && + (req->size < sizeof (target_ulong))) { + req->data &= ((target_ulong) 1 << (8 * req->size)) - 1; + } + + switch (req->type) { + case IOREQ_TYPE_PIO: + cpu_ioreq_pio(req); + break; + case IOREQ_TYPE_COPY: + cpu_ioreq_move(req); + break; + case IOREQ_TYPE_TIMEOFFSET: + break; + case IOREQ_TYPE_INVALIDATE: + xen_invalidate_map_cache(); + break; + default: + hw_error("Invalid ioreq type 0x%x\n", req->type); + } +} + +static void handle_buffered_iopage(XenIOState *state) +{ + buf_ioreq_t *buf_req = NULL; + ioreq_t req; + int qw; + + if (!state->buffered_io_page) { + return; + } + + while (state->buffered_io_page->read_pointer != state->buffered_io_page->write_pointer) { + buf_req = &state->buffered_io_page->buf_ioreq[ + state->buffered_io_page->read_pointer % IOREQ_BUFFER_SLOT_NUM]; + req.size = 1UL << buf_req->size; + req.count = 1; + req.addr = buf_req->addr; + req.data = buf_req->data; + req.state = STATE_IOREQ_READY; + req.dir = buf_req->dir; + req.df = 1; + req.type = buf_req->type; + req.data_is_ptr = 0; + qw = (req.size == 8); + if (qw) { + buf_req = &state->buffered_io_page->buf_ioreq[ + (state->buffered_io_page->read_pointer + 1) % IOREQ_BUFFER_SLOT_NUM]; + req.data |= ((uint64_t)buf_req->data) << 32; + } + + handle_ioreq(&req); + + xen_mb(); + state->buffered_io_page->read_pointer += qw ? 2 : 1; + } +} + +static void handle_buffered_io(void *opaque) +{ + XenIOState *state = opaque; + + handle_buffered_iopage(state); + qemu_mod_timer(state->buffered_io_timer, + BUFFER_IO_MAX_DELAY + qemu_get_clock_ms(rt_clock)); +} + +static void cpu_handle_ioreq(void *opaque) +{ + XenIOState *state = opaque; + ioreq_t *req = cpu_get_ioreq(state); + + handle_buffered_iopage(state); + if (req) { + handle_ioreq(req); + + if (req->state != STATE_IOREQ_INPROCESS) { + fprintf(stderr, "Badness in I/O request ... not in service?!: " + "%x, ptr: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %" FMT_ioreq_size ", size: %" FMT_ioreq_size "\n", + req->state, req->data_is_ptr, req->addr, + req->data, req->count, req->size); + destroy_hvm_domain(); + return; + } + + xen_wmb(); /* Update ioreq contents /then/ update state. */ + + /* + * We do this before we send the response so that the tools + * have the opportunity to pick up on the reset before the + * guest resumes and does a hlt with interrupts disabled which + * causes Xen to powerdown the domain. + */ + if (vm_running) { + if (qemu_shutdown_requested_get()) { + destroy_hvm_domain(); + } + if (qemu_reset_requested_get()) { + qemu_system_reset(VMRESET_REPORT); + } + } + + req->state = STATE_IORESP_READY; + xc_evtchn_notify(state->xce_handle, state->ioreq_local_port[state->send_vcpu]); + } +} + +static int store_dev_info(int domid, CharDriverState *cs, const char *string) +{ + struct xs_handle *xs = NULL; + char *path = NULL; + char *newpath = NULL; + char *pts = NULL; + int ret = -1; + + /* Only continue if we're talking to a pty. */ + if (strncmp(cs->filename, "pty:", 4)) { + return 0; + } + pts = cs->filename + 4; + + /* We now have everything we need to set the xenstore entry. */ + xs = xs_open(0); + if (xs == NULL) { + fprintf(stderr, "Could not contact XenStore\n"); + goto out; + } + + path = xs_get_domain_path(xs, domid); + if (path == NULL) { + fprintf(stderr, "xs_get_domain_path() error\n"); + goto out; + } + newpath = realloc(path, (strlen(path) + strlen(string) + + strlen("/tty") + 1)); + if (newpath == NULL) { + fprintf(stderr, "realloc error\n"); + goto out; + } + path = newpath; + + strcat(path, string); + strcat(path, "/tty"); + if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) { + fprintf(stderr, "xs_write for '%s' fail", string); + goto out; + } + ret = 0; + +out: + free(path); + xs_close(xs); + + return ret; +} + +void xenstore_store_pv_console_info(int i, CharDriverState *chr) +{ + if (i == 0) { + store_dev_info(xen_domid, chr, "/console"); + } else { + char buf[32]; + snprintf(buf, sizeof(buf), "/device/console/%d", i); + store_dev_info(xen_domid, chr, buf); + } +} + +static void xenstore_record_dm_state(XenIOState *s, const char *state) +{ + char path[50]; + + snprintf(path, sizeof (path), "/local/domain/0/device-model/%u/state", xen_domid); + if (!xs_write(s->xenstore, XBT_NULL, path, state, strlen(state))) { + fprintf(stderr, "error recording dm state\n"); + exit(1); + } +} + +static void xen_main_loop_prepare(XenIOState *state) +{ + int evtchn_fd = -1; + + if (state->xce_handle != XC_HANDLER_INITIAL_VALUE) { + evtchn_fd = xc_evtchn_fd(state->xce_handle); + } + + state->buffered_io_timer = qemu_new_timer_ms(rt_clock, handle_buffered_io, + state); + qemu_mod_timer(state->buffered_io_timer, qemu_get_clock_ms(rt_clock)); + + if (evtchn_fd != -1) { + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, state); + } + + /* record state running */ + xenstore_record_dm_state(state, "running"); +} + + +/* Initialise Xen */ + +static void xen_vm_change_state_handler(void *opaque, int running, int reason) +{ + XenIOState *state = opaque; + if (running) { + xen_main_loop_prepare(state); + } +} + +static void xen_exit_notifier(Notifier *n, void *data) +{ + XenIOState *state = container_of(n, XenIOState, exit); + + xc_evtchn_close(state->xce_handle); + xs_daemon_close(state->xenstore); +} + +int xen_init(void) +{ + xen_xc = xen_xc_interface_open(0, 0, 0); + if (xen_xc == XC_HANDLER_INITIAL_VALUE) { + xen_be_printf(NULL, 0, "can't open xen interface\n"); + return -1; + } + + return 0; +} + +int xen_hvm_init(void) +{ + int i, rc; + unsigned long ioreq_pfn; + XenIOState *state; + + state = qemu_mallocz(sizeof (XenIOState)); + + state->xce_handle = xen_xc_evtchn_open(NULL, 0); + if (state->xce_handle == XC_HANDLER_INITIAL_VALUE) { + perror("xen: event channel open"); + return -errno; + } + + state->xenstore = xs_daemon_open(); + if (state->xenstore == NULL) { + perror("xen: xenstore open"); + return -errno; + } + + state->exit.notify = xen_exit_notifier; + qemu_add_exit_notifier(&state->exit); + + xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_IOREQ_PFN, &ioreq_pfn); + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + state->shared_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, ioreq_pfn); + if (state->shared_page == NULL) { + hw_error("map shared IO page returned error %d handle=" XC_INTERFACE_FMT, + errno, xen_xc); + } + + xc_get_hvm_param(xen_xc, xen_domid, HVM_PARAM_BUFIOREQ_PFN, &ioreq_pfn); + DPRINTF("buffered io page at pfn %lx\n", ioreq_pfn); + state->buffered_io_page = xc_map_foreign_range(xen_xc, xen_domid, XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, ioreq_pfn); + if (state->buffered_io_page == NULL) { + hw_error("map buffered IO page returned error %d", errno); + } + + state->ioreq_local_port = qemu_mallocz(smp_cpus * sizeof (evtchn_port_t)); + + /* FIXME: how about if we overflow the page here? */ + for (i = 0; i < smp_cpus; i++) { + rc = xc_evtchn_bind_interdomain(state->xce_handle, xen_domid, + xen_vcpu_eport(state->shared_page, i)); + if (rc == -1) { + fprintf(stderr, "bind interdomain ioctl error %d\n", errno); + return -1; + } + state->ioreq_local_port[i] = rc; + } + + /* Init RAM management */ + xen_map_cache_init(); + xen_ram_init(ram_size); + + qemu_add_vm_change_state_handler(xen_vm_change_state_handler, state); + + state->client = xen_cpu_phys_memory_client; + QLIST_INIT(&state->physmap); + cpu_register_phys_memory_client(&state->client); + state->log_for_dirtybit = NULL; + + /* Initialize backend core & drivers */ + if (xen_be_init() != 0) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + exit(1); + } + xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("qdisk", &xen_blkdev_ops); + + return 0; +} + +void destroy_hvm_domain(void) +{ + XenXC xc_handle; + int sts; + + xc_handle = xen_xc_interface_open(0, 0, 0); + if (xc_handle == XC_HANDLER_INITIAL_VALUE) { + fprintf(stderr, "Cannot acquire xenctrl handle\n"); + } else { + sts = xc_domain_shutdown(xc_handle, xen_domid, SHUTDOWN_poweroff); + if (sts != 0) { + fprintf(stderr, "? xc_domain_shutdown failed to issue poweroff, " + "sts %d, %s\n", sts, strerror(errno)); + } else { + fprintf(stderr, "Issued domain %d poweroff\n", xen_domid); + } + xc_interface_close(xc_handle); + } +} diff -Nru qemu-kvm-0.14.1+noroms/xen-mapcache.c qemu-kvm-0.15.0+noroms/xen-mapcache.c --- qemu-kvm-0.14.1+noroms/xen-mapcache.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/xen-mapcache.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "config.h" + +#include + +#include "hw/xen_backend.h" +#include "blockdev.h" +#include "bitmap.h" + +#include +#include + +#include "xen-mapcache.h" +#include "trace.h" + + +//#define MAPCACHE_DEBUG + +#ifdef MAPCACHE_DEBUG +# define DPRINTF(fmt, ...) do { \ + fprintf(stderr, "xen_mapcache: " fmt, ## __VA_ARGS__); \ +} while (0) +#else +# define DPRINTF(fmt, ...) do { } while (0) +#endif + +#if defined(__i386__) +# define MCACHE_BUCKET_SHIFT 16 +# define MCACHE_MAX_SIZE (1UL<<31) /* 2GB Cap */ +#elif defined(__x86_64__) +# define MCACHE_BUCKET_SHIFT 20 +# define MCACHE_MAX_SIZE (1UL<<35) /* 32GB Cap */ +#endif +#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) + +#define mapcache_lock() ((void)0) +#define mapcache_unlock() ((void)0) + +typedef struct MapCacheEntry { + target_phys_addr_t paddr_index; + uint8_t *vaddr_base; + unsigned long *valid_mapping; + uint8_t lock; + target_phys_addr_t size; + struct MapCacheEntry *next; +} MapCacheEntry; + +typedef struct MapCacheRev { + uint8_t *vaddr_req; + target_phys_addr_t paddr_index; + target_phys_addr_t size; + QTAILQ_ENTRY(MapCacheRev) next; +} MapCacheRev; + +typedef struct MapCache { + MapCacheEntry *entry; + unsigned long nr_buckets; + QTAILQ_HEAD(map_cache_head, MapCacheRev) locked_entries; + + /* For most cases (>99.9%), the page address is the same. */ + target_phys_addr_t last_address_index; + uint8_t *last_address_vaddr; + unsigned long max_mcache_size; + unsigned int mcache_bucket_shift; +} MapCache; + +static MapCache *mapcache; + +static inline int test_bits(int nr, int size, const unsigned long *addr) +{ + unsigned long res = find_next_zero_bit(addr, size + nr, nr); + if (res >= nr + size) + return 1; + else + return 0; +} + +void xen_map_cache_init(void) +{ + unsigned long size; + struct rlimit rlimit_as; + + mapcache = qemu_mallocz(sizeof (MapCache)); + + QTAILQ_INIT(&mapcache->locked_entries); + mapcache->last_address_index = -1; + + getrlimit(RLIMIT_AS, &rlimit_as); + if (rlimit_as.rlim_max < MCACHE_MAX_SIZE) { + rlimit_as.rlim_cur = rlimit_as.rlim_max; + } else { + rlimit_as.rlim_cur = MCACHE_MAX_SIZE; + } + + setrlimit(RLIMIT_AS, &rlimit_as); + mapcache->max_mcache_size = rlimit_as.rlim_cur; + + mapcache->nr_buckets = + (((mapcache->max_mcache_size >> XC_PAGE_SHIFT) + + (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >> + (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)); + + size = mapcache->nr_buckets * sizeof (MapCacheEntry); + size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); + DPRINTF("%s, nr_buckets = %lx size %lu\n", __func__, + mapcache->nr_buckets, size); + mapcache->entry = qemu_mallocz(size); +} + +static void xen_remap_bucket(MapCacheEntry *entry, + target_phys_addr_t size, + target_phys_addr_t address_index) +{ + uint8_t *vaddr_base; + xen_pfn_t *pfns; + int *err; + unsigned int i; + target_phys_addr_t nb_pfn = size >> XC_PAGE_SHIFT; + + trace_xen_remap_bucket(address_index); + + pfns = qemu_mallocz(nb_pfn * sizeof (xen_pfn_t)); + err = qemu_mallocz(nb_pfn * sizeof (int)); + + if (entry->vaddr_base != NULL) { + if (munmap(entry->vaddr_base, entry->size) != 0) { + perror("unmap fails"); + exit(-1); + } + } + if (entry->valid_mapping != NULL) { + qemu_free(entry->valid_mapping); + entry->valid_mapping = NULL; + } + + for (i = 0; i < nb_pfn; i++) { + pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i; + } + + vaddr_base = xc_map_foreign_bulk(xen_xc, xen_domid, PROT_READ|PROT_WRITE, + pfns, err, nb_pfn); + if (vaddr_base == NULL) { + perror("xc_map_foreign_bulk"); + exit(-1); + } + + entry->vaddr_base = vaddr_base; + entry->paddr_index = address_index; + entry->size = size; + entry->valid_mapping = (unsigned long *) qemu_mallocz(sizeof(unsigned long) * + BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); + + bitmap_zero(entry->valid_mapping, nb_pfn); + for (i = 0; i < nb_pfn; i++) { + if (!err[i]) { + bitmap_set(entry->valid_mapping, i, 1); + } + } + + qemu_free(pfns); + qemu_free(err); +} + +uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, + uint8_t lock) +{ + MapCacheEntry *entry, *pentry = NULL; + target_phys_addr_t address_index = phys_addr >> MCACHE_BUCKET_SHIFT; + target_phys_addr_t address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1); + target_phys_addr_t __size = size; + + trace_xen_map_cache(phys_addr); + + if (address_index == mapcache->last_address_index && !lock && !__size) { + trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset); + return mapcache->last_address_vaddr + address_offset; + } + + /* size is always a multiple of MCACHE_BUCKET_SIZE */ + if ((address_offset + (__size % MCACHE_BUCKET_SIZE)) > MCACHE_BUCKET_SIZE) + __size += MCACHE_BUCKET_SIZE; + if (__size % MCACHE_BUCKET_SIZE) + __size += MCACHE_BUCKET_SIZE - (__size % MCACHE_BUCKET_SIZE); + if (!__size) + __size = MCACHE_BUCKET_SIZE; + + entry = &mapcache->entry[address_index % mapcache->nr_buckets]; + + while (entry && entry->lock && entry->vaddr_base && + (entry->paddr_index != address_index || entry->size != __size || + !test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT, + entry->valid_mapping))) { + pentry = entry; + entry = entry->next; + } + if (!entry) { + entry = qemu_mallocz(sizeof (MapCacheEntry)); + pentry->next = entry; + xen_remap_bucket(entry, __size, address_index); + } else if (!entry->lock) { + if (!entry->vaddr_base || entry->paddr_index != address_index || + entry->size != __size || + !test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT, + entry->valid_mapping)) { + xen_remap_bucket(entry, __size, address_index); + } + } + + if(!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT, + entry->valid_mapping)) { + mapcache->last_address_index = -1; + trace_xen_map_cache_return(NULL); + return NULL; + } + + mapcache->last_address_index = address_index; + mapcache->last_address_vaddr = entry->vaddr_base; + if (lock) { + MapCacheRev *reventry = qemu_mallocz(sizeof(MapCacheRev)); + entry->lock++; + reventry->vaddr_req = mapcache->last_address_vaddr + address_offset; + reventry->paddr_index = mapcache->last_address_index; + reventry->size = entry->size; + QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next); + } + + trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset); + return mapcache->last_address_vaddr + address_offset; +} + +ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + MapCacheEntry *entry = NULL, *pentry = NULL; + MapCacheRev *reventry; + target_phys_addr_t paddr_index; + target_phys_addr_t size; + int found = 0; + + QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + if (reventry->vaddr_req == ptr) { + paddr_index = reventry->paddr_index; + size = reventry->size; + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, "%s, could not find %p\n", __func__, ptr); + QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, + reventry->vaddr_req); + } + abort(); + return 0; + } + + entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; + while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { + pentry = entry; + entry = entry->next; + } + if (!entry) { + DPRINTF("Trying to find address %p that is not in the mapcache!\n", ptr); + return 0; + } + return (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); +} + +void xen_invalidate_map_cache_entry(uint8_t *buffer) +{ + MapCacheEntry *entry = NULL, *pentry = NULL; + MapCacheRev *reventry; + target_phys_addr_t paddr_index; + target_phys_addr_t size; + int found = 0; + + if (mapcache->last_address_vaddr == buffer) { + mapcache->last_address_index = -1; + } + + QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + if (reventry->vaddr_req == buffer) { + paddr_index = reventry->paddr_index; + size = reventry->size; + found = 1; + break; + } + } + if (!found) { + DPRINTF("%s, could not find %p\n", __func__, buffer); + QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req); + } + return; + } + QTAILQ_REMOVE(&mapcache->locked_entries, reventry, next); + qemu_free(reventry); + + entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; + while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { + pentry = entry; + entry = entry->next; + } + if (!entry) { + DPRINTF("Trying to unmap address %p that is not in the mapcache!\n", buffer); + return; + } + entry->lock--; + if (entry->lock > 0 || pentry == NULL) { + return; + } + + pentry->next = entry->next; + if (munmap(entry->vaddr_base, entry->size) != 0) { + perror("unmap fails"); + exit(-1); + } + qemu_free(entry->valid_mapping); + qemu_free(entry); +} + +void xen_invalidate_map_cache(void) +{ + unsigned long i; + MapCacheRev *reventry; + + /* Flush pending AIO before destroying the mapcache */ + qemu_aio_flush(); + + QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { + DPRINTF("There should be no locked mappings at this time, " + "but "TARGET_FMT_plx" -> %p is present\n", + reventry->paddr_index, reventry->vaddr_req); + } + + mapcache_lock(); + + for (i = 0; i < mapcache->nr_buckets; i++) { + MapCacheEntry *entry = &mapcache->entry[i]; + + if (entry->vaddr_base == NULL) { + continue; + } + + if (munmap(entry->vaddr_base, entry->size) != 0) { + perror("unmap fails"); + exit(-1); + } + + entry->paddr_index = 0; + entry->vaddr_base = NULL; + entry->size = 0; + qemu_free(entry->valid_mapping); + entry->valid_mapping = NULL; + } + + mapcache->last_address_index = -1; + mapcache->last_address_vaddr = NULL; + + mapcache_unlock(); +} diff -Nru qemu-kvm-0.14.1+noroms/xen-mapcache.h qemu-kvm-0.15.0+noroms/xen-mapcache.h --- qemu-kvm-0.14.1+noroms/xen-mapcache.h 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/xen-mapcache.h 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef XEN_MAPCACHE_H +#define XEN_MAPCACHE_H + +#include + +#ifdef CONFIG_XEN + +void xen_map_cache_init(void); +uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, + uint8_t lock); +ram_addr_t xen_ram_addr_from_mapcache(void *ptr); +void xen_invalidate_map_cache_entry(uint8_t *buffer); +void xen_invalidate_map_cache(void); + +#else + +static inline void xen_map_cache_init(void) +{ +} + +static inline uint8_t *xen_map_cache(target_phys_addr_t phys_addr, + target_phys_addr_t size, + uint8_t lock) +{ + abort(); +} + +static inline ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + abort(); +} + +static inline void xen_invalidate_map_cache_entry(uint8_t *buffer) +{ +} + +static inline void xen_invalidate_map_cache(void) +{ +} + +#endif + +#endif /* !XEN_MAPCACHE_H */ diff -Nru qemu-kvm-0.14.1+noroms/xen-stub.c qemu-kvm-0.15.0+noroms/xen-stub.c --- qemu-kvm-0.14.1+noroms/xen-stub.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-kvm-0.15.0+noroms/xen-stub.c 2011-08-09 07:40:29.000000000 -0500 @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Citrix Ltd. + * + * 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 "hw/xen.h" + +void xenstore_store_pv_console_info(int i, CharDriverState *chr) +{ +} + +int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +{ + return -1; +} + +void xen_piix3_set_irq(void *opaque, int irq_num, int level) +{ +} + +void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) +{ +} + +void xen_cmos_set_s3_resume(void *opaque, int irq, int level) +{ +} + +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size) +{ +} + +qemu_irq *xen_interrupt_controller_init(void) +{ + return NULL; +} + +int xen_init(void) +{ + return -ENOSYS; +}