Skip to content

Commit

Permalink
x86: X86 instruction decoder build-time selftest
Browse files Browse the repository at this point in the history
Add a user-space selftest of x86 instruction decoder at kernel build
time.
When CONFIG_X86_DECODER_SELFTEST=y, Kbuild builds a test harness of x86
instruction decoder and performs it after building vmlinux.
The test compares the results of objdump and x86 instruction decoder
code and check there are no differences.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Avi Kivity <avi@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jason Baron <jbaron@redhat.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Przemysław Pawełczyk <przemyslaw@pawelczyk.it>
Cc: Roland McGrath <roland@redhat.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tom Zanussi <tzanussi@gmail.com>
Cc: Vegard Nossum <vegard.nossum@gmail.com>
LKML-Reference: <20090813203421.31965.29006.stgit@localhost.localdomain>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
  • Loading branch information
Masami Hiramatsu authored and Frederic Weisbecker committed Aug 26, 2009
1 parent eb13296 commit ca0e9ba
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 0 deletions.
9 changes: 9 additions & 0 deletions arch/x86/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,15 @@ config X86_DS_SELFTEST
config HAVE_MMIOTRACE_SUPPORT
def_bool y

config X86_DECODER_SELFTEST
bool "x86 instruction decoder selftest"
depends on DEBUG_KERNEL
---help---
Perform x86 instruction decoder selftests at build time.
This option is useful for checking the sanity of x86 instruction
decoder code.
If unsure, say "N".

#
# IO delay types:
#
Expand Down
3 changes: 3 additions & 0 deletions arch/x86/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ all: bzImage
KBUILD_IMAGE := $(boot)/bzImage

bzImage: vmlinux
ifeq ($(CONFIG_X86_DECODER_SELFTEST),y)
$(Q)$(MAKE) $(build)=arch/x86/tools posttest
endif
$(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)
$(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot
$(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@
Expand Down
15 changes: 15 additions & 0 deletions arch/x86/tools/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
PHONY += posttest
quiet_cmd_posttest = TEST $@
cmd_posttest = $(OBJDUMP) -d $(objtree)/vmlinux | awk -f $(srctree)/arch/x86/tools/distill.awk | $(obj)/test_get_len

posttest: $(obj)/test_get_len vmlinux
$(call cmd,posttest)

hostprogs-y := test_get_len

# -I needed for generated C source and C source which in the kernel tree.
HOSTCFLAGS_test_get_len.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/

# Dependancies are also needed.
$(obj)/test_get_len.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c

42 changes: 42 additions & 0 deletions arch/x86/tools/distill.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/awk -f
# Usage: objdump -d a.out | awk -f distill.awk | ./test_get_len
# Distills the disassembly as follows:
# - Removes all lines except the disassembled instructions.
# - For instructions that exceed 1 line (7 bytes), crams all the hex bytes
# into a single line.
# - Remove bad(or prefix only) instructions

BEGIN {
prev_addr = ""
prev_hex = ""
prev_mnemonic = ""
bad_expr = "(\\(bad\\)|^rex|^.byte|^rep(z|nz)$|^lock$|^es$|^cs$|^ss$|^ds$|^fs$|^gs$|^data(16|32)$|^addr(16|32|64))"
fwait_expr = "^9b "
fwait_str="9b\tfwait"
}

/^ *[0-9a-f]+:/ {
if (split($0, field, "\t") < 3) {
# This is a continuation of the same insn.
prev_hex = prev_hex field[2]
} else {
# Skip bad instructions
if (match(prev_mnemonic, bad_expr))
prev_addr = ""
# Split fwait from other f* instructions
if (match(prev_hex, fwait_expr) && prev_mnemonic != "fwait") {
printf "%s\t%s\n", prev_addr, fwait_str
sub(fwait_expr, "", prev_hex)
}
if (prev_addr != "")
printf "%s\t%s\t%s\n", prev_addr, prev_hex, prev_mnemonic
prev_addr = field[1]
prev_hex = field[2]
prev_mnemonic = field[3]
}
}

END {
if (prev_addr != "")
printf "%s\t%s\t%s\n", prev_addr, prev_hex, prev_mnemonic
}
113 changes: 113 additions & 0 deletions arch/x86/tools/test_get_len.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) IBM Corporation, 2009
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#ifdef __x86_64__
#define CONFIG_X86_64
#else
#define CONFIG_X86_32
#endif
#define unlikely(cond) (cond)

#include <asm/insn.h>
#include <inat.c>
#include <insn.c>

/*
* Test of instruction analysis in general and insn_get_length() in
* particular. See if insn_get_length() and the disassembler agree
* on the length of each instruction in an elf disassembly.
*
* Usage: objdump -d a.out | awk -f distill.awk | ./test_get_len
*/

const char *prog;

static void usage(void)
{
fprintf(stderr, "Usage: objdump -d a.out | awk -f distill.awk |"
" ./test_get_len\n");
exit(1);
}

static void malformed_line(const char *line, int line_nr)
{
fprintf(stderr, "%s: malformed line %d:\n%s", prog, line_nr, line);
exit(3);
}

#define BUFSIZE 256

int main(int argc, char **argv)
{
char line[BUFSIZE];
unsigned char insn_buf[16];
struct insn insn;
int insns = 0;

prog = argv[0];
if (argc > 1)
usage();

while (fgets(line, BUFSIZE, stdin)) {
char copy[BUFSIZE], *s, *tab1, *tab2;
int nb = 0;
unsigned int b;

insns++;
memset(insn_buf, 0, 16);
strcpy(copy, line);
tab1 = strchr(copy, '\t');
if (!tab1)
malformed_line(line, insns);
s = tab1 + 1;
s += strspn(s, " ");
tab2 = strchr(s, '\t');
if (!tab2)
malformed_line(line, insns);
*tab2 = '\0'; /* Characters beyond tab2 aren't examined */
while (s < tab2) {
if (sscanf(s, "%x", &b) == 1) {
insn_buf[nb++] = (unsigned char) b;
s += 3;
} else
break;
}
/* Decode an instruction */
#ifdef __x86_64__
insn_init(&insn, insn_buf, 1);
#else
insn_init(&insn, insn_buf, 0);
#endif
insn_get_length(&insn);
if (insn.length != nb) {
fprintf(stderr, "Error: %s", line);
fprintf(stderr, "Error: objdump says %d bytes, but "
"insn_get_length() says %d (attr:%x)\n", nb,
insn.length, insn.attr);
exit(2);
}
}
fprintf(stderr, "Succeed: decoded and checked %d instructions\n",
insns);
return 0;
}

0 comments on commit ca0e9ba

Please sign in to comment.