Skip to content

Commit 59a11ab

Browse files
committed
FIXME: Provisional R5900 short loop bug verification tool
1 parent baf32bb commit 59a11ab

23 files changed

Lines changed: 1694 additions & 0 deletions

tools/r5900check/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.o
2+
*.o.d
3+
r5900check

tools/r5900check/Makefile

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
#
3+
# Define V=1 for more verbose compile.
4+
# Define S=1 for sanitation checks.
5+
6+
CFLAGS += -g -O2 -Wall -Iinclude -D_GNU_SOURCE
7+
8+
ifeq "$(S)" "1"
9+
CFLAGS += -fsanitize=address -fsanitize=leak -fsanitize=undefined \
10+
-fsanitize-address-use-after-scope -fstack-check
11+
endif
12+
13+
ALL_CFLAGS += $(CFLAGS) $(BASIC_CFLAGS)
14+
15+
R5900CHECK := r5900check
16+
17+
.PHONY: tool
18+
all: $(R5900CHECK)
19+
20+
SRC := $(filter-out elf.c, $(wildcard *.c))
21+
OBJ = $(patsubst %.c, %.o, $(SRC))
22+
23+
$(R5900CHECK): $(OBJ)
24+
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $^
25+
26+
$(OBJ): %.o : %.c
27+
$(QUIET_CC)$(CC) $(ALL_CFLAGS) -c -o $@ $<
28+
29+
.PHONY: clean
30+
clean:
31+
$(QUIET_RM)$(RM) -f *.o *.o.d $(R5900CHECK)
32+
33+
V = @
34+
Q = $(V:1=)
35+
QUIET_AR = $(Q:@=@echo ' AR '$@;)
36+
QUIET_AS = $(Q:@=@echo ' AS '$@;)
37+
QUIET_CC = $(Q:@=@echo ' CC '$@;)
38+
QUIET_GEN = $(Q:@=@echo ' GEN '$@;)
39+
QUIET_LINK = $(Q:@=@echo ' LD '$@;)
40+
QUIET_TEST = $(Q:@=@echo ' TEST '$@;)
41+
QUIET_RM = $(Q:@=@echo ' RM '$@;)
42+
43+
BASIC_CFLAGS += -Wp,-MMD,$(@D)/$(@F).d -MT $(@D)/$(@F)
44+
45+
ifneq "$(MAKECMDGOALS)" "clean"
46+
DEP_FILES := $(addsuffix .d, $(OBJ))
47+
$(if $(DEP_FILES),$(eval -include $(DEP_FILES)))
48+
endif

tools/r5900check/assert.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#ifndef R5900CHECK_ASSERT_H
4+
#define R5900CHECK_ASSERT_H
5+
6+
#include "types.h"
7+
8+
/* Macro definitions from the Linux kernel. */
9+
10+
#define __compiletime_error(message) __attribute__((__error__(message)))
11+
12+
#ifdef __OPTIMIZE__
13+
# define __compiletime_assert(condition, msg, prefix, suffix) \
14+
do { \
15+
extern void prefix ## suffix(void) __compiletime_error(msg); \
16+
if (!(condition)) \
17+
prefix ## suffix(); \
18+
} while (0)
19+
#else
20+
# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
21+
#endif
22+
23+
#define _compiletime_assert(condition, msg, prefix, suffix) \
24+
__compiletime_assert(condition, msg, prefix, suffix)
25+
26+
/**
27+
* compiletime_assert - break build and emit msg if condition is false
28+
* @condition: a compile-time constant condition to check
29+
* @msg: a message to emit if condition is false
30+
*
31+
* In tradition of POSIX assert, this macro will break the build if the
32+
* supplied condition is *false*, emitting the supplied error message if the
33+
* compiler has support to do so.
34+
*/
35+
#define compiletime_assert(condition, msg) \
36+
_compiletime_assert(condition, msg, __compiletime_assert_, __LINE__)
37+
38+
#endif /* R5900CHECK_ASSERT_H */

