diff options
139 files changed, 5883 insertions, 1096 deletions
diff --git a/CODING_STYLE b/CODING_STYLE index 2c8268ddc2..5ecfa22161 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -1,6 +1,9 @@ Qemu Coding Style ================= +Please use the script checkpatch.pl in the scripts directory to check +patches before submitting. + 1. Whitespace Of course, the most important aspect in any coding style is whitespace. diff --git a/MAINTAINERS b/MAINTAINERS index 4b07192d4e..f20d390d0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -81,8 +81,8 @@ S: Maintained F: target-microblaze/ MIPS -M: qemu-devel@nongnu.org -S: Orphan +M: Aurelien Jarno <aurelien@aurel32.net> +S: Maintained F: target-mips/ PowerPC @@ -96,8 +96,8 @@ S: Maintained F: target-s390x/ SH4 -M: qemu-devel@nongnu.org -S: Orphan +M: Aurelien Jarno <aurelien@aurel32.net> +S: Maintained F: target-sh4/ SPARC @@ -202,7 +202,7 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com> S: Maintained F: hw/etraxfs.c -M86K Machines +M68K Machines ------------- an5206 M: Paul Brook <paul@codesourcery.com> @@ -279,7 +279,7 @@ F: hw/r2d.c Shix M: Magnus Damm <magnus.damm@gmail.com> -S: Oprhan +S: Orphan F: hw/shix.c SPARC Machines @@ -426,3 +426,56 @@ Linux user M: Riku Voipio <riku.voipio@iki.fi> S: Maintained F: linux-user/ + +Tiny Code Generator (TCG) +------------------------- +Common code +M: qemu-devel@nongnu.org +S: Maintained +F: tcg/ + +ARM target +M: Andrzej Zaborowski <balrogg@gmail.com> +S: Maintained +F: tcg/arm/ + +HPPA target +M: Richard Henderson <rth@twiddle.net> +S: Maintained +F: tcg/hppa/ + +i386 target +M: qemu-devel@nongnu.org +S: Maintained +F: tcg/i386/ + +IA64 target +M: Aurelien Jarno <aurelien@aurel32.net> +S: Maintained +F: tcg/ia64/ + +MIPS target +M: Aurelien Jarno <aurelien@aurel32.ne> +S: Maintained +F: tcg/mips/ + +PPC +M: Vassili Karpov (malc) <av1474@comtv.ru> +S: Maintained +F: tcg/ppc/ + +PPC64 target +M: Vassili Karpov (malc) <av1474@comtv.ru> +S: Maintained +F: tcg/ppc64/ + +S390 target +M: Alexander Graf <agraf@suse.de> +M: Richard Henderson <rth@twiddle.net> +S: Maintained +F: tcg/s390/ + +SPARC target +M: Blue Swirl <blauwirbel@gmail.com> +S: Maintained +F: tcg/sparc/ @@ -47,7 +47,7 @@ config-all-devices.mak: $(SUBDIR_DEVICES_MAK) -include $(SUBDIR_DEVICES_MAK_DEP) %/config-devices.mak: default-configs/%.mak - $(call quiet-command,$(SHELL) $(SRC_PATH)/make_device_config.sh $@ $<, " GEN $@") + $(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $@ $<, " GEN $@") @if test -f $@; then \ if cmp -s $@.old $@; then \ mv $@.tmp $@; \ @@ -76,7 +76,7 @@ build-all: $(DOCS) $(TOOLS) recurse-all config-host.h: config-host.h-timestamp config-host.h-timestamp: config-host.mak qemu-options.def: $(SRC_PATH)/qemu-options.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) @@ -118,12 +118,12 @@ else trace.h: trace.h-timestamp endif trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak - $(call quiet-command,sh $(SRC_PATH)/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h") + $(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)/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c") + $(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) @@ -136,7 +136,7 @@ trace-dtrace.h: trace-dtrace.dtrace # 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)/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace") + $(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) @@ -160,7 +160,7 @@ qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-ob qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS) @@ -282,32 +282,32 @@ TEXIFLAG=$(if $(V),,--quiet) $(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<," GEN $@") qemu-options.texi: $(SRC_PATH)/qemu-options.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -q < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@") qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi $(call quiet-command, \ - perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu.pod && \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \ " GEN $@") qemu-img.1: qemu-img.texi qemu-img-cmds.texi $(call quiet-command, \ - perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-img.pod && \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \ pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \ " GEN $@") qemu-nbd.8: qemu-nbd.texi $(call quiet-command, \ - perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod && \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \ pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ " GEN $@") diff --git a/Makefile.target b/Makefile.target index e15b1c4fb6..cd2abde394 100644 --- a/Makefile.target +++ b/Makefile.target @@ -52,7 +52,7 @@ TARGET_TYPE=system endif $(QEMU_PROG).stp: - $(call quiet-command,sh $(SRC_PATH)/tracetool \ + $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \ --$(TRACE_BACKEND) \ --binary $(bindir)/$(QEMU_PROG) \ --target-arch $(TARGET_ARCH) \ @@ -344,14 +344,14 @@ $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)) -gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/feature_to_c.sh - $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@") +gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh + $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@") hmp-commands.h: $(SRC_PATH)/hmp-commands.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") qmp-commands.h: $(SRC_PATH)/qmp-commands.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") clean: rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o @@ -4101,6 +4101,30 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) addresses, since the addend is not currently pc-relative. */ pc = 0; + /* We include the hexdump of the instruction. The format here + matches that used by objdump and the ARM ARM (in particular, + 32 bit Thumb instructions are displayed as pairs of halfwords, + not as a single word.) */ + if (is_thumb) + { + if (size == 2) + { + info->fprintf_func(info->stream, "%04lx ", + ((unsigned long)given) & 0xffff); + } + else + { + info->fprintf_func(info->stream, "%04lx %04lx ", + (((unsigned long)given) >> 16) & 0xffff, + ((unsigned long)given) & 0xffff); + } + } + else + { + info->fprintf_func(info->stream, "%08lx ", + ((unsigned long)given) & 0xffffffff); + } + printer (pc, info, given); if (is_thumb) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 727b9f84c3..4d720146df 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -1097,7 +1097,7 @@ static int alsa_run_in (HWVoiceIn *hw) } } - hw->conv (dst, src, nread, &nominal_volume); + hw->conv (dst, src, nread); src = advance (src, nread << hwshift); dst += nread; diff --git a/audio/audio.c b/audio/audio.c index 17074469b2..1729c0be2c 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -104,7 +104,7 @@ static struct { static AudioState glob_audio_state; -struct mixeng_volume nominal_volume = { +const struct mixeng_volume nominal_volume = { .mute = 0, #ifdef FLOAT_MIXENG .r = 1.0, @@ -702,13 +702,11 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) /* * Capture */ -static void noop_conv (struct st_sample *dst, const void *src, - int samples, struct mixeng_volume *vol) +static void noop_conv (struct st_sample *dst, const void *src, int samples) { (void) src; (void) dst; (void) samples; - (void) vol; } static CaptureVoiceOut *audio_pcm_capture_find_specific ( @@ -956,6 +954,8 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) total += isamp; } + mixeng_volume (sw->buf, ret, &sw->vol); + sw->clip (buf, sw->buf, ret); sw->total_hw_samples_acquired += total; return ret << sw->info.shift; @@ -1037,7 +1037,8 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) swlim = ((int64_t) dead << 32) / sw->ratio; swlim = audio_MIN (swlim, samples); if (swlim) { - sw->conv (sw->buf, buf, swlim, &sw->vol); + sw->conv (sw->buf, buf, swlim); + mixeng_volume (sw->buf, swlim, &sw->vol); } while (swlim) { diff --git a/audio/audio_int.h b/audio/audio_int.h index d66f2c3bf6..2003f8bf20 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -211,7 +211,7 @@ extern struct audio_driver esd_audio_driver; extern struct audio_driver pa_audio_driver; extern struct audio_driver spice_audio_driver; extern struct audio_driver winwave_audio_driver; -extern struct mixeng_volume nominal_volume; +extern const struct mixeng_volume nominal_volume; void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index e5479555fa..e2d89fd5d5 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -831,11 +831,11 @@ static int dsound_run_in (HWVoiceIn *hw) decr = len1 + len2; if (p1 && len1) { - hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); + hw->conv (hw->conv_buf + hw->wpos, p1, len1); } if (p2 && len2) { - hw->conv (hw->conv_buf, p2, len2, &nominal_volume); + hw->conv (hw->conv_buf, p2, len2); } dsound_unlock_in (dscb, p1, p2, blen1, blen2); diff --git a/audio/esdaudio.c b/audio/esdaudio.c index 9a1f2f8617..ff97b397d2 100644 --- a/audio/esdaudio.c +++ b/audio/esdaudio.c @@ -346,8 +346,7 @@ static void *qesd_thread_in (void *arg) break; } - hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift, - &nominal_volume); + hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift); wpos = (wpos + chunk) % hw->samples; to_grab -= chunk; } diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c index 7f08e14718..c34cf53567 100644 --- a/audio/fmodaudio.c +++ b/audio/fmodaudio.c @@ -488,10 +488,10 @@ static int fmod_run_in (HWVoiceIn *hw) decr = len1 + len2; if (p1 && blen1) { - hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); + hw->conv (hw->conv_buf + hw->wpos, p1, len1); } if (p2 && len2) { - hw->conv (hw->conv_buf, p2, len2, &nominal_volume); + hw->conv (hw->conv_buf, p2, len2); } fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); diff --git a/audio/mixeng.c b/audio/mixeng.c index 9f1d93fcfc..4a9e8ebe2a 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -333,3 +333,28 @@ void mixeng_clear (struct st_sample *buf, int len) { memset (buf, 0, len * sizeof (struct st_sample)); } + +void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) +{ +#ifdef CONFIG_MIXEMU + if (vol->mute) { + mixeng_clear (buf, len); + return; + } + + while (len--) { +#ifdef FLOAT_MIXENG + buf->l = buf->l * vol->l; + buf->r = buf->r * vol->r; +#else + buf->l = (buf->l * vol->l) >> 32; + buf->r = (buf->r * vol->r) >> 32; +#endif + buf += 1; + } +#else + (void) buf; + (void) len; + (void) vol; +#endif +} diff --git a/audio/mixeng.h b/audio/mixeng.h index 4af1dd9891..9de443b01d 100644 --- a/audio/mixeng.h +++ b/audio/mixeng.h @@ -33,8 +33,7 @@ struct mixeng_volume { int mute; int64_t r; int64_t l; }; struct st_sample { int64_t l; int64_t r; }; #endif -typedef void (t_sample) (struct st_sample *dst, const void *src, - int samples, struct mixeng_volume *vol); +typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); extern t_sample *mixeng_conv[2][2][2][3]; @@ -47,5 +46,6 @@ void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *o int *isamp, int *osamp); void st_rate_stop (void *opaque); void mixeng_clear (struct st_sample *buf, int len); +void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); #endif /* mixeng.h */ diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h index 56177056ce..a2d0ef84fd 100644 --- a/audio/mixeng_template.h +++ b/audio/mixeng_template.h @@ -31,16 +31,6 @@ #define HALF (IN_MAX >> 1) #endif -#ifdef CONFIG_MIXEMU -#ifdef FLOAT_MIXENG -#define VOL(a, b) ((a) * (b)) -#else -#define VOL(a, b) ((a) * (b)) >> 32 -#endif -#else -#define VOL(a, b) a -#endif - #define ET glue (ENDIAN_CONVERSION, glue (_, IN_T)) #ifdef FLOAT_MIXENG @@ -109,40 +99,26 @@ static inline IN_T glue (clip_, ET) (int64_t v) #endif static void glue (glue (conv_, ET), _to_stereo) - (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) + (struct st_sample *dst, const void *src, int samples) { struct st_sample *out = dst; IN_T *in = (IN_T *) src; -#ifdef CONFIG_MIXEMU - if (vol->mute) { - mixeng_clear (dst, samples); - return; - } -#else - (void) vol; -#endif + while (samples--) { - out->l = VOL (glue (conv_, ET) (*in++), vol->l); - out->r = VOL (glue (conv_, ET) (*in++), vol->r); + out->l = glue (conv_, ET) (*in++); + out->r = glue (conv_, ET) (*in++); out += 1; } } static void glue (glue (conv_, ET), _to_mono) - (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) + (struct st_sample *dst, const void *src, int samples) { struct st_sample *out = dst; IN_T *in = (IN_T *) src; -#ifdef CONFIG_MIXEMU - if (vol->mute) { - mixeng_clear (dst, samples); - return; - } -#else - (void) vol; -#endif + while (samples--) { - out->l = VOL (glue (conv_, ET) (in[0]), vol->l); + out->l = glue (conv_, ET) (in[0]); out->r = out->l; out += 1; in += 1; @@ -174,4 +150,3 @@ static void glue (glue (clip_, ET), _from_mono) #undef ET #undef HALF -#undef VOL diff --git a/audio/ossaudio.c b/audio/ossaudio.c index d7a55e5441..b49e102747 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -788,8 +788,7 @@ static int oss_run_in (HWVoiceIn *hw) hw->info.align + 1); } read_samples += nread >> hwshift; - hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift, - &nominal_volume); + hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift); } if (bufs[i].len - nread) { diff --git a/audio/paaudio.c b/audio/paaudio.c index ff71dac2a5..9cf685d30f 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -195,7 +195,7 @@ static void *qpa_thread_in (void *arg) return NULL; } - hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); + hw->conv (hw->conv_buf + wpos, buf, chunk); wpos = (wpos + chunk) % hw->samples; to_grab -= chunk; } diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index 373e4c43ed..a5c0d6bc66 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -268,11 +268,10 @@ static int line_in_run (HWVoiceIn *hw) len[1] = 0; } - hw->conv (hw->conv_buf + hw->wpos, samples, len[0], &nominal_volume); + hw->conv (hw->conv_buf + hw->wpos, samples, len[0]); if (len[1]) { - hw->conv (hw->conv_buf, samples + len[0], len[1], - &nominal_volume); + hw->conv (hw->conv_buf, samples + len[0], len[1]); } hw->wpos = (hw->wpos + num_samples) % hw->samples; diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c index cdf483b89a..e5ad3c6604 100644 --- a/audio/winwaveaudio.c +++ b/audio/winwaveaudio.c @@ -581,8 +581,7 @@ static int winwave_run_in (HWVoiceIn *hw) int conv = audio_MIN (left, decr); hw->conv (hw->conv_buf + hw->wpos, advance (wave->pcm_buf, wave->rpos << hw->info.shift), - conv, - &nominal_volume); + conv); wave->rpos = (wave->rpos + conv) % hw->samples; hw->wpos = (hw->wpos + conv) % hw->samples; diff --git a/block/vpc.c b/block/vpc.c index 21e2a6870c..7b025be01d 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -502,6 +502,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) uint8_t secs_per_cyl = 0; size_t block_size, num_bat_entries; int64_t total_sectors = 0; + int ret = -EIO; // Read out options while (options && options->name) { @@ -521,7 +522,8 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { if (calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl)) { - return -EFBIG; + ret = -EFBIG; + goto fail; } } total_sectors = (int64_t) cyls * heads * secs_per_cyl; @@ -560,22 +562,28 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) block_size = 0x200000; num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); - if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) - return -EIO; + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { + goto fail; + } - if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) - return -EIO; - if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) - return -EIO; + if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) { + goto fail; + } + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { + goto fail; + } // Write the initial BAT - if (lseek(fd, 3 * 512, SEEK_SET) < 0) - return -EIO; + if (lseek(fd, 3 * 512, SEEK_SET) < 0) { + goto fail; + } memset(buf, 0xFF, 512); - for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) - if (write(fd, buf, 512) != 512) - return -EIO; + for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { + if (write(fd, buf, 512) != 512) { + goto fail; + } + } // Prepare the Dynamic Disk Header @@ -592,13 +600,18 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); // Write the header - if (lseek(fd, 512, SEEK_SET) < 0) - return -EIO; - if (write(fd, buf, 1024) != 1024) - return -EIO; + if (lseek(fd, 512, SEEK_SET) < 0) { + goto fail; + } + if (write(fd, buf, 1024) != 1024) { + goto fail; + } + ret = 0; + + fail: close(fd); - return 0; + return ret; } static void vpc_close(BlockDriverState *bs) diff --git a/block/vvfat.c b/block/vvfat.c index 26dd474bb5..fe568fe2c7 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -756,6 +756,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) if (st.st_size > 0x7fffffff) { fprintf(stderr, "File %s is larger than 2GB\n", buffer); free(buffer); + closedir(dir); return -2; } direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 7374912b19..1ef1f972fc 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -1044,7 +1044,7 @@ static void load_symbols(struct elfhdr *hdr, int fd) struct elf_shdr sechdr, symtab, strtab; char *strings; struct syminfo *s; - struct elf_sym *syms; + struct elf_sym *syms, *new_syms; lseek(fd, hdr->e_shoff, SEEK_SET); for (i = 0; i < hdr->e_shnum; i++) { @@ -1072,15 +1072,24 @@ static void load_symbols(struct elfhdr *hdr, int fd) /* Now know where the strtab and symtab are. Snarf them. */ s = malloc(sizeof(*s)); syms = malloc(symtab.sh_size); - if (!syms) + if (!syms) { + free(s); return; + } s->disas_strtab = strings = malloc(strtab.sh_size); - if (!s->disas_strtab) + if (!s->disas_strtab) { + free(s); + free(syms); return; + } lseek(fd, symtab.sh_offset, SEEK_SET); - if (read(fd, syms, symtab.sh_size) != symtab.sh_size) + if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { + free(s); + free(syms); + free(strings); return; + } nsyms = symtab.sh_size / sizeof(struct elf_sym); @@ -1105,13 +1114,29 @@ static void load_symbols(struct elfhdr *hdr, int fd) #endif i++; } - syms = realloc(syms, nsyms * sizeof(*syms)); + + /* Attempt to free the storage associated with the local symbols + that we threw away. Whether or not this has any effect on the + memory allocation depends on the malloc implementation and how + many symbols we managed to discard. */ + new_syms = realloc(syms, nsyms * sizeof(*syms)); + if (new_syms == NULL) { + free(s); + free(syms); + free(strings); + return; + } + syms = new_syms; qsort(syms, nsyms, sizeof(*syms), symcmp); lseek(fd, strtab.sh_offset, SEEK_SET); - if (read(fd, strings, strtab.sh_size) != strtab.sh_size) + if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { + free(s); + free(syms); + free(strings); return; + } s->disas_num_syms = nsyms; #if ELF_CLASS == ELFCLASS32 s->disas_symtab.elf32 = syms; @@ -32,6 +32,12 @@ compile_prog() { $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1 } +# symbolically link $1 to $2. Portable version of "ln -sf". +symlink() { + rm -f $2 + ln -s $1 $2 +} + # check whether a command is available to this shell (may be either an # executable or a builtin) has() { @@ -69,24 +75,17 @@ path_of() { } # default parameters +source_path=`dirname "$0"` cpu="" interp_prefix="/usr/gnemul/qemu-%M" static="no" sparc_cpu="" cross_prefix="" -cc="gcc" audio_drv_list="" audio_card_list="ac97 es1370 sb16 hda" audio_possible_cards="ac97 es1370 sb16 cs4231a adlib gus hda" block_drv_whitelist="" host_cc="gcc" -ar="ar" -make="make" -install="install" -objcopy="objcopy" -ld="ld" -strip="strip" -windres="windres" helper_cflags="" libs_softmmu="" libs_tools="" @@ -94,13 +93,97 @@ audio_pt_int="" audio_win_int="" cc_i386=i386-pc-linux-gnu-gcc +target_list="" + +# Default value for a variable defining feature "foo". +# * foo="no" feature will only be used if --enable-foo arg is given +# * foo="" feature will be searched for, and if found, will be used +# unless --disable-foo is given +# * foo="yes" this value will only be set by --enable-foo flag. +# feature will searched for, +# if not found, configure exits with error +# +# Always add --enable-foo and --disable-foo command line args. +# Distributions want to ensure that several features are compiled in, and it +# is impossible without a --enable-foo that exits if a feature is not found. + +bluez="" +brlapi="" +curl="" +curses="" +docs="" +fdt="" +kvm="" +kvm_para="" +nptl="" +sdl="" +sparse="no" +uuid="" +vde="" +vnc_tls="" +vnc_sasl="" +vnc_jpeg="" +vnc_png="" +vnc_thread="no" +xen="" +linux_aio="" +attr="" +vhost_net="" +xfs="" + +gprof="no" +debug_tcg="no" +debug_mon="no" +debug="no" +strip_opt="yes" +bigendian="no" +mingw32="no" +EXESUF="" +prefix="/usr/local" +mandir="\${prefix}/share/man" +datadir="\${prefix}/share/qemu" +docdir="\${prefix}/share/doc/qemu" +bindir="\${prefix}/bin" +sysconfdir="\${prefix}/etc" +confsuffix="/qemu" +slirp="yes" +fmod_lib="" +fmod_inc="" +oss_lib="" +bsd="no" +linux="no" +solaris="no" +profiler="no" +cocoa="no" +softmmu="yes" +linux_user="no" +darwin_user="no" +bsd_user="no" +guest_base="" +uname_release="" +io_thread="no" +mixemu="no" +kerneldir="" +aix="no" +blobs="yes" +pkgversion="" +check_utests="no" +user_pie="no" +zero_malloc="" +trace_backend="nop" +trace_file="trace" +spice="" +rbd="" + # parse CC options first for opt do optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'` case "$opt" in --cross-prefix=*) cross_prefix="$optarg" ;; - --cc=*) cc="$optarg" + --cc=*) CC="$optarg" + ;; + --source-path=*) source_path="$optarg" ;; --cpu=*) cpu="$optarg" ;; @@ -129,12 +212,14 @@ done # Using uname is really, really broken. Once we have the right set of checks # we can eliminate it's usage altogether -cc="${cross_prefix}${cc}" -ar="${cross_prefix}${ar}" -objcopy="${cross_prefix}${objcopy}" -ld="${cross_prefix}${ld}" -strip="${cross_prefix}${strip}" -windres="${cross_prefix}${windres}" +cc="${cross_prefix}${CC-gcc}" +ar="${cross_prefix}${AR-ar}" +objcopy="${cross_prefix}${OBJCOPY-objcopy}" +ld="${cross_prefix}${LD-ld}" +strip="${cross_prefix}${STRIP-strip}" +windres="${cross_prefix}${WINDRES-windres}" +pkg_config="${cross_prefix}${PKG_CONFIG-pkg-config}" +sdl_config="${cross_prefix}${SDL_CONFIG-sdl-config}" # default flags for all hosts QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS" @@ -143,33 +228,11 @@ QEMU_CFLAGS="-Wall -Wundef -Wendif-labels -Wwrite-strings -Wmissing-prototypes $ 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" -QEMU_CFLAGS="-I. -I\$(SRC_PATH) $QEMU_CFLAGS" +QEMU_INCLUDES="-I. -I\$(SRC_PATH)" LDFLAGS="-g $LDFLAGS" -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" -cat > $TMPC << EOF -int main(void) { return 0; } -EOF -for flag in $gcc_flags; do - if compile_prog "-Werror $QEMU_CFLAGS" "-Werror $flag" ; then - QEMU_CFLAGS="$QEMU_CFLAGS $flag" - fi -done - -# check that the C compiler works. -cat > $TMPC <<EOF -int main(void) {} -EOF - -if compile_object ; then - : C compiler works ok -else - echo "ERROR: \"$cc\" either does not exist or does not work" - exit 1 -fi +# make source path absolute +source_path=`cd "$source_path"; pwd` check_define() { cat > $TMPC <<EOF @@ -216,7 +279,6 @@ else cpu=`uname -m` fi -target_list="" case "$cpu" in alpha|cris|ia64|m68k|microblaze|ppc|ppc64|sparc64) cpu="$cpu" @@ -254,87 +316,6 @@ case "$cpu" in ;; esac -# Default value for a variable defining feature "foo". -# * foo="no" feature will only be used if --enable-foo arg is given -# * foo="" feature will be searched for, and if found, will be used -# unless --disable-foo is given -# * foo="yes" this value will only be set by --enable-foo flag. -# feature will searched for, -# if not found, configure exits with error -# -# Always add --enable-foo and --disable-foo command line args. -# Distributions want to ensure that several features are compiled in, and it -# is impossible without a --enable-foo that exits if a feature is not found. - -bluez="" -brlapi="" -curl="" -curses="" -docs="" -fdt="" -kvm="" -kvm_para="" -nptl="" -sdl="" -sparse="no" -uuid="" -vde="" -vnc_tls="" -vnc_sasl="" -vnc_jpeg="" -vnc_png="" -vnc_thread="no" -xen="" -linux_aio="" -attr="" -vhost_net="" -xfs="" - -gprof="no" -debug_tcg="no" -debug_mon="no" -debug="no" -strip_opt="yes" -bigendian="no" -mingw32="no" -EXESUF="" -prefix="/usr/local" -mandir="\${prefix}/share/man" -datadir="\${prefix}/share/qemu" -docdir="\${prefix}/share/doc/qemu" -bindir="\${prefix}/bin" -sysconfdir="\${prefix}/etc" -confsuffix="/qemu" -slirp="yes" -fmod_lib="" -fmod_inc="" -oss_lib="" -bsd="no" -linux="no" -solaris="no" -profiler="no" -cocoa="no" -softmmu="yes" -linux_user="no" -darwin_user="no" -bsd_user="no" -guest_base="" -uname_release="" -io_thread="no" -mixemu="no" -kerneldir="" -aix="no" -haiku="no" -blobs="yes" -pkgversion="" -check_utests="no" -user_pie="no" -zero_malloc="" -trace_backend="nop" -trace_file="trace" -spice="" -rbd="" - # OS specific if check_define __linux__ ; then targetos="Linux" @@ -369,7 +350,7 @@ GNU/kFreeBSD) ;; FreeBSD) bsd="yes" - make="gmake" + make="${MAKE-gmake}" audio_drv_list="oss" audio_possible_drivers="oss sdl esd pa" # needed for kinfo_getvmmap(3) in libutil.h @@ -377,20 +358,20 @@ FreeBSD) ;; DragonFly) bsd="yes" - make="gmake" + make="${MAKE-gmake}" audio_drv_list="oss" audio_possible_drivers="oss sdl esd pa" ;; NetBSD) bsd="yes" - make="gmake" + make="${MAKE-gmake}" audio_drv_list="oss" audio_possible_drivers="oss sdl esd" oss_lib="-lossaudio" ;; OpenBSD) bsd="yes" - make="gmake" + make="${MAKE-gmake}" audio_drv_list="oss" audio_possible_drivers="oss sdl esd" oss_lib="-lossaudio" @@ -419,8 +400,8 @@ Darwin) ;; SunOS) solaris="yes" - make="gmake" - install="ginstall" + make="${MAKE-gmake}" + install="${INSTALL-ginstall}" ld="gld" needs_libsunmath="no" solarisrev=`uname -r | cut -f2 -d.` @@ -459,7 +440,7 @@ SunOS) ;; AIX) aix="yes" - make="gmake" + make="${MAKE-gmake}" ;; Haiku) haiku="yes" @@ -485,6 +466,9 @@ if [ "$bsd" = "yes" ] ; then bsd_user="yes" fi +: ${make=${MAKE-make}} +: ${install=${INSTALL-install}} + if test "$mingw32" = "yes" ; then EXESUF=".exe" QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS" @@ -500,17 +484,6 @@ if test "$mingw32" = "yes" ; then confsuffix="" fi -# find source path -source_path=`dirname "$0"` -source_path_used="no" -workdir=`pwd` -if [ -z "$source_path" ]; then - source_path=$workdir -else - source_path=`cd "$source_path"; pwd` -fi -[ -f "$workdir/vl.c" ] || source_path_used="yes" - werror="" for opt do @@ -522,8 +495,7 @@ for opt do ;; --interp-prefix=*) interp_prefix="$optarg" ;; - --source-path=*) source_path="$optarg" - source_path_used="yes" + --source-path=*) ;; --cross-prefix=*) ;; @@ -543,9 +515,9 @@ for opt do ;; --target-list=*) target_list="$optarg" ;; - --trace-backend=*) trace_backend="$optarg" + --enable-trace-backend=*) trace_backend="$optarg" ;; - --trace-file=*) trace_file="$optarg" + --with-trace-file=*) trace_file="$optarg" ;; --enable-gprof) gprof="yes" ;; @@ -935,8 +907,8 @@ echo " --enable-docs enable documentation build" echo " --disable-docs disable documentation build" echo " --disable-vhost-net disable vhost-net acceleration support" echo " --enable-vhost-net enable vhost-net acceleration support" -echo " --trace-backend=B Trace backend nop simple ust dtrace" -echo " --trace-file=NAME Full PATH,NAME of file to store traces" +echo " --enable-trace-backend=B Trace backend nop simple ust dtrace" +echo " --with-trace-file=NAME Full PATH,NAME of file to store traces" echo " Default:trace-<pid>" echo " --disable-spice disable spice" echo " --enable-spice enable spice" @@ -946,6 +918,31 @@ echo "NOTE: The object files are built at the place where configure is launched" exit 1 fi +# check that the C compiler works. +cat > $TMPC <<EOF +int main(void) {} +EOF + +if compile_object ; then + : C compiler works ok +else + echo "ERROR: \"$cc\" either does not exist or does not work" + exit 1 +fi + +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" +cat > $TMPC << EOF +int main(void) { return 0; } +EOF +for flag in $gcc_flags; do + if compile_prog "-Werror $QEMU_CFLAGS" "-Werror $flag" ; then + QEMU_CFLAGS="$QEMU_CFLAGS $flag" + fi +done + # # Solaris specific configure tool chain decisions # @@ -1089,13 +1086,15 @@ esac fi -# host long bits test -hostlongbits="32" -case "$cpu" in - x86_64|alpha|ia64|sparc64|ppc64|s390x) - hostlongbits=64 - ;; -esac +# host long bits test, actually a pointer size test +cat > $TMPC << EOF +int sizeof_pointer_is_8[sizeof(void *) == 8 ? 1 : -1]; +EOF +if compile_object; then +hostlongbits=64 +else +hostlongbits=32 +fi ########################################## @@ -1162,12 +1161,11 @@ EOF fi ########################################## -# pkgconfig probe +# pkg-config probe -pkgconfig="${cross_prefix}pkg-config" -if ! has $pkgconfig; then - # likely not cross compiling, or hope for the best - pkgconfig=pkg-config +if ! has $pkg_config; then + echo warning: proceeding without "$pkg_config" >&2 + pkg_config=/bin/false fi ########################################## @@ -1186,21 +1184,17 @@ fi ########################################## # SDL probe -# Look for sdl configuration program (pkg-config or sdl-config). -# Prefer variant with cross prefix if cross compiling, -# and favour pkg-config with sdl over sdl-config. -if test -n "$cross_prefix" -a $pkgconfig != pkg-config && \ - $pkgconfig sdl --modversion >/dev/null 2>&1; then - sdlconfig="$pkgconfig sdl" - _sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'` -elif test -n "$cross_prefix" && has ${cross_prefix}sdl-config; then - sdlconfig="${cross_prefix}sdl-config" - _sdlversion=`$sdlconfig --version | sed 's/[^0-9]//g'` -elif $pkgconfig sdl --modversion >/dev/null 2>&1; then - sdlconfig="$pkgconfig sdl" +# Look for sdl configuration program (pkg-config or sdl-config). Try +# sdl-config even without cross prefix, and favour pkg-config over sdl-config. +if test "`basename $sdl_config`" != sdl-config && ! has ${sdl_config}; then + sdl_config=sdl-config +fi + +if $pkg_config sdl --modversion >/dev/null 2>&1; then + sdlconfig="$pkg_config sdl" _sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'` -elif has sdl-config; then - sdlconfig='sdl-config' +elif has ${sdl_config}; then + sdlconfig="$sdl_config" _sdlversion=`$sdlconfig --version | sed 's/[^0-9]//g'` else if test "$sdl" = "yes" ; then @@ -1208,6 +1202,9 @@ else fi sdl=no fi +if test -n "$cross_prefix" && test "`basename $sdlconfig`" = sdl-config; then + echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2 +fi sdl_too_old=no if test "$sdl" != "no" ; then @@ -1277,8 +1274,8 @@ if test "$vnc_tls" != "no" ; then #include <gnutls/gnutls.h> int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } EOF - vnc_tls_cflags=`$pkgconfig --cflags gnutls 2> /dev/null` - vnc_tls_libs=`$pkgconfig --libs gnutls 2> /dev/null` + vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` + vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then vnc_tls=yes libs_softmmu="$vnc_tls_libs $libs_softmmu" @@ -1592,8 +1589,8 @@ fi ########################################## # curl probe -if $pkgconfig libcurl --modversion >/dev/null 2>&1; then - curlconfig="$pkgconfig libcurl" +if $pkg_config libcurl --modversion >/dev/null 2>&1; then + curlconfig="$pkg_config libcurl" else curlconfig=curl-config fi @@ -1625,7 +1622,7 @@ if test "$check_utests" != "no" ; then #include <check.h> int main(void) { suite_create("qemu test"); return 0; } EOF - check_libs=`$pkgconfig --libs check` + check_libs=`$pkg_config --libs check` if compile_prog "" $check_libs ; then check_utests=yes libs_tools="$check_libs $libs_tools" @@ -1644,8 +1641,8 @@ if test "$bluez" != "no" ; then #include <bluetooth/bluetooth.h> int main(void) { return bt_error(0); } EOF - bluez_cflags=`$pkgconfig --cflags bluez 2> /dev/null` - bluez_libs=`$pkgconfig --libs bluez 2> /dev/null` + bluez_cflags=`$pkg_config --cflags bluez 2> /dev/null` + bluez_libs=`$pkg_config --libs bluez 2> /dev/null` if compile_prog "$bluez_cflags" "$bluez_libs" ; then bluez=yes libs_softmmu="$bluez_libs $libs_softmmu" @@ -1689,7 +1686,7 @@ EOF kvm_cflags="$kvm_cflags -I$kerneldir/arch/$cpu/include" fi else - kvm_cflags=`$pkgconfig --cflags kvm-kmod 2>/dev/null` + kvm_cflags=`$pkg_config --cflags kvm-kmod 2>/dev/null` fi if compile_prog "$kvm_cflags" "" ; then kvm=yes @@ -2090,6 +2087,23 @@ if compile_prog "$ARCH_CFLAGS" "" ; then sync_file_range=yes fi +# check for linux/fiemap.h and FS_IOC_FIEMAP +fiemap=no +cat > $TMPC << EOF +#include <sys/ioctl.h> +#include <linux/fs.h> +#include <linux/fiemap.h> + +int main(void) +{ + ioctl(0, FS_IOC_FIEMAP, 0); + return 0; +} +EOF +if compile_prog "$ARCH_CFLAGS" "" ; then + fiemap=yes +fi + # check for dup3 dup3=no cat > $TMPC << EOF @@ -2252,7 +2266,7 @@ fi ########################################## # check if trace backend exists -sh "$source_path/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null +sh "$source_path/scripts/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null if test "$?" -ne 0 ; then echo echo "Error: invalid trace backend" @@ -2533,7 +2547,7 @@ if test $profiler = "yes" ; then fi if test "$slirp" = "yes" ; then echo "CONFIG_SLIRP=y" >> $config_host_mak - QEMU_CFLAGS="-I\$(SRC_PATH)/slirp $QEMU_CFLAGS" + QEMU_INCLUDES="-I\$(SRC_PATH)/slirp $QEMU_INCLUDES" fi if test "$vde" = "yes" ; then echo "CONFIG_VDE=y" >> $config_host_mak @@ -2631,6 +2645,9 @@ fi if test "$sync_file_range" = "yes" ; then echo "CONFIG_SYNC_FILE_RANGE=y" >> $config_host_mak fi +if test "$fiemap" = "yes" ; then + echo "CONFIG_FIEMAP=y" >> $config_host_mak +fi if test "$dup3" = "yes" ; then echo "CONFIG_DUP3=y" >> $config_host_mak fi @@ -2760,17 +2777,18 @@ echo "INSTALL_PROG=$install -m0755 -p" >> $config_host_mak echo "CC=$cc" >> $config_host_mak echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak -if test "$sparse" = "yes" ; then - echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak - echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak - echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak -fi echo "AR=$ar" >> $config_host_mak echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak echo "WINDRES=$windres" >> $config_host_mak echo "CFLAGS=$CFLAGS" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak +echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak +if test "$sparse" = "yes" ; then + echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak + echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak + echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak +fi echo "HELPER_CFLAGS=$helper_cflags" >> $config_host_mak echo "LDFLAGS=$LDFLAGS" >> $config_host_mak echo "ARLIBS_BEGIN=$arlibs_begin" >> $config_host_mak @@ -2793,8 +2811,7 @@ fi for d in libdis libdis-user; do mkdir -p $d - rm -f $d/Makefile - ln -s $source_path/Makefile.dis $d/Makefile + symlink $source_path/Makefile.dis $d/Makefile echo > $d/config.mak done if test "$static" = "no" -a "$user_pie" = "yes" ; then @@ -2858,12 +2875,7 @@ mkdir -p $target_dir/ide if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" -o "$target" = "arm-bsd-user" -o "$target" = "armeb-bsd-user" ; then mkdir -p $target_dir/nwfpe fi - -# -# don't use ln -sf as not all "ln -sf" over write the file/link -# -rm -f $target_dir/Makefile -ln -s $source_path/Makefile.target $target_dir/Makefile +symlink $source_path/Makefile.target $target_dir/Makefile echo "# Automatically generated by configure - do not modify" > $config_target_mak @@ -3049,11 +3061,11 @@ if test ! -z "$gdb_xml_files" ; then fi case "$target_arch2" in - alpha|arm|armeb|m68k|microblaze|mips|mipsel|mipsn32|mipsn32el|mips64|mips64el|ppc|ppc64|ppc64abi32|ppcemb|s390x|sparc|sparc64|sparc32plus) - echo "CONFIG_SOFTFLOAT=y" >> $config_target_mak + i386|x86_64) + echo "CONFIG_NOSOFTFLOAT=y" >> $config_target_mak ;; *) - echo "CONFIG_NOSOFTFLOAT=y" >> $config_target_mak + echo "CONFIG_SOFTFLOAT=y" >> $config_target_mak ;; esac @@ -3074,19 +3086,20 @@ fi # generate QEMU_CFLAGS/LDFLAGS for targets cflags="" +includes="" ldflags="" if test "$ARCH" = "sparc64" ; then - cflags="-I\$(SRC_PATH)/tcg/sparc $cflags" + includes="-I\$(SRC_PATH)/tcg/sparc $includes" elif test "$ARCH" = "s390x" ; then - cflags="-I\$(SRC_PATH)/tcg/s390 $cflags" + includes="-I\$(SRC_PATH)/tcg/s390 $includes" elif test "$ARCH" = "x86_64" ; then - cflags="-I\$(SRC_PATH)/tcg/i386 $cflags" + includes="-I\$(SRC_PATH)/tcg/i386 $includes" else - cflags="-I\$(SRC_PATH)/tcg/\$(ARCH) $cflags" + includes="-I\$(SRC_PATH)/tcg/\$(ARCH) $includes" fi -cflags="-I\$(SRC_PATH)/tcg $cflags" -cflags="-I\$(SRC_PATH)/fpu $cflags" +includes="-I\$(SRC_PATH)/tcg $includes" +includes="-I\$(SRC_PATH)/fpu $includes" if test "$target_user_only" = "yes" ; then libdis_config_mak=libdis-user/config.mak @@ -3211,31 +3224,25 @@ fi echo "LDFLAGS+=$ldflags" >> $config_target_mak echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak +echo "QEMU_INCLUDES+=$includes" >> $config_target_mak done # for target in $targets -# build tree in object directory if source path is different from current one -if test "$source_path_used" = "yes" ; then - DIRS="tests tests/cris slirp audio block net pc-bios/optionrom" - DIRS="$DIRS roms/seabios roms/vgabios" - DIRS="$DIRS fsdev ui" - FILES="Makefile tests/Makefile" - FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" - FILES="$FILES tests/test-mmap.c" - FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" - FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" - for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do - FILES="$FILES pc-bios/`basename $bios_file`" - done - for dir in $DIRS ; do - mkdir -p $dir - done - # remove the link and recreate it, as not all "ln -sf" overwrite the link - for f in $FILES ; do - rm -f $f - ln -s $source_path/$f $f - done -fi +# build tree in object directory in case the source is not in the current directory +DIRS="tests tests/cris slirp audio block net pc-bios/optionrom" +DIRS="$DIRS roms/seabios roms/vgabios" +DIRS="$DIRS fsdev ui" +FILES="Makefile tests/Makefile" +FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" +FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" +FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" +for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do + FILES="$FILES pc-bios/`basename $bios_file`" +done +mkdir -p $DIRS +for f in $FILES ; do + test -e $f || symlink $source_path/$f $f +done # temporary config to build submodules for rom in seabios vgabios ; do @@ -3247,7 +3254,6 @@ for rom in seabios vgabios ; do echo "CPP=${cross_prefix}cpp" >> $config_mak echo "OBJCOPY=objcopy" >> $config_mak echo "IASL=iasl" >> $config_mak - echo "HOST_CC=$host_cc" >> $config_mak echo "LD=$ld" >> $config_mak done @@ -3255,15 +3261,13 @@ for hwlib in 32 64; do d=libhw$hwlib mkdir -p $d mkdir -p $d/ide - rm -f $d/Makefile - ln -s $source_path/Makefile.hw $d/Makefile + symlink $source_path/Makefile.hw $d/Makefile echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak done d=libuser mkdir -p $d -rm -f $d/Makefile -ln -s $source_path/Makefile.user $d/Makefile +symlink $source_path/Makefile.user $d/Makefile if test "$static" = "no" -a "$user_pie" = "yes" ; then echo "QEMU_CFLAGS+=-fpie" > $d/config.mak fi @@ -307,11 +307,6 @@ void disas(FILE *out, void *code, unsigned long size) #endif for (pc = (unsigned long)code; size > 0; pc += count, size -= count) { fprintf(out, "0x%08lx: ", pc); -#ifdef __arm__ - /* since data is included in the code, it is better to - display code data too */ - fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); -#endif count = print_insn(pc, &disasm_info); fprintf(out, "\n"); if (count < 0) diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index f252c8e3bc..f2f9b757a5 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -97,10 +97,13 @@ The -device argument differs in detail for each kind of drive: * if=virtio - -device virtio-blk-pci,drive=DRIVE-ID,class=C,vectors=V + -device virtio-blk-pci,drive=DRIVE-ID,class=C,vectors=V,ioeventfd=IOEVENTFD This lets you control PCI device class and MSI-X vectors. + IOEVENTFD controls whether or not ioeventfd is used for virtqueue notify. It + can be set to on (default) or off. + As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI device address. @@ -240,6 +243,9 @@ For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI device address, as usual. The old -net nic provides parameter addr for that, it is silently ignored when the NIC is not a PCI device. +For virtio-net-pci, you can control whether or not ioeventfd is used for +virtqueue notify by setting ioeventfd= to on or off (default). + -net nic accepts vectors=V for all models, but it's silently ignored except for virtio-net-pci (model=virtio). With -device, only devices that support it accept it. diff --git a/docs/specs/acpi_pci_hotplug.txt b/docs/specs/acpi_pci_hotplug.txt new file mode 100644 index 0000000000..f0f74a7c7c --- /dev/null +++ b/docs/specs/acpi_pci_hotplug.txt @@ -0,0 +1,37 @@ +QEMU<->ACPI BIOS PCI hotplug interface +-------------------------------------- + +QEMU supports PCI hotplug via ACPI, for PCI bus 0. This document +describes the interface between QEMU and the ACPI BIOS. + +ACPI GPE block (IO ports 0xafe0-0xafe3, byte access): +----------------------------------------- + +Generic ACPI GPE block. Bit 1 (GPE.1) used to notify PCI hotplug/eject +event to ACPI BIOS, via SCI interrupt. + +PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access): +--------------------------------------------------------------- +Slot injection notification pending. One bit per slot. + +Read by ACPI BIOS GPE.1 handler to notify OS of injection +events. + +PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access): +----------------------------------------------------- +Slot removal notification pending. One bit per slot. + +Read by ACPI BIOS GPE.1 handler to notify OS of removal +events. + +PCI device eject (IO port 0xae08-0xae0b, 4-byte access): +---------------------------------------- + +Used by ACPI BIOS _EJ0 method to request device removal. One bit per slot. +Reads return 0. + +PCI removability status (IO port 0xae0c-0xae0f, 4-byte access): +----------------------------------------------- + +Used by ACPI BIOS _RMV method to indicate removability status to OS. One +bit per slot. @@ -531,6 +531,13 @@ static void code_gen_alloc(unsigned long tb_size) /* Cannot map more than that */ if (code_gen_buffer_size > (800 * 1024 * 1024)) code_gen_buffer_size = (800 * 1024 * 1024); +#elif defined(__sparc_v9__) + // Map the buffer below 2G, so we can use direct calls and branches + flags |= MAP_FIXED; + addr = (void *) 0x60000000UL; + if (code_gen_buffer_size > (512 * 1024 * 1024)) { + code_gen_buffer_size = (512 * 1024 * 1024); + } #endif code_gen_buffer = mmap(addr, code_gen_buffer_size, PROT_WRITE | PROT_READ | PROT_EXEC, diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index f293f24356..eb644b2273 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -30,7 +30,7 @@ these four paragraphs for those parts of this code that are retained. =============================================================================*/ -#if defined(TARGET_MIPS) +#if defined(TARGET_MIPS) || defined(TARGET_SH4) #define SNAN_BIT_IS_ONE 1 #else #define SNAN_BIT_IS_ONE 0 @@ -108,7 +108,7 @@ float32 float32_maybe_silence_nan( float32 a_ ) { if (float32_is_signaling_nan(a_)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) return float32_default_nan; # else # error Rules for silencing a signaling NaN are target-specific @@ -278,9 +278,6 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) flag aIsLargerSignificand; bits32 av, bv; - if ( STATUS(default_nan_mode) ) - return float32_default_nan; - aIsQuietNaN = float32_is_quiet_nan( a ); aIsSignalingNaN = float32_is_signaling_nan( a ); bIsQuietNaN = float32_is_quiet_nan( b ); @@ -290,6 +287,9 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( STATUS(default_nan_mode) ) + return float32_default_nan; + if ((bits32)(av<<1) < (bits32)(bv<<1)) { aIsLargerSignificand = 0; } else if ((bits32)(bv<<1) < (bits32)(av<<1)) { @@ -362,7 +362,7 @@ float64 float64_maybe_silence_nan( float64 a_ ) { if (float64_is_signaling_nan(a_)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) return float64_default_nan; # else # error Rules for silencing a signaling NaN are target-specific @@ -423,9 +423,6 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) flag aIsLargerSignificand; bits64 av, bv; - if ( STATUS(default_nan_mode) ) - return float64_default_nan; - aIsQuietNaN = float64_is_quiet_nan( a ); aIsSignalingNaN = float64_is_signaling_nan( a ); bIsQuietNaN = float64_is_quiet_nan( b ); @@ -435,6 +432,9 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( STATUS(default_nan_mode) ) + return float64_default_nan; + if ((bits64)(av<<1) < (bits64)(bv<<1)) { aIsLargerSignificand = 0; } else if ((bits64)(bv<<1) < (bits64)(av<<1)) { @@ -468,7 +468,8 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a -| quiet NaN; otherwise returns 0. +| quiet NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. *----------------------------------------------------------------------------*/ int floatx80_is_quiet_nan( floatx80 a ) @@ -482,19 +483,22 @@ int floatx80_is_quiet_nan( floatx80 a ) && (bits64) ( aLow<<1 ) && ( a.low == aLow ); #else - return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + return ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 ))); #endif } /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a -| signaling NaN; otherwise returns 0. +| signaling NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. *----------------------------------------------------------------------------*/ int floatx80_is_signaling_nan( floatx80 a ) { #if SNAN_BIT_IS_ONE - return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + return ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 ))); #else bits64 aLow; @@ -515,7 +519,7 @@ floatx80 floatx80_maybe_silence_nan( floatx80 a ) { if (floatx80_is_signaling_nan(a)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) a.low = floatx80_default_nan_low; a.high = floatx80_default_nan_high; # else @@ -574,12 +578,6 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; - if ( STATUS(default_nan_mode) ) { - a.low = floatx80_default_nan_low; - a.high = floatx80_default_nan_high; - return a; - } - aIsQuietNaN = floatx80_is_quiet_nan( a ); aIsSignalingNaN = floatx80_is_signaling_nan( a ); bIsQuietNaN = floatx80_is_quiet_nan( b ); @@ -587,6 +585,12 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( STATUS(default_nan_mode) ) { + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + if (a.low < b.low) { aIsLargerSignificand = 0; } else if (b.low < a.low) { @@ -664,7 +668,7 @@ float128 float128_maybe_silence_nan( float128 a ) { if (float128_is_signaling_nan(a)) { #if SNAN_BIT_IS_ONE -# if defined(TARGET_MIPS) +# if defined(TARGET_MIPS) || defined(TARGET_SH4) a.low = float128_default_nan_low; a.high = float128_default_nan_high; # else @@ -719,12 +723,6 @@ static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; - if ( STATUS(default_nan_mode) ) { - a.low = float128_default_nan_low; - a.high = float128_default_nan_high; - return a; - } - aIsQuietNaN = float128_is_quiet_nan( a ); aIsSignalingNaN = float128_is_signaling_nan( a ); bIsQuietNaN = float128_is_quiet_nan( b ); @@ -732,6 +730,12 @@ static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( STATUS(default_nan_mode) ) { + a.low = float128_default_nan_low; + a.high = float128_default_nan_high; + return a; + } + if (lt128(a.high<<1, a.low, b.high<<1, b.low)) { aIsLargerSignificand = 0; } else if (lt128(b.high<<1, b.low, a.high<<1, a.low)) { diff --git a/fpu/softfloat.h b/fpu/softfloat.h index a6d0f16b42..4a5345ceca 100644 --- a/fpu/softfloat.h +++ b/fpu/softfloat.h @@ -336,6 +336,11 @@ INLINE int float32_is_any_nan(float32 a) return ((float32_val(a) & ~(1 << 31)) > 0x7f800000UL); } +INLINE int float32_is_zero_or_denormal(float32 a) +{ + return (float32_val(a) & 0x7f800000) == 0; +} + #define float32_zero make_float32(0) #define float32_one make_float32(0x3f800000) #define float32_ln2 make_float32(0x3f317218) @@ -2391,6 +2391,12 @@ void gdb_exit(CPUState *env, int code) snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); put_packet(s, buf); + +#ifndef CONFIG_USER_ONLY + if (s->chr) { + qemu_chr_close(s->chr); + } +#endif } #ifdef CONFIG_USER_ONLY @@ -38,7 +38,7 @@ int gdbserver_start(int); int gdbserver_start(const char *port); #endif -/* in gdbstub-xml.c, generated by feature_to_c.sh */ +/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ extern const char *const xml_builtin[][2]; #endif diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 173d78148d..5bbc2b5a26 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -37,6 +37,7 @@ #define GPE_BASE 0xafe0 #define PCI_BASE 0xae00 #define PCI_EJ_BASE 0xae08 +#define PCI_RMV_BASE 0xae0c #define PIIX4_PCI_HOTPLUG_STATUS 2 @@ -73,6 +74,7 @@ typedef struct PIIX4PMState { /* for pci hotplug */ struct gpe_regs gpe; struct pci_status pci0_status; + uint32_t pci0_hotplug_enable; } PIIX4PMState; static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s); @@ -322,6 +324,25 @@ static const VMStateDescription vmstate_acpi = { } }; +static void piix4_update_hotplug(PIIX4PMState *s) +{ + PCIDevice *dev = &s->dev; + BusState *bus = qdev_get_parent_bus(&dev->qdev); + DeviceState *qdev, *next; + + s->pci0_hotplug_enable = ~0; + + QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { + PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev); + PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, qdev); + int slot = PCI_SLOT(pdev->devfn); + + if (info->no_hotplug) { + s->pci0_hotplug_enable &= ~(1 << slot); + } + } +} + static void piix4_reset(void *opaque) { PIIX4PMState *s = opaque; @@ -336,6 +357,7 @@ static void piix4_reset(void *opaque) /* Mark SMM as already inited (until KVM supports SMM). */ pci_conf[0x5B] = 0x02; } + piix4_update_hotplug(s); } static void piix4_powerdown(void *opaque, int irq, int power_failing) @@ -428,6 +450,8 @@ static PCIDeviceInfo piix4_pm_info = { .qdev.desc = "PM", .qdev.size = sizeof(PIIX4PMState), .qdev.vmsd = &vmstate_acpi, + .qdev.no_user = 1, + .no_hotplug = 1, .init = piix4_pm_initfn, .config_write = pm_write_config, .qdev.props = (Property[]) { @@ -574,6 +598,18 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val) PIIX4_DPRINTF("pciej write %x <== %d\n", addr, val); } +static uint32_t pcirmv_read(void *opaque, uint32_t addr) +{ + PIIX4PMState *s = opaque; + + return s->pci0_hotplug_enable; +} + +static void pcirmv_write(void *opaque, uint32_t addr, uint32_t val) +{ + return; +} + static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, PCIHotplugState state); @@ -590,6 +626,9 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s) register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, bus); register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, bus); + register_ioport_write(PCI_RMV_BASE, 4, 4, pcirmv_write, s); + register_ioport_read(PCI_RMV_BASE, 4, 4, pcirmv_read, s); + pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev); } diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index bd0664fe7a..d8b062c1bf 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -28,6 +28,22 @@ typedef struct { uint32_t proc_id; } arm_sysctl_state; +static const VMStateDescription vmstate_arm_sysctl = { + .name = "realview_sysctl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(leds, arm_sysctl_state), + VMSTATE_UINT16(lockval, arm_sysctl_state), + VMSTATE_UINT32(cfgdata1, arm_sysctl_state), + VMSTATE_UINT32(cfgdata2, arm_sysctl_state), + VMSTATE_UINT32(flags, arm_sysctl_state), + VMSTATE_UINT32(nvflags, arm_sysctl_state), + VMSTATE_UINT32(resetlevel, arm_sysctl_state), + VMSTATE_END_OF_LIST() + } +}; + static void arm_sysctl_reset(DeviceState *d) { arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, sysbus_from_qdev(d)); @@ -231,6 +247,7 @@ static SysBusDeviceInfo arm_sysctl_info = { .init = arm_sysctl_init1, .qdev.name = "realview_sysctl", .qdev.size = sizeof(arm_sysctl_state), + .qdev.vmsd = &vmstate_arm_sysctl, .qdev.reset = arm_sysctl_reset, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0), diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 75d1cc6f57..5f45b5dee7 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -3140,6 +3140,7 @@ static PCIDeviceInfo cirrus_vga_info = { .qdev.desc = "Cirrus CLGD 54xx VGA", .qdev.size = sizeof(PCICirrusVGAState), .qdev.vmsd = &vmstate_pci_cirrus_vga, + .no_hotplug = 1, .init = pci_cirrus_vga_initfn, .romfile = VGABIOS_CIRRUS_FILENAME, .config_write = pci_cirrus_write_config, diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c index 14c6ad364c..923073be94 100644 --- a/hw/gt64xxx.c +++ b/hw/gt64xxx.c @@ -223,16 +223,14 @@ #define GT_PCI0_HICMASK (0xca4 >> 2) #define GT_PCI1_SERR1MASK (0xca8 >> 2) - -typedef PCIHostState GT64120PCIState; - #define PCI_MAPPING_ENTRY(regname) \ target_phys_addr_t regname ##_start; \ target_phys_addr_t regname ##_length; \ int regname ##_handle typedef struct GT64120State { - GT64120PCIState *pci; + SysBusDevice busdev; + PCIHostState pci; uint32_t regs[GT_REGS]; PCI_MAPPING_ENTRY(PCI0IO); PCI_MAPPING_ENTRY(ISD); @@ -525,13 +523,13 @@ static void gt64120_writel (void *opaque, target_phys_addr_t addr, /* not implemented */ break; case GT_PCI0_CFGADDR: - s->pci->config_reg = val & 0x80fffffc; + s->pci.config_reg = val & 0x80fffffc; break; case GT_PCI0_CFGDATA: - if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci->config_reg & 0x00fff800)) + if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800)) val = bswap32(val); - if (s->pci->config_reg & (1u << 31)) - pci_data_write(s->pci->bus, s->pci->config_reg, val, 4); + if (s->pci.config_reg & (1u << 31)) + pci_data_write(s->pci.bus, s->pci.config_reg, val, 4); break; /* Interrupts */ @@ -765,14 +763,14 @@ static uint32_t gt64120_readl (void *opaque, /* PCI Internal */ case GT_PCI0_CFGADDR: - val = s->pci->config_reg; + val = s->pci.config_reg; break; case GT_PCI0_CFGDATA: - if (!(s->pci->config_reg & (1 << 31))) + if (!(s->pci.config_reg & (1 << 31))) val = 0xffffffff; else - val = pci_data_read(s->pci->bus, s->pci->config_reg, 4); - if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci->config_reg & 0x00fff800)) + val = pci_data_read(s->pci.bus, s->pci.config_reg, 4); + if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800)) val = bswap32(val); break; @@ -864,7 +862,7 @@ static CPUReadMemoryFunc * const gt64120_read[] = { >64120_readl, }; -static int pci_gt64120_map_irq(PCIDevice *pci_dev, int irq_num) +static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num) { int slot; @@ -891,7 +889,7 @@ static int pci_gt64120_map_irq(PCIDevice *pci_dev, int irq_num) static int pci_irq_levels[4]; -static void pci_gt64120_set_irq(void *opaque, int irq_num, int level) +static void gt64120_pci_set_irq(void *opaque, int irq_num, int level) { int i, pic_irq, pic_level; qemu_irq *pic = opaque; @@ -1101,50 +1099,76 @@ static int gt64120_load(QEMUFile* f, void *opaque, int version_id) return 0; } -PCIBus *pci_gt64120_init(qemu_irq *pic) +PCIBus *gt64120_register(qemu_irq *pic) +{ + SysBusDevice *s; + GT64120State *d; + DeviceState *dev; + + dev = qdev_create(NULL, "gt64120"); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + d = FROM_SYSBUS(GT64120State, s); + d->pci.bus = pci_register_bus(&d->busdev.qdev, "pci", + gt64120_pci_set_irq, gt64120_pci_map_irq, + pic, PCI_DEVFN(18, 0), 4); + d->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, d, + DEVICE_NATIVE_ENDIAN); + + pci_create_simple(d->pci.bus, PCI_DEVFN(0, 0), "gt64120_pci"); + return d->pci.bus; +} + +static int gt64120_init(SysBusDevice *dev) { GT64120State *s; - PCIDevice *d; - s = qemu_mallocz(sizeof(GT64120State)); - s->pci = qemu_mallocz(sizeof(GT64120PCIState)); + s = FROM_SYSBUS(GT64120State, dev); - s->pci->bus = pci_register_bus(NULL, "pci", - pci_gt64120_set_irq, pci_gt64120_map_irq, - pic, PCI_DEVFN(18, 0), 4); - s->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, s, - DEVICE_NATIVE_ENDIAN); - d = pci_register_device(s->pci->bus, "GT64120 PCI Bus", sizeof(PCIDevice), - 0, NULL, NULL); + /* FIXME: This value is computed from registers during reset, but some + devices (e.g. VGA card) need to know it when they are registered. + This also mean that changing the register to change the mapping + does not fully work. */ + isa_mem_base = 0x10000000; + qemu_register_reset(gt64120_reset, s); + register_savevm(&dev->qdev, "GT64120 PCI Bus", 0, 1, + gt64120_save, gt64120_load, &s->pci); + return 0; +} +static int gt64120_pci_init(PCIDevice *d) +{ /* FIXME: Malta specific hw assumptions ahead */ - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MARVELL); pci_config_set_device_id(d->config, PCI_DEVICE_ID_MARVELL_GT6412X); - - d->config[0x04] = 0x00; - d->config[0x05] = 0x00; - d->config[0x06] = 0x80; - d->config[0x07] = 0x02; - - d->config[0x08] = 0x10; - d->config[0x09] = 0x00; + pci_set_word(d->config + PCI_COMMAND, 0); + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + pci_set_byte(d->config + PCI_CLASS_REVISION, 0x10); + pci_config_set_prog_interface(d->config, 0); pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); + pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); + pci_set_byte(d->config + 0x3d, 0x01); - d->config[0x10] = 0x08; - d->config[0x14] = 0x08; - d->config[0x17] = 0x01; - d->config[0x1B] = 0x1c; - d->config[0x1F] = 0x1f; - d->config[0x23] = 0x14; - d->config[0x24] = 0x01; - d->config[0x27] = 0x14; - d->config[0x3D] = 0x01; - - gt64120_reset(s); + return 0; +} - register_savevm(&d->qdev, "GT64120 PCI Bus", 0, 1, - gt64120_save, gt64120_load, d); +static PCIDeviceInfo gt64120_pci_info = { + .qdev.name = "gt64120_pci", + .qdev.size = sizeof(PCIDevice), + .init = gt64120_pci_init, +}; - return s->pci->bus; +static void gt64120_pci_register_devices(void) +{ + sysbus_register_dev("gt64120", sizeof(GT64120State), + gt64120_init); + pci_qdev_register(>64120_pci_info); } + +device_init(gt64120_pci_register_devices) diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 1cad9066a0..d4289af9c4 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -194,11 +194,13 @@ static PCIDeviceInfo piix_ide_info[] = { .qdev.name = "piix3-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, + .no_hotplug = 1, .init = pci_piix3_ide_initfn, },{ .qdev.name = "piix4-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, + .no_hotplug = 1, .init = pci_piix4_ide_initfn, },{ /* end of list */ diff --git a/hw/lan9118.c b/hw/lan9118.c index a98866479b..9cc7952b2a 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -187,7 +187,7 @@ typedef struct { uint32_t phy_int_mask; int eeprom_writable; - uint8_t eeprom[8]; + uint8_t eeprom[128]; int tx_fifo_size; LAN9118Packet *txp; @@ -1003,7 +1003,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset, s->afc_cfg = val & 0x00ffffff; break; case CSR_E2P_CMD: - lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0xff); + lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f); break; case CSR_E2P_DATA: s->e2p_data = val & 0xff; diff --git a/hw/loader.c b/hw/loader.c index eb198f6723..35d792e647 100644 --- a/hw/loader.c +++ b/hw/loader.c @@ -307,8 +307,9 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), target_data_order = ELFDATA2LSB; } - if (target_data_order != e_ident[EI_DATA]) - return -1; + if (target_data_order != e_ident[EI_DATA]) { + goto fail; + } lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 0129ae3cc0..9c761cdb66 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -1930,7 +1930,7 @@ static uint32_t lsi_ram_readw(void *opaque, target_phys_addr_t addr) val = s->script_ram[addr >> 2]; if (addr & 2) val >>= 16; - return le16_to_cpu(val); + return val; } static uint32_t lsi_ram_readl(void *opaque, target_phys_addr_t addr) @@ -1938,7 +1938,7 @@ static uint32_t lsi_ram_readl(void *opaque, target_phys_addr_t addr) LSIState *s = opaque; addr &= 0x1fff; - return le32_to_cpu(s->script_ram[addr >> 2]); + return s->script_ram[addr >> 2]; } static CPUReadMemoryFunc * const lsi_ram_readfn[3] = { diff --git a/hw/m48t59.c b/hw/m48t59.c index 6991e2e8e1..2020487bbe 100644 --- a/hw/m48t59.c +++ b/hw/m48t59.c @@ -642,6 +642,7 @@ M48t59State *m48t59_init(qemu_irq IRQ, target_phys_addr_t mem_base, DeviceState *dev; SysBusDevice *s; M48t59SysBusState *d; + M48t59State *state; dev = qdev_create(NULL, "m48t59"); qdev_prop_set_uint32(dev, "type", type); @@ -649,18 +650,18 @@ M48t59State *m48t59_init(qemu_irq IRQ, target_phys_addr_t mem_base, qdev_prop_set_uint32(dev, "io_base", io_base); qdev_init_nofail(dev); s = sysbus_from_qdev(dev); + d = FROM_SYSBUS(M48t59SysBusState, s); + state = &d->state; sysbus_connect_irq(s, 0, IRQ); if (io_base != 0) { - register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s); - register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s); + register_ioport_read(io_base, 0x04, 1, NVRAM_readb, state); + register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, state); } if (mem_base != 0) { sysbus_mmio_map(s, 0, mem_base); } - d = FROM_SYSBUS(M48t59SysBusState, s); - - return &d->state; + return state; } M48t59State *m48t59_init_isa(uint32_t io_base, uint16_t size, int type) diff --git a/hw/mainstone.c b/hw/mainstone.c index efa2959c72..58e3f8670d 100644 --- a/hw/mainstone.c +++ b/hw/mainstone.c @@ -106,7 +106,8 @@ static void mainstone_common_init(ram_addr_t ram_size, } if (!pflash_cfi01_register(mainstone_flash_base[i], - qemu_ram_alloc(NULL, "mainstone.flash", + qemu_ram_alloc(NULL, i ? "mainstone.flash1" : + "mainstone.flash0", MAINSTONE_FLASH), dinfo->bdrv, sector_len, MAINSTONE_FLASH / sector_len, 4, 0, 0, 0, 0, @@ -3,7 +3,7 @@ /* Definitions for mips board emulation. */ /* gt64xxx.c */ -PCIBus *pci_gt64120_init(qemu_irq *pic); +PCIBus *gt64120_register(qemu_irq *pic); /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); diff --git a/hw/mips_malta.c b/hw/mips_malta.c index 5ef3fcbea9..87554169c5 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -910,7 +910,7 @@ void mips_malta_init (ram_addr_t ram_size, /* Board ID = 0x420 (Malta Board with CoreLV) XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should map to the board ID. */ - stl_phys(0x1fc00010LL, 0x00000420); + stl_p(qemu_get_ram_ptr(bios_offset) + 0x10, 0x00000420); /* Init internal devices */ cpu_mips_irq_init_cpu(env); @@ -921,7 +921,7 @@ void mips_malta_init (ram_addr_t ram_size, i8259 = i8259_init(env->irq[2]); /* Northbridge */ - pci_bus = pci_gt64120_init(i8259); + pci_bus = gt64120_register(i8259); /* Southbridge */ diff --git a/hw/mips_timer.c b/hw/mips_timer.c index e3beee8281..9c95f282a2 100644 --- a/hw/mips_timer.c +++ b/hw/mips_timer.c @@ -42,16 +42,6 @@ uint32_t cpu_mips_get_random (CPUState *env) } /* MIPS R4K timer */ -uint32_t cpu_mips_get_count (CPUState *env) -{ - if (env->CP0_Cause & (1 << CP0Ca_DC)) - return env->CP0_Count; - else - return env->CP0_Count + - (uint32_t)muldiv64(qemu_get_clock(vm_clock), - TIMER_FREQ, get_ticks_per_sec()); -} - static void cpu_mips_timer_update(CPUState *env) { uint64_t now, next; @@ -64,6 +54,35 @@ static void cpu_mips_timer_update(CPUState *env) qemu_mod_timer(env->timer, next); } +/* Expire the timer. */ +static void cpu_mips_timer_expire(CPUState *env) +{ + cpu_mips_timer_update(env); + if (env->insn_flags & ISA_MIPS32R2) { + env->CP0_Cause |= 1 << CP0Ca_TI; + } + qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); +} + +uint32_t cpu_mips_get_count (CPUState *env) +{ + if (env->CP0_Cause & (1 << CP0Ca_DC)) { + return env->CP0_Count; + } else { + uint64_t now; + + now = qemu_get_clock(vm_clock); + if (qemu_timer_pending(env->timer) + && qemu_timer_expired(env->timer, now)) { + /* The timer has already expired. */ + cpu_mips_timer_expire(env); + } + + return env->CP0_Count + + (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec()); + } +} + void cpu_mips_store_count (CPUState *env, uint32_t count) { if (env->CP0_Cause & (1 << CP0Ca_DC)) @@ -116,11 +135,8 @@ static void mips_timer_cb (void *opaque) the comparator value. Offset the count by one to avoid immediately retriggering the callback before any virtual time has passed. */ env->CP0_Count++; - cpu_mips_timer_update(env); + cpu_mips_timer_expire(env); env->CP0_Count--; - if (env->insn_flags & ISA_MIPS32R2) - env->CP0_Cause |= 1 << CP0Ca_TI; - qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); } void cpu_mips_clock_init (CPUState *env) @@ -255,7 +255,6 @@ void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) uint8_t log_max_vecs; unsigned int vector; uint32_t pending; - int i; if (!ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) { return; @@ -296,9 +295,7 @@ void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) * from using its INTx# pin (if implemented) to request * service (MSI, MSI-X, and INTx# are mutually exclusive). */ - for (i = 0; i < PCI_NUM_PINS; ++i) { - qemu_set_irq(dev->irq[i], 0); - } + pci_device_deassert_intx(dev); /* * nr_vectors might be set bigger than capable. So clamp it. @@ -159,7 +159,6 @@ void msix_write_config(PCIDevice *dev, uint32_t addr, { unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; int vector; - int i; if (!range_covers_byte(addr, len, enable_pos)) { return; @@ -169,9 +168,7 @@ void msix_write_config(PCIDevice *dev, uint32_t addr, return; } - for (i = 0; i < PCI_NUM_PINS; ++i) { - qemu_set_irq(dev->irq[i], 0); - } + pci_device_deassert_intx(dev); if (msix_function_masked(dev)) { return; @@ -137,6 +137,14 @@ static void pci_update_irq_status(PCIDevice *dev) } } +void pci_device_deassert_intx(PCIDevice *dev) +{ + int i; + for (i = 0; i < PCI_NUM_PINS; ++i) { + qemu_set_irq(dev->irq[i], 0); + } +} + /* * This function is called on #RST and FLR. * FLR if PCI_EXP_DEVCTL_BCR_FLR is set @@ -152,6 +160,7 @@ void pci_device_reset(PCIDevice *dev) dev->irq_state = 0; pci_update_irq_status(dev); + pci_device_deassert_intx(dev); /* Clear all writeable bits */ pci_word_test_and_clear_mask(dev->config + PCI_COMMAND, pci_get_word(dev->wmask + PCI_COMMAND) | @@ -1624,6 +1633,11 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) info->is_bridge); if (pci_dev == NULL) return -1; + if (qdev->hotplugged && info->no_hotplug) { + qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name); + do_pci_unregister_device(pci_dev); + return -1; + } rc = info->init(pci_dev); if (rc != 0) { do_pci_unregister_device(pci_dev); @@ -1656,7 +1670,12 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) static int pci_unplug_device(DeviceState *qdev) { PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); + PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev); + if (info->no_hotplug) { + qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name); + return -1; + } return dev->bus->hotplug(dev->bus->hotplug_qdev, dev, PCI_HOTPLUG_DISABLED); } @@ -2022,10 +2041,13 @@ static char *pcibus_get_dev_path(DeviceState *dev) * domain:Bus:Slot.Func for systems without nested PCI bridges. * Slot.Function list specifies the slot and function numbers for all * devices on the path from root to the specific device. */ - int domain_len = strlen("DDDD:00"); - int slot_len = strlen(":SS.F"); + char domain[] = "DDDD:00"; + char slot[] = ":SS.F"; + int domain_len = sizeof domain - 1 /* For '\0' */; + int slot_len = sizeof slot - 1 /* For '\0' */; int path_len; char *path, *p; + int s; /* Calculate # of slots on path between device and root. */; slot_depth = 0; @@ -2036,18 +2058,23 @@ static char *pcibus_get_dev_path(DeviceState *dev) path_len = domain_len + slot_len * slot_depth; /* Allocate memory, fill in the terminating null byte. */ - path = malloc(path_len + 1 /* For '\0' */); + path = qemu_malloc(path_len + 1 /* For '\0' */); path[path_len] = '\0'; /* First field is the domain. */ - snprintf(path, domain_len, "%04x:00", pci_find_domain(d->bus)); + s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus)); + assert(s == domain_len); + memcpy(path, domain, domain_len); /* Fill in slot numbers. We walk up from device to root, so need to print * them in the reverse order, last to first. */ p = path + path_len; for (t = d; t; t = t->bus->parent_dev) { p -= slot_len; - snprintf(p, slot_len, ":%02x.%x", PCI_SLOT(t->devfn), PCI_FUNC(d->devfn)); + s = snprintf(slot, sizeof slot, ":%02x.%x", + PCI_SLOT(t->devfn), PCI_FUNC(d->devfn)); + assert(s == slot_len); + memcpy(p, slot, slot_len); } return path; @@ -264,6 +264,8 @@ void do_pci_info_print(Monitor *mon, const QObject *data); void do_pci_info(Monitor *mon, QObject **ret_data); void pci_bridge_update_mappings(PCIBus *b); +void pci_device_deassert_intx(PCIDevice *dev); + static inline void pci_set_byte(uint8_t *config, uint8_t val) { @@ -436,6 +438,9 @@ typedef struct { /* pcie stuff */ int is_express; /* is this device pci express? */ + /* device isn't hot-pluggable */ + int no_hotplug; + /* rom bar */ const char *romfile; } PCIDeviceInfo; diff --git a/hw/piix4.c b/hw/piix4.c index 5489386d68..72073cd0a0 100644 --- a/hw/piix4.c +++ b/hw/piix4.c @@ -113,6 +113,7 @@ static PCIDeviceInfo piix4_info[] = { .qdev.desc = "ISA bridge", .qdev.size = sizeof(PCIDevice), .qdev.no_user = 1, + .no_hotplug = 1, .init = piix4_initfn, },{ /* end of list */ diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 38f9d9eea4..358da58a80 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -348,6 +348,7 @@ static PCIDeviceInfo i440fx_info[] = { .qdev.size = sizeof(PCII440FXState), .qdev.vmsd = &vmstate_i440fx, .qdev.no_user = 1, + .no_hotplug = 1, .init = i440fx_initfn, .config_write = i440fx_write_config, },{ @@ -356,6 +357,7 @@ static PCIDeviceInfo i440fx_info[] = { .qdev.size = sizeof(PIIX3State), .qdev.vmsd = &vmstate_piix3, .qdev.no_user = 1, + .no_hotplug = 1, .init = piix3_initfn, },{ /* end of list */ diff --git a/hw/pl031.c b/hw/pl031.c index e3700c169c..c488f69511 100644 --- a/hw/pl031.c +++ b/hw/pl031.c @@ -44,6 +44,21 @@ typedef struct { uint32_t is; } pl031_state; +static const VMStateDescription vmstate_pl031 = { + .name = "pl031", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(tick_offset, pl031_state), + VMSTATE_UINT32(mr, pl031_state), + VMSTATE_UINT32(lr, pl031_state), + VMSTATE_UINT32(cr, pl031_state), + VMSTATE_UINT32(im, pl031_state), + VMSTATE_UINT32(is, pl031_state), + VMSTATE_END_OF_LIST() + } +}; + static const unsigned char pl031_id[] = { 0x31, 0x10, 0x14, 0x00, /* Device ID */ 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */ @@ -206,9 +221,17 @@ static int pl031_init(SysBusDevice *dev) return 0; } +static SysBusDeviceInfo pl031_info = { + .init = pl031_init, + .qdev.name = "pl031", + .qdev.size = sizeof(pl031_state), + .qdev.vmsd = &vmstate_pl031, + .qdev.no_user = 1, +}; + static void pl031_register_devices(void) { - sysbus_register_dev("pl031", sizeof(pl031_state), pl031_init); + sysbus_register_withprop(&pl031_info); } device_init(pl031_register_devices) diff --git a/hw/pl050.c b/hw/pl050.c index 057383985e..b155cc07b6 100644 --- a/hw/pl050.c +++ b/hw/pl050.c @@ -21,6 +21,20 @@ typedef struct { int is_mouse; } pl050_state; +static const VMStateDescription vmstate_pl050 = { + .name = "pl050", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, pl050_state), + VMSTATE_UINT32(clk, pl050_state), + VMSTATE_UINT32(last, pl050_state), + VMSTATE_INT32(pending, pl050_state), + VMSTATE_INT32(is_mouse, pl050_state), + VMSTATE_END_OF_LIST() + } +}; + #define PL050_TXEMPTY (1 << 6) #define PL050_TXBUSY (1 << 5) #define PL050_RXFULL (1 << 4) @@ -137,7 +151,6 @@ static int pl050_init(SysBusDevice *dev, int is_mouse) s->dev = ps2_mouse_init(pl050_update, s); else s->dev = ps2_kbd_init(pl050_update, s); - /* ??? Save/restore. */ return 0; } @@ -151,12 +164,24 @@ static int pl050_init_mouse(SysBusDevice *dev) return pl050_init(dev, 1); } +static SysBusDeviceInfo pl050_kbd_info = { + .init = pl050_init_keyboard, + .qdev.name = "pl050_keyboard", + .qdev.size = sizeof(pl050_state), + .qdev.vmsd = &vmstate_pl050, +}; + +static SysBusDeviceInfo pl050_mouse_info = { + .init = pl050_init_mouse, + .qdev.name = "pl050_mouse", + .qdev.size = sizeof(pl050_state), + .qdev.vmsd = &vmstate_pl050, +}; + static void pl050_register_devices(void) { - sysbus_register_dev("pl050_keyboard", sizeof(pl050_state), - pl050_init_keyboard); - sysbus_register_dev("pl050_mouse", sizeof(pl050_state), - pl050_init_mouse); + sysbus_register_withprop(&pl050_kbd_info); + sysbus_register_withprop(&pl050_mouse_info); } device_init(pl050_register_devices) diff --git a/hw/pl080.c b/hw/pl080.c index 1a3e06c920..901f04a844 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -52,6 +52,43 @@ typedef struct { qemu_irq irq; } pl080_state; +static const VMStateDescription vmstate_pl080_channel = { + .name = "pl080_channel", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(src, pl080_channel), + VMSTATE_UINT32(dest, pl080_channel), + VMSTATE_UINT32(lli, pl080_channel), + VMSTATE_UINT32(ctrl, pl080_channel), + VMSTATE_UINT32(conf, pl080_channel), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pl080 = { + .name = "pl080", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_UINT8(tc_mask, pl080_state), + VMSTATE_UINT8(err_int, pl080_state), + VMSTATE_UINT8(err_mask, pl080_state), + VMSTATE_UINT32(conf, pl080_state), + VMSTATE_UINT32(sync, pl080_state), + VMSTATE_UINT32(req_single, pl080_state), + VMSTATE_UINT32(req_burst, pl080_state), + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_STRUCT_ARRAY(chan, pl080_state, PL080_MAX_CHANNELS, + 1, vmstate_pl080_channel, pl080_channel), + VMSTATE_INT32(running, pl080_state), + VMSTATE_END_OF_LIST() + } +}; + static const unsigned char pl080_id[] = { 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -330,7 +367,6 @@ static int pl08x_init(SysBusDevice *dev, int nchannels) sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq); s->nchannels = nchannels; - /* ??? Save/restore. */ return 0; } @@ -344,12 +380,28 @@ static int pl081_init(SysBusDevice *dev) return pl08x_init(dev, 2); } +static SysBusDeviceInfo pl080_info = { + .init = pl080_init, + .qdev.name = "pl080", + .qdev.size = sizeof(pl080_state), + .qdev.vmsd = &vmstate_pl080, + .qdev.no_user = 1, +}; + +static SysBusDeviceInfo pl081_info = { + .init = pl081_init, + .qdev.name = "pl081", + .qdev.size = sizeof(pl080_state), + .qdev.vmsd = &vmstate_pl080, + .qdev.no_user = 1, +}; + /* The PL080 and PL081 are the same except for the number of channels they implement (8 and 2 respectively). */ static void pl080_register_devices(void) { - sysbus_register_dev("pl080", sizeof(pl080_state), pl080_init); - sysbus_register_dev("pl081", sizeof(pl080_state), pl081_init); + sysbus_register_withprop(&pl080_info); + sysbus_register_withprop(&pl081_info); } device_init(pl080_register_devices) diff --git a/hw/pl110.c b/hw/pl110.c index a4adb630b0..06d2dfada6 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -48,6 +48,28 @@ typedef struct { qemu_irq irq; } pl110_state; +static const VMStateDescription vmstate_pl110 = { + .name = "pl110", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(versatile, pl110_state), + VMSTATE_UINT32_ARRAY(timing, pl110_state, 4), + VMSTATE_UINT32(cr, pl110_state), + VMSTATE_UINT32(upbase, pl110_state), + VMSTATE_UINT32(lpbase, pl110_state), + VMSTATE_UINT32(int_status, pl110_state), + VMSTATE_UINT32(int_mask, pl110_state), + VMSTATE_INT32(cols, pl110_state), + VMSTATE_INT32(rows, pl110_state), + VMSTATE_UINT32(bpp, pl110_state), + VMSTATE_INT32(invalidate, pl110_state), + VMSTATE_UINT32_ARRAY(pallette, pl110_state, 256), + VMSTATE_UINT32_ARRAY(raw_pallette, pl110_state, 128), + VMSTATE_END_OF_LIST() + } +}; + static const unsigned char pl110_id[] = { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -365,7 +387,6 @@ static int pl110_init(SysBusDevice *dev) s->ds = graphic_console_init(pl110_update_display, pl110_invalidate_display, NULL, NULL, s); - /* ??? Save/restore. */ return 0; } @@ -376,11 +397,26 @@ static int pl110_versatile_init(SysBusDevice *dev) return pl110_init(dev); } +static SysBusDeviceInfo pl110_info = { + .init = pl110_init, + .qdev.name = "pl110", + .qdev.size = sizeof(pl110_state), + .qdev.vmsd = &vmstate_pl110, + .qdev.no_user = 1, +}; + +static SysBusDeviceInfo pl110_versatile_info = { + .init = pl110_versatile_init, + .qdev.name = "pl110_versatile", + .qdev.size = sizeof(pl110_state), + .qdev.vmsd = &vmstate_pl110, + .qdev.no_user = 1, +}; + static void pl110_register_devices(void) { - sysbus_register_dev("pl110", sizeof(pl110_state), pl110_init); - sysbus_register_dev("pl110_versatile", sizeof(pl110_state), - pl110_versatile_init); + sysbus_register_withprop(&pl110_info); + sysbus_register_withprop(&pl110_versatile_info); } device_init(pl110_register_devices) diff --git a/hw/pl190.c b/hw/pl190.c index e04e6c17fb..75f2ba1966 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -21,7 +21,6 @@ typedef struct { uint32_t soft_level; uint32_t irq_enable; uint32_t fiq_select; - uint32_t default_addr; uint8_t vect_control[16]; uint32_t vect_addr[PL190_NUM_PRIO]; /* Mask containing interrupts with higher priority than this one. */ @@ -186,7 +185,7 @@ static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val) s->priority = s->prev_prio[s->priority]; break; case 13: /* DEFVECTADDR */ - s->default_addr = val; + s->vect_addr[16] = val; break; case 0xc0: /* ITCR */ if (val) { @@ -212,8 +211,9 @@ static CPUWriteMemoryFunc * const pl190_writefn[] = { pl190_write }; -static void pl190_reset(pl190_state *s) +static void pl190_reset(DeviceState *d) { + pl190_state *s = DO_UPCAST(pl190_state, busdev.qdev, d); int i; for (i = 0; i < 16; i++) @@ -239,14 +239,40 @@ static int pl190_init(SysBusDevice *dev) qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32); sysbus_init_irq(dev, &s->irq); sysbus_init_irq(dev, &s->fiq); - pl190_reset(s); - /* ??? Save/restore. */ return 0; } +static const VMStateDescription vmstate_pl190 = { + .name = "pl190", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, pl190_state), + VMSTATE_UINT32(soft_level, pl190_state), + VMSTATE_UINT32(irq_enable, pl190_state), + VMSTATE_UINT32(fiq_select, pl190_state), + VMSTATE_UINT8_ARRAY(vect_control, pl190_state, 16), + VMSTATE_UINT32_ARRAY(vect_addr, pl190_state, PL190_NUM_PRIO), + VMSTATE_UINT32_ARRAY(prio_mask, pl190_state, PL190_NUM_PRIO+1), + VMSTATE_INT32(protected, pl190_state), + VMSTATE_INT32(priority, pl190_state), + VMSTATE_INT32_ARRAY(prev_prio, pl190_state, PL190_NUM_PRIO), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo pl190_info = { + .init = pl190_init, + .qdev.name = "pl190", + .qdev.size = sizeof(pl190_state), + .qdev.vmsd = &vmstate_pl190, + .qdev.reset = pl190_reset, + .qdev.no_user = 1, +}; + static void pl190_register_devices(void) { - sysbus_register_dev("pl190", sizeof(pl190_state), pl190_init); + sysbus_register_withprop(&pl190_info); } device_init(pl190_register_devices) diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c index 8136cb962b..334187e53b 100644 --- a/hw/ppc405_uc.c +++ b/hw/ppc405_uc.c @@ -68,8 +68,9 @@ ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd, stl_phys(bdloc + 0x34, bd->bi_baudrate); for (i = 0; i < 4; i++) stb_phys(bdloc + 0x38 + i, bd->bi_s_version[i]); - for (i = 0; i < 32; i++) - stb_phys(bdloc + 0x3C + i, bd->bi_s_version[i]); + for (i = 0; i < 32; i++) { + stb_phys(bdloc + 0x3C + i, bd->bi_r_version[i]); + } stl_phys(bdloc + 0x5C, bd->bi_plb_busfreq); stl_phys(bdloc + 0x60, bd->bi_pci_busfreq); for (i = 0; i < 6; i++) diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index ab524a779a..6e72a5c753 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -1476,7 +1476,7 @@ static const VMStateDescription vmstate_pxa2xx_i2c = { VMSTATE_UINT8(ibmr, PXA2xxI2CState), VMSTATE_UINT8(data, PXA2xxI2CState), VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState, - vmstate_pxa2xx_i2c, PXA2xxI2CSlaveState *), + vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState *), VMSTATE_END_OF_LIST() } }; diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c index 1f2211a2f0..5b2b07e02c 100644 --- a/hw/pxa2xx_lcd.c +++ b/hw/pxa2xx_lcd.c @@ -796,9 +796,9 @@ static void pxa2xx_update_display(void *opaque) if (miny >= 0) { if (s->orientation) - dpy_update(s->ds, miny, 0, maxy, s->xres); + dpy_update(s->ds, miny, 0, maxy - miny, s->xres); else - dpy_update(s->ds, 0, miny, s->xres, maxy); + dpy_update(s->ds, 0, miny, s->xres, maxy - miny); } pxa2xx_lcdc_int_update(s); @@ -32,6 +32,8 @@ #include "blockdev.h" static int qdev_hotplug = 0; +static bool qdev_hot_added = false; +static bool qdev_hot_removed = false; /* This is a nasty hack to allow passing a NULL bus to qdev_create. */ static BusState *main_system_bus; @@ -93,6 +95,7 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info) if (qdev_hotplug) { assert(bus->allow_hotplug); dev->hotplugged = 1; + qdev_hot_added = true; } dev->instance_id_alias = -1; dev->state = DEV_STATE_CREATED; @@ -294,6 +297,8 @@ int qdev_unplug(DeviceState *dev) } assert(dev->info->unplug != NULL); + qdev_hot_removed = true; + return dev->info->unplug(dev); } @@ -395,6 +400,11 @@ void qdev_machine_creation_done(void) qdev_hotplug = 1; } +bool qdev_machine_modified(void) +{ + return qdev_hot_added || qdev_hot_removed; +} + /* Get a character (serial) device interface. */ CharDriverState *qdev_init_chardev(DeviceState *dev) { @@ -132,6 +132,7 @@ int qdev_unplug(DeviceState *dev); void qdev_free(DeviceState *dev); int qdev_simple_unplug_cb(DeviceState *dev); void qdev_machine_creation_done(void); +bool qdev_machine_modified(void); qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); @@ -1546,6 +1546,7 @@ static PCIDeviceInfo qxl_info_primary = { .qdev.size = sizeof(PCIQXLDevice), .qdev.reset = qxl_reset_handler, .qdev.vmsd = &qxl_vmstate, + .no_hotplug = 1, .init = qxl_init_primary, .config_write = qxl_write_config, .romfile = "vgabios-qxl.bin", @@ -23,13 +23,13 @@ * THE SOFTWARE. */ +#include "sysbus.h" #include "hw.h" #include "sh.h" #include "devices.h" #include "sysemu.h" #include "boards.h" #include "pci.h" -#include "sh_pci.h" #include "net.h" #include "sh7750_regs.h" #include "ide.h" @@ -195,17 +195,18 @@ static qemu_irq *r2d_fpga_init(target_phys_addr_t base, qemu_irq irl) return qemu_allocate_irqs(r2d_fpga_irq_set, s, NR_IRQS); } -static void r2d_pci_set_irq(void *opaque, int n, int l) -{ - qemu_irq *p = opaque; - - qemu_set_irq(p[n], l); -} +typedef struct ResetData { + CPUState *env; + uint32_t vector; +} ResetData; -static int r2d_pci_map_irq(PCIDevice *d, int irq_num) +static void main_cpu_reset(void *opaque) { - const int intx[] = { PCI_INTA, PCI_INTB, PCI_INTC, PCI_INTD }; - return intx[d->devfn >> 3]; + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; + + cpu_reset(env); + env->pc = s->vector; } static struct __attribute__((__packed__)) @@ -228,6 +229,7 @@ static void r2d_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { CPUState *env; + ResetData *reset_info; struct SH7750State *s; ram_addr_t sdram_addr; qemu_irq *irq; @@ -242,6 +244,10 @@ static void r2d_init(ram_addr_t ram_size, fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + reset_info->vector = env->pc; + qemu_register_reset(main_cpu_reset, reset_info); /* Allocate memory space */ sdram_addr = qemu_ram_alloc(NULL, "r2d.sdram", SDRAM_SIZE); @@ -249,7 +255,8 @@ static void r2d_init(ram_addr_t ram_size, /* Register peripherals */ s = sh7750_init(env); irq = r2d_fpga_init(0x04000000, sh7750_irl(s)); - sh_pci_register_bus(r2d_pci_set_irq, r2d_pci_map_irq, irq, 0, 4); + sysbus_create_varargs("sh_pci", 0x1e200000, irq[PCI_INTA], irq[PCI_INTB], + irq[PCI_INTC], irq[PCI_INTD], NULL); sm501_init(0x10000000, SM501_VRAM_SIZE, irq[SM501], serial_hds[2]); @@ -290,7 +297,7 @@ static void r2d_init(ram_addr_t ram_size, /* initialization which should be done by firmware */ stl_phys(SH7750_BCR1, 1<<3); /* cs3 SDRAM */ stw_phys(SH7750_BCR2, 3<<(3*2)); /* cs3 32bit */ - env->pc = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */ + reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */ } if (initrd_filename) { diff --git a/hw/rtl8139.c b/hw/rtl8139.c index a8aed89074..a22530cf89 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -495,6 +495,8 @@ typedef struct RTL8139State { QEMUTimer *timer; int64_t TimerExpire; + /* Support migration to/from old versions */ + int rtl8139_mmio_io_addr_dummy; } RTL8139State; static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time); @@ -3162,6 +3164,21 @@ static int rtl8139_post_load(void *opaque, int version_id) return 0; } +static bool rtl8139_hotplug_ready_needed(void *opaque) +{ + return qdev_machine_modified(); +} + +static const VMStateDescription vmstate_rtl8139_hotplug_ready ={ + .name = "rtl8139/hotplug_ready", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_END_OF_LIST() + } +}; + static void rtl8139_pre_save(void *opaque) { RTL8139State* s = opaque; @@ -3171,6 +3188,7 @@ static void rtl8139_pre_save(void *opaque) rtl8139_set_next_tctr_time(s, current_time); s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, get_ticks_per_sec()); + s->rtl8139_mmio_io_addr_dummy = s->rtl8139_mmio_io_addr; } static const VMStateDescription vmstate_rtl8139 = { @@ -3223,7 +3241,7 @@ static const VMStateDescription vmstate_rtl8139 = { VMSTATE_UNUSED(4), VMSTATE_MACADDR(conf.macaddr, RTL8139State), - VMSTATE_INT32(rtl8139_mmio_io_addr, RTL8139State), + VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State), VMSTATE_UINT32(currTxDesc, RTL8139State), VMSTATE_UINT32(currCPlusRxDesc, RTL8139State), @@ -3252,6 +3270,14 @@ static const VMStateDescription vmstate_rtl8139 = { VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_rtl8139_hotplug_ready, + .needed = rtl8139_hotplug_ready_needed, + }, { + /* empty */ + } } }; diff --git a/hw/sh_pci.c b/hw/sh_pci.c index 072078be51..e99d8dbfb5 100644 --- a/hw/sh_pci.c +++ b/hw/sh_pci.c @@ -21,24 +21,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "sysbus.h" #include "sh.h" #include "pci.h" #include "pci_host.h" -#include "sh_pci.h" #include "bswap.h" -typedef struct { +typedef struct SHPCIState { + SysBusDevice busdev; PCIBus *bus; PCIDevice *dev; + qemu_irq irq[4]; + int memconfig; uint32_t par; uint32_t mbr; uint32_t iobr; -} SHPCIC; +} SHPCIState; static void sh_pci_reg_write (void *p, target_phys_addr_t addr, uint32_t val) { - SHPCIC *pcic = p; + SHPCIState *pcic = p; switch(addr) { case 0 ... 0xfc: cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val); @@ -65,7 +67,7 @@ static void sh_pci_reg_write (void *p, target_phys_addr_t addr, uint32_t val) static uint32_t sh_pci_reg_read (void *p, target_phys_addr_t addr) { - SHPCIC *pcic = p; + SHPCIState *pcic = p; switch(addr) { case 0 ... 0xfc: return le32_to_cpup((uint32_t*)(pcic->dev->config + addr)); @@ -91,32 +93,69 @@ static MemOp sh_pci_reg = { { NULL, NULL, sh_pci_reg_write }, }; -PCIBus *sh_pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *opaque, int devfn_min, int nirq) +static int sh_pci_map_irq(PCIDevice *d, int irq_num) +{ + return (d->devfn >> 3); +} + +static void sh_pci_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + qemu_set_irq(pic[irq_num], level); +} + +static void sh_pci_map(SysBusDevice *dev, target_phys_addr_t base) +{ + SHPCIState *s = FROM_SYSBUS(SHPCIState, dev); + + cpu_register_physical_memory(P4ADDR(base), 0x224, s->memconfig); + cpu_register_physical_memory(A7ADDR(base), 0x224, s->memconfig); + + s->iobr = 0xfe240000; + isa_mmio_init(s->iobr, 0x40000); +} + +static int sh_pci_init_device(SysBusDevice *dev) +{ + SHPCIState *s; + int i; + + s = FROM_SYSBUS(SHPCIState, dev); + for (i = 0; i < 4; i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + s->bus = pci_register_bus(&s->busdev.qdev, "pci", + sh_pci_set_irq, sh_pci_map_irq, + s->irq, PCI_DEVFN(0, 0), 4); + s->memconfig = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w, + s, DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio_cb(dev, 0x224, sh_pci_map); + s->dev = pci_create_simple(s->bus, PCI_DEVFN(0, 0), "sh_pci_host"); + return 0; +} + +static int sh_pci_host_init(PCIDevice *d) +{ + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_HITACHI); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_HITACHI_SH7751R); + pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT); + pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST | + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + return 0; +} + +static PCIDeviceInfo sh_pci_host_info = { + .qdev.name = "sh_pci_host", + .qdev.size = sizeof(PCIDevice), + .init = sh_pci_host_init, +}; + +static void sh_pci_register_devices(void) { - SHPCIC *p; - int reg; - - p = qemu_mallocz(sizeof(SHPCIC)); - p->bus = pci_register_bus(NULL, "pci", - set_irq, map_irq, opaque, devfn_min, nirq); - - p->dev = pci_register_device(p->bus, "SH PCIC", sizeof(PCIDevice), - -1, NULL, NULL); - reg = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w, p, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(0x1e200000, 0x224, reg); - cpu_register_physical_memory(0xfe200000, 0x224, reg); - - p->iobr = 0xfe240000; - isa_mmio_init(p->iobr, 0x40000); - - pci_config_set_vendor_id(p->dev->config, PCI_VENDOR_ID_HITACHI); - pci_config_set_device_id(p->dev->config, PCI_DEVICE_ID_HITACHI_SH7751R); - p->dev->config[0x04] = 0x80; - p->dev->config[0x05] = 0x00; - p->dev->config[0x06] = 0x90; - p->dev->config[0x07] = 0x02; - - return p->bus; + sysbus_register_dev("sh_pci", sizeof(SHPCIState), + sh_pci_init_device); + pci_qdev_register(&sh_pci_host_info); } + +device_init(sh_pci_register_devices) diff --git a/hw/sh_pci.h b/hw/sh_pci.h deleted file mode 100644 index b1a5ec37c3..0000000000 --- a/hw/sh_pci.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef QEMU_SH_PCI_H -#define QEMU_SH_PCI_H - -#include "qemu-common.h" - -PCIBus *sh_pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *pic, int devfn_min, int nirq); - -#endif diff --git a/hw/sh_serial.c b/hw/sh_serial.c index 1bdc0a5ab2..191f4a60c6 100644 --- a/hw/sh_serial.c +++ b/hw/sh_serial.c @@ -74,7 +74,7 @@ static void sh_serial_clear_fifo(sh_serial_state * s) s->rx_tail = 0; } -static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) +static void sh_serial_write(void *opaque, uint32_t offs, uint32_t val) { sh_serial_state *s = opaque; unsigned char ch; @@ -185,7 +185,7 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) abort(); } -static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) +static uint32_t sh_serial_read(void *opaque, uint32_t offs) { sh_serial_state *s = opaque; uint32_t ret = ~0; @@ -293,26 +293,6 @@ static int sh_serial_can_receive(sh_serial_state *s) return s->scr & (1 << 4); } -static void sh_serial_receive_byte(sh_serial_state *s, int ch) -{ - if (s->feat & SH_SERIAL_FEAT_SCIF) { - if (s->rx_cnt < SH_RX_FIFO_LENGTH) { - s->rx_fifo[s->rx_head++] = ch; - if (s->rx_head == SH_RX_FIFO_LENGTH) - s->rx_head = 0; - s->rx_cnt++; - if (s->rx_cnt >= s->rtrg) { - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - qemu_set_irq(s->rxi, 1); - } - } - } - } else { - s->rx_fifo[0] = ch; - } -} - static void sh_serial_receive_break(sh_serial_state *s) { if (s->feat & SH_SERIAL_FEAT_SCIF) @@ -328,7 +308,27 @@ static int sh_serial_can_receive1(void *opaque) static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) { sh_serial_state *s = opaque; - sh_serial_receive_byte(s, buf[0]); + + if (s->feat & SH_SERIAL_FEAT_SCIF) { + int i; + for (i = 0; i < size; i++) { + if (s->rx_cnt < SH_RX_FIFO_LENGTH) { + s->rx_fifo[s->rx_head++] = buf[i]; + if (s->rx_head == SH_RX_FIFO_LENGTH) { + s->rx_head = 0; + } + s->rx_cnt++; + if (s->rx_cnt >= s->rtrg) { + s->flags |= SH_SERIAL_FLAG_RDF; + if (s->scr & (1 << 6) && s->rxi) { + qemu_set_irq(s->rxi, 1); + } + } + } + } + } else { + s->rx_fifo[0] = buf[0]; + } } static void sh_serial_event(void *opaque, int event) @@ -338,19 +338,6 @@ static void sh_serial_event(void *opaque, int event) sh_serial_receive_break(s); } -static uint32_t sh_serial_read (void *opaque, target_phys_addr_t addr) -{ - sh_serial_state *s = opaque; - return sh_serial_ioport_read(s, addr); -} - -static void sh_serial_write (void *opaque, - target_phys_addr_t addr, uint32_t value) -{ - sh_serial_state *s = opaque; - sh_serial_ioport_write(s, addr, value); -} - static CPUReadMemoryFunc * const sh_serial_readfn[] = { &sh_serial_read, &sh_serial_read, diff --git a/hw/sm501.c b/hw/sm501.c index f16e6e4e65..0f0bf96609 100644 --- a/hw/sm501.c +++ b/hw/sm501.c @@ -511,6 +511,7 @@ typedef struct SM501State { uint32_t dc_crt_hwc_color_1_2; uint32_t dc_crt_hwc_color_3; + uint32_t twoD_source; uint32_t twoD_destination; uint32_t twoD_dimension; uint32_t twoD_control; @@ -636,6 +637,9 @@ static void sm501_2d_operation(SM501State * s) { /* obtain operation parameters */ int operation = (s->twoD_control >> 16) & 0x1f; + int rtl = s->twoD_control & 0x8000000; + int src_x = (s->twoD_source >> 16) & 0x01FFF; + int src_y = s->twoD_source & 0xFFFF; int dst_x = (s->twoD_destination >> 16) & 0x01FFF; int dst_y = s->twoD_destination & 0xFFFF; int operation_width = (s->twoD_dimension >> 16) & 0x1FFF; @@ -645,10 +649,9 @@ static void sm501_2d_operation(SM501State * s) int addressing = (s->twoD_stretch >> 16) & 0xF; /* get frame buffer info */ -#if 0 /* for future use */ uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); -#endif uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); + int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; if (addressing != 0x0) { @@ -663,8 +666,36 @@ static void sm501_2d_operation(SM501State * s) } switch (operation) { - case 0x01: /* fill rectangle */ + case 0x00: /* copy area */ +#define COPY_AREA(_bpp, _pixel_type, rtl) { \ + int y, x, index_d, index_s; \ + for (y = 0; y < operation_height; y++) { \ + for (x = 0; x < operation_width; x++) { \ + if (rtl) { \ + index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ + index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ + } else { \ + index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ + index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + } \ + *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\ + } \ + } \ + } + switch (format_flags) { + case 0: + COPY_AREA(1, uint8_t, rtl); + break; + case 1: + COPY_AREA(2, uint16_t, rtl); + break; + case 2: + COPY_AREA(4, uint32_t, rtl); + break; + } + break; + case 0x01: /* fill rectangle */ #define FILL_RECT(_bpp, _pixel_type) { \ int y, x; \ for (y = 0; y < operation_height; y++) { \ @@ -1072,6 +1103,9 @@ static void sm501_2d_engine_write(void *opaque, addr, value); switch(addr) { + case SM501_2D_SOURCE: + s->twoD_source = value; + break; case SM501_2D_DESTINATION: s->twoD_destination = value; break; @@ -1240,8 +1274,8 @@ static void sm501_draw_crt(SM501State * s) draw_hwc_line_func * draw_hwc_line = NULL; int full_update = 0; int y_start = -1; - int page_min = 0x7fffffff; - int page_max = -1; + ram_addr_t page_min = ~0l; + ram_addr_t page_max = 0l; ram_addr_t offset = s->local_mem_offset; /* choose draw_line function */ @@ -1337,9 +1371,10 @@ static void sm501_draw_crt(SM501State * s) dpy_update(s->ds, 0, y_start, width, y - y_start); /* clear dirty flags */ - if (page_max != -1) + if (page_min != ~0l) { cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE, VGA_DIRTY_FLAG); + } } static void sm501_update_display(void *opaque) diff --git a/hw/smc91c111.c b/hw/smc91c111.c index fc714d7f28..dafea5cc6e 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -46,6 +46,35 @@ typedef struct { int mmio_index; } smc91c111_state; +static const VMStateDescription vmstate_smc91c111 = { + .name = "smc91c111", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT16(tcr, smc91c111_state), + VMSTATE_UINT16(rcr, smc91c111_state), + VMSTATE_UINT16(cr, smc91c111_state), + VMSTATE_UINT16(ctr, smc91c111_state), + VMSTATE_UINT16(gpr, smc91c111_state), + VMSTATE_UINT16(ptr, smc91c111_state), + VMSTATE_UINT16(ercv, smc91c111_state), + VMSTATE_INT32(bank, smc91c111_state), + VMSTATE_INT32(packet_num, smc91c111_state), + VMSTATE_INT32(tx_alloc, smc91c111_state), + VMSTATE_INT32(allocated, smc91c111_state), + VMSTATE_INT32(tx_fifo_len, smc91c111_state), + VMSTATE_INT32_ARRAY(tx_fifo, smc91c111_state, NUM_PACKETS), + VMSTATE_INT32(rx_fifo_len, smc91c111_state), + VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS), + VMSTATE_INT32(tx_fifo_done_len, smc91c111_state), + VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS), + VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048), + VMSTATE_UINT8(int_level, smc91c111_state), + VMSTATE_UINT8(int_mask, smc91c111_state), + VMSTATE_END_OF_LIST() + } +}; + #define RCR_SOFT_RST 0x8000 #define RCR_STRIP_CRC 0x0200 #define RCR_RXEN 0x0100 @@ -738,6 +767,7 @@ static SysBusDeviceInfo smc91c111_info = { .init = smc91c111_init1, .qdev.name = "smc91c111", .qdev.size = sizeof(smc91c111_state), + .qdev.vmsd = &vmstate_smc91c111, .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(smc91c111_state, conf), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 1fec163d05..90a2b49e1d 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -416,7 +416,7 @@ static const uint8_t usb_hid_usage_keys[0x100] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -479,15 +479,18 @@ static void usb_keyboard_event(void *opaque, int keycode) 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; } diff --git a/hw/versatilepb.c b/hw/versatilepb.c index be758e447f..9f1bfcf941 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -30,6 +30,18 @@ typedef struct vpb_sic_state int irq; } vpb_sic_state; +static const VMStateDescription vmstate_vpb_sic = { + .name = "versatilepb_sic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, vpb_sic_state), + VMSTATE_UINT32(mask, vpb_sic_state), + VMSTATE_UINT32(pic_enable, vpb_sic_state), + VMSTATE_END_OF_LIST() + } +}; + static void vpb_sic_update(vpb_sic_state *s) { uint32_t flags; @@ -146,7 +158,6 @@ static int vpb_sic_init(SysBusDevice *dev) vpb_sic_writefn, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); - /* ??? Save/restore. */ return 0; } @@ -335,10 +346,17 @@ static void versatile_machine_init(void) machine_init(versatile_machine_init); +static SysBusDeviceInfo vpb_sic_info = { + .init = vpb_sic_init, + .qdev.name = "versatilepb_sic", + .qdev.size = sizeof(vpb_sic_state), + .qdev.vmsd = &vmstate_vpb_sic, + .qdev.no_user = 1, +}; + static void versatilepb_register_devices(void) { - sysbus_register_dev("versatilepb_sic", sizeof(vpb_sic_state), - vpb_sic_init); + sysbus_register_withprop(&vpb_sic_info); } device_init(versatilepb_register_devices) diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 791ca22763..ce9ec45777 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -110,6 +110,7 @@ static PCIDeviceInfo vga_info = { .qdev.name = "VGA", .qdev.size = sizeof(PCIVGAState), .qdev.vmsd = &vmstate_vga_pci, + .no_hotplug = 1, .init = pci_vga_initfn, .config_write = pci_vga_write_config, .romfile = "vgabios-stdvga.bin", diff --git a/hw/virtio-net.c b/hw/virtio-net.c index ec1bf8dda7..ccb3e632a4 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -54,8 +54,6 @@ typedef struct VirtIONet uint8_t nouni; uint8_t nobcast; uint8_t vhost_started; - bool vm_running; - VMChangeStateEntry *vmstate; struct { int in_use; int first_multi; @@ -102,7 +100,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) static bool virtio_net_started(VirtIONet *n, uint8_t status) { return (status & VIRTIO_CONFIG_S_DRIVER_OK) && - (n->status & VIRTIO_NET_S_LINK_UP) && n->vm_running; + (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running; } static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) @@ -453,7 +451,7 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) static int virtio_net_can_receive(VLANClientState *nc) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - if (!n->vm_running) { + if (!n->vdev.vm_running) { return 0; } @@ -708,7 +706,7 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) return num_packets; } - assert(n->vm_running); + assert(n->vdev.vm_running); if (n->async_tx.elem.out_num) { virtio_queue_set_notification(n->tx_vq, 0); @@ -769,7 +767,7 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) VirtIONet *n = to_virtio_net(vdev); /* This happens when device was stopped but VCPU wasn't. */ - if (!n->vm_running) { + if (!n->vdev.vm_running) { n->tx_waiting = 1; return; } @@ -796,7 +794,7 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) } n->tx_waiting = 1; /* This happens when device was stopped but VCPU wasn't. */ - if (!n->vm_running) { + if (!n->vdev.vm_running) { return; } virtio_queue_set_notification(vq, 0); @@ -806,7 +804,7 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) static void virtio_net_tx_timer(void *opaque) { VirtIONet *n = opaque; - assert(n->vm_running); + assert(n->vdev.vm_running); n->tx_waiting = 0; @@ -823,7 +821,7 @@ static void virtio_net_tx_bh(void *opaque) VirtIONet *n = opaque; int32_t ret; - assert(n->vm_running); + assert(n->vdev.vm_running); n->tx_waiting = 0; @@ -988,16 +986,6 @@ static NetClientInfo net_virtio_info = { .link_status_changed = virtio_net_set_link_status, }; -static void virtio_net_vmstate_change(void *opaque, int running, int reason) -{ - VirtIONet *n = opaque; - n->vm_running = running; - /* This is called when vm is started/stopped, - * it will start/stop vhost backend if appropriate - * e.g. after migration. */ - virtio_net_set_status(&n->vdev, n->vdev.status); -} - VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, virtio_net_conf *net) { @@ -1052,7 +1040,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->qdev = dev; register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, virtio_net_save, virtio_net_load, n); - n->vmstate = qemu_add_vm_change_state_handler(virtio_net_vmstate_change, n); add_boot_device_path(conf->bootindex, dev, "/ethernet-phy@0"); @@ -1062,7 +1049,6 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, void virtio_net_exit(VirtIODevice *vdev) { VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); - qemu_del_vm_change_state_handler(n->vmstate); /* This will stop vhost backend if appropriate. */ virtio_net_set_status(vdev, 0); diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 6186142b2b..d07ff976be 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -80,9 +80,13 @@ * 12 is historical, and due to x86 page size. */ #define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 -/* We can catch some guest bugs inside here so we continue supporting older - guests. */ -#define VIRTIO_PCI_BUG_BUS_MASTER (1 << 0) +/* Flags track per-device state like workarounds for quirks in older guests. */ +#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) + +/* Performance improves when virtqueue kick processing is decoupled from the + * vcpu thread using ioeventfd for some devices. */ +#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1 +#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) /* QEMU doesn't strictly need write barriers since everything runs in * lock-step. We'll leave the calls to wmb() in though to make it obvious for @@ -95,7 +99,7 @@ typedef struct { PCIDevice pci_dev; VirtIODevice *vdev; - uint32_t bugs; + uint32_t flags; uint32_t addr; uint32_t class_code; uint32_t nvectors; @@ -108,6 +112,8 @@ typedef struct { /* 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 */ @@ -159,7 +165,7 @@ static int virtio_pci_load_config(void * opaque, QEMUFile *f) in ready state. Then we have a buggy guest OS. */ if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->bugs |= VIRTIO_PCI_BUG_BUS_MASTER; + proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } return 0; } @@ -180,12 +186,139 @@ static int virtio_pci_load_queue(void * opaque, int n, QEMUFile *f) return 0; } +static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, + int n, bool assign) +{ + VirtQueue *vq = virtio_get_queue(proxy->vdev, n); + EventNotifier *notifier = virtio_queue_get_host_notifier(vq); + int r; + if (assign) { + r = event_notifier_init(notifier, 1); + if (r < 0) { + error_report("%s: unable to init event notifier: %d", + __func__, r); + return r; + } + r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier), + proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY, + n, assign); + if (r < 0) { + error_report("%s: unable to map ioeventfd: %d", + __func__, r); + event_notifier_cleanup(notifier); + } + } else { + r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier), + proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY, + n, assign); + if (r < 0) { + error_report("%s: unable to unmap ioeventfd: %d", + __func__, r); + return r; + } + + /* Handle the race condition where the guest kicked and we deassigned + * before we got around to handling the kick. + */ + if (event_notifier_test_and_clear(notifier)) { + virtio_queue_notify_vq(vq); + } + + event_notifier_cleanup(notifier); + } + return r; +} + +static void virtio_pci_host_notifier_read(void *opaque) +{ + VirtQueue *vq = opaque; + EventNotifier *n = virtio_queue_get_host_notifier(vq); + if (event_notifier_test_and_clear(n)) { + virtio_queue_notify_vq(vq); + } +} + +static void virtio_pci_set_host_notifier_fd_handler(VirtIOPCIProxy *proxy, + int n, bool assign) +{ + VirtQueue *vq = virtio_get_queue(proxy->vdev, n); + EventNotifier *notifier = virtio_queue_get_host_notifier(vq); + if (assign) { + qemu_set_fd_handler(event_notifier_get_fd(notifier), + virtio_pci_host_notifier_read, NULL, vq); + } else { + qemu_set_fd_handler(event_notifier_get_fd(notifier), + NULL, NULL, NULL); + } +} + +static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy) +{ + int n, r; + + if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) || + proxy->ioeventfd_disabled || + proxy->ioeventfd_started) { + return; + } + + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(proxy->vdev, n)) { + continue; + } + + r = virtio_pci_set_host_notifier_internal(proxy, n, true); + if (r < 0) { + goto assign_error; + } + + virtio_pci_set_host_notifier_fd_handler(proxy, n, true); + } + proxy->ioeventfd_started = true; + return; + +assign_error: + while (--n >= 0) { + if (!virtio_queue_get_num(proxy->vdev, n)) { + continue; + } + + virtio_pci_set_host_notifier_fd_handler(proxy, n, false); + r = virtio_pci_set_host_notifier_internal(proxy, n, false); + assert(r >= 0); + } + proxy->ioeventfd_started = false; + error_report("%s: failed. Fallback to a userspace (slower).", __func__); +} + +static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) +{ + int r; + int n; + + if (!proxy->ioeventfd_started) { + return; + } + + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(proxy->vdev, n)) { + continue; + } + + virtio_pci_set_host_notifier_fd_handler(proxy, n, false); + r = virtio_pci_set_host_notifier_internal(proxy, n, false); + assert(r >= 0); + } + proxy->ioeventfd_started = false; +} + static void virtio_pci_reset(DeviceState *d) { VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev); + virtio_pci_stop_ioeventfd(proxy); virtio_reset(proxy->vdev); msix_reset(&proxy->pci_dev); - proxy->bugs = 0; + proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) @@ -210,6 +343,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) case VIRTIO_PCI_QUEUE_PFN: pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; if (pa == 0) { + virtio_pci_stop_ioeventfd(proxy); virtio_reset(proxy->vdev); msix_unuse_all_vectors(&proxy->pci_dev); } @@ -224,7 +358,16 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) virtio_queue_notify(vdev, val); break; case VIRTIO_PCI_STATUS: + if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { + virtio_pci_stop_ioeventfd(proxy); + } + virtio_set_status(vdev, val & 0xFF); + + if (val & VIRTIO_CONFIG_S_DRIVER_OK) { + virtio_pci_start_ioeventfd(proxy); + } + if (vdev->status == 0) { virtio_reset(proxy->vdev); msix_unuse_all_vectors(&proxy->pci_dev); @@ -235,7 +378,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) some safety checks. */ if ((val & VIRTIO_CONFIG_S_DRIVER_OK) && !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->bugs |= VIRTIO_PCI_BUG_BUS_MASTER; + proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } break; case VIRTIO_MSI_CONFIG_VECTOR: @@ -403,7 +546,8 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, if (PCI_COMMAND == address) { if (!(val & PCI_COMMAND_MASTER)) { - if (!(proxy->bugs & VIRTIO_PCI_BUG_BUS_MASTER)) { + if (!(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { + virtio_pci_stop_ioeventfd(proxy); virtio_set_status(proxy->vdev, proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); } @@ -481,30 +625,30 @@ assign_error: static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign) { VirtIOPCIProxy *proxy = opaque; - VirtQueue *vq = virtio_get_queue(proxy->vdev, n); - EventNotifier *notifier = virtio_queue_get_host_notifier(vq); - int r; + + /* Stop using ioeventfd for virtqueue kick if the device starts using host + * notifiers. This makes it easy to avoid stepping on each others' toes. + */ + proxy->ioeventfd_disabled = assign; if (assign) { - r = event_notifier_init(notifier, 1); - if (r < 0) { - return r; - } - r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier), - proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY, - n, assign); - if (r < 0) { - event_notifier_cleanup(notifier); - } + virtio_pci_stop_ioeventfd(proxy); + } + /* We don't need to start here: it's not needed because backend + * currently only stops on status change away from ok, + * reset, vmstop and such. If we do add code to start here, + * need to check vmstate, device state etc. */ + return virtio_pci_set_host_notifier_internal(proxy, n, assign); +} + +static void virtio_pci_vmstate_change(void *opaque, bool running) +{ + VirtIOPCIProxy *proxy = opaque; + + if (running) { + virtio_pci_start_ioeventfd(proxy); } else { - r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier), - proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY, - n, assign); - if (r < 0) { - return r; - } - event_notifier_cleanup(notifier); + virtio_pci_stop_ioeventfd(proxy); } - return r; } static const VirtIOBindings virtio_pci_bindings = { @@ -516,6 +660,7 @@ static const VirtIOBindings virtio_pci_bindings = { .get_features = virtio_pci_get_features, .set_host_notifier = virtio_pci_set_host_notifier, .set_guest_notifiers = virtio_pci_set_guest_notifiers, + .vmstate_change = virtio_pci_vmstate_change, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, @@ -560,6 +705,10 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO, virtio_map); + if (!kvm_has_many_ioeventfds()) { + proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; + } + virtio_bind_device(vdev, &virtio_pci_bindings, proxy); proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; @@ -598,6 +747,7 @@ static int virtio_blk_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + virtio_pci_stop_ioeventfd(proxy); virtio_blk_exit(proxy->vdev); blockdev_mark_auto_del(proxy->block.bs); return virtio_exit_pci(pci_dev); @@ -659,6 +809,7 @@ static int virtio_net_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + virtio_pci_stop_ioeventfd(proxy); virtio_net_exit(proxy->vdev); return virtio_exit_pci(pci_dev); } @@ -706,6 +857,8 @@ static PCIDeviceInfo virtio_info[] = { .qdev.props = (Property[]) { DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block), + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), DEFINE_PROP_END_OF_LIST(), @@ -718,6 +871,8 @@ static PCIDeviceInfo virtio_info[] = { .exit = virtio_net_exit_pci, .romfile = "pxe-virtio.bin", .qdev.props = (Property[]) { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features), DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic), diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 74ba5ec3d3..b728040f3a 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -769,10 +769,16 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) /* Add a queue for guest to host transfers for port 0 (backward compat) */ vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); + /* TODO: host to guest notifications can get dropped + * if the queue fills up. Implement queueing in host, + * this might also make it possible to reduce the control + * queue size: as guest preposts buffers there, + * this will save 4Kbyte of guest memory per entry. */ + /* control queue: host to guest */ - vser->c_ivq = virtio_add_queue(vdev, 16, control_in); + vser->c_ivq = virtio_add_queue(vdev, 32, control_in); /* control queue: guest to host */ - vser->c_ovq = virtio_add_queue(vdev, 16, control_out); + vser->c_ovq = virtio_add_queue(vdev, 32, control_out); for (i = 1; i < vser->bus->max_nr_ports; i++) { /* Add a per-port queue for host to guest transfers */ diff --git a/hw/virtio.c b/hw/virtio.c index 07dbf868fd..31bd9e32dc 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -575,11 +575,19 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n) return vdev->vq[n].vring.num; } +void virtio_queue_notify_vq(VirtQueue *vq) +{ + if (vq->vring.desc) { + VirtIODevice *vdev = vq->vdev; + trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); + vq->handle_output(vdev, vq); + } +} + void virtio_queue_notify(VirtIODevice *vdev, int n) { - if (n < VIRTIO_PCI_QUEUE_MAX && vdev->vq[n].vring.desc) { - trace_virtio_queue_notify(vdev, n, &vdev->vq[n]); - vdev->vq[n].handle_output(vdev, &vdev->vq[n]); + if (n < VIRTIO_PCI_QUEUE_MAX) { + virtio_queue_notify_vq(&vdev->vq[n]); } } @@ -743,11 +751,31 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) void virtio_cleanup(VirtIODevice *vdev) { + qemu_del_vm_change_state_handler(vdev->vmstate); if (vdev->config) qemu_free(vdev->config); qemu_free(vdev->vq); } +static void virtio_vmstate_change(void *opaque, int running, int reason) +{ + VirtIODevice *vdev = opaque; + bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK); + vdev->vm_running = running; + + if (backend_run) { + virtio_set_status(vdev, vdev->status); + } + + if (vdev->binding->vmstate_change) { + vdev->binding->vmstate_change(vdev->binding_opaque, backend_run); + } + + if (!backend_run) { + virtio_set_status(vdev, vdev->status); + } +} + VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, size_t config_size, size_t struct_size) { @@ -774,6 +802,8 @@ VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, else vdev->config = NULL; + vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev); + return vdev; } diff --git a/hw/virtio.h b/hw/virtio.h index 02fa312d3e..d8546d5b30 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -95,6 +95,7 @@ typedef struct { unsigned (*get_features)(void * opaque); int (*set_guest_notifiers)(void * opaque, bool assigned); int (*set_host_notifier)(void * opaque, int n, bool assigned); + void (*vmstate_change)(void * opaque, bool running); } VirtIOBindings; #define VIRTIO_PCI_QUEUE_MAX 64 @@ -123,6 +124,8 @@ struct VirtIODevice const VirtIOBindings *binding; void *binding_opaque; uint16_t device_id; + bool vm_running; + VMChangeStateEntry *vmstate; }; static inline void virtio_set_status(VirtIODevice *vdev, uint8_t val) @@ -219,5 +222,6 @@ void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); +void virtio_queue_notify_vq(VirtQueue *vq); void virtio_irq(VirtQueue *vq); #endif diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index d9dd52fc60..6c59053308 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -1318,6 +1318,7 @@ static PCIDeviceInfo vmsvga_info = { .qdev.name = "vmware-svga", .qdev.size = sizeof(struct pci_vmsvga_state_s), .qdev.vmsd = &vmstate_vmware_vga, + .no_hotplug = 1, .init = pci_vmsvga_initfn, .romfile = "vgabios-vmware.bin", }; diff --git a/hw/zaurus.c b/hw/zaurus.c index 54ec3f00d5..36be94a179 100644 --- a/hw/zaurus.c +++ b/hw/zaurus.c @@ -70,7 +70,7 @@ static uint32_t scoop_readb(void *opaque, target_phys_addr_t addr) { ScoopInfo *s = (ScoopInfo *) opaque; - switch (addr) { + switch (addr & 0x3f) { case SCOOP_MCR: return s->mcr; case SCOOP_CDR: @@ -104,7 +104,7 @@ static void scoop_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) ScoopInfo *s = (ScoopInfo *) opaque; value &= 0xffff; - switch (addr) { + switch (addr & 0x3f) { case SCOOP_MCR: s->mcr = value; break; @@ -28,6 +28,11 @@ #include "kvm.h" #include "bswap.h" +/* This check must be after config-host.h is included */ +#ifdef CONFIG_EVENTFD +#include <sys/eventfd.h> +#endif + /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ #define PAGE_SIZE TARGET_PAGE_SIZE @@ -72,6 +77,7 @@ struct KVMState int irqchip_in_kernel; int pit_in_kernel; int xsave, xcrs; + int many_ioeventfds; }; static KVMState *kvm_state; @@ -441,6 +447,39 @@ int kvm_check_extension(KVMState *s, unsigned int extension) return ret; } +static int kvm_check_many_ioeventfds(void) +{ + /* Older kernels have a 6 device limit on the KVM io bus. Find out so we + * can avoid creating too many ioeventfds. + */ +#ifdef CONFIG_EVENTFD + int ioeventfds[7]; + int i, ret = 0; + for (i = 0; i < ARRAY_SIZE(ioeventfds); i++) { + ioeventfds[i] = eventfd(0, EFD_CLOEXEC); + if (ioeventfds[i] < 0) { + break; + } + ret = kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, true); + if (ret < 0) { + close(ioeventfds[i]); + break; + } + } + + /* Decide whether many devices are supported or not */ + ret = i == ARRAY_SIZE(ioeventfds); + + while (i-- > 0) { + kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, false); + close(ioeventfds[i]); + } + return ret; +#else + return 0; +#endif +} + static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, ram_addr_t phys_offset) @@ -717,6 +756,8 @@ int kvm_init(int smp_cpus) kvm_state = s; cpu_register_phys_memory_client(&kvm_cpu_phys_memory_client); + s->many_ioeventfds = kvm_check_many_ioeventfds(); + return 0; err: @@ -1046,6 +1087,14 @@ int kvm_has_xcrs(void) return kvm_state->xcrs; } +int kvm_has_many_ioeventfds(void) +{ + if (!kvm_enabled()) { + return 0; + } + return kvm_state->many_ioeventfds; +} + void kvm_setup_guest_memory(void *start, size_t size) { if (!kvm_has_sync_mmu()) { diff --git a/kvm-stub.c b/kvm-stub.c index 5384a4b9a4..33d4476fa3 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -99,6 +99,11 @@ int kvm_has_robust_singlestep(void) return 0; } +int kvm_has_many_ioeventfds(void) +{ + return 0; +} + void kvm_setup_guest_memory(void *start, size_t size) { } @@ -42,6 +42,7 @@ int kvm_has_robust_singlestep(void); int kvm_has_debugregs(void); int kvm_has_xsave(void); int kvm_has_xcrs(void); +int kvm_has_many_ioeventfds(void); #ifdef NEED_CPU_H int kvm_init_vcpu(CPUState *env); diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 538e2572bb..acff781081 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -76,7 +76,7 @@ #ifdef FIGETBSZ IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG)) #endif -#ifdef FS_IOC_FIEMAP +#ifdef CONFIG_FIEMAP IOCTL_SPECIAL(FS_IOC_FIEMAP, IOC_W | IOC_R, do_ioctl_fs_ioc_fiemap, MK_PTR(MK_STRUCT(STRUCT_fiemap))) #endif diff --git a/linux-user/signal.c b/linux-user/signal.c index c846b8c7d4..0664770c94 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1256,6 +1256,14 @@ setup_return(CPUState *env, struct target_sigaction *ka, abi_ulong handler = ka->_sa_handler; abi_ulong retcode; int thumb = handler & 1; + uint32_t cpsr = cpsr_read(env); + + cpsr &= ~CPSR_IT; + if (thumb) { + cpsr |= CPSR_T; + } else { + cpsr &= ~CPSR_T; + } if (ka->sa_flags & TARGET_SA_RESTORER) { retcode = ka->sa_restorer; @@ -1278,13 +1286,7 @@ setup_return(CPUState *env, struct target_sigaction *ka, env->regs[13] = frame_addr; env->regs[14] = retcode; env->regs[15] = handler & (thumb ? ~1 : ~3); - env->thumb = thumb; - -#if 0 -#ifdef TARGET_CONFIG_CPU_32 - env->cpsr = cpsr; -#endif -#endif + cpsr_write(env, cpsr, 0xffffffff); return 0; } diff --git a/linux-user/syscall.c b/linux-user/syscall.c index f10e17ae23..499c4d7d62 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -83,7 +83,9 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include <linux/kd.h> #include <linux/mtio.h> #include <linux/fs.h> +#if defined(CONFIG_FIEMAP) #include <linux/fiemap.h> +#endif #include <linux/fb.h> #include <linux/vt.h> #include "linux_loop.h" @@ -2986,6 +2988,7 @@ struct IOCTLEntry { #define MAX_STRUCT_SIZE 4096 +#ifdef CONFIG_FIEMAP /* So fiemap access checks don't overflow on 32 bit systems. * This is very slightly smaller than the limit imposed by * the underlying kernel. @@ -3072,6 +3075,7 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, } return ret; } +#endif static IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, ...) \ diff --git a/migration.c b/migration.c index e5ba51c314..d593b1df5d 100644 --- a/migration.c +++ b/migration.c @@ -88,6 +88,10 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) return -1; } + if (qemu_savevm_state_blocked(mon)) { + return -1; + } + if (strstart(uri, "tcp:", &p)) { s = tcp_start_outgoing_migration(mon, p, max_throttle, detach, blk, inc); @@ -2275,8 +2275,8 @@ static void mem_info_64(Monitor *mon, CPUState *env) end = (l1 << 39) + (l2 << 30); if (pdpe & PG_PRESENT_MASK) { if (pdpe & PG_PSE_MASK) { - prot = pde & (PG_USER_MASK | PG_RW_MASK | - PG_PRESENT_MASK); + prot = pdpe & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); mem_print(mon, &start, &last_prot, end, prot); } else { pd_addr = pdpe & 0x3fffffffff000ULL; diff --git a/pc-bios/keymaps/bepo b/pc-bios/keymaps/bepo new file mode 100644 index 0000000000..d40041a4cb --- /dev/null +++ b/pc-bios/keymaps/bepo @@ -0,0 +1,333 @@ +include common + +# Bépo : Improved ergonomic french keymap using Dvorak method. +# Built by community on 'Dvorak Fr / Bépo' : +# see http://www.clavier-dvorak.org/wiki/ to join and help. +# +# Bépo layout (1.0rc2 version) for a pc105 keyboard (french) : +# ┌────┐ +# │ S A│ S = Shift, A = AltGr + Shift +# │ s a│ s = normal, a = AltGr +# └────┘ +# +# ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┲━━━━━━━━━┓ +# │ # ¶ │ 1 „ │ 2 “ │ 3 ” │ 4 ≤ │ 5 ≥ │ 6 │ 7 ¬ │ 8 ¼ │ 9 ½ │ 0 ¾ │ ° ′ │ ` ″ ┃ ⌫ Retour┃ +# │ $ – │ " — │ « < │ » > │ ( [ │ ) ] │ @ ^ │ + ± │ - − │ / ÷ │ * × │ = ≠ │ % ‰ ┃ arrière┃ +# ┢━━━━━┷━┱───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┺━┳━━━━━━━┫ +# ┃ ┃ B ¦ │ É ˝ │ P § │ O Œ │ È ` │ ! │ V │ D Ð │ L │ J IJ │ Z Ə │ W ┃Entrée ┃ +# ┃Tab ↹ ┃ b | │ é ˊ │ p & │ o œ │ è ` │ ˆ ¡ │ v ˇ │ d ð │ l / │ j ij │ z ə │ w ̆ ┃ ⏎ ┃ +# ┣━━━━━━━┻┱────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┺┓ ┃ +# ┃ ┃ A Æ │ U Ù │ I ˙ │ E ¤ │ ; ̛ │ C ſ │ T Þ │ S ẞ │ R ™ │ N │ M º │ Ç , ┃ ┃ +# ┃Maj ⇬ ┃ a æ │ u ù │ i ̈ │ e € │ , ’ │ c © │ t þ │ s ß │ r ® │ n ˜ │ m ¯ │ ç ¸ ┃ ┃ +# ┣━━━━━━━┳┹────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┲┷━━━━━┻━━━━━━┫ +# ┃ ┃ Ê │ À │ Y ‘ │ X ’ │ : · │ K │ ? ̉ │ Q ̣ │ G │ H ‡ │ F ª ┃ ┃ +# ┃Shift ⇧┃ ê / │ à \ │ y { │ x } │ . … │ k ~ │ ' ¿ │ q ˚ │ g µ │ h † │ f ˛ ┃Shift ⇧ ┃ +# ┣━━━━━━━╋━━━━━┷━┳━━━┷━━━┱─┴─────┴─────┴─────┴─────┴─────┴───┲━┷━━━━━╈━━━━━┻━┳━━━━━━━┳━━━┛ +# ┃ ┃ ┃ ┃ Espace inséc. Espace inséc. fin ┃ ┃ ┃ ┃ +# ┃Ctrl ┃Meta ┃Alt ┃ ␣ (Espace) _ ␣ ┃AltGr ⇮┃Menu ┃Ctrl ┃ +# ┗━━━━━━━┻━━━━━━━┻━━━━━━━┹───────────────────────────────────┺━━━━━━━┻━━━━━━━┻━━━━━━━┛ + + +# First row +## keycode 41 = dollar numbersign U+2013 U+00b6 +dollar 0x29 +numbersign 0x29 shift +U2013 0x29 altgr +U00b6 0x29 shift altgr + +## keycode 2 = +quotedbl +one U+2014 U+201e +quotedbl 0x2 +one 0x2 shift +U2014 0x2 altgr +U201e 0x2 shift altgr + +## keycode 3 = +guillemotleft +two less U+201c +guillemotleft 0x3 +two 0x3 shift +less 0x3 altgr +U201c 0x3 shift altgr + +## keycode 4 = +guillemotright +three greater U+201d +guillemotright 0x4 +three 0x4 shift +greater 0x4 altgr +U201d 0x4 shift altgr + +## keycode 5 = +parenleft +four bracketleft U+2264 +parenleft 0x5 +four 0x5 shift +bracketleft 0x5 altgr +U2264 0x5 shift altgr + +## keycode 6 = +parenright +five bracketright U+2265 +parenright 0x6 +five 0x6 shift +bracketright 0x6 altgr +U2265 0x6 shift altgr + +## keycode 7 = +at +six asciicircum +at 0x7 +six 0x7 shift +asciicircum 0x7 altgr + +## keycode 8 = +plus +seven U+00b1 U+00ac +plus 0x8 +seven 0x8 shift +U00b1 0x8 altgr +U00ac 0x8 shift altgr + +## keycode 9 = +minus +eight U+2212 U+00bc +minus 0x9 +eight 0x9 shift +U2212 0x9 altgr +U00bc 0x9 shift altgr + +## keycode 10 = +slash +nine U+00f7 U+00bd +slash 0xa +nine 0xa shift +U00f7 0xa altgr +U00bd 0xa shift altgr + +## keycode 11 = +asterisk +zero U+00d7 U+00be +asterisk 0xb +zero 0xb shift +U00d7 0xb altgr +U00be 0xb shift altgr + +## keycode 12 = equal U+00b0 U+2260 U+2032 +equal 0xc +U00b0 0xc shift +U2260 0xc altgr +U2032 0xc shift altgr + +## keycode 13 = percent grave U+2030 U+2033 +percent 0xd +grave 0xd shift +U2030 0xd altgr +U2033 0xd shift altgr + + +# Second row + +# simplified letter definitions notation : +## keycode 16 = b +b 0x10 addupper +## keycode 18 = p +p 0x12 addupper +## keycode 19 = o +o 0x13 addupper +## keycode 22 = v +v 0x16 addupper +## keycode 23 = d +d 0x17 addupper +## keycode 24 = l +l 0x18 addupper +## keycode 25 = j +j 0x19 addupper +## keycode 26 = z +z 0x1a addupper +## keycode 27 = w +w 0x1b addupper + +# then, add specific definitions +## AltGr keycode 16 = bar +bar 0x10 altgr +## Shift AltGr keycode 16 = brokenbar +brokenbar 0x10 shift altgr + +## keycode 17 = +eacute +Eacute dead_acute +eacute 0x11 +Eacute 0x11 shift +dead_acute 0x11 altgr + +## AltGr keycode 18 = ampersand +ampersand 0x12 altgr +## Shift AltGr keycode 18 = U+00a7 +U00a7 0x12 shift altgr + +## AltGr keycode 19 = +U+0153 +U+0153 0x13 altgr +## Shift AltGr keycode 19 = +U+0152 +U+0152 0x13 shift altgr + +## keycode 20 = +egrave +Egrave dead_grave grave # no Meta ! +egrave 0x14 +Egrave 0x14 shift +dead_grave 0x14 altgr + +## keycode 21 = dead_circumflex exclam exclamdown +dead_circumflex 0x15 +exclam 0x15 shift +exclamdown 0x15 altgr + +## AltGr keycode 22 = dead_caron +dead_caron 0x16 altgr + +## AltGr keycode 23 = eth +eth 0x17 altgr +## Shift AltGr keycode 23 = ETH +ETH 0x17 shift altgr + +## AltGr keycode 25 = +U+0133 +U+0133 0x19 altgr +## Shift AltGr keycode 25 = +U+0132 +U+0132 0x19 shift altgr + +## AltGr keycode 26 = +U+0259 +U+0259 0x1a altgr +## Shift AltGr keycode 26 = +U+018f +U+018f 0x1a shift altgr + + + +# Third row + +# simplified letter definitions notation : +## keycode 30 = a +a 0x1e addupper +## keycode 31 = u +u 0x1f addupper +## keycode 32 = i +i 0x20 addupper +## keycode 33 = e +e 0x21 addupper +## keycode 35 = c +c 0x23 addupper +## keycode 36 = t +t 0x24 addupper +## keycode 37 = s +s 0x25 addupper +## keycode 38 = r +r 0x26 addupper +## keycode 39 = n +n 0x27 addupper +## keycode 40 = m +m 0x28 addupper + +# then, add specific definitions +## AltGr keycode 30 = +ae +ae 0x1e altgr +## Shift AltGr keycode 30 = +AE +AE 0x1e shift altgr + +## AltGr keycode 31 = +ugrave +ugrave 0x1f altgr +## Shift AltGr keycode 31 = +Ugrave +Ugrave 0x1f shift altgr + +## AltGr keycode 32 = dead_diaeresis +dead_diaeresis 0x20 altgr + + +## AltGr keycode 33 = U+20ac +U20ac 0x21 altgr + +## keycode 34 = comma semicolon U+2019 +U+031b +comma 0x22 +semicolon 0x22 shift +U2019 0x22 altgr +U+031b 0x22 shift altgr + +## AltGr keycode 35 = copyright +copyright 0x23 altgr +## Shift AltGr keycode 35 = U+017f +U017f 0x23 shift altgr + +## AltGr keycode 36 = +thorn +thorn 0x24 altgr +## Shift AltGr keycode 36 = +THORN +THORN 0x24 shift altgr + +## AltGr keycode 37 = +ssharp +ssharp 0x25 altgr +## Shift AltGr keycode 37 = U+1e9e +U1e9e 0x25 shift altgr + +## AltGr keycode 38 = registered +registered 0x26 altgr +## Shift AltGr keycode 38 = U+2122 +U2122 0x26 shift altgr + +## AltGr keycode 39 = dead_tilde +dead_tilde 0x27 altgr + +## Shift AltGr keycode 40 = masculine +masculine 0x28 shift altgr + +## keycode 43 = +ccedilla +Ccedilla dead_cedilla +ccedilla 0x2b +Ccedilla 0x2b shift +dead_cedilla 0x2b altgr + + +# Fourth row + +# simplified letter definitions notation : +## keycode 45 = y +y 0x2d addupper +## keycode 46 = x +x 0x2e addupper +## keycode 48 = k +k 0x30 addupper +## keycode 50 = q +q 0x32 addupper +## keycode 51 = g +g 0x33 addupper +## keycode 52 = h +h 0x34 addupper +## keycode 53 = f +f 0x35 addupper + +# then, add specific definitions +## keycode 86 = +ecircumflex +Ecircumflex slash slash +ecircumflex 0x56 +Ecircumflex 0x56 shift + +## keycode 44 = +agrave +Agrave backslash +agrave 0x2c +Agrave 0x2c shift +backslash 0x2c altgr + +## AltGr keycode 45 = braceleft +braceleft 0x2d altgr +## Shift AltGr keycode 45 = U+2018 +U2018 0x2d shift altgr + +## AltGr keycode 46 = braceright +braceright 0x2e altgr + +## keycode 47 = period colon U+2026 periodcentered +period 0x2f +colon 0x2f shift +U2026 0x2f altgr +periodcentered 0x2f shift altgr + +## AltGr keycode 48 = asciitilde +asciitilde 0x30 altgr +## Shift AltGr keycode 48 = U+2328 +U2328 0x30 shift altgr + +## keycode 49 = apostrophe question questiondown +U+0309 +apostrophe 0x31 +question 0x31 shift +questiondown 0x31 altgr +U+0309 0x31 shift altgr + +## AltGr keycode 51 = mu +mu 0x33 altgr + +## AltGr keycode 52 = U+2020 +U2020 0x34 altgr +## Shift AltGr keycode 52 = U+2021 +U2021 0x34 shift altgr + +## Shift AltGr keycode 53 = ordfeminine +ordfeminine 0x35 shift altgr + + + +## keycode 57 = space nobreakspace underscore U+202f +space 0x39 +nobreakspace 0x39 shift +underscore 0x39 altgr +U202f 0x39 shift altgr diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index b4be31ec8f..51da288e32 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -23,7 +23,7 @@ build-all: multiboot.bin linuxboot.bin $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@") %.bin: %.raw - $(call quiet-command,$(SHELL) $(SRC_PATH)/pc-bios/optionrom/signrom.sh $< $@," Signing $(TARGET_DIR)$@") + $(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/signrom.sh $< $@," Signing $(TARGET_DIR)$@") clean: rm -f *.o *.d *.raw *.img *.bin *~ diff --git a/qemu-doc.texi b/qemu-doc.texi index 45190f6d3b..22a8663f6b 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2132,7 +2132,7 @@ Then you can launch the precompiled @file{ls} x86 executable: @example qemu-i386 tests/i386/ls @end example -You can look at @file{qemu-binfmt-conf.sh} so that +You can look at @file{scripts/qemu-binfmt-conf.sh} so that QEMU is automatically launched by the Linux kernel when you try to launch x86 executables. It requires the @code{binfmt_misc} module in the Linux kernel. @@ -1131,8 +1131,10 @@ aio_read_f(int argc, char **argv) case 'P': ctx->Pflag = 1; ctx->pattern = parse_pattern(optarg); - if (ctx->pattern < 0) + if (ctx->pattern < 0) { + free(ctx); return 0; + } break; case 'q': ctx->qflag = 1; @@ -101,6 +101,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "Device '%(device)' has no child bus", }, { + .error_fmt = QERR_DEVICE_NO_HOTPLUG, + .desc = "Device '%(device)' does not support hotplugging", + }, + { .error_fmt = QERR_DUPLICATE_ID, .desc = "Duplicate ID '%(id)' for %(object)", }, @@ -90,6 +90,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_DEVICE_NO_BUS \ "{ 'class': 'DeviceNoBus', 'data': { 'device': %s } }" +#define QERR_DEVICE_NO_HOTPLUG \ + "{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }" + #define QERR_DUPLICATE_ID \ "{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }" @@ -15,13 +15,13 @@ MAKEFLAGS += -rR QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d %.o: %.c - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") %.o: %.S - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") %.o: %.m - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@") LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(1) $(LIBS)," LINK $(TARGET_DIR)$@") @@ -57,7 +57,7 @@ find-in-path = $(if $(find-string /, $1), \ @test -f $@ || cp $< $@ %.h-timestamp: %.mak - $(call quiet-command, sh $(SRC_PATH)/create_config < $< > $@, " GEN $*.h") + $(call quiet-command, sh $(SRC_PATH)/scripts/create_config < $< > $@, " GEN $*.h") @cmp $@ $*.h >/dev/null 2>&1 || cp $@ $*.h # will delete the target of a rule if commands exit with a nonzero exit status @@ -1401,19 +1401,13 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) return vmstate_load_state(f, se->vmsd, se->opaque, version_id); } -static int vmstate_save(QEMUFile *f, SaveStateEntry *se) +static void vmstate_save(QEMUFile *f, SaveStateEntry *se) { - if (se->no_migrate) { - return -1; - } - if (!se->vmsd) { /* Old style */ se->save_state(f, se->opaque); - return 0; + return; } vmstate_save_state(f,se->vmsd, se->opaque); - - return 0; } #define QEMU_VM_FILE_MAGIC 0x5145564d @@ -1427,6 +1421,20 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se) #define QEMU_VM_SECTION_FULL 0x04 #define QEMU_VM_SUBSECTION 0x05 +bool qemu_savevm_state_blocked(Monitor *mon) +{ + SaveStateEntry *se; + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (se->no_migrate) { + monitor_printf(mon, "state blocked by non-migratable device '%s'\n", + se->idstr); + return true; + } + } + return false; +} + int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, int shared) { @@ -1508,7 +1516,6 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; - int r; cpu_synchronize_all_states(); @@ -1541,11 +1548,7 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - r = vmstate_save(f, se); - if (r < 0) { - monitor_printf(mon, "cannot migrate with device '%s'\n", se->idstr); - return r; - } + vmstate_save(f, se); } qemu_put_byte(f, QEMU_VM_EOF); @@ -1575,6 +1578,11 @@ static int qemu_savevm_state(Monitor *mon, QEMUFile *f) saved_vm_running = vm_running; vm_stop(0); + if (qemu_savevm_state_blocked(mon)) { + ret = -EINVAL; + goto out; + } + ret = qemu_savevm_state_begin(mon, f, 0, 0); if (ret < 0) goto out; @@ -1692,6 +1700,10 @@ int qemu_loadvm_state(QEMUFile *f) unsigned int v; int ret; + if (qemu_savevm_state_blocked(default_mon)) { + return -EINVAL; + } + v = qemu_get_be32(f); if (v != QEMU_VM_FILE_MAGIC) return -EINVAL; diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl new file mode 100755 index 0000000000..55ef439865 --- /dev/null +++ b/scripts/checkpatch.pl @@ -0,0 +1,2909 @@ +#!/usr/bin/perl -w +# (c) 2001, Dave Jones. (the file handling bit) +# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit) +# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite) +# (c) 2008-2010 Andy Whitcroft <apw@canonical.com> +# Licensed under the terms of the GNU GPL License version 2 + +use strict; + +my $P = $0; +$P =~ s@.*/@@g; + +my $V = '0.31'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $quiet = 0; +my $tree = 1; +my $chk_signoff = 1; +my $chk_patch = 1; +my $tst_only; +my $emacs = 0; +my $terse = 0; +my $file = 0; +my $check = 0; +my $summary = 1; +my $mailback = 0; +my $summary_file = 0; +my $root; +my %debug; +my $help = 0; + +sub help { + my ($exitcode) = @_; + + print << "EOM"; +Usage: $P [OPTION]... [FILE]... +Version: $V + +Options: + -q, --quiet quiet + --no-tree run without a kernel tree + --no-signoff do not check for 'Signed-off-by' line + --patch treat FILE as patchfile (default) + --emacs emacs compile window format + --terse one line per report + -f, --file treat FILE as regular source file + --subjective, --strict enable more subjective tests + --root=PATH PATH to the kernel tree root + --no-summary suppress the per-file summary + --mailback only produce a report in case of warnings/errors + --summary-file include the filename in summary + --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of + 'values', 'possible', 'type', and 'attr' (default + is all off) + --test-only=WORD report only warnings/errors containing WORD + literally + -h, --help, --version display this help and exit + +When FILE is - read standard input. +EOM + + exit($exitcode); +} + +GetOptions( + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'emacs!' => \$emacs, + 'terse!' => \$terse, + 'f|file!' => \$file, + 'subjective!' => \$check, + 'strict!' => \$check, + 'root=s' => \$root, + 'summary!' => \$summary, + 'mailback!' => \$mailback, + 'summary-file!' => \$summary_file, + + 'debug=s' => \%debug, + 'test-only=s' => \$tst_only, + 'h|help' => \$help, + 'version' => \$help +) or help(1); + +help(0) if ($help); + +my $exit = 0; + +if ($#ARGV < 0) { + print "$P: no input files\n"; + exit(1); +} + +my $dbg_values = 0; +my $dbg_possible = 0; +my $dbg_type = 0; +my $dbg_attr = 0; +for my $key (keys %debug) { + ## no critic + eval "\${dbg_$key} = '$debug{$key}';"; + die "$@" if ($@); +} + +my $rpt_cleaners = 0; + +if ($terse) { + $emacs = 1; + $quiet++; +} + +if ($tree) { + if (defined $root) { + if (!top_of_kernel_tree($root)) { + die "$P: $root: --root does not point at a valid tree\n"; + } + } else { + if (top_of_kernel_tree('.')) { + $root = '.'; + } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && + top_of_kernel_tree($1)) { + $root = $1; + } + } + + if (!defined $root) { + print "Must be run from the top-level dir. of a kernel tree\n"; + exit(2); + } +} + +my $emitted_corrupt = 0; + +our $Ident = qr{ + [A-Za-z_][A-Za-z\d_]* + (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* + }x; +our $Storage = qr{extern|static|asmlinkage}; +our $Sparse = qr{ + __user| + __kernel| + __force| + __iomem| + __must_check| + __init_refok| + __kprobes| + __ref + }x; + +# Notes to $Attribute: +# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check +our $Attribute = qr{ + const| + __percpu| + __nocast| + __safe| + __bitwise__| + __packed__| + __packed2__| + __naked| + __maybe_unused| + __always_unused| + __noreturn| + __used| + __cold| + __noclone| + __deprecated| + __read_mostly| + __kprobes| + __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| + ____cacheline_aligned| + ____cacheline_aligned_in_smp| + ____cacheline_internodealigned_in_smp| + __weak + }x; +our $Modifier; +our $Inline = qr{inline|__always_inline|noinline}; +our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; +our $Lval = qr{$Ident(?:$Member)*}; + +our $Constant = qr{(?:[0-9]+|0x[0-9a-fA-F]+)[UL]*}; +our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; +our $Compare = qr{<=|>=|==|!=|<|>}; +our $Operators = qr{ + <=|>=|==|!=| + =>|->|<<|>>|<|>|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% + }x; + +our $NonptrType; +our $Type; +our $Declare; + +our $UTF8 = qr { + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +}x; + +our $typeTypedefs = qr{(?x: + (?:__)?(?:u|s|be|le)(?:8|16|32|64)| + atomic_t +)}; + +our $logFunctions = qr{(?x: + printk| + pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)| + (dev|netdev|netif)_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)| + WARN| + panic +)}; + +our @typeList = ( + qr{void}, + qr{(?:unsigned\s+)?char}, + qr{(?:unsigned\s+)?short}, + qr{(?:unsigned\s+)?int}, + qr{(?:unsigned\s+)?long}, + qr{(?:unsigned\s+)?long\s+int}, + qr{(?:unsigned\s+)?long\s+long}, + qr{(?:unsigned\s+)?long\s+long\s+int}, + qr{unsigned}, + qr{float}, + qr{double}, + qr{bool}, + qr{struct\s+$Ident}, + qr{union\s+$Ident}, + qr{enum\s+$Ident}, + qr{${Ident}_t}, + qr{${Ident}_handler}, + qr{${Ident}_handler_fn}, +); +our @modifierList = ( + qr{fastcall}, +); + +our $allowed_asm_includes = qr{(?x: + irq| + memory +)}; +# memory.h: ARM has a custom one + +sub build_types { + my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; + my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + $NonptrType = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\)| + (?:$typeTypedefs\b)| + (?:${all}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $Type = qr{ + $NonptrType + (?:[\s\*]+\s*const|[\s\*]+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $Declare = qr{(?:$Storage\s+)?$Type}; +} +build_types(); + +$chk_signoff = 0 if ($file); + +my @dep_includes = (); +my @dep_functions = (); +my $removal = "Documentation/feature-removal-schedule.txt"; +if ($tree && -f "$root/$removal") { + open(my $REMOVE, '<', "$root/$removal") || + die "$P: $removal: open failed - $!\n"; + while (<$REMOVE>) { + if (/^Check:\s+(.*\S)/) { + for my $entry (split(/[, ]+/, $1)) { + if ($entry =~ m@include/(.*)@) { + push(@dep_includes, $1); + + } elsif ($entry !~ m@/@) { + push(@dep_functions, $entry); + } + } + } + } + close($REMOVE); +} + +my @rawlines = (); +my @lines = (); +my $vname; +for my $filename (@ARGV) { + my $FILE; + if ($file) { + open($FILE, '-|', "diff -u /dev/null $filename") || + die "$P: $filename: diff failed - $!\n"; + } elsif ($filename eq '-') { + open($FILE, '<&STDIN'); + } else { + open($FILE, '<', "$filename") || + die "$P: $filename: open failed - $!\n"; + } + if ($filename eq '-') { + $vname = 'Your patch'; + } else { + $vname = $filename; + } + while (<$FILE>) { + chomp; + push(@rawlines, $_); + } + close($FILE); + if (!process($filename)) { + $exit = 1; + } + @rawlines = (); + @lines = (); +} + +exit($exit); + +sub top_of_kernel_tree { + my ($root) = @_; + + my @tree_check = ( + "COPYING", "MAINTAINERS", "Makefile", + "README", "docs", "VERSION", + "vl.c" + ); + + foreach my $check (@tree_check) { + if (! -e $root . '/' . $check) { + return 0; + } + } + return 1; +} + +sub expand_tabs { + my ($str) = @_; + + my $res = ''; + my $n = 0; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= ' '; + $n++; + for (; ($n % 8) != 0; $n++) { + $res .= ' '; + } + next; + } + $res .= $c; + $n++; + } + + return $res; +} +sub copy_spacing { + (my $res = shift) =~ tr/\t/ /c; + return $res; +} + +sub line_stats { + my ($line) = @_; + + # Drop the diff line leader and expand tabs + $line =~ s/^.//; + $line = expand_tabs($line); + + # Pick the indent from the front of the line. + my ($white) = ($line =~ /^(\s*)/); + + return (length($line), length($white)); +} + +my $sanitise_quote = ''; + +sub sanitise_line_reset { + my ($in_comment) = @_; + + if ($in_comment) { + $sanitise_quote = '*/'; + } else { + $sanitise_quote = ''; + } +} +sub sanitise_line { + my ($line) = @_; + + my $res = ''; + my $l = ''; + + my $qlen = 0; + my $off = 0; + my $c; + + # Always copy over the diff marker. + $res = substr($line, 0, 1); + + for ($off = 1; $off < length($line); $off++) { + $c = substr($line, $off, 1); + + # Comments we are wacking completly including the begin + # and end, all to $;. + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { + $sanitise_quote = '*/'; + + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { + $sanitise_quote = ''; + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { + $sanitise_quote = '//'; + + substr($res, $off, 2, $sanitise_quote); + $off++; + next; + } + + # A \ in a string means ignore the next character. + if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && + $c eq "\\") { + substr($res, $off, 2, 'XX'); + $off++; + next; + } + # Regular quotes. + if ($c eq "'" || $c eq '"') { + if ($sanitise_quote eq '') { + $sanitise_quote = $c; + + substr($res, $off, 1, $c); + next; + } elsif ($sanitise_quote eq $c) { + $sanitise_quote = ''; + } + } + + #print "c<$c> SQ<$sanitise_quote>\n"; + if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { + substr($res, $off, 1, 'X'); + } else { + substr($res, $off, 1, $c); + } + } + + if ($sanitise_quote eq '//') { + $sanitise_quote = ''; + } + + # The pathname on a #include may be surrounded by '<' and '>'. + if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { + my $clean = 'X' x length($1); + $res =~ s@\<.*\>@<$clean>@; + + # The whole of a #error is a string. + } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { + my $clean = 'X' x length($1); + $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; + } + + return $res; +} + +sub ctx_statement_block { + my ($linenr, $remain, $off) = @_; + my $line = $linenr - 1; + my $blk = ''; + my $soff = $off; + my $coff = $off - 1; + my $coff_set = 0; + + my $loff = 0; + + my $type = ''; + my $level = 0; + my @stack = (); + my $p; + my $c; + my $len = 0; + + my $remainder; + while (1) { + @stack = (['', 0]) if ($#stack == -1); + + #warn "CSB: blk<$blk> remain<$remain>\n"; + # If we are about to drop off the end, pull in more + # context. + if ($off >= $len) { + for (; $remain > 0; $line++) { + last if (!defined $lines[$line]); + next if ($lines[$line] =~ /^-/); + $remain--; + $loff = $len; + $blk .= $lines[$line] . "\n"; + $len = length($blk); + $line++; + last; + } + # Bail if there is no further context. + #warn "CSB: blk<$blk> off<$off> len<$len>\n"; + if ($off >= $len) { + last; + } + } + $p = $c; + $c = substr($blk, $off, 1); + $remainder = substr($blk, $off); + + #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; + + # Handle nested #if/#else. + if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, [ $type, $level ]); + } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { + ($type, $level) = @{$stack[$#stack - 1]}; + } elsif ($remainder =~ /^#\s*endif\b/) { + ($type, $level) = @{pop(@stack)}; + } + + # Statement ends at the ';' or a close '}' at the + # outermost level. + if ($level == 0 && $c eq ';') { + last; + } + + # An else is really a conditional as long as its not else if + if ($level == 0 && $coff_set == 0 && + (!defined($p) || $p =~ /(?:\s|\}|\+)/) && + $remainder =~ /^(else)(?:\s|{)/ && + $remainder !~ /^else\s+if\b/) { + $coff = $off + length($1) - 1; + $coff_set = 1; + #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; + #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; + } + + if (($type eq '' || $type eq '(') && $c eq '(') { + $level++; + $type = '('; + } + if ($type eq '(' && $c eq ')') { + $level--; + $type = ($level != 0)? '(' : ''; + + if ($level == 0 && $coff < $soff) { + $coff = $off; + $coff_set = 1; + #warn "CSB: mark coff<$coff>\n"; + } + } + if (($type eq '' || $type eq '{') && $c eq '{') { + $level++; + $type = '{'; + } + if ($type eq '{' && $c eq '}') { + $level--; + $type = ($level != 0)? '{' : ''; + + if ($level == 0) { + if (substr($blk, $off + 1, 1) eq ';') { + $off++; + } + last; + } + } + $off++; + } + # We are truly at the end, so shuffle to the next line. + if ($off == $len) { + $loff = $len + 1; + $line++; + $remain--; + } + + my $statement = substr($blk, $soff, $off - $soff + 1); + my $condition = substr($blk, $soff, $coff - $soff + 1); + + #warn "STATEMENT<$statement>\n"; + #warn "CONDITION<$condition>\n"; + + #print "coff<$coff> soff<$off> loff<$loff>\n"; + + return ($statement, $condition, + $line, $remain + 1, $off - $loff + 1, $level); +} + +sub statement_lines { + my ($stmt) = @_; + + # Strip the diff line prefixes and rip blank lines at start and end. + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_rawlines { + my ($stmt) = @_; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_block_size { + my ($stmt) = @_; + + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*{//; + $stmt =~ s/}\s*$//; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + my @stmt_statements = ($stmt =~ /;/g); + + my $stmt_lines = $#stmt_lines + 2; + my $stmt_statements = $#stmt_statements + 1; + + if ($stmt_lines > $stmt_statements) { + return $stmt_lines; + } else { + return $stmt_statements; + } +} + +sub ctx_statement_full { + my ($linenr, $remain, $off) = @_; + my ($statement, $condition, $level); + + my (@chunks); + + # Grab the first conditional/block pair. + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "F: c<$condition> s<$statement> remain<$remain>\n"; + push(@chunks, [ $condition, $statement ]); + if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { + return ($level, $linenr, @chunks); + } + + # Pull in the following conditional/block pairs and see if they + # could continue the statement. + for (;;) { + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "C: c<$condition> s<$statement> remain<$remain>\n"; + last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); + #print "C: push\n"; + push(@chunks, [ $condition, $statement ]); + } + + return ($level, $linenr, @chunks); +} + +sub ctx_block_get { + my ($linenr, $remain, $outer, $open, $close, $off) = @_; + my $line; + my $start = $linenr - 1; + my $blk = ''; + my @o; + my @c; + my @res = (); + + my $level = 0; + my @stack = ($level); + for ($line = $start; $remain > 0; $line++) { + next if ($rawlines[$line] =~ /^-/); + $remain--; + + $blk .= $rawlines[$line]; + + # Handle nested #if/#else. + if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, $level); + } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { + $level = $stack[$#stack - 1]; + } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { + $level = pop(@stack); + } + + foreach my $c (split(//, $lines[$line])) { + ##print "C<$c>L<$level><$open$close>O<$off>\n"; + if ($off > 0) { + $off--; + next; + } + + if ($c eq $close && $level > 0) { + $level--; + last if ($level == 0); + } elsif ($c eq $open) { + $level++; + } + } + + if (!$outer || $level <= 1) { + push(@res, $rawlines[$line]); + } + + last if ($level == 0); + } + + return ($level, @res); +} +sub ctx_block_outer { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); + return @r; +} +sub ctx_block { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); + return @r; +} +sub ctx_statement { + my ($linenr, $remain, $off) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); + return @r; +} +sub ctx_block_level { + my ($linenr, $remain) = @_; + + return ctx_block_get($linenr, $remain, 0, '{', '}', 0); +} +sub ctx_statement_level { + my ($linenr, $remain, $off) = @_; + + return ctx_block_get($linenr, $remain, 0, '(', ')', $off); +} + +sub ctx_locate_comment { + my ($first_line, $end_line) = @_; + + # Catch a comment on the end of the line itself. + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); + return $current_comment if (defined $current_comment); + + # Look through the context and try and figure out if there is a + # comment. + my $in_comment = 0; + $current_comment = ''; + for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { + my $line = $rawlines[$linenr - 1]; + #warn " $line\n"; + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if (!$in_comment && $current_comment ne '') { + $current_comment = ''; + } + $current_comment .= $line . "\n" if ($in_comment); + if ($line =~ m@\*/@) { + $in_comment = 0; + } + } + + chomp($current_comment); + return($current_comment); +} +sub ctx_has_comment { + my ($first_line, $end_line) = @_; + my $cmt = ctx_locate_comment($first_line, $end_line); + + ##print "LINE: $rawlines[$end_line - 1 ]\n"; + ##print "CMMT: $cmt\n"; + + return ($cmt ne ''); +} + +sub raw_line { + my ($linenr, $cnt) = @_; + + my $offset = $linenr - 1; + $cnt++; + + my $line; + while ($cnt) { + $line = $rawlines[$offset++]; + next if (defined($line) && $line =~ /^-/); + $cnt--; + } + + return $line; +} + +sub cat_vet { + my ($vet) = @_; + my ($res, $coded); + + $res = ''; + while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { + $res .= $1; + if ($2 ne '') { + $coded = sprintf("^%c", unpack('C', $2) + 64); + $res .= $coded; + } + } + $res =~ s/$/\$/; + + return $res; +} + +my $av_preprocessor = 0; +my $av_pending; +my @av_paren_type; +my $av_pend_colon; + +sub annotate_reset { + $av_preprocessor = 0; + $av_pending = '_'; + @av_paren_type = ('E'); + $av_pend_colon = 'O'; +} + +sub annotate_values { + my ($stream, $type) = @_; + + my $res; + my $var = '_' x length($stream); + my $cur = $stream; + + print "$stream\n" if ($dbg_values > 1); + + while (length($cur)) { + @av_paren_type = ('E') if ($#av_paren_type < 0); + print " <" . join('', @av_paren_type) . + "> <$type> <$av_pending>" if ($dbg_values > 1); + if ($cur =~ /^(\s+)/o) { + print "WS($1)\n" if ($dbg_values > 1); + if ($1 =~ /\n/ && $av_preprocessor) { + $type = pop(@av_paren_type); + $av_preprocessor = 0; + } + + } elsif ($cur =~ /^(\(\s*$Type\s*)\)/) { + print "CAST($1)\n" if ($dbg_values > 1); + push(@av_paren_type, $type); + $type = 'C'; + + } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { + print "DECLARE($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^($Modifier)\s*/) { + print "MODIFIER($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { + print "DEFINE($1,$2)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + if ($2 ne '') { + $av_pending = 'N'; + } + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { + print "UNDEF($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + + } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { + print "PRE_START($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { + print "PRE_RESTART($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $av_paren_type[$#av_paren_type]); + + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:endif))/o) { + print "PRE_END($1)\n" if ($dbg_values > 1); + + $av_preprocessor = 1; + + # Assume all arms of the conditional end as this + # one does, and continue as if the #endif was not here. + pop(@av_paren_type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\\\n)/o) { + print "PRECONT($1)\n" if ($dbg_values > 1); + + } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { + print "ATTR($1)\n" if ($dbg_values > 1); + $av_pending = $type; + $type = 'N'; + + } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { + print "SIZEOF($1)\n" if ($dbg_values > 1); + if (defined $2) { + $av_pending = 'V'; + } + $type = 'N'; + + } elsif ($cur =~ /^(if|while|for)\b/o) { + print "COND($1)\n" if ($dbg_values > 1); + $av_pending = 'E'; + $type = 'N'; + + } elsif ($cur =~/^(case)/o) { + print "CASE($1)\n" if ($dbg_values > 1); + $av_pend_colon = 'C'; + $type = 'N'; + + } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { + print "KEYWORD($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(\()/o) { + print "PAREN('$1')\n" if ($dbg_values > 1); + push(@av_paren_type, $av_pending); + $av_pending = '_'; + $type = 'N'; + + } elsif ($cur =~ /^(\))/o) { + my $new_type = pop(@av_paren_type); + if ($new_type ne '_') { + $type = $new_type; + print "PAREN('$1') -> $type\n" + if ($dbg_values > 1); + } else { + print "PAREN('$1')\n" if ($dbg_values > 1); + } + + } elsif ($cur =~ /^($Ident)\s*\(/o) { + print "FUNC($1)\n" if ($dbg_values > 1); + $type = 'V'; + $av_pending = 'V'; + + } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { + if (defined $2 && $type eq 'C' || $type eq 'T') { + $av_pend_colon = 'B'; + } elsif ($type eq 'E') { + $av_pend_colon = 'L'; + } + print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Ident|$Constant)/o) { + print "IDENT($1)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Assignment)/o) { + print "ASSIGN($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~/^(;|{|})/) { + print "END($1)\n" if ($dbg_values > 1); + $type = 'E'; + $av_pend_colon = 'O'; + + } elsif ($cur =~/^(,)/) { + print "COMMA($1)\n" if ($dbg_values > 1); + $type = 'C'; + + } elsif ($cur =~ /^(\?)/o) { + print "QUESTION($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(:)/o) { + print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); + + substr($var, length($res), 1, $av_pend_colon); + if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { + $type = 'E'; + } else { + $type = 'N'; + } + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(\[)/o) { + print "CLOSE($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { + my $variant; + + print "OPV($1)\n" if ($dbg_values > 1); + if ($type eq 'V') { + $variant = 'B'; + } else { + $variant = 'U'; + } + + substr($var, length($res), 1, $variant); + $type = 'N'; + + } elsif ($cur =~ /^($Operators)/o) { + print "OP($1)\n" if ($dbg_values > 1); + if ($1 ne '++' && $1 ne '--') { + $type = 'N'; + } + + } elsif ($cur =~ /(^.)/o) { + print "C($1)\n" if ($dbg_values > 1); + } + if (defined $1) { + $cur = substr($cur, length($1)); + $res .= $type x length($1); + } + } + + return ($res, $var); +} + +sub possible { + my ($possible, $line) = @_; + my $notPermitted = qr{(?: + ^(?: + $Modifier| + $Storage| + $Type| + DEFINE_\S+ + )$| + ^(?: + goto| + return| + case| + else| + asm|__asm__| + do + )(?:\s|$)| + ^(?:typedef|struct|enum)\b + )}x; + warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); + if ($possible !~ $notPermitted) { + # Check for modifiers. + $possible =~ s/\s*$Storage\s*//g; + $possible =~ s/\s*$Sparse\s*//g; + if ($possible =~ /^\s*$/) { + + } elsif ($possible =~ /\s/) { + $possible =~ s/\s*$Type\s*//g; + for my $modifier (split(' ', $possible)) { + if ($modifier !~ $notPermitted) { + warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); + push(@modifierList, $modifier); + } + } + + } else { + warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); + push(@typeList, $possible); + } + build_types(); + } else { + warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); + } +} + +my $prefix = ''; + +sub report { + if (defined $tst_only && $_[0] !~ /\Q$tst_only\E/) { + return 0; + } + my $line = $prefix . $_[0]; + + $line = (split('\n', $line))[0] . "\n" if ($terse); + + push(our @report, $line); + + return 1; +} +sub report_dump { + our @report; +} +sub ERROR { + if (report("ERROR: $_[0]\n")) { + our $clean = 0; + our $cnt_error++; + } +} +sub WARN { + if (report("WARNING: $_[0]\n")) { + our $clean = 0; + our $cnt_warn++; + } +} +sub CHK { + if ($check && report("CHECK: $_[0]\n")) { + our $clean = 0; + our $cnt_chk++; + } +} + +sub check_absolute_file { + my ($absolute, $herecurr) = @_; + my $file = $absolute; + + ##print "absolute<$absolute>\n"; + + # See if any suffix of this path is a path within the tree. + while ($file =~ s@^[^/]*/@@) { + if (-f "$root/$file") { + ##print "file<$file>\n"; + last; + } + } + if (! -f _) { + return 0; + } + + # It is, so see if the prefix is acceptable. + my $prefix = $absolute; + substr($prefix, -length($file)) = ''; + + ##print "prefix<$prefix>\n"; + if ($prefix ne ".../") { + WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr); + } +} + +sub process { + my $filename = shift; + + my $linenr=0; + my $prevline=""; + my $prevrawline=""; + my $stashline=""; + my $stashrawline=""; + + my $length; + my $indent; + my $previndent=0; + my $stashindent=0; + + our $clean = 1; + my $signoff = 0; + my $is_patch = 0; + + our @report = (); + our $cnt_lines = 0; + our $cnt_error = 0; + our $cnt_warn = 0; + our $cnt_chk = 0; + + # Trace the real file/line as we go. + my $realfile = ''; + my $realline = 0; + my $realcnt = 0; + my $here = ''; + my $in_comment = 0; + my $comment_edge = 0; + my $first_line = 0; + my $p1_prefix = ''; + + my $prev_values = 'E'; + + # suppression flags + my %suppress_ifbraces; + my %suppress_whiletrailers; + my %suppress_export; + + # Pre-scan the patch sanitizing the lines. + # Pre-scan the patch looking for any __setup documentation. + # + my @setup_docs = (); + my $setup_docs = 0; + + sanitise_line_reset(); + my $line; + foreach my $rawline (@rawlines) { + $linenr++; + $line = $rawline; + + if ($rawline=~/^\+\+\+\s+(\S+)/) { + $setup_docs = 0; + if ($1 =~ m@Documentation/kernel-parameters.txt$@) { + $setup_docs = 1; + } + #next; + } + if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + $in_comment = 0; + + # Guestimate if this is a continuing comment. Run + # the context looking for a comment "edge". If this + # edge is a close comment then we must be in a comment + # at context start. + my $edge; + my $cnt = $realcnt; + for (my $ln = $linenr + 1; $cnt > 0; $ln++) { + next if (defined $rawlines[$ln - 1] && + $rawlines[$ln - 1] =~ /^-/); + $cnt--; + #print "RAW<$rawlines[$ln - 1]>\n"; + last if (!defined $rawlines[$ln - 1]); + if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && + $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { + ($edge) = $1; + last; + } + } + if (defined $edge && $edge eq '*/') { + $in_comment = 1; + } + + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if (!defined $edge && + $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) + { + $in_comment = 1; + } + + ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; + sanitise_line_reset($in_comment); + + } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { + # Standardise the strings and chars within the input to + # simplify matching -- only bother with positive lines. + $line = sanitise_line($rawline); + } + push(@lines, $line); + + if ($realcnt > 1) { + $realcnt-- if ($line =~ /^(?:\+| |$)/); + } else { + $realcnt = 0; + } + + #print "==>$rawline\n"; + #print "-->$line\n"; + + if ($setup_docs && $line =~ /^\+/) { + push(@setup_docs, $line); + } + } + + $prefix = ''; + + $realcnt = 0; + $linenr = 0; + foreach my $line (@lines) { + $linenr++; + + my $rawline = $rawlines[$linenr - 1]; + +#extract the line range in the file after the patch is applied + if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $is_patch = 1; + $first_line = $linenr + 1; + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + annotate_reset(); + $prev_values = 'E'; + + %suppress_ifbraces = (); + %suppress_whiletrailers = (); + %suppress_export = (); + next; + +# track the line number as we move through the hunk, note that +# new versions of GNU diff omit the leading space on completely +# blank context lines so we need to count that too. + } elsif ($line =~ /^( |\+|$)/) { + $realline++; + $realcnt-- if ($realcnt != 0); + + # Measure the line length and indent. + ($length, $indent) = line_stats($rawline); + + # Track the previous line. + ($prevline, $stashline) = ($stashline, $line); + ($previndent, $stashindent) = ($stashindent, $indent); + ($prevrawline, $stashrawline) = ($stashrawline, $rawline); + + #warn "line<$line>\n"; + + } elsif ($realcnt == 1) { + $realcnt--; + } + + my $hunk_line = ($realcnt != 0); + +#make up the handle for any error we report on this line + $prefix = "$filename:$realline: " if ($emacs && $file); + $prefix = "$filename:$linenr: " if ($emacs && !$file); + + $here = "#$linenr: " if (!$file); + $here = "#$realline: " if ($file); + + # extract the filename as it passes + if ($line =~ /^diff --git.*?(\S+)$/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@; + + } elsif ($line =~ /^\+\+\+\s+(\S+)/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@; + + $p1_prefix = $1; + if (!$file && $tree && $p1_prefix ne '' && + -e "$root/$p1_prefix") { + WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); + } + + if ($realfile =~ m@^include/asm/@) { + ERROR("do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n"); + } + next; + } + + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); + + my $hereline = "$here\n$rawline\n"; + my $herecurr = "$here\n$rawline\n"; + my $hereprev = "$here\n$prevrawline\n$rawline\n"; + + $cnt_lines++ if ($realcnt != 0); + +# Check for incorrect file permissions + if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { + my $permhere = $here . "FILE: $realfile\n"; + if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) { + ERROR("do not set execute permissions for source files\n" . $permhere); + } + } + +#check the patch for a signoff: + if ($line =~ /^\s*signed-off-by:/i) { + # This is a signoff, if ugly, so do not double report. + $signoff++; + if (!($line =~ /^\s*Signed-off-by:/)) { + WARN("Signed-off-by: is the preferred form\n" . + $herecurr); + } + if ($line =~ /^\s*signed-off-by:\S/i) { + WARN("space required after Signed-off-by:\n" . + $herecurr); + } + } + +# Check for wrappage within a valid hunk of the file + if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { + ERROR("patch seems to be corrupt (line wrapped?)\n" . + $herecurr) if (!$emitted_corrupt++); + } + +# Check for absolute kernel paths. + if ($tree) { + while ($line =~ m{(?:^|\s)(/\S*)}g) { + my $file = $1; + + if ($file =~ m{^(.*?)(?::\d+)+:?$} && + check_absolute_file($1, $herecurr)) { + # + } else { + check_absolute_file($file, $herecurr); + } + } + } + +# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php + if (($realfile =~ /^$/ || $line =~ /^\+/) && + $rawline !~ m/^$UTF8*$/) { + my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); + + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; + my $hereptr = "$hereline$ptr\n"; + + ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); + } + +# ignore non-hunk lines and lines being removed + next if (!$hunk_line || $line =~ /^-/); + +#trailing whitespace + if ($line =~ /^\+.*\015/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("DOS line endings\n" . $herevet); + + } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("trailing whitespace\n" . $herevet); + $rpt_cleaners = 1; + } + +# check for Kconfig help text having a real description +# Only applies when adding the entry originally, after that we do not have +# sufficient context to determine whether it is indeed long enough. + if ($realfile =~ /Kconfig/ && + $line =~ /\+\s*(?:---)?help(?:---)?$/) { + my $length = 0; + my $cnt = $realcnt; + my $ln = $linenr + 1; + my $f; + my $is_end = 0; + while ($cnt > 0 && defined $lines[$ln - 1]) { + $f = $lines[$ln - 1]; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $is_end = $lines[$ln - 1] =~ /^\+/; + $ln++; + + next if ($f =~ /^-/); + $f =~ s/^.//; + $f =~ s/#.*//; + $f =~ s/^\s+//; + next if ($f =~ /^$/); + if ($f =~ /^\s*config\s/) { + $is_end = 1; + last; + } + $length++; + } + WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($is_end && $length < 4); + #print "is_end<$is_end> length<$length>\n"; + } + +# check we are in a valid source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); + +#80 column limit + if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && + $rawline !~ /^.\s*\*\s*\@$Ident\s/ && + !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ || + $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && + $length > 80) + { + WARN("line over 80 characters\n" . $herecurr); + } + +# check for spaces before a quoted newline + if ($rawline =~ /^.*\".*\s\\n/) { + WARN("unnecessary whitespace before a quoted newline\n" . $herecurr); + } + +# check for adding lines without a newline. + if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { + WARN("adding a line without newline at end of file\n" . $herecurr); + } + +# Blackfin: use hi/lo macros + if ($realfile =~ m@arch/blackfin/.*\.S$@) { + if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet); + } + if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the HI() macro, not (... >> 16)\n" . $herevet); + } + } + +# check we are in a valid source file C or perl if not then ignore this hunk + next if ($realfile !~ /\.(h|c|pl)$/); + +# in QEMU, no tabs are allowed + if ($rawline =~ /\t/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("code indent should never use tabs\n" . $herevet); + $rpt_cleaners = 1; + } + +# check we are in a valid C source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c)$/); + +# check for RCS/CVS revision markers + if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { + WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); + } + +# Blackfin: don't use __builtin_bfin_[cs]sync + if ($line =~ /__builtin_bfin_csync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet); + } + if ($line =~ /__builtin_bfin_ssync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet); + } + +# Check for potential 'bare' types + my ($stat, $cond, $line_nr_next, $remain_next, $off_next, + $realline_next); + if ($realcnt && $line =~ /.\s*\S/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0); + $stat =~ s/\n./\n /g; + $cond =~ s/\n./\n /g; + + # Find the real next line. + $realline_next = $line_nr_next; + if (defined $realline_next && + (!defined $lines[$realline_next - 1] || + substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { + $realline_next++; + } + + my $s = $stat; + $s =~ s/{.*$//s; + + # Ignore goto labels. + if ($s =~ /$Ident:\*$/s) { + + # Ignore functions being called + } elsif ($s =~ /^.\s*$Ident\s*\(/s) { + + } elsif ($s =~ /^.\s*else\b/s) { + + # declarations always start with types + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { + my $type = $1; + $type =~ s/\s+/ /g; + possible($type, "A:" . $s); + + # definitions in global scope can only start with types + } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { + possible($1, "B:" . $s); + } + + # any (foo ... *) is a pointer cast, and foo is a type + while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { + possible($1, "C:" . $s); + } + + # Check for any sort of function declaration. + # int foo(something bar, other baz); + # void (*store_gdt)(x86_descr_ptr *); + if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { + my ($name_len) = length($1); + + my $ctx = $s; + substr($ctx, 0, $name_len + 1, ''); + $ctx =~ s/\)[^\)]*$//; + + for my $arg (split(/\s*,\s*/, $ctx)) { + if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { + + possible($1, "D:" . $s); + } + } + } + + } + +# +# Checks which may be anchored in the context. +# + +# Check for switch () and associated case and default +# statements should be at the same indent. + if ($line=~/\bswitch\s*\(.*\)/) { + my $err = ''; + my $sep = ''; + my @ctx = ctx_block_outer($linenr, $realcnt); + shift(@ctx); + for my $ctx (@ctx) { + my ($clen, $cindent) = line_stats($ctx); + if ($ctx =~ /^\+\s*(case\s+|default:)/ && + $indent != $cindent) { + $err .= "$sep$ctx\n"; + $sep = ''; + } else { + $sep = "[...]\n"; + } + } + if ($err ne '') { + ERROR("switch and case should be at the same indent\n$hereline$err"); + } + } + +# if/while/etc brace do not go on next line, unless defining a do while loop, +# or if that brace on the next line is for something else + if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + my $pre_ctx = "$1$2"; + + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + my $ctx_cnt = $realcnt - $#ctx - 1; + my $ctx = join("\n", @ctx); + + my $ctx_ln = $linenr; + my $ctx_skip = $realcnt; + + while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && + defined $lines[$ctx_ln - 1] && + $lines[$ctx_ln - 1] =~ /^-/)) { + ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; + $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); + $ctx_ln++; + } + + #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; + + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("that open brace { should be on the previous line\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && + $ctx =~ /\)\s*\;\s*$/ && + defined $lines[$ctx_ln - 1]) + { + my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); + if ($nindent > $indent) { + WARN("trailing semicolon indicates no statements, indent implies otherwise\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + } + } + +# Check relative indent for conditionals and blocks. + if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + my ($s, $c) = ($stat, $cond); + + substr($s, 0, length($c), ''); + + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + + # We want to check the first line inside the block + # starting at the end of the conditional, so remove: + # 1) any blank line termination + # 2) any opening brace { on end of the line + # 3) any do (...) { + my $continuation = 0; + my $check = 0; + $s =~ s/^.*\bdo\b//; + $s =~ s/^\s*{//; + if ($s =~ s/^\s*\\//) { + $continuation = 1; + } + if ($s =~ s/^\s*?\n//) { + $check = 1; + $cond_lines++; + } + + # Also ignore a loop construct at the end of a + # preprocessor statement. + if (($prevline =~ /^.\s*#\s*define\s/ || + $prevline =~ /\\\s*$/) && $continuation == 0) { + $check = 0; + } + + my $cond_ptr = -1; + $continuation = 0; + while ($cond_ptr != $cond_lines) { + $cond_ptr = $cond_lines; + + # If we see an #else/#elif then the code + # is not linear. + if ($s =~ /^\s*\#\s*(?:else|elif)/) { + $check = 0; + } + + # Ignore: + # 1) blank lines, they should be at 0, + # 2) preprocessor lines, and + # 3) labels. + if ($continuation || + $s =~ /^\s*?\n/ || + $s =~ /^\s*#\s*?/ || + $s =~ /^\s*$Ident\s*:/) { + $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; + if ($s =~ s/^.*?\n//) { + $cond_lines++; + } + } + } + + my (undef, $sindent) = line_stats("+" . $s); + my $stat_real = raw_line($linenr, $cond_lines); + + # Check if either of these lines are modified, else + # this is not this patch's fault. + if (!defined($stat_real) || + $stat !~ /^\+/ && $stat_real !~ /^\+/) { + $check = 0; + } + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; + + if ($check && (($sindent % 4) != 0 || + ($sindent <= $indent && $s ne ''))) { + WARN("suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); + } + } + + # Track the 'values' across context and added lines. + my $opline = $line; $opline =~ s/^./ /; + my ($curr_values, $curr_vars) = + annotate_values($opline . "\n", $prev_values); + $curr_values = $prev_values . $curr_values; + if ($dbg_values) { + my $outline = $opline; $outline =~ s/\t/ /g; + print "$linenr > .$outline\n"; + print "$linenr > $curr_values\n"; + print "$linenr > $curr_vars\n"; + } + $prev_values = substr($curr_values, -1); + +#ignore lines not being added + if ($line=~/^[^\+]/) {next;} + +# TEST: allow direct testing of the type matcher. + if ($dbg_type) { + if ($line =~ /^.\s*$Declare\s*$/) { + ERROR("TEST: is type\n" . $herecurr); + } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { + ERROR("TEST: is not type ($1 is)\n". $herecurr); + } + next; + } +# TEST: allow direct testing of the attribute matcher. + if ($dbg_attr) { + if ($line =~ /^.\s*$Modifier\s*$/) { + ERROR("TEST: is attr\n" . $herecurr); + } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { + ERROR("TEST: is not attr ($1 is)\n". $herecurr); + } + next; + } + +# check for initialisation to aggregates open brace on the next line + if ($line =~ /^.\s*{/ && + $prevline =~ /(?:^|[^=])=\s*$/) { + ERROR("that open brace { should be on the previous line\n" . $hereprev); + } + +# +# Checks which are anchored on the added line. +# + +# check for malformed paths in #include statements (uses RAW line) + if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { + my $path = $1; + if ($path =~ m{//}) { + ERROR("malformed #include filename\n" . + $herecurr); + } + } + +# no C99 // comments + if ($line =~ m{//}) { + ERROR("do not use C99 // comments\n" . $herecurr); + } + # Remove C99 comments. + $line =~ s@//.*@@; + $opline =~ s@//.*@@; + +# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider +# the whole statement. +#print "APW <$lines[$realline_next - 1]>\n"; + if (defined $realline_next && + exists $lines[$realline_next - 1] && + !defined $suppress_export{$realline_next} && + ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + # Handle definitions which produce identifiers with + # a prefix: + # XXX(foo); + # EXPORT_SYMBOL(something_foo); + my $name = $1; + if ($stat =~ /^.([A-Z_]+)\s*\(\s*($Ident)/ && + $name =~ /^${Ident}_$2/) { +#print "FOO C name<$name>\n"; + $suppress_export{$realline_next} = 1; + + } elsif ($stat !~ /(?: + \n.}\s*$| + ^.DEFINE_$Ident\(\Q$name\E\)| + ^.DECLARE_$Ident\(\Q$name\E\)| + ^.LIST_HEAD\(\Q$name\E\)| + ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| + \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() + )/x) { +#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; + $suppress_export{$realline_next} = 2; + } else { + $suppress_export{$realline_next} = 1; + } + } + if (!defined $suppress_export{$linenr} && + $prevline =~ /^.\s*$/ && + ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { +#print "FOO B <$lines[$linenr - 1]>\n"; + $suppress_export{$linenr} = 2; + } + if (defined $suppress_export{$linenr} && + $suppress_export{$linenr} == 2) { + WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); + } + +# check for global initialisers. + if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { + ERROR("do not initialise globals to 0 or NULL\n" . + $herecurr); + } +# check for static initialisers. + if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) { + ERROR("do not initialise statics to 0 or NULL\n" . + $herecurr); + } + +# * goes on variable not on type + # (char*[ const]) + if ($line =~ m{\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\)}) { + my ($from, $to) = ($1, $1); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + + #print "from<$from> to<$to>\n"; + if ($from ne $to) { + ERROR("\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr); + } + } elsif ($line =~ m{\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident)}) { + my ($from, $to, $ident) = ($1, $1, $2); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + # Modifiers should have spaces. + $to =~ s/(\b$Modifier$)/$1 /; + + #print "from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to && $ident !~ /^$Modifier$/) { + ERROR("\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr); + } + } + +# # no BUG() or BUG_ON() +# if ($line =~ /\b(BUG|BUG_ON)\b/) { +# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; +# print "$herecurr"; +# $clean = 0; +# } + + if ($line =~ /\bLINUX_VERSION_CODE\b/) { + WARN("LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); + } + +# printk should use KERN_* levels. Note that follow on printk's on the +# same line do not need a level, so we use the current block context +# to try and find and validate the current printk. In summary the current +# printk includes all preceeding printk's which have no newline on the end. +# we assume the first bad printk is the one to report. + if ($line =~ /\bprintk\((?!KERN_)\s*"/) { + my $ok = 0; + for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { + #print "CHECK<$lines[$ln - 1]\n"; + # we have a preceeding printk if it ends + # with "\n" ignore it, else it is to blame + if ($lines[$ln - 1] =~ m{\bprintk\(}) { + if ($rawlines[$ln - 1] !~ m{\\n"}) { + $ok = 1; + } + last; + } + } + if ($ok == 0) { + WARN("printk() should include KERN_ facility level\n" . $herecurr); + } + } + +# function brace can't be on same line, except for #defines of do while, +# or if closed on same line + if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and + !($line=~/\#\s*define.*do\s{/) and !($line=~/}/)) { + ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr); + } + +# open braces for enum, union and struct go on the same line. + if ($line =~ /^.\s*{/ && + $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { + ERROR("open brace '{' following $1 go on the same line\n" . $hereprev); + } + +# missing space after union, struct or enum definition + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { + WARN("missing space after $1 definition\n" . $herecurr); + } + +# check for spacing round square brackets; allowed: +# 1. with a type on the left -- int [] a; +# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, +# 3. inside a curly brace -- = { [0...10] = 5 } + while ($line =~ /(.*?\s)\[/g) { + my ($where, $prefix) = ($-[1], $1); + if ($prefix !~ /$Type\s+$/ && + ($where != 0 || $prefix !~ /^.\s+$/) && + $prefix !~ /{\s+$/) { + ERROR("space prohibited before open square bracket '['\n" . $herecurr); + } + } + +# check for spaces between functions and their parentheses. + while ($line =~ /($Ident)\s+\(/g) { + my $name = $1; + my $ctx_before = substr($line, 0, $-[1]); + my $ctx = "$ctx_before$name"; + + # Ignore those directives where spaces _are_ permitted. + if ($name =~ /^(?: + if|for|while|switch|return|case| + volatile|__volatile__| + __attribute__|format|__extension__| + asm|__asm__)$/x) + { + + # cpp #define statements have non-optional spaces, ie + # if there is a space between the name and the open + # parenthesis it is simply not a parameter group. + } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { + + # cpp #elif statement condition may start with a ( + } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { + + # If this whole things ends with a type its most + # likely a typedef for a function. + } elsif ($ctx =~ /$Type$/) { + + } else { + WARN("space prohibited between function name and open parenthesis '('\n" . $herecurr); + } + } +# Check operator spacing. + if (!($line=~/\#\s*include/)) { + my $ops = qr{ + <<=|>>=|<=|>=|==|!=| + \+=|-=|\*=|\/=|%=|\^=|\|=|&=| + =>|->|<<|>>|<|>|=|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| + \?|: + }x; + my @elements = split(/($ops|;)/, $opline); + my $off = 0; + + my $blank = copy_spacing($opline); + + for (my $n = 0; $n < $#elements; $n += 2) { + $off += length($elements[$n]); + + # Pick up the preceeding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + + my $a = ''; + $a = 'V' if ($elements[$n] ne ''); + $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'C' if ($elements[$n] =~ /$;$/); + $a = 'B' if ($elements[$n] =~ /(\[|\()$/); + $a = 'O' if ($elements[$n] eq ''); + $a = 'E' if ($ca =~ /^\s*$/); + + my $op = $elements[$n + 1]; + + my $c = ''; + if (defined $elements[$n + 2]) { + $c = 'V' if ($elements[$n + 2] ne ''); + $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'C' if ($elements[$n + 2] =~ /^$;/); + $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); + $c = 'O' if ($elements[$n + 2] eq ''); + $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); + } else { + $c = 'E'; + } + + my $ctx = "${a}x${c}"; + + my $at = "(ctx:$ctx)"; + + my $ptr = substr($blank, 0, $off) . "^"; + my $hereptr = "$hereline$ptr\n"; + + # Pull out the value of this operator. + my $op_type = substr($curr_values, $off + 1, 1); + + # Get the full operator variant. + my $opv = $op . substr($curr_vars, $off, 1); + + # Ignore operators passed as parameters. + if ($op_type ne 'V' && + $ca =~ /\s$/ && $cc =~ /^\s*,/) { + +# # Ignore comments +# } elsif ($op =~ /^$;+$/) { + + # ; should have either the end of line or a space or \ after it + } elsif ($op eq ';') { + if ($ctx !~ /.x[WEBC]/ && + $cc !~ /^\\/ && $cc !~ /^;/) { + ERROR("space required after that '$op' $at\n" . $hereptr); + } + + # // is a comment + } elsif ($op eq '//') { + + # No spaces for: + # -> + # : when part of a bitfield + } elsif ($op eq '->' || $opv eq ':B') { + if ($ctx =~ /Wx.|.xW/) { + ERROR("spaces prohibited around that '$op' $at\n" . $hereptr); + } + + # , must have a space on the right. + } elsif ($op eq ',') { + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { + ERROR("space required after that '$op' $at\n" . $hereptr); + } + + # '*' as part of a type definition -- reported already. + } elsif ($opv eq '*_') { + #warn "'*' is part of type\n"; + + # unary operators should have a space before and + # none after. May be left adjacent to another + # unary operator, or a cast + } elsif ($op eq '!' || $op eq '~' || + $opv eq '*U' || $opv eq '-U' || + $opv eq '&U' || $opv eq '&&U') { + if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + ERROR("space required before that '$op' $at\n" . $hereptr); + } + if ($op eq '*' && $cc =~/\s*$Modifier\b/) { + # A unary '*' may be const + + } elsif ($ctx =~ /.xW/) { + ERROR("space prohibited after that '$op' $at\n" . $hereptr); + } + + # unary ++ and unary -- are allowed no space on one side. + } elsif ($op eq '++' or $op eq '--') { + if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { + ERROR("space required one side of that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /Wx[BE]/ || + ($ctx =~ /Wx./ && $cc =~ /^;/)) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /ExW/) { + ERROR("space prohibited after that '$op' $at\n" . $hereptr); + } + + + # << and >> may either have or not have spaces both sides + } elsif ($op eq '<<' or $op eq '>>' or + $op eq '&' or $op eq '^' or $op eq '|' or + $op eq '+' or $op eq '-' or + $op eq '*' or $op eq '/' or + $op eq '%') + { + if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { + ERROR("need consistent spacing around '$op' $at\n" . + $hereptr); + } + + # A colon needs no spaces before when it is + # terminating a case value or a label. + } elsif ($opv eq ':C' || $opv eq ':L') { + if ($ctx =~ /Wx./) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); + } + + # All the others need spaces both sides. + } elsif ($ctx !~ /[EWC]x[CWE]/) { + my $ok = 0; + + # Ignore email addresses <foo@bar> + if (($op eq '<' && + $cc =~ /^\S+\@\S+>/) || + ($op eq '>' && + $ca =~ /<\S+\@\S+$/)) + { + $ok = 1; + } + + # Ignore ?: + if (($opv eq ':O' && $ca =~ /\?$/) || + ($op eq '?' && $cc =~ /^:/)) { + $ok = 1; + } + + if ($ok == 0) { + ERROR("spaces required around that '$op' $at\n" . $hereptr); + } + } + $off += length($elements[$n + 1]); + } + } + +# check for multiple assignments + if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { + CHK("multiple assignments should be avoided\n" . $herecurr); + } + +## # check for multiple declarations, allowing for a function declaration +## # continuation. +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("declaring multiple variables together should be avoided\n" . $herecurr); +## } +## } + +#need space before brace following if, while, etc + if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || + $line =~ /do{/) { + ERROR("space required before the open brace '{'\n" . $herecurr); + } + +# closing brace should have a space following it when it has anything +# on the line + if ($line =~ /}(?!(?:,|;|\)))\S/) { + ERROR("space required after that close brace '}'\n" . $herecurr); + } + +# check spacing on square brackets + if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { + ERROR("space prohibited after that open square bracket '['\n" . $herecurr); + } + if ($line =~ /\s\]/) { + ERROR("space prohibited before that close square bracket ']'\n" . $herecurr); + } + +# check spacing on parentheses + if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && + $line !~ /for\s*\(\s+;/) { + ERROR("space prohibited after that open parenthesis '('\n" . $herecurr); + } + if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && + $line !~ /for\s*\(.*;\s+\)/ && + $line !~ /:\s+\)/) { + ERROR("space prohibited before that close parenthesis ')'\n" . $herecurr); + } + +#goto labels aren't indented, allow a single space however + if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and + !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { + WARN("labels should not be indented\n" . $herecurr); + } + +# Return is not a function. + if (defined($stat) && $stat =~ /^.\s*return(\s*)(\(.*);/s) { + my $spacing = $1; + my $value = $2; + + # Flatten any parentheses + $value =~ s/\(/ \(/g; + $value =~ s/\)/\) /g; + while ($value =~ s/\[[^\{\}]*\]/1/ || + $value !~ /(?:$Ident|-?$Constant)\s* + $Compare\s* + (?:$Ident|-?$Constant)/x && + $value =~ s/\([^\(\)]*\)/1/) { + } +#print "value<$value>\n"; + if ($value =~ /^\s*(?:$Ident|-?$Constant)\s*$/) { + ERROR("return is not a function, parentheses are not required\n" . $herecurr); + + } elsif ($spacing !~ /\s+/) { + ERROR("space required before the open parenthesis '('\n" . $herecurr); + } + } +# Return of what appears to be an errno should normally be -'ve + if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { + my $name = $1; + if ($name ne 'EOF' && $name ne 'ERROR') { + CHK("return of an errno should typically be -ve (return -$1)\n" . $herecurr); + } + } + +# Need a space before open parenthesis after if, while etc + if ($line=~/\b(if|while|for|switch)\(/) { + ERROR("space required before the open parenthesis '('\n" . $herecurr); + } + +# Check for illegal assignment in if conditional -- and check for trailing +# statements after the conditional. + if ($line =~ /do\s*(?!{)/) { + my ($stat_next) = ctx_statement_block($line_nr_next, + $remain_next, $off_next); + $stat_next =~ s/\n./\n /g; + ##print "stat<$stat> stat_next<$stat_next>\n"; + + if ($stat_next =~ /^\s*while\b/) { + # If the statement carries leading newlines, + # then count those as offsets. + my ($whitespace) = + ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = + statement_rawlines($whitespace) - 1; + + $suppress_whiletrailers{$line_nr_next + + $offset} = 1; + } + } + if (!defined $suppress_whiletrailers{$linenr} && + $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { + my ($s, $c) = ($stat, $cond); + + if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { + ERROR("do not use assignment in if condition\n" . $herecurr); + } + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + $s =~ s/$;//g; # Remove any comments + if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && + $c !~ /}\s*while\s*/) + { + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + my $stat_real = ''; + + $stat_real = raw_line($linenr, $cond_lines) + . "\n" if ($cond_lines); + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + ERROR("trailing statements should be on next line\n" . $herecurr . $stat_real); + } + } + +# Check for bitwise tests written as boolean + if ($line =~ / + (?: + (?:\[|\(|\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\|) + | + (?:\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\||\)|\]) + )/x) + { + WARN("boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); + } + +# if and else should not have general statements after it + if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { + my $s = $1; + $s =~ s/$;//g; # Remove any comments + if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { + ERROR("trailing statements should be on next line\n" . $herecurr); + } + } +# if should not continue a brace + if ($line =~ /}\s*if\b/) { + ERROR("trailing statements should be on next line\n" . + $herecurr); + } +# case and default should not have general statements after them + if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && + $line !~ /\G(?: + (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| + \s*return\s+ + )/xg) + { + ERROR("trailing statements should be on next line\n" . $herecurr); + } + + # Check for }<nl>else {, these must be at the same + # indent level to be relevant to each other. + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and + $previndent == $indent) { + ERROR("else should follow close brace '}'\n" . $hereprev); + } + + if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and + $previndent == $indent) { + my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + + if ($s =~ /^\s*;/) { + ERROR("while should follow close brace '}'\n" . $hereprev); + } + } + +#studly caps, commented out until figure out how to distinguish between use of existing and adding new +# if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) { +# print "No studly caps, use _\n"; +# print "$herecurr"; +# $clean = 0; +# } + +#no spaces allowed after \ in define + if ($line=~/\#\s*define.*\\\s$/) { + WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr); + } + +#warn if <asm/foo.h> is #included and <linux/foo.h> is available (uses RAW line) + if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { + my $file = "$1.h"; + my $checkfile = "include/linux/$file"; + if (-f "$root/$checkfile" && + $realfile ne $checkfile && + $1 !~ /$allowed_asm_includes/) + { + if ($realfile =~ m{^arch/}) { + CHK("Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } else { + WARN("Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); + } + } + } + +# multi-statement macros should be enclosed in a do while loop, grab the +# first statement and ensure its the whole macro if its not enclosed +# in a known good container + if ($realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + + my $args = defined($1); + + # Find the end of the macro and limit our statement + # search to that. + while ($cnt > 0 && defined $lines[$ln - 1] && + $lines[$ln - 1] =~ /^(?:-|..*\\$)/) + { + $ctx .= $rawlines[$ln - 1] . "\n"; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $ln++; + } + $ctx .= $rawlines[$ln - 1]; + + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $ln - $linenr + 1, 0); + #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; + #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; + + # Extract the remainder of the define (if any) and + # rip off surrounding spaces, and trailing \'s. + $rest = ''; + while ($off != 0 || ($cnt > 0 && $rest =~ /\\\s*$/)) { + #print "ADDING cnt<$cnt> $off <" . substr($lines[$ln - 1], $off) . "> rest<$rest>\n"; + if ($off != 0 || $lines[$ln - 1] !~ /^-/) { + $rest .= substr($lines[$ln - 1], $off) . "\n"; + $cnt--; + } + $ln++; + $off = 0; + } + $rest =~ s/\\\n.//g; + $rest =~ s/^\s*//s; + $rest =~ s/\s*$//s; + + # Clean up the original statement. + if ($args) { + substr($dstat, 0, length($dcond), ''); + } else { + $dstat =~ s/^.\s*\#\s*define\s+$Ident\s*//; + } + $dstat =~ s/$;//g; + $dstat =~ s/\\\n.//g; + $dstat =~ s/^\s*//s; + $dstat =~ s/\s*$//s; + + # Flatten any parentheses and braces + while ($dstat =~ s/\([^\(\)]*\)/1/ || + $dstat =~ s/\{[^\{\}]*\}/1/ || + $dstat =~ s/\[[^\{\}]*\]/1/) + { + } + + my $exceptions = qr{ + $Declare| + module_param_named| + MODULE_PARAM_DESC| + DECLARE_PER_CPU| + DEFINE_PER_CPU| + __typeof__\(| + union| + struct| + \.$Ident\s*=\s*| + ^\"|\"$ + }x; + #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; + if ($rest ne '' && $rest ne ',') { + if ($rest !~ /while\s*\(/ && + $dstat !~ /$exceptions/) + { + ERROR("Macros with multiple statements should be enclosed in a do - while loop\n" . "$here\n$ctx\n"); + } + + } elsif ($ctx !~ /;/) { + if ($dstat ne '' && + $dstat !~ /^(?:$Ident|-?$Constant)$/ && + $dstat !~ /$exceptions/ && + $dstat !~ /^\.$Ident\s*=/ && + $dstat =~ /$Operators/) + { + ERROR("Macros with complex values should be enclosed in parenthesis\n" . "$here\n$ctx\n"); + } + } + } + +# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... +# all assignments may have only one of the following with an assignment: +# . +# ALIGN(...) +# VMLINUX_SYMBOL(...) + if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { + WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); + } + +# check for missing bracing round if etc + if ($line =~ /(^.*)\bif\b/ && $line !~ /\#\s*if/) { + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, 1); + #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; + #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; + if ($#chunks >= 0 && $level == 0) { + my $allowed = 0; + my $seen = 0; + my $herectx = $here . "\n"; + my $ln = $linenr - 1; + for my $chunk (@chunks) { + my ($cond, $block) = @{$chunk}; + + # If the condition carries leading newlines, then count those as offsets. + my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = statement_rawlines($whitespace) - 1; + + #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; + + # We have looked at and allowed this specific line. + $suppress_ifbraces{$ln + $offset} = 1; + + $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; + $ln += statement_rawlines($block) - 1; + + substr($block, 0, length($cond), ''); + + $seen++ if ($block =~ /^\s*{/); + + #print "cond<$cond> block<$block> allowed<$allowed>\n"; + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + } + if (!$seen) { + WARN("braces {} are necessary for all arms of this statement\n" . $herectx); + } + } + } + if (!defined $suppress_ifbraces{$linenr - 1} && + $line =~ /\b(if|while|for|else)\b/) { + my $allowed = 0; + + # Check the pre-context. + if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { + #print "APW: ALLOWED: pre<$1>\n"; + $allowed = 1; + } + + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + # Check the post-context. + if (defined $chunks[1]) { + my ($cond, $block) = @{$chunks[1]}; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if ($block =~ /^\s*\{/) { + #print "APW: ALLOWED: chunk-1 block<$block>\n"; + $allowed = 1; + } + } + if ($level == 0 && $block !~ /^\s*\{/ && !$allowed) { + my $herectx = $here . "\n";; + my $cnt = statement_rawlines($block); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n";; + } + + WARN("braces {} are necessary even for single statement blocks\n" . $herectx); + } + } + +# don't include deprecated include files (uses RAW line) + for my $inc (@dep_includes) { + if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) { + ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# don't use deprecated functions + for my $func (@dep_functions) { + if ($line =~ /\b$func\b/) { + ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# no volatiles please + my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; + if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { + WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); + } + +# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated + if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) { + ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr); + } + +# warn about #if 0 + if ($line =~ /^.\s*\#\s*if\s+0\b/) { + CHK("if this code is redundant consider removing it\n" . + $herecurr); + } + +# check for needless kfree() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\bkfree\(\Q$expr\E\);/) { + WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev); + } + } +# check for needless usb_free_urb() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { + WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); + } + } + +# prefer usleep_range over udelay + if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) { + # ignore udelay's < 10, however + if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) { + CHK("usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line); + } + } + +# warn about unexpectedly long msleep's + if ($line =~ /\bmsleep\s*\((\d+)\);/) { + if ($1 < 20) { + WARN("msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line); + } + } + +# warn about #ifdefs in C files +# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { +# print "#ifdef in C files should be avoided\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# warn about spacing in #ifdefs + if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { + ERROR("exactly one space required after that #$1\n" . $herecurr); + } + +# check for spinlock_t definitions without a comment. + if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || + $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { + my $which = $1; + if (!ctx_has_comment($first_line, $linenr)) { + CHK("$1 definition without comment\n" . $herecurr); + } + } +# check for memory barriers without a comment. + if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + CHK("memory barrier without comment\n" . $herecurr); + } + } +# check of hardware specific defines + if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { + CHK("architecture specific defines should be avoided\n" . $herecurr); + } + +# Check that the storage class is at the beginning of a declaration + if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { + WARN("storage class should be at the beginning of the declaration\n" . $herecurr) + } + +# check the location of the inline attribute, that it is between +# storage class and type. + if ($line =~ /\b$Type\s+$Inline\b/ || + $line =~ /\b$Inline\s+$Storage\b/) { + ERROR("inline keyword should sit between storage class and type\n" . $herecurr); + } + +# Check for __inline__ and __inline, prefer inline + if ($line =~ /\b(__inline__|__inline)\b/) { + WARN("plain inline is preferred over $1\n" . $herecurr); + } + +# check for sizeof(&) + if ($line =~ /\bsizeof\s*\(\s*\&/) { + WARN("sizeof(& should be avoided\n" . $herecurr); + } + +# check for new externs in .c files. + if ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) + { + my $function_name = $1; + my $paren_space = $2; + + my $s = $stat; + if (defined $cond) { + substr($s, 0, length($cond), ''); + } + if ($s =~ /^\s*;/ && + $function_name ne 'uninitialized_var') + { + WARN("externs should be avoided in .c files\n" . $herecurr); + } + + if ($paren_space =~ /\n/) { + WARN("arguments for function declarations should follow identifier\n" . $herecurr); + } + + } elsif ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*extern\s+/) + { + WARN("externs should be avoided in .c files\n" . $herecurr); + } + +# checks for new __setup's + if ($rawline =~ /\b__setup\("([^"]*)"/) { + my $name = $1; + + if (!grep(/$name/, @setup_docs)) { + CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); + } + } + +# check for pointless casting of kmalloc return + if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) { + WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); + } + +# check for gcc specific __FUNCTION__ + if ($line =~ /__FUNCTION__/) { + WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); + } + +# check for semaphores used as mutexes + if ($line =~ /^.\s*(DECLARE_MUTEX|init_MUTEX)\s*\(/) { + WARN("mutexes are preferred for single holder semaphores\n" . $herecurr); + } +# check for semaphores used as mutexes + if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) { + WARN("consider using a completion\n" . $herecurr); + + } +# recommend strict_strto* over simple_strto* + if ($line =~ /\bsimple_(strto.*?)\s*\(/) { + WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); + } +# check for __initcall(), use device_initcall() explicitly please + if ($line =~ /^.\s*__initcall\s*\(/) { + WARN("please use device_initcall() instead of __initcall()\n" . $herecurr); + } +# check for various ops structs, ensure they are const. + my $struct_ops = qr{acpi_dock_ops| + address_space_operations| + backlight_ops| + block_device_operations| + dentry_operations| + dev_pm_ops| + dma_map_ops| + extent_io_ops| + file_lock_operations| + file_operations| + hv_ops| + ide_dma_ops| + intel_dvo_dev_ops| + item_operations| + iwl_ops| + kgdb_arch| + kgdb_io| + kset_uevent_ops| + lock_manager_operations| + microcode_ops| + mtrr_ops| + neigh_ops| + nlmsvc_binding| + pci_raw_ops| + pipe_buf_operations| + platform_hibernation_ops| + platform_suspend_ops| + proto_ops| + rpc_pipe_ops| + seq_operations| + snd_ac97_build_ops| + soc_pcmcia_socket_ops| + stacktrace_ops| + sysfs_ops| + tty_operations| + usb_mon_operations| + wd_ops}x; + if ($line !~ /\bconst\b/ && + $line =~ /\bstruct\s+($struct_ops)\b/) { + WARN("struct $1 should normally be const\n" . + $herecurr); + } + +# use of NR_CPUS is usually wrong +# ignore definitions of NR_CPUS and usage to define arrays as likely right + if ($line =~ /\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) + { + WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); + } + +# check for %L{u,d,i} in strings + my $string; + while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { + $string = substr($rawline, $-[1], $+[1] - $-[1]); + $string =~ s/%%/__/g; + if ($string =~ /(?<!%)%L[udi]/) { + WARN("\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr); + last; + } + } + +# whine mightly about in_atomic + if ($line =~ /\bin_atomic\s*\(/) { + if ($realfile =~ m@^drivers/@) { + ERROR("do not use in_atomic in drivers\n" . $herecurr); + } elsif ($realfile !~ m@^kernel/@) { + WARN("use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); + } + } + +# check for lockdep_set_novalidate_class + if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || + $line =~ /__lockdep_no_validate__\s*\)/ ) { + if ($realfile !~ m@^kernel/lockdep@ && + $realfile !~ m@^include/linux/lockdep@ && + $realfile !~ m@^drivers/base/core@) { + ERROR("lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); + } + } + } + + # If we have no input at all, then there is nothing to report on + # so just keep quiet. + if ($#rawlines == -1) { + exit(0); + } + + # In mailback mode only produce a report in the negative, for + # things that appear to be patches. + if ($mailback && ($clean == 1 || !$is_patch)) { + exit(0); + } + + # This is not a patch, and we are are in 'no-patch' mode so + # just keep quiet. + if (!$chk_patch && !$is_patch) { + exit(0); + } + + if (!$is_patch) { + ERROR("Does not appear to be a unified-diff format patch\n"); + } + if ($is_patch && $chk_signoff && $signoff == 0) { + ERROR("Missing Signed-off-by: line(s)\n"); + } + + print report_dump(); + if ($summary && !($clean == 1 && $quiet == 1)) { + print "$filename " if ($summary_file); + print "total: $cnt_error errors, $cnt_warn warnings, " . + (($check)? "$cnt_chk checks, " : "") . + "$cnt_lines lines checked\n"; + print "\n" if ($quiet == 0); + } + + if ($quiet == 0) { + # If there were whitespace errors which cleanpatch can fix + # then suggest that. +# if ($rpt_cleaners) { +# print "NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or\n"; +# print " scripts/cleanfile\n\n"; +# } + } + + if ($clean == 1 && $quiet == 0) { + print "$vname has no obvious style problems and is ready for submission.\n" + } + if ($clean == 0 && $quiet == 0) { + print "$vname has style problems, please review. If any of these errors\n"; + print "are false positives report them to the maintainer, see\n"; + print "CHECKPATCH in MAINTAINERS.\n"; + } + + return $clean; +} diff --git a/create_config b/scripts/create_config index 0098e683e2..0098e683e2 100755 --- a/create_config +++ b/scripts/create_config diff --git a/feature_to_c.sh b/scripts/feature_to_c.sh index 0994d9546e..0994d9546e 100644 --- a/feature_to_c.sh +++ b/scripts/feature_to_c.sh diff --git a/hxtool b/scripts/hxtool index 7ca83ed1ff..7ca83ed1ff 100644 --- a/hxtool +++ b/scripts/hxtool diff --git a/make_device_config.sh b/scripts/make_device_config.sh index 596fc5bf69..5d14885dfc 100644 --- a/make_device_config.sh +++ b/scripts/make_device_config.sh @@ -18,7 +18,7 @@ process_includes () { f=$src while [ -n "$f" ] ; do - f=`tr -d '\r' < $f | awk '/^include / {ORS=" "; print "'$src_dir'/" $2}'` + f=`tr -d '\r' < $f | awk '/^include / {printf "'$src_dir'/%s", $2}'` [ $? = 0 ] || exit 1 all_includes="$all_includes $f" done diff --git a/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index c50beb7337..c50beb7337 100644 --- a/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh diff --git a/pc-bios/optionrom/signrom.sh b/scripts/signrom.sh index 9dc5c63dde..9dc5c63dde 100755 --- a/pc-bios/optionrom/signrom.sh +++ b/scripts/signrom.sh diff --git a/simpletrace.py b/scripts/simpletrace.py index 553a72709f..553a72709f 100755 --- a/simpletrace.py +++ b/scripts/simpletrace.py diff --git a/texi2pod.pl b/scripts/texi2pod.pl index 9ed056ad1c..9ed056ad1c 100755 --- a/texi2pod.pl +++ b/scripts/texi2pod.pl diff --git a/tracetool b/scripts/tracetool index fce491c505..fce491c505 100755 --- a/tracetool +++ b/scripts/tracetool diff --git a/slirp/tftp.c b/slirp/tftp.c index 55e4692acc..1821648251 100644 --- a/slirp/tftp.c +++ b/slirp/tftp.c @@ -311,7 +311,7 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) return; } - if (memcmp(&tp->x.tp_buf[k], "octet\0", 6) != 0) { + if (strcasecmp((const char *)&tp->x.tp_buf[k], "octet") != 0) { tftp_send_error(spt, 4, "Unsupported transfer mode", tp); return; } @@ -351,7 +351,7 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) value = (const char *)&tp->x.tp_buf[k]; k += strlen(value) + 1; - if (strcmp(key, "tsize") == 0) { + if (strcasecmp(key, "tsize") == 0) { int tsize = atoi(value); struct stat stat_p; @@ -75,6 +75,7 @@ void qemu_announce_self(void); void main_loop_wait(int nonblocking); +bool qemu_savevm_state_blocked(Monitor *mon); int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, int shared); int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 340933ef50..5bcd53ac73 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -173,7 +173,20 @@ typedef struct CPUARMState { /* scratch space when Tn are not sufficient. */ uint32_t scratch[8]; + /* fp_status is the "normal" fp status. standard_fp_status retains + * values corresponding to the ARM "Standard FPSCR Value", ie + * default-NaN, flush-to-zero, round-to-nearest and is used by + * any operations (generally Neon) which the architecture defines + * as controlled by the standard FPSCR value rather than the FPSCR. + * + * To avoid having to transfer exception bits around, we simply + * say that the FPSCR cumulative exception flags are the logical + * OR of the flags in the two fp statuses. This relies on the + * only thing which needs to read the exception flags being + * an explicit FPSCR read. + */ float_status fp_status; + float_status standard_fp_status; } vfp; uint32_t exclusive_addr; uint32_t exclusive_val; @@ -440,17 +453,56 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) #include "cpu-all.h" +/* Bit usage in the TB flags field: */ +#define ARM_TBFLAG_THUMB_SHIFT 0 +#define ARM_TBFLAG_THUMB_MASK (1 << ARM_TBFLAG_THUMB_SHIFT) +#define ARM_TBFLAG_VECLEN_SHIFT 1 +#define ARM_TBFLAG_VECLEN_MASK (0x7 << ARM_TBFLAG_VECLEN_SHIFT) +#define ARM_TBFLAG_VECSTRIDE_SHIFT 4 +#define ARM_TBFLAG_VECSTRIDE_MASK (0x3 << ARM_TBFLAG_VECSTRIDE_SHIFT) +#define ARM_TBFLAG_PRIV_SHIFT 6 +#define ARM_TBFLAG_PRIV_MASK (1 << ARM_TBFLAG_PRIV_SHIFT) +#define ARM_TBFLAG_VFPEN_SHIFT 7 +#define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) +#define ARM_TBFLAG_CONDEXEC_SHIFT 8 +#define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) +/* Bits 31..16 are currently unused. */ + +/* some convenience accessor macros */ +#define ARM_TBFLAG_THUMB(F) \ + (((F) & ARM_TBFLAG_THUMB_MASK) >> ARM_TBFLAG_THUMB_SHIFT) +#define ARM_TBFLAG_VECLEN(F) \ + (((F) & ARM_TBFLAG_VECLEN_MASK) >> ARM_TBFLAG_VECLEN_SHIFT) +#define ARM_TBFLAG_VECSTRIDE(F) \ + (((F) & ARM_TBFLAG_VECSTRIDE_MASK) >> ARM_TBFLAG_VECSTRIDE_SHIFT) +#define ARM_TBFLAG_PRIV(F) \ + (((F) & ARM_TBFLAG_PRIV_MASK) >> ARM_TBFLAG_PRIV_SHIFT) +#define ARM_TBFLAG_VFPEN(F) \ + (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) +#define ARM_TBFLAG_CONDEXEC(F) \ + (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) + static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { + int privmode; *pc = env->regs[15]; *cs_base = 0; - *flags = env->thumb | (env->vfp.vec_len << 1) - | (env->vfp.vec_stride << 4) | (env->condexec_bits << 8); - if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) - *flags |= (1 << 6); - if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) - *flags |= (1 << 7); + *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) + | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) + | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) + | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT); + if (arm_feature(env, ARM_FEATURE_M)) { + privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1)); + } else { + privmode = (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR; + } + if (privmode) { + *flags |= ARM_TBFLAG_PRIV_MASK; + } + if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { + *flags |= ARM_TBFLAG_VFPEN_MASK; + } } #endif diff --git a/target-arm/helper.c b/target-arm/helper.c index 705b99f9cb..b562767279 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -237,6 +237,9 @@ void cpu_reset(CPUARMState *env) env->vfp.xregs[ARM_VFP_FPEXC] = 0; env->cp15.c2_base_mask = 0xffffc000u; #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); tlb_flush(env, 1); } @@ -1846,12 +1849,20 @@ bad_reg: void HELPER(set_r13_banked)(CPUState *env, uint32_t mode, uint32_t val) { - env->banked_r13[bank_number(mode)] = val; + if ((env->uncached_cpsr & CPSR_M) == mode) { + env->regs[13] = val; + } else { + env->banked_r13[bank_number(mode)] = val; + } } uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode) { - return env->banked_r13[bank_number(mode)]; + if ((env->uncached_cpsr & CPSR_M) == mode) { + return env->regs[13]; + } else { + return env->banked_r13[bank_number(mode)]; + } } uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg) @@ -2256,6 +2267,7 @@ uint32_t HELPER(vfp_get_fpscr)(CPUState *env) | (env->vfp.vec_len << 16) | (env->vfp.vec_stride << 20); i = get_float_exception_flags(&env->vfp.fp_status); + i |= get_float_exception_flags(&env->vfp.standard_fp_status); fpscr |= vfp_exceptbits_from_host(i); return fpscr; } @@ -2323,6 +2335,7 @@ void HELPER(vfp_set_fpscr)(CPUState *env, uint32_t val) i = vfp_exceptbits_to_host(val); set_float_exception_flags(i, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.standard_fp_status); } void vfp_set_fpscr(CPUState *env, uint32_t val) @@ -2611,9 +2624,17 @@ float32 HELPER(recps_f32)(float32 a, float32 b, CPUState *env) float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUState *env) { - float_status *s = &env->vfp.fp_status; + float_status *s = &env->vfp.standard_fp_status; + float32 two = int32_to_float32(2, s); float32 three = int32_to_float32(3, s); - return float32_sub(three, float32_mul(a, b, s), s); + float32 product; + if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) || + (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) { + product = float32_zero; + } else { + product = float32_mul(a, b, s); + } + return float32_div(float32_sub(three, product, s), two, s); } /* NEON helpers. */ diff --git a/target-arm/helpers.h b/target-arm/helpers.h index 0d1bc47d00..b88ebaecc6 100644 --- a/target-arm/helpers.h +++ b/target-arm/helpers.h @@ -249,6 +249,10 @@ 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) diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c index dae063ea1a..20f3c162cd 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -632,6 +632,53 @@ uint64_t HELPER(neon_qshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop) return val; } +#define NEON_FN(dest, src1, src2) do { \ + if (src1 & (1 << (sizeof(src1) * 8 - 1))) { \ + SET_QC(); \ + dest = 0; \ + } else { \ + int8_t tmp; \ + tmp = (int8_t)src2; \ + if (tmp >= (ssize_t)sizeof(src1) * 8) { \ + if (src1) { \ + SET_QC(); \ + dest = ~0; \ + } else { \ + dest = 0; \ + } \ + } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \ + dest = 0; \ + } else if (tmp < 0) { \ + dest = src1 >> -tmp; \ + } else { \ + dest = src1 << tmp; \ + if ((dest >> tmp) != src1) { \ + SET_QC(); \ + dest = ~0; \ + } \ + } \ + }} while (0) +NEON_VOP_ENV(qshlu_s8, neon_u8, 4) +NEON_VOP_ENV(qshlu_s16, neon_u16, 2) +#undef NEON_FN + +uint32_t HELPER(neon_qshlu_s32)(CPUState *env, uint32_t valop, uint32_t shiftop) +{ + if ((int32_t)valop < 0) { + SET_QC(); + return 0; + } + return helper_neon_qshl_u32(env, valop, shiftop); +} + +uint64_t HELPER(neon_qshlu_s64)(CPUState *env, uint64_t valop, uint64_t shiftop) +{ + if ((int64_t)valop < 0) { + SET_QC(); + return 0; + } + return helper_neon_qshl_u64(env, valop, shiftop); +} /* FIXME: This is wrong. */ #define NEON_FN(dest, src1, src2) do { \ diff --git a/target-arm/translate.c b/target-arm/translate.c index 2ce82f3bbb..c60cd18e1e 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -59,8 +59,13 @@ typedef struct DisasContext { #if !defined(CONFIG_USER_ONLY) int user; #endif + int vfp_enabled; + int vec_len; + int vec_stride; } DisasContext; +static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; + #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 #else @@ -2603,12 +2608,6 @@ static void gen_vfp_msr(TCGv tmp) dead_tmp(tmp); } -static inline int -vfp_enabled(CPUState * env) -{ - return ((env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) != 0); -} - static void gen_neon_dup_u8(TCGv var, int shift) { TCGv tmp = new_tmp(); @@ -2653,7 +2652,7 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) if (!arm_feature(env, ARM_FEATURE_VFP)) return 1; - if (!vfp_enabled(env)) { + if (!s->vfp_enabled) { /* VFP disabled. Only allow fmxr/fmrx to/from some control regs. */ if ((insn & 0x0fe00fff) != 0x0ee00a10) return 1; @@ -2900,7 +2899,7 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) rm = VFP_SREG_M(insn); } - veclen = env->vfp.vec_len; + veclen = s->vec_len; if (op == 15 && rn > 3) veclen = 0; @@ -2921,9 +2920,9 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) veclen = 0; } else { if (dp) - delta_d = (env->vfp.vec_stride >> 1) + 1; + delta_d = (s->vec_stride >> 1) + 1; else - delta_d = env->vfp.vec_stride + 1; + delta_d = s->vec_stride + 1; if ((rm & bank_mask) == 0) { /* mixed scalar/vector */ @@ -3488,6 +3487,14 @@ gen_set_condexec (DisasContext *s) } } +static void gen_exception_insn(DisasContext *s, int offset, int excp) +{ + gen_set_condexec(s); + gen_set_pc_im(s->pc - offset); + gen_exception(excp); + s->is_jmp = DISAS_JUMP; +} + static void gen_nop_hint(DisasContext *s, int val) { switch (val) { @@ -3804,7 +3811,7 @@ static int disas_neon_ls_insn(CPUState * env, DisasContext *s, uint32_t insn) TCGv tmp2; TCGv_i64 tmp64; - if (!vfp_enabled(env)) + if (!s->vfp_enabled) return 1; VFP_DREG_D(rd, insn); rn = (insn >> 16) & 0xf; @@ -4199,7 +4206,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) TCGv tmp, tmp2, tmp3, tmp4, tmp5; TCGv_i64 tmp64; - if (!vfp_enabled(env)) + if (!s->vfp_enabled) return 1; q = (insn & (1 << 6)) != 0; u = (insn >> 24) & 1; @@ -4652,14 +4659,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) case 5: /* VSHL, VSLI */ gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1); break; - case 6: /* VQSHL */ - if (u) - gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1); - else - gen_helper_neon_qshl_s64(cpu_V0, cpu_env, cpu_V0, cpu_V1); + case 6: /* VQSHLU */ + if (u) { + gen_helper_neon_qshlu_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); + } else { + return 1; + } break; - case 7: /* VQSHLU */ - gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1); + case 7: /* VQSHL */ + if (u) { + gen_helper_neon_qshl_u64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); + } else { + gen_helper_neon_qshl_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); + } break; } if (op == 1 || op == 3) { @@ -4698,17 +4713,30 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) default: return 1; } break; - case 6: /* VQSHL */ - GEN_NEON_INTEGER_OP_ENV(qshl); - break; - case 7: /* VQSHLU */ + case 6: /* VQSHLU */ + if (!u) { + return 1; + } switch (size) { - case 0: gen_helper_neon_qshl_u8(tmp, cpu_env, tmp, tmp2); break; - case 1: gen_helper_neon_qshl_u16(tmp, cpu_env, tmp, tmp2); break; - case 2: gen_helper_neon_qshl_u32(tmp, cpu_env, tmp, tmp2); break; - default: return 1; + case 0: + gen_helper_neon_qshlu_s8(tmp, cpu_env, + tmp, tmp2); + break; + case 1: + gen_helper_neon_qshlu_s16(tmp, cpu_env, + tmp, tmp2); + break; + case 2: + gen_helper_neon_qshlu_s32(tmp, cpu_env, + tmp, tmp2); + break; + default: + return 1; } break; + case 7: /* VQSHL */ + GEN_NEON_INTEGER_OP_ENV(qshl); + break; } dead_tmp(tmp2); @@ -5958,10 +5986,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_mov_i32(cpu_exclusive_test, addr); tcg_gen_movi_i32(cpu_exclusive_info, size | (rd << 4) | (rt << 8) | (rt2 << 12)); - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_STREX); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 4, EXCP_STREX); } #else static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, @@ -6101,14 +6126,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) goto illegal_op; ARCH(6); op1 = (insn & 0x1f); - if (op1 == (env->uncached_cpsr & CPSR_M)) { - addr = load_reg(s, 13); - } else { - addr = new_tmp(); - tmp = tcg_const_i32(op1); - gen_helper_get_r13_banked(addr, cpu_env, tmp); - tcg_temp_free_i32(tmp); - } + addr = new_tmp(); + tmp = tcg_const_i32(op1); + gen_helper_get_r13_banked(addr, cpu_env, tmp); + tcg_temp_free_i32(tmp); i = (insn >> 23) & 3; switch (i) { case 0: offset = -4; break; /* DA */ @@ -6135,14 +6156,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) } if (offset) tcg_gen_addi_i32(addr, addr, offset); - if (op1 == (env->uncached_cpsr & CPSR_M)) { - store_reg(s, 13, addr); - } else { - tmp = tcg_const_i32(op1); - gen_helper_set_r13_banked(cpu_env, tmp, addr); - tcg_temp_free_i32(tmp); - dead_tmp(addr); - } + tmp = tcg_const_i32(op1); + gen_helper_set_r13_banked(cpu_env, tmp, addr); + tcg_temp_free_i32(tmp); + dead_tmp(addr); } else { dead_tmp(addr); } @@ -6366,10 +6383,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) goto illegal_op; } /* bkpt */ - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_BKPT); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 4, EXCP_BKPT); break; case 0x8: /* signed multiply */ case 0xa: @@ -7268,10 +7282,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) break; default: illegal_op: - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_UDEF); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 4, EXCP_UDEF); break; } } @@ -7554,14 +7565,10 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1) } else { /* srs */ op = (insn & 0x1f); - if (op == (env->uncached_cpsr & CPSR_M)) { - addr = load_reg(s, 13); - } else { - addr = new_tmp(); - tmp = tcg_const_i32(op); - gen_helper_get_r13_banked(addr, cpu_env, tmp); - tcg_temp_free_i32(tmp); - } + addr = new_tmp(); + tmp = tcg_const_i32(op); + gen_helper_get_r13_banked(addr, cpu_env, tmp); + tcg_temp_free_i32(tmp); if ((insn & (1 << 24)) == 0) { tcg_gen_addi_i32(addr, addr, -8); } @@ -7577,13 +7584,9 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1) } else { tcg_gen_addi_i32(addr, addr, 4); } - if (op == (env->uncached_cpsr & CPSR_M)) { - store_reg(s, 13, addr); - } else { - tmp = tcg_const_i32(op); - gen_helper_set_r13_banked(cpu_env, tmp, addr); - tcg_temp_free_i32(tmp); - } + tmp = tcg_const_i32(op); + gen_helper_set_r13_banked(cpu_env, tmp, addr); + tcg_temp_free_i32(tmp); } else { dead_tmp(addr); } @@ -8925,10 +8928,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s) break; case 0xe: /* bkpt */ - gen_set_condexec(s); - gen_set_pc_im(s->pc - 2); - gen_exception(EXCP_BKPT); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 2, EXCP_BKPT); break; case 0xa: /* rev */ @@ -9014,7 +9014,6 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s) if (cond == 0xf) { /* swi */ - gen_set_condexec(s); gen_set_pc_im(s->pc); s->is_jmp = DISAS_SWI; break; @@ -9051,17 +9050,11 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s) } return; undef32: - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_UDEF); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 4, EXCP_UDEF); return; illegal_op: undef: - gen_set_condexec(s); - gen_set_pc_im(s->pc - 2); - gen_exception(EXCP_UDEF); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 2, EXCP_UDEF); } /* generate intermediate code in gen_opc_buf and gen_opparam_buf for @@ -9093,16 +9086,15 @@ static inline void gen_intermediate_code_internal(CPUState *env, dc->pc = pc_start; dc->singlestep_enabled = env->singlestep_enabled; dc->condjmp = 0; - dc->thumb = env->thumb; - dc->condexec_mask = (env->condexec_bits & 0xf) << 1; - dc->condexec_cond = env->condexec_bits >> 4; + dc->thumb = ARM_TBFLAG_THUMB(tb->flags); + dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; + dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; #if !defined(CONFIG_USER_ONLY) - if (IS_M(env)) { - dc->user = ((env->v7m.exception == 0) && (env->v7m.control & 1)); - } else { - dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR; - } + dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); #endif + dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); + dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); + dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); cpu_F0s = tcg_temp_new_i32(); cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); @@ -9119,9 +9111,41 @@ static inline void gen_intermediate_code_internal(CPUState *env, max_insns = CF_COUNT_MASK; gen_icount_start(); + + /* A note on handling of the condexec (IT) bits: + * + * We want to avoid the overhead of having to write the updated condexec + * bits back to the CPUState for every instruction in an IT block. So: + * (1) if the condexec bits are not already zero then we write + * zero back into the CPUState now. This avoids complications trying + * to do it at the end of the block. (For example if we don't do this + * it's hard to identify whether we can safely skip writing condexec + * at the end of the TB, which we definitely want to do for the case + * where a TB doesn't do anything with the IT state at all.) + * (2) if we are going to leave the TB then we call gen_set_condexec() + * which will write the correct value into CPUState if zero is wrong. + * This is done both for leaving the TB at the end, and for leaving + * it because of an exception we know will happen, which is done in + * gen_exception_insn(). The latter is necessary because we need to + * leave the TB with the PC/IT state just prior to execution of the + * instruction which caused the exception. + * (3) if we leave the TB unexpectedly (eg a data abort on a load) + * then the CPUState will be wrong and we need to reset it. + * This is handled in the same way as restoration of the + * PC in these situations: we will be called again with search_pc=1 + * and generate a mapping of the condexec bits for each PC in + * gen_opc_condexec_bits[]. gen_pc_load[] then uses this to restore + * the condexec bits. + * + * Note that there are no instructions which can read the condexec + * bits, and none which can write non-static values to them, so + * we don't need to care about whether CPUState is correct in the + * middle of a TB. + */ + /* Reset the conditional execution bits immediately. This avoids complications trying to do it at the end of the block. */ - if (env->condexec_bits) + if (dc->condexec_mask || dc->condexec_cond) { TCGv tmp = new_tmp(); tcg_gen_movi_i32(tmp, 0); @@ -9150,10 +9174,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { if (bp->pc == dc->pc) { - gen_set_condexec(dc); - gen_set_pc_im(dc->pc); - gen_exception(EXCP_DEBUG); - dc->is_jmp = DISAS_JUMP; + gen_exception_insn(dc, 0, EXCP_DEBUG); /* Advance PC so that clearing the breakpoint will invalidate this TB. */ dc->pc += 2; @@ -9170,6 +9191,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, gen_opc_instr_start[lj++] = 0; } gen_opc_pc[lj] = dc->pc; + gen_opc_condexec_bits[lj] = (dc->condexec_cond << 4) | (dc->condexec_mask >> 1); gen_opc_instr_start[lj] = 1; gen_opc_icount[lj] = num_insns; } @@ -9177,7 +9199,11 @@ static inline void gen_intermediate_code_internal(CPUState *env, if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); - if (env->thumb) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(dc->pc); + } + + if (dc->thumb) { disas_thumb_insn(env, dc); if (dc->condexec_mask) { dc->condexec_cond = (dc->condexec_cond & 0xe) @@ -9291,7 +9317,7 @@ done_generating: if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(pc_start, dc->pc - pc_start, env->thumb); + log_target_disas(pc_start, dc->pc - pc_start, dc->thumb); qemu_log("\n"); } #endif @@ -9377,4 +9403,5 @@ void gen_pc_load(CPUState *env, TranslationBlock *tb, unsigned long searched_pc, int pc_pos, void *puc) { env->regs[15] = gen_opc_pc[pc_pos]; + env->condexec_bits = gen_opc_condexec_bits[pc_pos]; } diff --git a/target-cris/translate.c b/target-cris/translate.c index e09aaa9e9d..f4cc1252a5 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -1129,6 +1129,9 @@ static void cris_store_direct_jmp(DisasContext *dc) { /* Store the direct jmp state into the cpu-state. */ if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { + if (dc->jmp == JMP_DIRECT) { + tcg_gen_movi_tl(env_btaken, 1); + } tcg_gen_movi_tl(env_btarget, dc->jmp_pc); dc->jmp = JMP_INDIRECT; } diff --git a/target-i386/translate.c b/target-i386/translate.c index 7b6e3c2eae..c00845038a 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -274,28 +274,16 @@ static inline void gen_op_andl_A0_ffff(void) static inline void gen_op_mov_reg_v(int ot, int reg, TCGv t0) { - TCGv tmp; - switch(ot) { case OT_BYTE: - tmp = tcg_temp_new(); - tcg_gen_ext8u_tl(tmp, t0); if (reg < 4 X86_64_DEF( || reg >= 8 || x86_64_hregs)) { - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], tmp); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8); } else { - tcg_gen_shli_tl(tmp, tmp, 8); - tcg_gen_andi_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], ~0xff00); - tcg_gen_or_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], tmp); + tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8); } - tcg_temp_free(tmp); break; case OT_WORD: - tmp = tcg_temp_new(); - tcg_gen_ext16u_tl(tmp, t0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], tmp); - tcg_temp_free(tmp); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16); break; default: /* XXX this shouldn't be reached; abort? */ case OT_LONG: @@ -323,15 +311,9 @@ static inline void gen_op_mov_reg_T1(int ot, int reg) static inline void gen_op_mov_reg_A0(int size, int reg) { - TCGv tmp; - switch(size) { case 0: - tmp = tcg_temp_new(); - tcg_gen_ext16u_tl(tmp, cpu_A0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], tmp); - tcg_temp_free(tmp); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_A0, 0, 16); break; default: /* XXX this shouldn't be reached; abort? */ case 1: @@ -415,9 +397,7 @@ static inline void gen_op_add_reg_im(int size, int reg, int32_t val) switch(size) { case 0: tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); - tcg_gen_ext16u_tl(cpu_tmp0, cpu_tmp0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16); break; case 1: tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); @@ -439,9 +419,7 @@ static inline void gen_op_add_reg_T0(int size, int reg) switch(size) { case 0: tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); - tcg_gen_ext16u_tl(cpu_tmp0, cpu_tmp0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16); break; case 1: tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index c018c99826..4b6ae068ce 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -78,9 +78,10 @@ typedef struct DisasContext { unsigned int clear_imm; int is_jmp; -#define JMP_NOJMP 0 -#define JMP_DIRECT 1 -#define JMP_INDIRECT 2 +#define JMP_NOJMP 0 +#define JMP_DIRECT 1 +#define JMP_DIRECT_CC 2 +#define JMP_INDIRECT 3 unsigned int jmp; uint32_t jmp_pc; @@ -751,7 +752,10 @@ static void dec_bit(DisasContext *dc) static inline void sync_jmpstate(DisasContext *dc) { - if (dc->jmp == JMP_DIRECT) { + if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { + if (dc->jmp == JMP_DIRECT) { + tcg_gen_movi_tl(env_btaken, 1); + } dc->jmp = JMP_INDIRECT; tcg_gen_movi_tl(env_btarget, dc->jmp_pc); } @@ -784,7 +788,7 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) { unsigned int extimm = dc->tb_flags & IMM_FLAG; - /* Treat the fast cases first. */ + /* Treat the common cases first. */ if (!dc->type_b) { /* If any of the regs is r0, return a ptr to the other. */ if (dc->ra == 0) { @@ -813,12 +817,35 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) return t; } +static inline void dec_byteswap(DisasContext *dc, TCGv dst, TCGv src, int size) +{ + if (size == 4) { + tcg_gen_bswap32_tl(dst, src); + } else if (size == 2) { + TCGv t = tcg_temp_new(); + + /* bswap16 assumes the high bits are zero. */ + tcg_gen_andi_tl(t, src, 0xffff); + tcg_gen_bswap16_tl(dst, t); + tcg_temp_free(t); + } else { + /* Ignore. + cpu_abort(dc->env, "Invalid ldst byteswap size %d\n", size); + */ + } +} + static void dec_load(DisasContext *dc) { TCGv t, *addr; - unsigned int size; + unsigned int size, rev = 0; size = 1 << (dc->opcode & 3); + + if (!dc->type_b) { + rev = (dc->ir >> 9) & 1; + } + if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); @@ -826,10 +853,63 @@ static void dec_load(DisasContext *dc) return; } - LOG_DIS("l %x %d\n", dc->opcode, size); + LOG_DIS("l%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : ""); + t_sync_flags(dc); addr = compute_ldst_addr(dc, &t); + /* + * When doing reverse accesses we need to do two things. + * + * 1. Reverse the address wrt endianess. + * 2. Byteswap the data lanes on the way back into the CPU core. + */ + if (rev && size != 4) { + /* Endian reverse the address. t is addr. */ + switch (size) { + case 1: + { + /* 00 -> 11 + 01 -> 10 + 10 -> 10 + 11 -> 00 */ + TCGv low = tcg_temp_new(); + + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_mov_tl(t, *addr); + addr = &t; + } + + tcg_gen_andi_tl(low, t, 3); + tcg_gen_sub_tl(low, tcg_const_tl(3), low); + tcg_gen_andi_tl(t, t, ~3); + tcg_gen_or_tl(t, t, low); + tcg_gen_mov_tl(env_debug, low); + tcg_gen_mov_tl(env_imm, t); + tcg_temp_free(low); + break; + } + + case 2: + /* 00 -> 10 + 10 -> 00. */ + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_xori_tl(t, *addr, 2); + addr = &t; + } else { + tcg_gen_xori_tl(t, t, 2); + } + break; + default: + cpu_abort(dc->env, "Invalid reverse size\n"); + break; + } + } + /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); @@ -848,13 +928,22 @@ static void dec_load(DisasContext *dc) tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); gen_helper_memalign(*addr, tcg_const_tl(dc->rd), tcg_const_tl(0), tcg_const_tl(size - 1)); - if (dc->rd) - tcg_gen_mov_tl(cpu_R[dc->rd], v); + if (dc->rd) { + if (rev) { + dec_byteswap(dc, cpu_R[dc->rd], v, size); + } else { + tcg_gen_mov_tl(cpu_R[dc->rd], v); + } + } tcg_temp_free(v); } else { if (dc->rd) { gen_load(dc, cpu_R[dc->rd], *addr, size); + if (rev) { + dec_byteswap(dc, cpu_R[dc->rd], cpu_R[dc->rd], size); + } } else { + /* We are loading into r0, no need to reverse. */ gen_load(dc, env_imm, *addr, size); } } @@ -881,9 +970,12 @@ static void gen_store(DisasContext *dc, TCGv addr, TCGv val, static void dec_store(DisasContext *dc) { TCGv t, *addr; - unsigned int size; + unsigned int size, rev = 0; size = 1 << (dc->opcode & 3); + if (!dc->type_b) { + rev = (dc->ir >> 9) & 1; + } if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { @@ -892,19 +984,84 @@ static void dec_store(DisasContext *dc) return; } - LOG_DIS("s%d%s\n", size, dc->type_b ? "i" : ""); + LOG_DIS("s%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : ""); t_sync_flags(dc); /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); addr = compute_ldst_addr(dc, &t); - gen_store(dc, *addr, cpu_R[dc->rd], size); + if (rev && size != 4) { + /* Endian reverse the address. t is addr. */ + switch (size) { + case 1: + { + /* 00 -> 11 + 01 -> 10 + 10 -> 10 + 11 -> 00 */ + TCGv low = tcg_temp_new(); + + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_mov_tl(t, *addr); + addr = &t; + } + + tcg_gen_andi_tl(low, t, 3); + tcg_gen_sub_tl(low, tcg_const_tl(3), low); + tcg_gen_andi_tl(t, t, ~3); + tcg_gen_or_tl(t, t, low); + tcg_gen_mov_tl(env_debug, low); + tcg_gen_mov_tl(env_imm, t); + tcg_temp_free(low); + break; + } + + case 2: + /* 00 -> 10 + 10 -> 00. */ + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_xori_tl(t, *addr, 2); + addr = &t; + } else { + tcg_gen_xori_tl(t, t, 2); + } + break; + default: + cpu_abort(dc->env, "Invalid reverse size\n"); + break; + } + + if (size != 1) { + TCGv bs_data = tcg_temp_new(); + dec_byteswap(dc, bs_data, cpu_R[dc->rd], size); + gen_store(dc, *addr, bs_data, size); + tcg_temp_free(bs_data); + } else { + gen_store(dc, *addr, cpu_R[dc->rd], size); + } + } else { + if (rev) { + TCGv bs_data = tcg_temp_new(); + dec_byteswap(dc, bs_data, cpu_R[dc->rd], size); + gen_store(dc, *addr, bs_data, size); + tcg_temp_free(bs_data); + } else { + gen_store(dc, *addr, cpu_R[dc->rd], size); + } + } /* Verify alignment if needed. */ if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); /* FIXME: if the alignment is wrong, we should restore the value - * in memory. + * in memory. One possible way to acheive this is to probe + * the MMU prior to the memaccess, thay way we could put + * the alignment checks in between the probe and the mem + * access. */ gen_helper_memalign(*addr, tcg_const_tl(dc->rd), tcg_const_tl(1), tcg_const_tl(size - 1)); @@ -975,7 +1132,7 @@ static void dec_bcc(DisasContext *dc) int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ tcg_gen_movi_tl(env_btarget, dc->pc + offset); - dc->jmp = JMP_DIRECT; + dc->jmp = JMP_DIRECT_CC; dc->jmp_pc = dc->pc + offset; } else { dc->jmp = JMP_INDIRECT; @@ -1029,7 +1186,6 @@ static void dec_br(DisasContext *dc) if (dec_alu_op_b_is_small_imm(dc)) { dc->jmp = JMP_DIRECT; dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); - tcg_gen_movi_tl(env_btaken, 1); } else { tcg_gen_movi_tl(env_btaken, 1); tcg_gen_movi_tl(env_btarget, dc->pc); @@ -1451,6 +1607,10 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb, eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc)); dc->is_jmp = DISAS_JUMP; } else if (dc->jmp == JMP_DIRECT) { + t_sync_flags(dc); + gen_goto_tb(dc, 0, dc->jmp_pc); + dc->is_jmp = DISAS_TB_JUMP; + } else if (dc->jmp == JMP_DIRECT_CC) { int l1; t_sync_flags(dc); @@ -1475,7 +1635,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb, && num_insns < max_insns); npc = dc->pc; - if (dc->jmp == JMP_DIRECT) { + if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->tb_flags & D_FLAG) { dc->is_jmp = DISAS_UPDATE; tcg_gen_movi_tl(cpu_SR[SR_PC], npc); diff --git a/target-mips/translate.c b/target-mips/translate.c index cce77be0d1..187930e3d2 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -3410,8 +3410,10 @@ static void gen_mfc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int s gen_helper_mfc0_count(arg); if (use_icount) { gen_io_end(); - ctx->bstate = BS_STOP; } + /* Break the TB to be able to take timer interrupts immediately + after reading count. */ + ctx->bstate = BS_STOP; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -4581,8 +4583,10 @@ static void gen_dmfc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int gen_helper_mfc0_count(arg); if (use_icount) { gen_io_end(); - ctx->bstate = BS_STOP; } + /* Break the TB to be able to take timer interrupts immediately + after reading count. */ + ctx->bstate = BS_STOP; rn = "Count"; break; /* 6,7 are implementation dependent */ diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index 279f345593..17e070ae75 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -546,7 +546,7 @@ uint32_t helper_compute_fprf (uint64_t arg, uint32_t set_fprf) int ret; farg.ll = arg; isneg = float64_is_neg(farg.d); - if (unlikely(float64_is_quiet_nan(farg.d))) { + if (unlikely(float64_is_any_nan(farg.d))) { if (float64_is_signaling_nan(farg.d)) { /* Signaling NaN: flags are undefined */ ret = 0x00; @@ -975,15 +975,16 @@ uint64_t helper_fadd (uint64_t arg1, uint64_t arg2) farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN addition */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN addition */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); } @@ -998,15 +999,16 @@ uint64_t helper_fsub (uint64_t arg1, uint64_t arg2) farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN subtraction */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN subtraction */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); } @@ -1021,15 +1023,16 @@ uint64_t helper_fmul (uint64_t arg1, uint64_t arg2) farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN multiplication */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN multiplication */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); } @@ -1044,17 +1047,18 @@ uint64_t helper_fdiv (uint64_t arg1, uint64_t arg2) farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN division */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { /* Division of infinity by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIDI); } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { /* Division of zero by zero */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXZDZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN division */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); } @@ -1232,16 +1236,17 @@ uint64_t helper_fmadd (uint64_t arg1, uint64_t arg2, uint64_t arg3) farg2.ll = arg2; farg3.ll = arg3; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1276,16 +1281,17 @@ uint64_t helper_fmsub (uint64_t arg1, uint64_t arg2, uint64_t arg3) farg2.ll = arg2; farg3.ll = arg3; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1319,16 +1325,17 @@ uint64_t helper_fnmadd (uint64_t arg1, uint64_t arg2, uint64_t arg3) farg2.ll = arg2; farg3.ll = arg3; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1349,8 +1356,9 @@ uint64_t helper_fnmadd (uint64_t arg1, uint64_t arg2, uint64_t arg3) /* This is OK on x86 hosts */ farg1.d = (farg1.d * farg2.d) + farg3.d; #endif - if (likely(!float64_is_quiet_nan(farg1.d))) + if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); + } } return farg1.ll; } @@ -1364,16 +1372,17 @@ uint64_t helper_fnmsub (uint64_t arg1, uint64_t arg2, uint64_t arg3) farg2.ll = arg2; farg3.ll = arg3; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1394,8 +1403,9 @@ uint64_t helper_fnmsub (uint64_t arg1, uint64_t arg2, uint64_t arg3) /* This is OK on x86 hosts */ farg1.d = (farg1.d * farg2.d) - farg3.d; #endif - if (likely(!float64_is_quiet_nan(farg1.d))) + if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); + } } return farg1.ll; } @@ -1409,11 +1419,11 @@ uint64_t helper_frsp (uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else { - f32 = float64_to_float32(farg.d, &env->fp_status); - farg.d = float32_to_float64(f32, &env->fp_status); + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); } + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + return farg.ll; } @@ -1423,13 +1433,14 @@ uint64_t helper_fsqrt (uint64_t arg) CPU_DoubleU farg; farg.ll = arg; - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN square root */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Square root of a negative nonzero number */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); } else { + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN square root */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg.d = float64_sqrt(farg.d, &env->fp_status); } return farg.ll; @@ -1443,10 +1454,9 @@ uint64_t helper_fre (uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else { - farg.d = float64_div(float64_one, farg.d, &env->fp_status); + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); return farg.d; } @@ -1459,12 +1469,12 @@ uint64_t helper_fres (uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else { - farg.d = float64_div(float64_one, farg.d, &env->fp_status); - f32 = float64_to_float32(farg.d, &env->fp_status); - farg.d = float32_to_float64(f32, &env->fp_status); + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + return farg.ll; } @@ -1475,13 +1485,14 @@ uint64_t helper_frsqrte (uint64_t arg) float32 f32; farg.ll = arg; - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN reciprocal square root */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Reciprocal square root of a negative nonzero number */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); } else { + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN reciprocal square root */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg.d = float64_sqrt(farg.d, &env->fp_status); farg.d = float64_div(float64_one, farg.d, &env->fp_status); f32 = float64_to_float32(farg.d, &env->fp_status); @@ -1497,10 +1508,11 @@ uint64_t helper_fsel (uint64_t arg1, uint64_t arg2, uint64_t arg3) farg1.ll = arg1; - if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && !float64_is_quiet_nan(farg1.d)) + if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && !float64_is_any_nan(farg1.d)) { return arg2; - else + } else { return arg3; + } } void helper_fcmpu (uint64_t arg1, uint64_t arg2, uint32_t crfD) @@ -1510,8 +1522,8 @@ void helper_fcmpu (uint64_t arg1, uint64_t arg2, uint32_t crfD) farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_quiet_nan(farg1.d) || - float64_is_quiet_nan(farg2.d))) { + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { ret = 0x01UL; } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { ret = 0x08UL; @@ -1539,8 +1551,8 @@ void helper_fcmpo (uint64_t arg1, uint64_t arg2, uint32_t crfD) farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_quiet_nan(farg1.d) || - float64_is_quiet_nan(farg2.d))) { + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { ret = 0x01UL; } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { ret = 0x08UL; @@ -3437,8 +3449,9 @@ uint32_t helper_efdctsi (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_int32(u.d, &env->vec_status); } @@ -3449,8 +3462,9 @@ uint32_t helper_efdctui (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_uint32(u.d, &env->vec_status); } @@ -3461,8 +3475,9 @@ uint32_t helper_efdctsiz (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_int32_round_to_zero(u.d, &env->vec_status); } @@ -3473,8 +3488,9 @@ uint64_t helper_efdctsidz (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_int64_round_to_zero(u.d, &env->vec_status); } @@ -3485,8 +3501,9 @@ uint32_t helper_efdctuiz (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_uint32_round_to_zero(u.d, &env->vec_status); } @@ -3497,8 +3514,9 @@ uint64_t helper_efdctuidz (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_uint64_round_to_zero(u.d, &env->vec_status); } @@ -3534,8 +3552,9 @@ uint32_t helper_efdctsf (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } tmp = uint64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_mul(u.d, tmp, &env->vec_status); @@ -3549,8 +3568,9 @@ uint32_t helper_efdctuf (uint64_t val) u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_quiet_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } tmp = uint64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_mul(u.d, tmp, &env->vec_status); diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index 8ccf25cafb..95df6d2a75 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -61,10 +61,37 @@ #define SR_S (1 << 1) #define SR_T (1 << 0) -#define FPSCR_FR (1 << 21) -#define FPSCR_SZ (1 << 20) -#define FPSCR_PR (1 << 19) -#define FPSCR_DN (1 << 18) +#define FPSCR_MASK (0x003fffff) +#define FPSCR_FR (1 << 21) +#define FPSCR_SZ (1 << 20) +#define FPSCR_PR (1 << 19) +#define FPSCR_DN (1 << 18) +#define FPSCR_CAUSE_MASK (0x3f << 12) +#define FPSCR_CAUSE_SHIFT (12) +#define FPSCR_CAUSE_E (1 << 17) +#define FPSCR_CAUSE_V (1 << 16) +#define FPSCR_CAUSE_Z (1 << 15) +#define FPSCR_CAUSE_O (1 << 14) +#define FPSCR_CAUSE_U (1 << 13) +#define FPSCR_CAUSE_I (1 << 12) +#define FPSCR_ENABLE_MASK (0x1f << 7) +#define FPSCR_ENABLE_SHIFT (7) +#define FPSCR_ENABLE_V (1 << 11) +#define FPSCR_ENABLE_Z (1 << 10) +#define FPSCR_ENABLE_O (1 << 9) +#define FPSCR_ENABLE_U (1 << 8) +#define FPSCR_ENABLE_I (1 << 7) +#define FPSCR_FLAG_MASK (0x1f << 2) +#define FPSCR_FLAG_SHIFT (2) +#define FPSCR_FLAG_V (1 << 6) +#define FPSCR_FLAG_Z (1 << 5) +#define FPSCR_FLAG_O (1 << 4) +#define FPSCR_FLAG_U (1 << 3) +#define FPSCR_FLAG_I (1 << 2) +#define FPSCR_RM_MASK (0x03 << 0) +#define FPSCR_RM_NEAREST (0 << 0) +#define FPSCR_RM_ZERO (1 << 0) + #define DELAY_SLOT (1 << 0) #define DELAY_SLOT_CONDITIONAL (1 << 1) #define DELAY_SLOT_TRUE (1 << 2) @@ -109,8 +136,6 @@ typedef struct memory_content { } memory_content; typedef struct CPUSH4State { - int id; /* CPU model */ - uint32_t flags; /* general execution flags */ uint32_t gregs[24]; /* general registers */ float32 fregs[32]; /* floating point registers */ @@ -146,14 +171,18 @@ typedef struct CPUSH4State { uint32_t expevt; /* exception event register */ uint32_t intevt; /* interrupt event register */ + tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ + tlb_t utlb[UTLB_SIZE]; /* unified translation table */ + + uint32_t ldst; + + CPU_COMMON + + int id; /* CPU model */ uint32_t pvr; /* Processor Version Register */ uint32_t prr; /* Processor Revision Register */ uint32_t cvr; /* Cache Version Register */ - uint32_t ldst; - - CPU_COMMON tlb_t utlb[UTLB_SIZE]; /* unified translation table */ - tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ void *intc_handle; int intr_at_halt; /* SR_BL ignored during sleep */ memory_content *movcal_backup; diff --git a/target-sh4/helper.c b/target-sh4/helper.c index 2343366762..45449ea64e 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -380,7 +380,7 @@ static int get_mmu_address(CPUState * env, target_ulong * physical, MMU_DTLB_VIOLATION_READ; } else if ((rw == 1) && !(matching->pr & 1)) { n = MMU_DTLB_VIOLATION_WRITE; - } else if ((rw == 1) & !matching->d) { + } else if ((rw == 1) && !matching->d) { n = MMU_DTLB_INITIAL_WRITE; } else { *prot = PAGE_READ; @@ -430,7 +430,7 @@ static int get_physical_address(CPUState * env, target_ulong * physical, } /* If MMU is disabled, return the corresponding physical page */ - if (!env->mmucr & MMUCR_AT) { + if (!(env->mmucr & MMUCR_AT)) { *physical = address & 0x1FFFFFFF; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return MMU_OK; diff --git a/target-sh4/helper.h b/target-sh4/helper.h index 4b2fcdd536..2e52768414 100644 --- a/target-sh4/helper.h +++ b/target-sh4/helper.h @@ -17,7 +17,6 @@ DEF_HELPER_2(addv, i32, i32, i32) DEF_HELPER_2(addc, i32, i32, i32) DEF_HELPER_2(subv, i32, i32, i32) DEF_HELPER_2(subc, i32, i32, i32) -DEF_HELPER_1(negc, i32, i32) DEF_HELPER_2(div1, i32, i32, i32) DEF_HELPER_2(macl, void, i32, i32) DEF_HELPER_2(macw, void, i32, i32) @@ -49,5 +48,7 @@ DEF_HELPER_1(fsqrt_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(fipr, void, i32, i32) +DEF_HELPER_1(ftrv, void, i32) #include "def-helper.h" diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c index 2e5f5552a2..30f9842295 100644 --- a/target-sh4/op_helper.c +++ b/target-sh4/op_helper.c @@ -21,6 +21,22 @@ #include "exec.h" #include "helper.h" +static void cpu_restore_state_from_retaddr(void *retaddr) +{ + TranslationBlock *tb; + unsigned long pc; + + if (retaddr) { + pc = (unsigned long) retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); + } + } +} + #ifndef CONFIG_USER_ONLY #define MMUSUFFIX _mmu @@ -39,9 +55,7 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) { - TranslationBlock *tb; CPUState *saved_env; - unsigned long pc; int ret; /* XXX: hack to restore env in all cases, even if not called from @@ -50,16 +64,8 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) env = cpu_single_env; ret = cpu_sh4_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); if (ret) { - if (retaddr) { - /* now we have a real cpu fault */ - pc = (unsigned long) retaddr; - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); - } - } + /* now we have a real cpu fault */ + cpu_restore_state_from_retaddr(retaddr); cpu_loop_exit(); } env = saved_env; @@ -77,28 +83,31 @@ void helper_ldtlb(void) #endif } -void helper_raise_illegal_instruction(void) +static inline void raise_exception(int index, void *retaddr) { - env->exception_index = 0x180; + env->exception_index = index; + cpu_restore_state_from_retaddr(retaddr); cpu_loop_exit(); } +void helper_raise_illegal_instruction(void) +{ + raise_exception(0x180, GETPC()); +} + void helper_raise_slot_illegal_instruction(void) { - env->exception_index = 0x1a0; - cpu_loop_exit(); + raise_exception(0x1a0, GETPC()); } void helper_raise_fpu_disable(void) { - env->exception_index = 0x800; - cpu_loop_exit(); + raise_exception(0x800, GETPC()); } void helper_raise_slot_fpu_disable(void) { - env->exception_index = 0x820; - cpu_loop_exit(); + raise_exception(0x820, GETPC()); } void helper_debug(void) @@ -118,8 +127,7 @@ void helper_sleep(uint32_t next_pc) void helper_trapa(uint32_t tra) { env->tra = tra << 2; - env->exception_index = 0x160; - cpu_loop_exit(); + raise_exception(0x160, GETPC()); } void helper_movcal(uint32_t address, uint32_t value) @@ -371,21 +379,6 @@ void helper_macw(uint32_t arg0, uint32_t arg1) } } -uint32_t helper_negc(uint32_t arg) -{ - uint32_t temp; - - temp = -arg; - arg = temp - (env->sr & SR_T); - if (0 < temp) - env->sr |= SR_T; - else - env->sr &= ~SR_T; - if (temp < arg) - env->sr |= SR_T; - return arg; -} - uint32_t helper_subc(uint32_t arg0, uint32_t arg1) { uint32_t tmp0, tmp1; @@ -443,11 +436,54 @@ static inline void clr_t(void) void helper_ld_fpscr(uint32_t val) { - env->fpscr = val & 0x003fffff; - if (val & 0x01) + env->fpscr = val & FPSCR_MASK; + if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) { set_float_rounding_mode(float_round_to_zero, &env->fp_status); - else + } else { set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + } + set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status); +} + +static void update_fpscr(void *retaddr) +{ + int xcpt, cause, enable; + + xcpt = get_float_exception_flags(&env->fp_status); + + /* Clear the flag entries */ + env->fpscr &= ~FPSCR_FLAG_MASK; + + if (unlikely(xcpt)) { + if (xcpt & float_flag_invalid) { + env->fpscr |= FPSCR_FLAG_V; + } + if (xcpt & float_flag_divbyzero) { + env->fpscr |= FPSCR_FLAG_Z; + } + if (xcpt & float_flag_overflow) { + env->fpscr |= FPSCR_FLAG_O; + } + if (xcpt & float_flag_underflow) { + env->fpscr |= FPSCR_FLAG_U; + } + if (xcpt & float_flag_inexact) { + env->fpscr |= FPSCR_FLAG_I; + } + + /* Accumulate in cause entries */ + env->fpscr |= (env->fpscr & FPSCR_FLAG_MASK) + << (FPSCR_CAUSE_SHIFT - FPSCR_FLAG_SHIFT); + + /* Generate an exception if enabled */ + cause = (env->fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT; + enable = (env->fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT; + if (cause & enable) { + cpu_restore_state_from_retaddr(retaddr); + env->exception_index = 0x120; + cpu_loop_exit(); + } + } } uint32_t helper_fabs_FT(uint32_t t0) @@ -471,7 +507,9 @@ uint32_t helper_fadd_FT(uint32_t t0, uint32_t 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); + update_fpscr(GETPC()); return f0.l; } @@ -480,56 +518,82 @@ uint64_t helper_fadd_DT(uint64_t t0, uint64_t t1) CPU_DoubleU d0, d1; d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_add(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); return d0.ll; } void helper_fcmp_eq_FT(uint32_t t0, uint32_t t1) { CPU_FloatU f0, f1; + int relation; f0.l = t0; f1.l = t1; - if (float32_compare(f0.f, f1.f, &env->fp_status) == 0) + set_float_exception_flags(0, &env->fp_status); + relation = float32_compare(f0.f, f1.f, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_equal) { set_t(); - else + } else { clr_t(); + } } void helper_fcmp_eq_DT(uint64_t t0, uint64_t t1) { CPU_DoubleU d0, d1; + int relation; d0.ll = t0; d1.ll = t1; - if (float64_compare(d0.d, d1.d, &env->fp_status) == 0) + set_float_exception_flags(0, &env->fp_status); + relation = float64_compare(d0.d, d1.d, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_equal) { set_t(); - else + } else { clr_t(); + } } void helper_fcmp_gt_FT(uint32_t t0, uint32_t t1) { CPU_FloatU f0, f1; + int relation; f0.l = t0; f1.l = t1; - if (float32_compare(f0.f, f1.f, &env->fp_status) == 1) + set_float_exception_flags(0, &env->fp_status); + relation = float32_compare(f0.f, f1.f, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_greater) { set_t(); - else + } else { clr_t(); + } } void helper_fcmp_gt_DT(uint64_t t0, uint64_t t1) { CPU_DoubleU d0, d1; + int relation; d0.ll = t0; d1.ll = t1; - if (float64_compare(d0.d, d1.d, &env->fp_status) == 1) + set_float_exception_flags(0, &env->fp_status); + relation = float64_compare(d0.d, d1.d, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_greater) { set_t(); - else + } else { clr_t(); + } } uint64_t helper_fcnvsd_FT_DT(uint32_t t0) @@ -537,7 +601,9 @@ uint64_t helper_fcnvsd_FT_DT(uint32_t t0) CPU_DoubleU d; CPU_FloatU f; f.l = t0; + set_float_exception_flags(0, &env->fp_status); d.d = float32_to_float64(f.f, &env->fp_status); + update_fpscr(GETPC()); return d.ll; } @@ -546,7 +612,9 @@ uint32_t helper_fcnvds_DT_FT(uint64_t t0) CPU_DoubleU d; CPU_FloatU f; d.ll = t0; + set_float_exception_flags(0, &env->fp_status); f.f = float64_to_float32(d.d, &env->fp_status); + update_fpscr(GETPC()); return f.l; } @@ -555,7 +623,9 @@ uint32_t helper_fdiv_FT(uint32_t t0, uint32_t 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); + update_fpscr(GETPC()); return f0.l; } @@ -564,21 +634,29 @@ uint64_t helper_fdiv_DT(uint64_t t0, uint64_t t1) CPU_DoubleU d0, d1; d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_div(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); return d0.ll; } uint32_t helper_float_FT(uint32_t t0) { CPU_FloatU f; + + set_float_exception_flags(0, &env->fp_status); f.f = int32_to_float32(t0, &env->fp_status); + update_fpscr(GETPC()); + return f.l; } uint64_t helper_float_DT(uint32_t t0) { CPU_DoubleU d; + set_float_exception_flags(0, &env->fp_status); d.d = int32_to_float64(t0, &env->fp_status); + update_fpscr(GETPC()); return d.ll; } @@ -588,8 +666,11 @@ uint32_t helper_fmac_FT(uint32_t t0, uint32_t t1, uint32_t t2) f0.l = t0; f1.l = t1; f2.l = t2; + set_float_exception_flags(0, &env->fp_status); f0.f = float32_mul(f0.f, f1.f, &env->fp_status); f0.f = float32_add(f0.f, f2.f, &env->fp_status); + update_fpscr(GETPC()); + return f0.l; } @@ -598,7 +679,9 @@ uint32_t helper_fmul_FT(uint32_t t0, uint32_t 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); + update_fpscr(GETPC()); return f0.l; } @@ -607,7 +690,10 @@ uint64_t helper_fmul_DT(uint64_t t0, uint64_t t1) CPU_DoubleU d0, d1; d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_mul(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); + return d0.ll; } @@ -623,7 +709,9 @@ uint32_t helper_fsqrt_FT(uint32_t t0) { CPU_FloatU f; f.l = t0; + set_float_exception_flags(0, &env->fp_status); f.f = float32_sqrt(f.f, &env->fp_status); + update_fpscr(GETPC()); return f.l; } @@ -631,7 +719,9 @@ uint64_t helper_fsqrt_DT(uint64_t t0) { CPU_DoubleU d; d.ll = t0; + set_float_exception_flags(0, &env->fp_status); d.d = float64_sqrt(d.d, &env->fp_status); + update_fpscr(GETPC()); return d.ll; } @@ -640,29 +730,88 @@ uint32_t helper_fsub_FT(uint32_t t0, uint32_t 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); + update_fpscr(GETPC()); return f0.l; } uint64_t helper_fsub_DT(uint64_t t0, uint64_t t1) { CPU_DoubleU d0, d1; + d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_sub(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); return d0.ll; } uint32_t helper_ftrc_FT(uint32_t t0) { CPU_FloatU f; + uint32_t ret; f.l = t0; - return float32_to_int32_round_to_zero(f.f, &env->fp_status); + set_float_exception_flags(0, &env->fp_status); + ret = float32_to_int32_round_to_zero(f.f, &env->fp_status); + update_fpscr(GETPC()); + return ret; } uint32_t helper_ftrc_DT(uint64_t t0) { CPU_DoubleU d; + uint32_t ret; d.ll = t0; - return float64_to_int32_round_to_zero(d.d, &env->fp_status); + set_float_exception_flags(0, &env->fp_status); + ret = float64_to_int32_round_to_zero(d.d, &env->fp_status); + update_fpscr(GETPC()); + return ret; +} + +void helper_fipr(uint32_t m, uint32_t n) +{ + int bank, i; + float32 r, p; + + bank = (env->sr & FPSCR_FR) ? 16 : 0; + r = float32_zero; + set_float_exception_flags(0, &env->fp_status); + + for (i = 0 ; i < 4 ; i++) { + p = float32_mul(env->fregs[bank + m + i], + env->fregs[bank + n + i], + &env->fp_status); + r = float32_add(r, p, &env->fp_status); + } + update_fpscr(GETPC()); + + env->fregs[bank + n + 3] = r; +} + +void helper_ftrv(uint32_t n) +{ + int bank_matrix, bank_vector; + int i, j; + float32 r[4]; + float32 p; + + bank_matrix = (env->sr & FPSCR_FR) ? 0 : 16; + bank_vector = (env->sr & FPSCR_FR) ? 16 : 0; + set_float_exception_flags(0, &env->fp_status); + for (i = 0 ; i < 4 ; i++) { + r[i] = float32_zero; + for (j = 0 ; j < 4 ; j++) { + p = float32_mul(env->fregs[bank_matrix + 4 * j + i], + env->fregs[bank_vector + j], + &env->fp_status); + r[i] = float32_add(r[i], p, &env->fp_status); + } + } + update_fpscr(GETPC()); + + for (i = 0 ; i < 4 ; i++) { + env->fregs[bank_vector + i] = r[i]; + } } diff --git a/target-sh4/translate.c b/target-sh4/translate.c index f418139931..35573be5ff 100644 --- a/target-sh4/translate.c +++ b/target-sh4/translate.c @@ -185,28 +185,27 @@ void cpu_dump_state(CPUState * env, FILE * f, } } -static void cpu_sh4_reset(CPUSH4State * env) +void cpu_reset(CPUSH4State * env) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); log_cpu_state(env, 0); } -#if defined(CONFIG_USER_ONLY) - env->sr = 0; -#else - env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0; -#endif - env->vbr = 0; + memset(env, 0, offsetof(CPUSH4State, breakpoints)); + tlb_flush(env, 1); + env->pc = 0xA0000000; #if defined(CONFIG_USER_ONLY) env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */ #else - env->fpscr = 0x00040001; /* CPU reset value according to SH4 manual */ + env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0; + env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */ set_float_rounding_mode(float_round_to_zero, &env->fp_status); + set_flush_to_zero(1, &env->fp_status); #endif - env->mmucr = 0; + set_default_nan_mode(1, &env->fp_status); } typedef struct { @@ -265,7 +264,7 @@ void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) (*cpu_fprintf)(f, "%s\n", sh4_defs[i].name); } -static void cpu_sh4_register(CPUSH4State *env, const sh4_def_t *def) +static void cpu_register(CPUSH4State *env, const sh4_def_t *def) { env->pvr = def->pvr; env->prr = def->prr; @@ -287,9 +286,8 @@ CPUSH4State *cpu_sh4_init(const char *cpu_model) env->movcal_backup_tail = &(env->movcal_backup); sh4_translate_init(); env->cpu_model_str = cpu_model; - cpu_sh4_reset(env); - cpu_sh4_register(env, def); - tlb_flush(env, 1); + cpu_reset(env); + cpu_register(env, def); qemu_init_vcpu(env); return env; } @@ -334,7 +332,7 @@ static inline void gen_branch_slot(uint32_t delayed_pc, int t) tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc); sr = tcg_temp_new(); tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(TCG_COND_NE, sr, t ? SR_T : 0, label); + tcg_gen_brcondi_i32(t ? TCG_COND_EQ:TCG_COND_NE, sr, 0, label); tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); gen_set_label(label); } @@ -349,7 +347,7 @@ static void gen_conditional_jump(DisasContext * ctx, l1 = gen_new_label(); sr = tcg_temp_new(); tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(TCG_COND_EQ, sr, SR_T, l1); + tcg_gen_brcondi_i32(TCG_COND_NE, sr, 0, l1); gen_goto_tb(ctx, 0, ifnott); gen_set_label(l1); gen_goto_tb(ctx, 1, ift); @@ -364,7 +362,7 @@ static void gen_delayed_conditional_jump(DisasContext * ctx) l1 = gen_new_label(); ds = tcg_temp_new(); tcg_gen_andi_i32(ds, cpu_flags, DELAY_SLOT_TRUE); - tcg_gen_brcondi_i32(TCG_COND_EQ, ds, DELAY_SLOT_TRUE, l1); + tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1); gen_goto_tb(ctx, 1, ctx->pc + 2); gen_set_label(l1); tcg_gen_andi_i32(cpu_flags, cpu_flags, ~DELAY_SLOT_TRUE); @@ -383,26 +381,26 @@ static inline void gen_clr_t(void) static inline void gen_cmp(int cond, TCGv t0, TCGv t1) { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - tcg_gen_brcond_i32(cond, t1, t0, label1); - gen_clr_t(); - tcg_gen_br(label2); - gen_set_label(label1); - gen_set_t(); - gen_set_label(label2); + TCGv t; + + t = tcg_temp_new(); + tcg_gen_setcond_i32(cond, t, t1, t0); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_or_i32(cpu_sr, cpu_sr, t); + + tcg_temp_free(t); } static inline void gen_cmp_imm(int cond, TCGv t0, int32_t imm) { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - tcg_gen_brcondi_i32(cond, t0, imm, label1); - gen_clr_t(); - tcg_gen_br(label2); - gen_set_label(label1); - gen_set_t(); - gen_set_label(label2); + TCGv t; + + t = tcg_temp_new(); + tcg_gen_setcondi_i32(cond, t, t0, imm); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_or_i32(cpu_sr, cpu_sr, t); + + tcg_temp_free(t); } static inline void gen_store_flags(uint32_t flags) @@ -469,27 +467,27 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg) #define CHECK_NOT_DELAY_SLOT \ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) \ { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc-2); \ gen_helper_raise_slot_illegal_instruction(); \ ctx->bstate = BS_EXCP; \ return; \ } -#define CHECK_PRIVILEGED \ - if (IS_USER(ctx)) { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc); \ - gen_helper_raise_illegal_instruction(); \ - ctx->bstate = BS_EXCP; \ - return; \ +#define CHECK_PRIVILEGED \ + if (IS_USER(ctx)) { \ + if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ + gen_helper_raise_slot_illegal_instruction(); \ + } else { \ + gen_helper_raise_illegal_instruction(); \ + } \ + ctx->bstate = BS_EXCP; \ + return; \ } #define CHECK_FPU_ENABLED \ if (ctx->flags & SR_FD) { \ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc-2); \ gen_helper_raise_slot_fpu_disable(); \ } else { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc); \ gen_helper_raise_fpu_disable(); \ } \ ctx->bstate = BS_EXCP; \ @@ -818,24 +816,22 @@ static void _decode_opc(DisasContext * ctx) return; case 0x200c: /* cmp/str Rm,Rn */ { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - TCGv cmp1 = tcg_temp_local_new(); - TCGv cmp2 = tcg_temp_local_new(); + TCGv cmp1 = tcg_temp_new(); + TCGv cmp2 = tcg_temp_new(); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8)); tcg_gen_andi_i32(cmp2, cmp1, 0xff000000); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_br(label2); - gen_set_label(label1); - tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_T); - gen_set_label(label2); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_temp_free(cmp2); tcg_temp_free(cmp1); } @@ -956,7 +952,21 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_neg_i32(REG(B11_8), REG(B7_4)); return; case 0x600a: /* negc Rm,Rn */ - gen_helper_negc(REG(B11_8), REG(B7_4)); + { + TCGv t0, t1; + t0 = tcg_temp_new(); + tcg_gen_neg_i32(t0, REG(B7_4)); + t1 = tcg_temp_new(); + tcg_gen_andi_i32(t1, cpu_sr, SR_T); + tcg_gen_sub_i32(REG(B11_8), t0, t1); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_setcond_i32(TCG_COND_GE, t1, REG(B11_8), t0); + tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_gen_setcondi_i32(TCG_COND_GE, t1, t0, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + } return; case 0x6007: /* not Rm,Rn */ tcg_gen_not_i32(REG(B11_8), REG(B7_4)); @@ -1375,7 +1385,6 @@ static void _decode_opc(DisasContext * ctx) { TCGv imm; CHECK_NOT_DELAY_SLOT - tcg_gen_movi_i32(cpu_pc, ctx->pc); imm = tcg_const_i32(B7_0); gen_helper_trapa(imm); tcg_temp_free(imm); @@ -1695,14 +1704,12 @@ static void _decode_opc(DisasContext * ctx) } return; case 0x4004: /* rotl Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31); - tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 0, cpu_sr, 0); + tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1); + gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); return; case 0x4005: /* rotr Rn */ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); - tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 31, cpu_sr, 0); + tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4000: /* shll Rn */ case 0x4020: /* shal Rn */ @@ -1854,13 +1861,40 @@ static void _decode_opc(DisasContext * ctx) tcg_temp_free_i64(fp); } return; + case 0xf0ed: /* fipr FVm,FVn */ + CHECK_FPU_ENABLED + if ((ctx->fpscr & FPSCR_PR) == 0) { + TCGv m, n; + m = tcg_const_i32((ctx->opcode >> 16) & 3); + n = tcg_const_i32((ctx->opcode >> 18) & 3); + gen_helper_fipr(m, n); + tcg_temp_free(m); + tcg_temp_free(n); + return; + } + break; + case 0xf0fd: /* ftrv XMTRX,FVn */ + CHECK_FPU_ENABLED + if ((ctx->opcode & 0x0300) == 0x0100 && + (ctx->fpscr & FPSCR_PR) == 0) { + TCGv n; + n = tcg_const_i32((ctx->opcode >> 18) & 3); + gen_helper_ftrv(n); + tcg_temp_free(n); + return; + } + break; } #if 0 fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n", ctx->opcode, ctx->pc); fflush(stderr); #endif - gen_helper_raise_illegal_instruction(); + if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { + gen_helper_raise_slot_illegal_instruction(); + } else { + gen_helper_raise_illegal_instruction(); + } ctx->bstate = BS_EXCP; } @@ -1868,6 +1902,10 @@ static void decode_opc(DisasContext * ctx) { uint32_t old_flags = ctx->flags; + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(ctx->pc); + } + _decode_opc(ctx); if (old_flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 58f9f82f52..b70970ac2a 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -857,65 +857,77 @@ void helper_fsqrtq(void) QT0 = float128_sqrt(QT1, &env->fp_status); } -#define GEN_FCMP(name, size, reg1, reg2, FS, TRAP) \ +#define GEN_FCMP(name, size, reg1, reg2, FS, E) \ void glue(helper_, name) (void) \ { \ - target_ulong new_fsr; \ - \ - env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr &= FSR_FTT_NMASK; \ + if (E && (glue(size, _is_any_nan)(reg1) || \ + glue(size, _is_any_nan)(reg2)) && \ + (env->fsr & FSR_NVM)) { \ + env->fsr |= FSR_NVC; \ + env->fsr |= FSR_FTT_IEEE_EXCP; \ + raise_exception(TT_FP_EXCP); \ + } \ switch (glue(size, _compare) (reg1, reg2, &env->fp_status)) { \ case float_relation_unordered: \ - new_fsr = (FSR_FCC1 | FSR_FCC0) << FS; \ - if ((env->fsr & FSR_NVM) || TRAP) { \ - env->fsr |= new_fsr; \ + if ((env->fsr & FSR_NVM)) { \ env->fsr |= FSR_NVC; \ env->fsr |= FSR_FTT_IEEE_EXCP; \ raise_exception(TT_FP_EXCP); \ } else { \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \ env->fsr |= FSR_NVA; \ } \ break; \ case float_relation_less: \ - new_fsr = FSR_FCC0 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC0 << FS; \ break; \ case float_relation_greater: \ - new_fsr = FSR_FCC1 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC1 << FS; \ break; \ default: \ - new_fsr = 0; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ break; \ } \ - env->fsr |= new_fsr; \ } -#define GEN_FCMPS(name, size, FS, TRAP) \ +#define GEN_FCMPS(name, size, FS, E) \ void glue(helper_, name)(float32 src1, float32 src2) \ { \ - target_ulong new_fsr; \ - \ - env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr &= FSR_FTT_NMASK; \ + if (E && (glue(size, _is_any_nan)(src1) || \ + glue(size, _is_any_nan)(src2)) && \ + (env->fsr & FSR_NVM)) { \ + env->fsr |= FSR_NVC; \ + env->fsr |= FSR_FTT_IEEE_EXCP; \ + raise_exception(TT_FP_EXCP); \ + } \ switch (glue(size, _compare) (src1, src2, &env->fp_status)) { \ case float_relation_unordered: \ - new_fsr = (FSR_FCC1 | FSR_FCC0) << FS; \ - if ((env->fsr & FSR_NVM) || TRAP) { \ - env->fsr |= new_fsr; \ + if ((env->fsr & FSR_NVM)) { \ env->fsr |= FSR_NVC; \ env->fsr |= FSR_FTT_IEEE_EXCP; \ raise_exception(TT_FP_EXCP); \ } else { \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \ env->fsr |= FSR_NVA; \ } \ break; \ case float_relation_less: \ - new_fsr = FSR_FCC0 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC0 << FS; \ break; \ case float_relation_greater: \ - new_fsr = FSR_FCC1 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC1 << FS; \ break; \ default: \ - new_fsr = 0; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ break; \ } \ - env->fsr |= new_fsr; \ } GEN_FCMPS(fcmps, float32, 0, 0); diff --git a/tcg/README b/tcg/README index a18a87ffcd..660012281f 100644 --- a/tcg/README +++ b/tcg/README @@ -285,6 +285,20 @@ the four high order bytes are set to zero. Indicate that the value of t0 won't be used later. It is useful to force dead code elimination. +* deposit_i32/i64 dest, t1, t2, pos, len + +Deposit T2 as a bitfield into T1, placing the result in DEST. +The bitfield is described by POS/LEN, which are immediate values: + + LEN - the length of the bitfield + POS - the position of the first bit, counting from the LSB + +For example, pos=8, len=4 indicates a 4-bit field at bit 8. +This operation would be equivalent to + + dest = (t1 & ~0x0f00) | ((t2 << 8) & 0x0f00) + + ********* Conditional moves * setcond_i32/i64 cond, dest, t1, t2 diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c index 1eb5605f8c..918e2f73cb 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -352,6 +352,9 @@ static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset) static inline void tcg_out_b_noaddr(TCGContext *s, int cond) { + /* We pay attention here to not modify the branch target by skipping + the corresponding bytes. This ensure that caches and memory are + kept coherent during retranslation. */ #ifdef HOST_WORDS_BIGENDIAN tcg_out8(s, (cond << 4) | 0x0a); s->code_ptr += 3; diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c index e2e44f7d76..8dac7f72fd 100644 --- a/tcg/ia64/tcg-target.c +++ b/tcg/ia64/tcg-target.c @@ -871,6 +871,9 @@ static void tcg_out_br(TCGContext *s, int label_index) { TCGLabel *l = &s->labels[label_index]; + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ tcg_out_bundle(s, mmB, tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c index 4e92a50e6f..e04b0dc32f 100644 --- a/tcg/mips/tcg-target.c +++ b/tcg/mips/tcg-target.c @@ -351,7 +351,9 @@ static inline void tcg_out_opc_imm(TCGContext *s, int opc, int rt, int rs, int i */ static inline void tcg_out_opc_br(TCGContext *s, int opc, int rt, int rs) { - /* We need to keep the offset unchanged for retranslation */ + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ uint16_t offset = (uint16_t)(*(uint32_t *) s->code_ptr); tcg_out_opc_imm(s, opc, rt, rs, offset); diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 3ee0a58ed1..207a89fd97 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -254,6 +254,30 @@ static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, *gen_opparam_ptr++ = arg5; } +static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGv_i32 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGv_i64 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5, TCGv_i32 arg6) @@ -2071,6 +2095,44 @@ static inline void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) } } +static inline void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, + TCGv_i32 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i32 + tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); +#else + uint32_t mask = (1u << len) - 1; + TCGv_i32 t1 = tcg_temp_new_i32 (); + + tcg_gen_andi_i32(t1, arg2, mask); + tcg_gen_shli_i32(t1, t1, ofs); + tcg_gen_andi_i32(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i32(ret, ret, t1); + + tcg_temp_free_i32(t1); +#endif +} + +static inline void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, + TCGv_i64 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i64 + tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); +#else + uint64_t mask = (1ull << len) - 1; + TCGv_i64 t1 = tcg_temp_new_i64 (); + + tcg_gen_andi_i64(t1, arg2, mask); + tcg_gen_shli_i64(t1, t1, ofs); + tcg_gen_andi_i64(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i64(ret, ret, t1); + + tcg_temp_free_i64(t1); +#endif +} + /***************************************/ /* QEMU specific operations. Their type depend on the QEMU CPU type. */ @@ -2384,6 +2446,7 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_rotli_tl tcg_gen_rotli_i64 #define tcg_gen_rotr_tl tcg_gen_rotr_i64 #define tcg_gen_rotri_tl tcg_gen_rotri_i64 +#define tcg_gen_deposit_tl tcg_gen_deposit_i64 #define tcg_const_tl tcg_const_i64 #define tcg_const_local_tl tcg_const_local_i64 #else @@ -2454,6 +2517,7 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_rotli_tl tcg_gen_rotli_i32 #define tcg_gen_rotr_tl tcg_gen_rotr_i32 #define tcg_gen_rotri_tl tcg_gen_rotri_i32 +#define tcg_gen_deposit_tl tcg_gen_deposit_i32 #define tcg_const_tl tcg_const_i32 #define tcg_const_local_tl tcg_const_local_i32 #endif diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h index 2a98fed909..2c7ca1a450 100644 --- a/tcg/tcg-opc.h +++ b/tcg/tcg-opc.h @@ -78,6 +78,9 @@ DEF(sar_i32, 1, 2, 0, 0) DEF(rotl_i32, 1, 2, 0, 0) DEF(rotr_i32, 1, 2, 0, 0) #endif +#ifdef TCG_TARGET_HAS_deposit_i32 +DEF(deposit_i32, 1, 2, 2, 0) +#endif DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) #if TCG_TARGET_REG_BITS == 32 @@ -168,6 +171,9 @@ DEF(sar_i64, 1, 2, 0, 0) DEF(rotl_i64, 1, 2, 0, 0) DEF(rotr_i64, 1, 2, 0, 0) #endif +#ifdef TCG_TARGET_HAS_deposit_i64 +DEF(deposit_i64, 1, 2, 2, 0) +#endif DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) #ifdef TCG_TARGET_HAS_ext8s_i64 diff --git a/tests/Makefile b/tests/Makefile index e43ec70b6d..9ded4b7349 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,8 @@ QEMU=../i386-linux-user/qemu-i386 QEMU_X86_64=../x86_64-linux-user/qemu-x86_64 CC_X86_64=$(CC_I386) -m64 -CFLAGS=-Wall -O2 -g -fno-strict-aliasing -I.. +QEMU_INCLUDES += -I.. +CFLAGS=-Wall -O2 -g -fno-strict-aliasing #CFLAGS+=-msse2 LDFLAGS= diff --git a/tests/cris/check_abs.c b/tests/cris/check_abs.c index 3966c87c78..9770a8d9ef 100644 --- a/tests/cris/check_abs.c +++ b/tests/cris/check_abs.c @@ -4,13 +4,14 @@ #include "sys.h" #include "crisutils.h" -extern inline int cris_abs(int n) { +static inline int cris_abs(int n) +{ int r; asm ("abs\t%1, %0\n" : "=r" (r) : "r" (n)); return r; } -extern inline void +static inline void verify_abs(int val, int res, const int n, const int z, const int v, const int c) { diff --git a/tests/cris/check_addc.c b/tests/cris/check_addc.c index e4078555f8..facd1bea2d 100644 --- a/tests/cris/check_addc.c +++ b/tests/cris/check_addc.c @@ -4,7 +4,8 @@ #include "sys.h" #include "crisutils.h" -extern inline int cris_addc(int a, const int b) { +static inline int cris_addc(int a, const int b) +{ asm ("addc\t%1, %0\n" : "+r" (a) : "r" (b)); return a; } diff --git a/tests/cris/check_addcm.c b/tests/cris/check_addcm.c index 9ffea29bd9..7928bc9999 100644 --- a/tests/cris/check_addcm.c +++ b/tests/cris/check_addcm.c @@ -5,13 +5,15 @@ #include "crisutils.h" /* need to avoid acr as source here. */ -extern inline int cris_addc_m(int a, const int *b) { +static inline int cris_addc_m(int a, const int *b) +{ asm volatile ("addc [%1], %0\n" : "+r" (a) : "r" (b)); return a; } /* 'b' is a crisv32 constrain to avoid postinc with $acr. */ -extern inline int cris_addc_pi_m(int a, int **b) { +static inline int cris_addc_pi_m(int a, int **b) +{ asm volatile ("addc [%1+], %0\n" : "+r" (a), "+b" (*b)); return a; } diff --git a/tests/cris/check_bound.c b/tests/cris/check_bound.c index 411d2ad56e..e8831754ec 100644 --- a/tests/cris/check_bound.c +++ b/tests/cris/check_bound.c @@ -4,19 +4,22 @@ #include "sys.h" #include "crisutils.h" -extern inline int cris_bound_b(int v, int b) { +static inline int cris_bound_b(int v, int b) +{ int r = v; asm ("bound.b\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; } -extern inline int cris_bound_w(int v, int b) { +static inline int cris_bound_w(int v, int b) +{ int r = v; asm ("bound.w\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; } -extern inline int cris_bound_d(int v, int b) { +static inline int cris_bound_d(int v, int b) +{ int r = v; asm ("bound.d\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; diff --git a/tests/cris/check_ftag.c b/tests/cris/check_ftag.c index 40d1507a87..908773a38a 100644 --- a/tests/cris/check_ftag.c +++ b/tests/cris/check_ftag.c @@ -4,19 +4,23 @@ #include "sys.h" #include "crisutils.h" -extern inline void cris_ftag_i(unsigned int x) { +static inline void cris_ftag_i(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("ftagi\t[%0]\n" : : "r" (v) ); } -extern inline void cris_ftag_d(unsigned int x) { +static inline void cris_ftag_d(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("ftagd\t[%0]\n" : : "r" (v) ); } -extern inline void cris_fidx_i(unsigned int x) { +static inline void cris_fidx_i(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("fidxi\t[%0]\n" : : "r" (v) ); } -extern inline void cris_fidx_d(unsigned int x) { +static inline void cris_fidx_d(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("fidxd\t[%0]\n" : : "r" (v) ); } diff --git a/tests/cris/check_int64.c b/tests/cris/check_int64.c index 99ca6f1158..fc600176e2 100644 --- a/tests/cris/check_int64.c +++ b/tests/cris/check_int64.c @@ -5,11 +5,13 @@ #include "crisutils.h" -extern inline int64_t add64(const int64_t a, const int64_t b) { +static inline int64_t add64(const int64_t a, const int64_t b) +{ return a + b; } -extern inline int64_t sub64(const int64_t a, const int64_t b) { +static inline int64_t sub64(const int64_t a, const int64_t b) +{ return a - b; } diff --git a/tests/cris/check_lz.c b/tests/cris/check_lz.c index 7b30a265ad..69c2e6d4ec 100644 --- a/tests/cris/check_lz.c +++ b/tests/cris/check_lz.c @@ -3,7 +3,7 @@ #include <stdint.h> #include "sys.h" -extern inline int cris_lz(int x) +static inline int cris_lz(int x) { int r; asm ("lz\t%1, %0\n" : "=r" (r) : "r" (x)); diff --git a/tests/cris/check_swap.c b/tests/cris/check_swap.c index 824a685517..f851cbcef1 100644 --- a/tests/cris/check_swap.c +++ b/tests/cris/check_swap.c @@ -9,7 +9,7 @@ #define B 2 #define R 1 -extern inline int cris_swap(const int mode, int x) +static inline int cris_swap(const int mode, int x) { switch (mode) { diff --git a/tests/cris/crisutils.h b/tests/cris/crisutils.h index 7d1ea86f73..29b71cd7b9 100644 --- a/tests/cris/crisutils.h +++ b/tests/cris/crisutils.h @@ -10,57 +10,57 @@ void _err(void) { _fail(tst_cc_loc); } -extern inline void cris_tst_cc_n1(void) +static inline void cris_tst_cc_n1(void) { asm volatile ("bpl _err\n" "nop\n"); } -extern inline void cris_tst_cc_n0(void) +static inline void cris_tst_cc_n0(void) { asm volatile ("bmi _err\n" "nop\n"); } -extern inline void cris_tst_cc_z1(void) +static inline void cris_tst_cc_z1(void) { asm volatile ("bne _err\n" "nop\n"); } -extern inline void cris_tst_cc_z0(void) +static inline void cris_tst_cc_z0(void) { asm volatile ("beq _err\n" "nop\n"); } -extern inline void cris_tst_cc_v1(void) +static inline void cris_tst_cc_v1(void) { asm volatile ("bvc _err\n" "nop\n"); } -extern inline void cris_tst_cc_v0(void) +static inline void cris_tst_cc_v0(void) { asm volatile ("bvs _err\n" "nop\n"); } -extern inline void cris_tst_cc_c1(void) +static inline void cris_tst_cc_c1(void) { asm volatile ("bcc _err\n" "nop\n"); } -extern inline void cris_tst_cc_c0(void) +static inline void cris_tst_cc_c0(void) { asm volatile ("bcs _err\n" "nop\n"); } -extern inline void cris_tst_mov_cc(int n, int z) +static inline void cris_tst_mov_cc(int n, int z) { if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); if (z) cris_tst_cc_z1(); else cris_tst_cc_z0(); asm volatile ("" : : "g" (_err)); } -extern inline void cris_tst_cc(const int n, const int z, +static inline void cris_tst_cc(const int n, const int z, const int v, const int c) { if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); diff --git a/tests/hello-i386.c b/tests/hello-i386.c index e00245d3fe..86afc34fb1 100644 --- a/tests/hello-i386.c +++ b/tests/hello-i386.c @@ -1,6 +1,6 @@ #include <asm/unistd.h> -extern inline volatile void exit(int status) +static inline volatile void exit(int status) { int __res; __asm__ volatile ("movl %%ecx,%%ebx\n"\ @@ -8,7 +8,7 @@ extern inline volatile void exit(int status) : "=a" (__res) : "0" (__NR_exit),"c" ((long)(status))); } -extern inline int write(int fd, const char * buf, int len) +static inline int write(int fd, const char * buf, int len) { int status; __asm__ volatile ("pushl %%ebx\n"\ diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index a51ddc8a18..17a621a2ba 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -444,22 +444,19 @@ static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_ if (vs->sasl.mechlist[len] != '\0' && vs->sasl.mechlist[len] != ',') { VNC_DEBUG("One %d", vs->sasl.mechlist[len]); - vnc_client_error(vs); - return -1; + goto fail; } } else { char *offset = strstr(vs->sasl.mechlist, mechname); VNC_DEBUG("Two %p\n", offset); if (!offset) { - vnc_client_error(vs); - return -1; + goto fail; } VNC_DEBUG("Two '%s'\n", offset); if (offset[-1] != ',' || (offset[len] != '\0'&& offset[len] != ',')) { - vnc_client_error(vs); - return -1; + goto fail; } } @@ -469,6 +466,11 @@ static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_ VNC_DEBUG("Validated mechname '%s'\n", mechname); vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4); return 0; + + fail: + vnc_client_error(vs); + free(mechname); + return -1; } static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len) |