Skip to content

Commit ca0e9ba

Browse files
Masami Hiramatsufweisbec
authored andcommitted
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 <[email protected]> Signed-off-by: Jim Keniston <[email protected]> Cc: Ananth N Mavinakayanahalli <[email protected]> Cc: Avi Kivity <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Frank Ch. Eigler <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jason Baron <[email protected]> Cc: K.Prasad <[email protected]> Cc: Lai Jiangshan <[email protected]> Cc: Li Zefan <[email protected]> Cc: Przemysław Pawełczyk <[email protected]> Cc: Roland McGrath <[email protected]> Cc: Sam Ravnborg <[email protected]> Cc: Srikar Dronamraju <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Tom Zanussi <[email protected]> Cc: Vegard Nossum <[email protected]> LKML-Reference: <[email protected]> Signed-off-by: Frederic Weisbecker <[email protected]>
1 parent eb13296 commit ca0e9ba

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

arch/x86/Kconfig.debug

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,15 @@ config X86_DS_SELFTEST
186186
config HAVE_MMIOTRACE_SUPPORT
187187
def_bool y
188188

189+
config 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+
189198
#
190199
# IO delay types:
191200
#

arch/x86/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ all: bzImage
154154
KBUILD_IMAGE := $(boot)/bzImage
155155

156156
bzImage: vmlinux
157+
ifeq ($(CONFIG_X86_DECODER_SELFTEST),y)
158+
$(Q)$(MAKE) $(build)=arch/x86/tools posttest
159+
endif
157160
$(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)
158161
$(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot
159162
$(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@

arch/x86/tools/Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
PHONY += posttest
2+
quiet_cmd_posttest = TEST $@
3+
cmd_posttest = $(OBJDUMP) -d $(objtree)/vmlinux | awk -f $(srctree)/arch/x86/tools/distill.awk | $(obj)/test_get_len
4+
5+
posttest: $(obj)/test_get_len vmlinux
6+
$(call cmd,posttest)
7+
8+
hostprogs-y := test_get_len
9+
10+
# -I needed for generated C source and C source which in the kernel tree.
11+
HOSTCFLAGS_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+

arch/x86/tools/distill.awk

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
9+
BEGIN {
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+
39+
END {
40+
if (prev_addr != "")
41+
printf "%s\t%s\t%s\n", prev_addr, prev_hex, prev_mnemonic
42+
}

arch/x86/tools/test_get_len.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
43+
const char *prog;
44+
45+
static 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+
52+
static 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+
60+
int 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+
}

0 commit comments

Comments
 (0)