tools/r5900check/build-bug.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#ifndef R5900CHECK_BUILD_BUG_H
4+
#define R5900CHECK_BUILD_BUG_H
5+
6+
#include "assert.h"
7+
8+
/* Macro definitions from the Linux kernel. */
9+
10+
/**
11+
* BUILD_BUG_ON_MSG - break compile if a condition is true & emit supplied
12+
* error message.
13+
* @condition: the condition which the compiler should know is false.
14+
*
15+
* See BUILD_BUG_ON for description.
16+
*/
17+
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
18+
19+
/**
20+
* BUILD_BUG_ON - break compile if a condition is true.
21+
* @condition: the condition which the compiler should know is false.
22+
*
23+
* If you have some code which relies on certain constants being equal, or
24+
* some other compile-time-evaluated condition, you should use BUILD_BUG_ON to
25+
* detect if someone changes it.
26+
*/
27+
#define BUILD_BUG_ON(condition) \
28+
BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
29+
30+
#endif /* R5900CHECK_BUILD_BUG_H */

tools/r5900check/check.c

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* The short loop bug under certain conditions causes loops to execute only
5+
* once or twice. The Gnu assembler (GAS) has the following note about it:
6+
*
7+
* On the R5900 short loops need to be fixed by inserting a NOP in the
8+
* branch delay slot.
9+
*
10+
* The short loop bug under certain conditions causes loops to execute
11+
* only once or twice. We must ensure that the assembler never
12+
* generates loops that satisfy all of the following conditions:
13+
*
14+
* - a loop consists of less than or equal to six instructions
15+
* (including the branch delay slot);
16+
* - a loop contains only one conditional branch instruction at the
17+
* end of the loop;
18+
* - a loop does not contain any other branch or jump instructions;
19+
* - a branch delay slot of the loop is not NOP (EE 2.9 or later).
20+
*
21+
* We need to do this because of a hardware bug in the R5900 chip.
22+
*/
23+
24+
#include <stdlib.h>
25+
26+
#include "check.h"
27+
#include "elf32.h"
28+
#include "file.h"
29+
#include "inst.h"
30+
#include "print.h"
31+
32+
bool inst_branch_offset(int *offset, const union mips_instruction inst)
33+
{
34+
if (inst.i_format.opcode == beq_op ||
35+
inst.i_format.opcode == beql_op ||
36+
inst.i_format.opcode == bne_op ||
37+
inst.i_format.opcode == bnel_op) {
38+
if (offset)
39+
*offset = inst.i_format.simmediate;
40+
return true;
41+
}
42+
43+
if (inst.i_format.opcode == bcond_op) {
44+
if (inst.i_format.rt == bgez_op ||
45+
inst.i_format.rt == bgezal_op ||
46+
inst.i_format.rt == bgezall_op ||
47+
inst.i_format.rt == bgezl_op ||
48+
inst.i_format.rt == bltz_op ||
49+
inst.i_format.rt == bltzal_op ||
50+
inst.i_format.rt == bltzall_op ||
51+
inst.i_format.rt == bltzl_op) {
52+
if (offset)
53+
*offset = inst.i_format.simmediate;
54+
return true;
55+
}
56+
}
57+
58+
if (inst.i_format.rt == 0) {
59+
if ((inst.i_format.opcode == bgtz_op) ||
60+
(inst.i_format.opcode == bgtzl_op) ||
61+
(inst.i_format.opcode == blez_op) ||
62+
(inst.i_format.opcode == blezl_op)) {
63+
if (offset)
64+
*offset = inst.i_format.simmediate;
65+
return true;
66+
}
67+
}
68+
69+
return false;
70+
}
71+
72+
bool inst_branch(const union mips_instruction inst)
73+
{
74+
return inst_branch_offset(NULL, inst);
75+
}
76+
77+
bool inst_jump(const union mips_instruction inst)
78+
{
79+
if (inst.j_format.opcode == j_op ||
80+
inst.j_format.opcode == jal_op)
81+
return true;
82+
83+
if (inst.r_format.opcode == spec_op &&
84+
inst.r_format.rt == 0 &&
85+
inst.r_format.re == 0 &&
86+
inst.r_format.func == jalr_op)
87+
return true;
88+
89+
if (inst.r_format.opcode == spec_op &&
90+
inst.r_format.rt == 0 &&
91+
inst.r_format.rd == 0 &&
92+
inst.r_format.re == 0 &&
93+
inst.r_format.func == jr_op)
94+
return true;
95+
96+
return false;
97+
}
98+
99+
static bool inst_nop(const size_t i,
100+
const union mips_instruction *inst, const size_t inst_count)
101+
{
102+
if (inst_count <= i)
103+
return false;
104+
105+
// pr_info("%s %08x\n", __func__, inst[i].word);
106+
107+
return inst[i].word == 0;
108+
}
109+
110+
static bool inst_branch_or_jump(const ssize_t i,
111+
const union mips_instruction *inst, const size_t inst_count)
112+
{
113+
if (i < 0 || inst_count <= i)
114+
return false;
115+
116+
// pr_info("%s %08x\n", __func__, inst[i].word);
117+
118+
return inst_branch(inst[i]) || inst_jump(inst[i]);
119+
}
120+
121+
static bool short_loop_erratum(const size_t i,
122+
const union mips_instruction *inst, const size_t inst_count)
123+
{
124+
int offset;
125+
126+
if (inst_count <= i)
127+
return false;
128+
129+
if (!inst_branch_offset(&offset, inst[i]))
130+
return false;
131+
132+
if (offset <= -6 || 0 <= offset)
133+
return false;
134+
135+
if (inst_nop(i + 1, inst, inst_count))
136+
return false;
137+
138+
for (int k = offset + 1; k < 0; k++)
139+
if (inst_branch_or_jump((ssize_t)i + k, inst, inst_count))
140+
return false;
141+
142+
// pr_info("\t%08x %d\n", inst[i].word, offset);
143+
144+
return true;
145+
}
146+
147+
static void pr_short_loop_erratum(const size_t i,
148+
const union mips_instruction *inst, const size_t inst_count,
149+
Elf_Shdr *shdr, Elf_Ehdr *ehdr, struct file file)
150+
{
151+
int offset;
152+
153+
if (inst_count <= i)
154+
return;
155+
156+
if (!inst_branch_offset(&offset, inst[i]))
157+
return;
158+
159+
printf("erratum shortloop path %s\n", file.path);
160+
161+
for (int k = offset + 1; k <= 1; k++) {
162+
const ssize_t j = (ssize_t)i + k;
163+
const u32 addr = shdr->sh_addr + j * sizeof(*inst);
164+
165+
printf("code %08x ", addr);
166+
167+
if (j < 0 || inst_count <= j)
168+
printf(" - -");
169+
else
170+
printf("%2d %08x", k, inst[j].word);
171+
172+
printf("\n");
173+
}
174+
175+
exit(EXIT_FAILURE);
176+
}
177+
178+
static void check_text_section(Elf_Shdr *shdr,
179+
Elf_Ehdr *ehdr, struct file file)
180+
{
181+
const union mips_instruction *inst =
182+
elf_ent_for_offset(shdr->sh_offset, ehdr);
183+
const size_t inst_count = shdr->sh_size / sizeof(*inst);
184+
size_t branch_count = 0;
185+
186+
pr_info("section name %s\n", elf_section_name(shdr, ehdr));
187+
pr_info("section instruction count %zu\n", inst_count);
188+
189+
for (size_t i = 0; i < inst_count; i++) {
190+
if (short_loop_erratum(i, inst, inst_count))
191+
pr_short_loop_erratum(i, inst, inst_count,
192+
shdr, ehdr, file);
193+
194+
if (inst_branch(inst[i]))
195+
branch_count++;
196+
}
197+
198+
pr_info("section branch count %zu\n", branch_count);
199+
}
200+
201+
void check_file(struct file file)
202+
{
203+
Elf_Ehdr *ehdr = file.data;
204+
Elf_Shdr *shdr;
205+
206+
pr_info("check %s\n", file.path);
207+
208+
if (!elf_identify(file.data, file.size)) {
209+
fprintf(stderr, "%s: not a valid ELF object\n", file.path);
210+
exit(EXIT_FAILURE);
211+
}
212+
213+
elf_for_each_section_type (shdr, ehdr, SHT_PROGBITS)
214+
if (shdr->sh_flags & SHF_EXECINSTR)
215+
check_text_section(shdr, ehdr, file);
216+
}

tools/r5900check/check.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#ifndef R5900CHECK_CHECK_H
4+
#define R5900CHECK_CHECK_H
5+
6+
#include "file.h"
7+
8+
void check_file(struct file file);
9+
10+
#endif /* R5900CHECK_CHECK_H */

0 commit comments

Comments
 (0)