x86emul/test: split generic and testcase specific parts Both the build logic and the invocation have their blowfish specific aspects abstracted out here. Additionally - run native execution (if suitable) first (as that one failing suggests a problem with the to be tested code itself, in which case having the emulator have a go over it is kind of pointless) - move the 64-bit tests up in blobs[] so 64-bit native execution will also precede 32-bit emulation (on 64-bit systems only of course) - instead of -msoft-float (we'd rather not have the compiler generate such code), pass -fno-asynchronous-unwind-tables and -g0 (reducing binary size of the helper images as well as [slightly] compilation time) - skip tests with zero length blobs (these can result from failed compilation, but not failing the build in this case seems desirable: it may allow partial testing - e.g. with older compilers - and permits manually removing certain tests from the generated headers without having to touch actual source code) - constrain rIP to the actual blob range rather than looking for the specific (fake) return address put on the stack - also print the opcode when x86_emulate() fails - print at least three progress dots (for relatively short tests) Signed-off-by: Jan Beulich --- v2: New. --- a/tools/tests/x86_emulator/Makefile +++ b/tools/tests/x86_emulator/Makefile @@ -11,18 +11,21 @@ all: $(TARGET) run: $(TARGET) ./$(TARGET) -cflags-x86_32 := "-mno-accumulate-outgoing-args -Dstatic=" +TESTCASES := blowfish -blowfish.h: blowfish.c blowfish.mk Makefile - rm -f $@.new blowfish.bin +blowfish-cflags := "" +blowfish-cflags-x86_32 := "-mno-accumulate-outgoing-args -Dstatic=" + +$(addsuffix .h,$(TESTCASES)): %.h: %.c testcase.mk Makefile + rm -f $@.new $*.bin $(foreach arch,$(filter-out $(XEN_COMPILE_ARCH),x86_32) $(XEN_COMPILE_ARCH), \ - for cflags in "" $(cflags-$(arch)); do \ - $(MAKE) -f blowfish.mk XEN_TARGET_ARCH=$(arch) BLOWFISH_CFLAGS="$$cflags" all; \ + for cflags in $($*-cflags) $($*-cflags-$(arch)); do \ + $(MAKE) -f testcase.mk TESTCASE=$* XEN_TARGET_ARCH=$(arch) $*-cflags="$$cflags" all; \ flavor=$$(echo $${cflags} | sed -e 's, .*,,' -e 'y,-=,__,') ; \ - (echo "static unsigned int blowfish_$(arch)$${flavor}[] = {"; \ - od -v -t x blowfish.bin | sed -e 's/^[0-9]* /0x/' -e 's/ /, 0x/g' -e 's/$$/,/'; \ + (echo "static const unsigned int $*_$(arch)$${flavor}[] = {"; \ + od -v -t x $*.bin | sed -e 's/^[0-9]* /0x/' -e 's/ /, 0x/g' -e 's/$$/,/'; \ echo "};") >>$@.new; \ - rm -f blowfish.bin; \ + rm -f $*.bin; \ done; \ ) mv $@.new $@ @@ -32,7 +35,7 @@ $(TARGET): x86_emulate.o test_x86_emulat .PHONY: clean clean: - rm -rf $(TARGET) *.o *~ core blowfish.h blowfish.bin x86_emulate asm + rm -rf $(TARGET) *.o *~ core $(addsuffix .h,$(TESTCASES)) *.bin x86_emulate asm .PHONY: distclean distclean: clean @@ -54,5 +57,5 @@ x86_emulate.h := x86_emulate.h x86_emula x86_emulate.o: x86_emulate.c x86_emulate/x86_emulate.c $(x86_emulate.h) $(HOSTCC) $(HOSTCFLAGS) -D__XEN_TOOLS__ -c -g -o $@ $< -test_x86_emulator.o: test_x86_emulator.c blowfish.h $(x86_emulate.h) +test_x86_emulator.o: test_x86_emulator.c $(addsuffix .h,$(TESTCASES)) $(x86_emulate.h) $(HOSTCC) $(HOSTCFLAGS) -c -g -o $@ $< --- a/tools/tests/x86_emulator/blowfish.mk +++ /dev/null @@ -1,17 +0,0 @@ - -XEN_ROOT = $(CURDIR)/../../.. -CFLAGS = -include $(XEN_ROOT)/tools/Rules.mk - -$(call cc-options-add,CFLAGS,CC,$(EMBEDDED_EXTRA_CFLAGS)) - -CFLAGS += -fno-builtin -msoft-float $(BLOWFISH_CFLAGS) - -.PHONY: all -all: blowfish.bin - -blowfish.bin: blowfish.c - $(CC) $(CFLAGS) -c blowfish.c - $(LD) $(LDFLAGS_DIRECT) -N -Ttext 0x100000 -o blowfish.tmp blowfish.o - $(OBJCOPY) -O binary blowfish.tmp blowfish.bin - rm -f blowfish.tmp --- a/tools/tests/x86_emulator/test_x86_emulator.c +++ b/tools/tests/x86_emulator/test_x86_emulator.c @@ -8,19 +8,37 @@ #define verbose false /* Switch to true for far more logging. */ +static void blowfish_set_regs(struct cpu_user_regs *regs) +{ + regs->eax = 2; + regs->edx = 1; +} + +static bool blowfish_check_regs(const struct cpu_user_regs *regs) +{ + return regs->eax == 2 && regs->edx == 1; +} + static const struct { const void *code; size_t size; unsigned int bitness; const char*name; + void (*set_regs)(struct cpu_user_regs *); + bool (*check_regs)(const struct cpu_user_regs *); } blobs[] = { - { blowfish_x86_32, sizeof(blowfish_x86_32), 32, "blowfish" }, - { blowfish_x86_32_mno_accumulate_outgoing_args, - sizeof(blowfish_x86_32_mno_accumulate_outgoing_args), - 32, "blowfish (push)" }, +#define BLOWFISH(bits, desc, tag) \ + { .code = blowfish_x86_##bits##tag, \ + .size = sizeof(blowfish_x86_##bits##tag), \ + .bitness = bits, .name = #desc, \ + .set_regs = blowfish_set_regs, \ + .check_regs = blowfish_check_regs } #ifdef __x86_64__ - { blowfish_x86_64, sizeof(blowfish_x86_64), 64, "blowfish" }, + BLOWFISH(64, blowfish, ), #endif + BLOWFISH(32, blowfish, ), + BLOWFISH(32, blowfish (push), _mno_accumulate_outgoing_args), +#undef BLOWFISH }; static unsigned int bytes_read; @@ -2565,13 +2583,40 @@ int main(int argc, char **argv) for ( j = 0; j < ARRAY_SIZE(blobs); j++ ) { + if ( !blobs[j].size ) + { + printf("%-39s n/a\n", blobs[j].name); + continue; + } + memcpy(res, blobs[j].code, blobs[j].size); ctxt.addr_size = ctxt.sp_size = blobs[j].bitness; + if ( ctxt.addr_size == sizeof(void *) * CHAR_BIT ) + { + i = printf("Testing %s native execution...", blobs[j].name); + if ( blobs[j].set_regs ) + blobs[j].set_regs(®s); + asm volatile ( +#if defined(__i386__) + "call *%%ecx" +#else + "call *%%rcx" +#endif + : "+a" (regs.eax), "+d" (regs.edx) : "c" (res) +#ifdef __x86_64__ + : "rsi", "rdi", "r8", "r9", "r10", "r11" +#endif + ); + if ( !blobs[j].check_regs(®s) ) + goto fail; + printf("%*sokay\n", i < 40 ? 40 - i : 0, ""); + } + printf("Testing %s %u-bit code sequence", blobs[j].name, ctxt.addr_size); - regs.eax = 2; - regs.edx = 1; + if ( blobs[j].set_regs ) + blobs[j].set_regs(®s); regs.eip = (unsigned long)res; regs.esp = (unsigned long)res + MMAP_SZ - 4; if ( ctxt.addr_size == 64 ) @@ -2582,41 +2627,26 @@ int main(int argc, char **argv) *(uint32_t *)(unsigned long)regs.esp = 0x12345678; regs.eflags = 2; i = 0; - while ( regs.eip != 0x12345678 ) + while ( regs.eip >= (unsigned long)res && + regs.eip < (unsigned long)res + blobs[j].size ) { if ( (i++ & 8191) == 0 ) printf("."); rc = x86_emulate(&ctxt, &emulops); if ( rc != X86EMUL_OKAY ) { - printf("failed at %%eip == %08x\n", (unsigned int)regs.eip); + printf("failed at %%eip == %08lx (opcode %08x)\n", + (unsigned long)regs.eip, ctxt.opcode); return 1; } } - if ( (regs.esp != ((unsigned long)res + MMAP_SZ)) || - (regs.eax != 2) || (regs.edx != 1) ) + for ( ; i < 2 * 8192; i += 8192 ) + printf("."); + if ( (regs.eip != 0x12345678) || + (regs.esp != ((unsigned long)res + MMAP_SZ)) || + !blobs[j].check_regs(®s) ) goto fail; printf("okay\n"); - - if ( ctxt.addr_size != sizeof(void *) * CHAR_BIT ) - continue; - - i = printf("Testing %s native execution...", blobs[j].name); - asm volatile ( -#if defined(__i386__) - "movl $0x100000,%%ecx; call *%%ecx" -#else - "movl $0x100000,%%ecx; call *%%rcx" -#endif - : "=a" (regs.eax), "=d" (regs.edx) - : "0" (2), "1" (1) : "ecx" -#ifdef __x86_64__ - , "rsi", "rdi", "r8", "r9", "r10", "r11" -#endif - ); - if ( (regs.eax != 2) || (regs.edx != 1) ) - goto fail; - printf("%*sokay\n", i < 40 ? 40 - i : 0, ""); } return 0; --- /dev/null +++ b/tools/tests/x86_emulator/testcase.mk @@ -0,0 +1,16 @@ +XEN_ROOT = $(CURDIR)/../../.. +CFLAGS := +include $(XEN_ROOT)/tools/Rules.mk + +$(call cc-options-add,CFLAGS,CC,$(EMBEDDED_EXTRA_CFLAGS)) + +CFLAGS += -fno-builtin -fno-asynchronous-unwind-tables -g0 $($(TESTCASE)-cflags) + +.PHONY: all +all: $(TESTCASE).bin + +%.bin: %.c + $(CC) $(filter-out -M% .%,$(CFLAGS)) -c $< + $(LD) $(LDFLAGS_DIRECT) -N -Ttext 0x100000 -o $*.tmp $*.o + $(OBJCOPY) -O binary $*.tmp $@ + rm -f $*.tmp