aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@redhat.com>2009-08-13 16:34:21 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2009-08-26 18:35:56 -0400
commitca0e9badd1a39fecdd235f4bf1481b9da756e27b (patch)
treee7d7f9a398bf50cb1bd327b38a29d756b54b7b95 /arch
parenteb13296cfaf6c699566473669a96a38a90562384 (diff)
x86: X86 instruction decoder build-time selftest
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/Kconfig.debug9
-rw-r--r--arch/x86/Makefile3
-rw-r--r--arch/x86/tools/Makefile15
-rw-r--r--arch/x86/tools/distill.awk42
-rw-r--r--arch/x86/tools/test_get_len.c113
5 files changed, 182 insertions, 0 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index d105f29bb6bb..7d0b681a132b 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -186,6 +186,15 @@ config X86_DS_SELFTEST
186config HAVE_MMIOTRACE_SUPPORT 186config HAVE_MMIOTRACE_SUPPORT
187 def_bool y 187 def_bool y
188 188
189config X86_DECODER_SELFTEST
190 bool "x86 instruction decoder selftest"
191 depends on DEBUG_KERNEL
192 ---help---
193 Perform x86 instruction decoder selftests at build time.
194 This option is useful for checking the sanity of x86 instruction
195 decoder code.
196 If unsure, say "N".
197
189# 198#
190# IO delay types: 199# IO delay types:
191# 200#
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 1b68659c41b4..5fe16bfd15ac 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -154,6 +154,9 @@ all: bzImage
154KBUILD_IMAGE := $(boot)/bzImage 154KBUILD_IMAGE := $(boot)/bzImage
155 155
156bzImage: vmlinux 156bzImage: vmlinux
157ifeq ($(CONFIG_X86_DECODER_SELFTEST),y)
158 $(Q)$(MAKE) $(build)=arch/x86/tools posttest
159endif
157 $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE) 160 $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)
158 $(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot 161 $(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot
159 $(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@ 162 $(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@
diff --git a/arch/x86/tools/Makefile b/arch/x86/tools/Makefile
new file mode 100644
index 000000000000..3dd626b99dc8
--- /dev/null
+++ b/arch/x86/tools/Makefile
@@ -0,0 +1,15 @@
1PHONY += posttest
2quiet_cmd_posttest = TEST $@
3 cmd_posttest = $(OBJDUMP) -d $(objtree)/vmlinux | awk -f $(srctree)/arch/x86/tools/distill.awk | $(obj)/test_get_len
4
5posttest: $(obj)/test_get_len vmlinux
6 $(call cmd,posttest)
7
8hostprogs-y := test_get_len
9
10# -I needed for generated C source and C source which in the kernel tree.
11HOSTCFLAGS_test_get_len.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/
12
13# Dependancies are also needed.
14$(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
15
diff --git a/arch/x86/tools/distill.awk b/arch/x86/tools/distill.awk
new file mode 100644
index 000000000000..d433619bb866
--- /dev/null
+++ b/arch/x86/tools/distill.awk
@@ -0,0 +1,42 @@
1#!/bin/awk -f
2# Usage: objdump -d a.out | awk -f distill.awk | ./test_get_len
3# Distills the disassembly as follows:
4# - Removes all lines except the disassembled instructions.
5# - For instructions that exceed 1 line (7 bytes), crams all the hex bytes
6# into a single line.
7# - Remove bad(or prefix only) instructions
8
9BEGIN {
10 prev_addr = ""
11 prev_hex = ""
12 prev_mnemonic = ""
13 bad_expr = "(\\(bad\\)|^rex|^.byte|^rep(z|nz)$|^lock$|^es$|^cs$|^ss$|^ds$|^fs$|^gs$|^data(16|32)$|^addr(16|32|64))"
14 fwait_expr = "^9b "
15 fwait_str="9b\tfwait"
16}
17
18/^ *[0-9a-f]+:/ {
19 if (split($0, field, "\t") < 3) {
20 # This is a continuation of the same insn.
21 prev_hex = prev_hex field[2]
22 } else {
23 # Skip bad instructions
24 if (match(prev_mnemonic, bad_expr))
25 prev_addr = ""
26 # Split fwait from other f* instructions
27 if (match(prev_hex, fwait_expr) && prev_mnemonic != "fwait") {
28 printf "%s\t%s\n", prev_addr, fwait_str
29 sub(fwait_expr, "", prev_hex)
30 }
31 if (prev_addr != "")
32 printf "%s\t%s\t%s\n", prev_addr, prev_hex, prev_mnemonic
33 prev_addr = field[1]
34 prev_hex = field[2]
35 prev_mnemonic = field[3]
36 }
37}
38
39END {
40 if (prev_addr != "")
41 printf "%s\t%s\t%s\n", prev_addr, prev_hex, prev_mnemonic
42}
diff --git a/arch/x86/tools/test_get_len.c b/arch/x86/tools/test_get_len.c
new file mode 100644
index 000000000000..1e81adb2d8a9
--- /dev/null
+++ b/arch/x86/tools/test_get_len.c
@@ -0,0 +1,113 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 *
16 * Copyright (C) IBM Corporation, 2009
17 */
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <assert.h>
23
24#ifdef __x86_64__
25#define CONFIG_X86_64
26#else
27#define CONFIG_X86_32
28#endif
29#define unlikely(cond) (cond)
30
31#include <asm/insn.h>
32#include <inat.c>
33#include <insn.c>
34
35/*
36 * Test of instruction analysis in general and insn_get_length() in
37 * particular. See if insn_get_length() and the disassembler agree
38 * on the length of each instruction in an elf disassembly.
39 *
40 * Usage: objdump -d a.out | awk -f distill.awk | ./test_get_len
41 */
42
43const char *prog;
44
45static void usage(void)
46{
47 fprintf(stderr, "Usage: objdump -d a.out | awk -f distill.awk |"
48 " ./test_get_len\n");
49 exit(1);
50}
51
52static void malformed_line(const char *line, int line_nr)
53{
54 fprintf(stderr, "%s: malformed line %d:\n%s", prog, line_nr, line);
55 exit(3);
56}
57
58#define BUFSIZE 256
59
60int main(int argc, char **argv)
61{
62 char line[BUFSIZE];
63 unsigned char insn_buf[16];
64 struct insn insn;
65 int insns = 0;
66
67 prog = argv[0];
68 if (argc > 1)
69 usage();
70
71 while (fgets(line, BUFSIZE, stdin)) {
72 char copy[BUFSIZE], *s, *tab1, *tab2;
73 int nb = 0;
74 unsigned int b;
75
76 insns++;
77 memset(insn_buf, 0, 16);
78 strcpy(copy, line);
79 tab1 = strchr(copy, '\t');
80 if (!tab1)
81 malformed_line(line, insns);
82 s = tab1 + 1;
83 s += strspn(s, " ");
84 tab2 = strchr(s, '\t');
85 if (!tab2)
86 malformed_line(line, insns);
87 *tab2 = '\0'; /* Characters beyond tab2 aren't examined */
88 while (s < tab2) {
89 if (sscanf(s, "%x", &b) == 1) {
90 insn_buf[nb++] = (unsigned char) b;
91 s += 3;
92 } else
93 break;
94 }
95 /* Decode an instruction */
96#ifdef __x86_64__
97 insn_init(&insn, insn_buf, 1);
98#else
99 insn_init(&insn, insn_buf, 0);
100#endif
101 insn_get_length(&insn);
102 if (insn.length != nb) {
103 fprintf(stderr, "Error: %s", line);
104 fprintf(stderr, "Error: objdump says %d bytes, but "
105 "insn_get_length() says %d (attr:%x)\n", nb,
106 insn.length, insn.attr);
107 exit(2);
108 }
109 }
110 fprintf(stderr, "Succeed: decoded and checked %d instructions\n",
111 insns);
112 return 0;
113}