/* Macro to generate a single test function. */
/* See lowlevel.h for a description of nomenclature. */
-.macro GEN_SINGLE insn, type
+.macro GEN_SINGLE name, insn, fep=0, addr=0
- /* Function label. e.g. stub_int3_red(). */
-ENTRY(stub_\insn\()_\type)
+ENTRY(stub_\name)
+
+ xor %eax, %eax
.ifc \insn, into
/* Ensure the overflow flag is set, to trigger 'into'. */
- mov $0x7f, %al
- add %al, %al
- .else
- /* Avoid function label and fault label possibly aliasing. */
- nop
+ mov $0x7f, %dl
+ add %dl, %dl
.endif
/* Possibly insert a Xen Forced Emulation prefix. */
- .ifc \type, force
- _ASM_XEN_FEP
- .endif
- .ifc \type, forcered
+ .if \fep
_ASM_XEN_FEP
.endif
- /* Label where a fault should occur. e.g. label_int3_red_fault. */
-GLOBAL(label_\insn\()_\type\()_fault)
+.L_\name\()_fault:
/*
* Possibly insert a redundant prefix.
* Uses 'address size override' which has no effect on any of the
* target instructions, but in a form which GAS won't complain about.
*/
- .ifc \type, red
- .byte 0x67
- .endif
- .ifc \type, forcered
+ .if \addr
.byte 0x67
.endif
.byte 0xce /* Raw opcode to avoid failure in 64bit build. */
.endif
- /* Label where a trap should occur. e.g. label_int3_red_trap. */
-GLOBAL(label_\insn\()_\type\()_trap)
+.L_\name\()_trap:
- /* Function return. */
ret
- /* Fixup from fault label to trap label. */
- _ASM_EXTABLE(label_\insn\()_\type\()_fault,
- label_\insn\()_\type\()_trap)
- /* Mark traps as ok. */
- _ASM_TRAP_OK(label_\insn\()_\type\()_trap)
+ _ASM_EXTABLE_HANDLER(.L_\name\()_fault, .L_\name\()_trap, ex_record_fault_eax)
+ _ASM_EXTABLE_HANDLER(.L_\name\()_trap, .L_\name\()_trap, ex_record_trap_eax)
-ENDFUNC(stub_\insn\()_\type)
+ENDFUNC(stub_\name\())
.endm
/* For a single instruction, generate each test variant. */
.macro GEN_SEQUENCE insn
- GEN_SINGLE \insn reg
- GEN_SINGLE \insn red
- GEN_SINGLE \insn force
- GEN_SINGLE \insn forcered
+ GEN_SINGLE \insn \insn
+ GEN_SINGLE \insn\()_A \insn addr=1
+ GEN_SINGLE \insn\()_F \insn fep=1
+ GEN_SINGLE \insn\()_FA \insn fep=1 addr=1
.endm
/* Generate test sequences for each instruction. */
* Nomaclature:
* - `stub_$X_$Y()`
* - Stub function executing instruction `$X` with prefix `$Y`.
- * - `label_$X_$Y_{trap, fault}:`
- * - Labels for where `$X` is expected to trap or fault.
*
* Instructions `$X`:
* - int3
* - `into` (`0xce`)
*
* Prefixes `$Y`:
- * - reg
+ * - (none)
* - Regular - no prefix
- * - red
- * - Redundant - address size override prefix (`0x67`)
- * - force
+ * - A
+ * - Address size override prefix (`0x67`)
+ * - F
* - Forced Emulation prefix - see @ref _ASM_XEN_FEP
- * - forcered
- * - Forced Emulation and redundant prefixes
+ * - FA
+ * - Forced Emulation and Address override prefixes
*/
#ifndef __LOWLEVEL_H__
#define __LOWLEVEL_H__
-void stub_int3_reg(void);
-void stub_int3_red(void);
-void stub_int3_force(void);
-void stub_int3_forcered(void);
-extern unsigned long label_int3_reg_trap[], label_int3_reg_fault[];
-extern unsigned long label_int3_red_trap[], label_int3_red_fault[];
-extern unsigned long label_int3_force_trap[], label_int3_force_fault[];
-extern unsigned long label_int3_forcered_trap[], label_int3_forcered_fault[];
+unsigned long stub_int3(void);
+unsigned long stub_int3_A(void);
+unsigned long stub_int3_F(void);
+unsigned long stub_int3_FA(void);
-void stub_int_0x3_reg(void);
-void stub_int_0x3_red(void);
-void stub_int_0x3_force(void);
-void stub_int_0x3_forcered(void);
-extern unsigned long label_int_0x3_reg_trap[], label_int_0x3_reg_fault[];
-extern unsigned long label_int_0x3_red_trap[], label_int_0x3_red_fault[];
-extern unsigned long label_int_0x3_force_trap[], label_int_0x3_force_fault[];
-extern unsigned long label_int_0x3_forcered_trap[], label_int_0x3_forcered_fault[];
+unsigned long stub_int_0x3(void);
+unsigned long stub_int_0x3_A(void);
+unsigned long stub_int_0x3_F(void);
+unsigned long stub_int_0x3_FA(void);
-void stub_icebp_reg(void);
-void stub_icebp_red(void);
-void stub_icebp_force(void);
-void stub_icebp_forcered(void);
-extern unsigned long label_icebp_reg_trap[], label_icebp_reg_fault[];
-extern unsigned long label_icebp_red_trap[], label_icebp_red_fault[];
-extern unsigned long label_icebp_force_trap[], label_icebp_force_fault[];
-extern unsigned long label_icebp_forcered_trap[], label_icebp_forcered_fault[];
+unsigned long stub_icebp(void);
+unsigned long stub_icebp_A(void);
+unsigned long stub_icebp_F(void);
+unsigned long stub_icebp_FA(void);
-void stub_int_0x1_reg(void);
-void stub_int_0x1_red(void);
-void stub_int_0x1_force(void);
-void stub_int_0x1_forcered(void);
-extern unsigned long label_int_0x1_reg_trap[], label_int_0x1_reg_fault[];
-extern unsigned long label_int_0x1_red_trap[], label_int_0x1_red_fault[];
-extern unsigned long label_int_0x1_force_trap[], label_int_0x1_force_fault[];
-extern unsigned long label_int_0x1_forcered_trap[], label_int_0x1_forcered_fault[];
+unsigned long stub_int_0x1(void);
+unsigned long stub_int_0x1_A(void);
+unsigned long stub_int_0x1_F(void);
+unsigned long stub_int_0x1_FA(void);
-void stub_into_reg(void);
-void stub_into_red(void);
-void stub_into_force(void);
-void stub_into_forcered(void);
-extern unsigned long label_into_reg_trap[], label_into_reg_fault[];
-extern unsigned long label_into_red_trap[], label_into_red_fault[];
-extern unsigned long label_into_force_trap[], label_into_force_fault[];
-extern unsigned long label_into_forcered_trap[], label_into_forcered_fault[];
+unsigned long stub_into(void);
+unsigned long stub_into_A(void);
+unsigned long stub_into_F(void);
+unsigned long stub_into_FA(void);
#endif /* __LOWLEVEL_H__ */
# define COND(_32, _64) _64
#endif
+#define EXINFO_TRAP EXINFO_AVAIL0
+
enum mode { KERN, USER };
-/** Single stub's worth of information. */
-struct single
+bool ex_record_trap_eax(struct cpu_regs *regs, const struct extable_entry *ex)
{
- const char *type;
- void (*fn)(void);
- void *trap, *fault;
-};
+ regs->ax = EXINFO(regs->entry_vector, regs->error_code) | EXINFO_TRAP;
+ regs->ip = ex->fixup;
+
+ return true;
+}
struct insn
{
const char *name;
- struct single tests[4];
+ unsigned long (*fn[4])(void);
};
const struct insn int3 = {
"int3",
{
- {"regular", stub_int3_reg,
- label_int3_reg_trap, label_int3_reg_fault},
-
- {"redundant", stub_int3_red,
- label_int3_red_trap, label_int3_red_fault},
-
- {"forced", stub_int3_force,
- label_int3_force_trap, label_int3_force_fault},
-
- {"forced redundant", stub_int3_forcered,
- label_int3_forcered_trap, label_int3_forcered_fault},
+ stub_int3,
+ stub_int3_A,
+ stub_int3_F,
+ stub_int3_FA,
},
};
const struct insn int_0x3 = {
"int $3",
{
- {"regular", stub_int_0x3_reg,
- label_int_0x3_reg_trap, label_int_0x3_reg_fault},
-
- {"redundant", stub_int_0x3_red,
- label_int_0x3_red_trap, label_int_0x3_red_fault},
-
- {"forced", stub_int_0x3_force,
- label_int_0x3_force_trap, label_int_0x3_force_fault},
-
- {"forced redundant", stub_int_0x3_forcered,
- label_int_0x3_forcered_trap, label_int_0x3_forcered_fault},
+ stub_int_0x3,
+ stub_int_0x3_A,
+ stub_int_0x3_F,
+ stub_int_0x3_FA,
},
};
const struct insn icebp = {
"icebp",
{
- {"regular", stub_icebp_reg,
- label_icebp_reg_trap, label_icebp_reg_fault},
-
- {"redundant", stub_icebp_red,
- label_icebp_red_trap, label_icebp_red_fault},
-
- {"forced", stub_icebp_force,
- label_icebp_force_trap, label_icebp_force_fault},
-
- {"forced redundant", stub_icebp_forcered,
- label_icebp_forcered_trap, label_icebp_forcered_fault},
+ stub_icebp,
+ stub_icebp_A,
+ stub_icebp_F,
+ stub_icebp_FA,
},
};
const struct insn int_0x1 = {
"int $1",
{
- {"regular", stub_int_0x1_reg,
- label_int_0x1_reg_trap, label_int_0x1_reg_fault},
-
- {"redundant", stub_int_0x1_red,
- label_int_0x1_red_trap, label_int_0x1_red_fault},
-
- {"forced", stub_int_0x1_force,
- label_int_0x1_force_trap, label_int_0x1_force_fault},
-
- {"forced redundant", stub_int_0x1_forcered,
- label_int_0x1_forcered_trap, label_int_0x1_forcered_fault},
+ stub_int_0x1,
+ stub_int_0x1_A,
+ stub_int_0x1_F,
+ stub_int_0x1_FA,
},
};
const struct insn into = {
"into",
{
- {"regular", stub_into_reg,
- label_into_reg_trap, label_into_reg_fault},
-
- {"redundant", stub_into_red,
- label_into_red_trap, label_into_red_fault},
-
- {"forced", stub_into_force,
- label_into_force_trap, label_into_force_fault},
-
- {"forced redundant", stub_into_forcered,
- label_into_forcered_trap, label_into_forcered_fault},
+ stub_into,
+ stub_into_A,
+ stub_into_F,
+ stub_into_FA,
},
};
-struct expectation {
- const char *prefix;
- const void *ip;
- unsigned int ev, ec;
-} /** Expected %%eip, vector and error code from the stub under test. */
- expectation;
-
-/** Latch details of the stub under test. */
-void expect(const void *prefix, const void *ip,
- unsigned int ev, unsigned int ec)
-{
- expectation = (struct expectation){prefix, ip, ev, ec};
- xtf_exlog_reset();
-}
-
-/** Check the exception long against the expected details. */
-void check(void)
-{
- unsigned int entries = xtf_exlog_entries();
-
- if ( entries != 1 )
- {
- xtf_failure("Fail %s: Expected 1 exception (vec %u at %p), got %u\n",
- expectation.prefix, expectation.ev,
- expectation.ip, entries);
- xtf_exlog_dump_log();
- return;
- }
-
- exlog_entry_t *entry = xtf_exlog_entry(0);
- if ( !entry )
- {
- xtf_failure("Fail %s: Unable to retrieve exception log\n",
- expectation.prefix);
- return;
- }
-
- if ( (_p(entry->ip) != expectation.ip) ||
- (entry->ev != expectation.ev) ||
- (entry->ec != expectation.ec) )
- {
- xtf_failure("Fail %s:\n"
- " Expected vec %2u[%04x] at %p\n"
- " got vec %2u[%04x] at %p\n",
- expectation.prefix,
- expectation.ev, expectation.ec, expectation.ip,
- entry->ev, entry->ec, _p(entry->ip));
- return;
- }
-}
-
-/** Print expected information in the case of an unexpected exception. */
-bool do_unhandled_exception(struct cpu_regs *regs)
-{
- printk("Unhandled Exception at %p\n", _p(regs));
- check();
-
- return false;
-}
-
void test_insn(enum mode user, const struct insn *insn, exinfo_t exp)
{
- unsigned int vector = exinfo_vec(exp);
- unsigned int error = exinfo_ec(exp);
- bool fault = X86_EXC_FAULTS & (1u << vector);
-
printk(" Testing %s\n", insn->name);
- for ( unsigned int i = 0; i < ARRAY_SIZE(insn->tests); ++i )
+ for ( unsigned int i = 0; i < ARRAY_SIZE(insn->fn); ++i )
{
- const struct single *s = &insn->tests[i];
-
- expect(s->type,
- fault ? s->fault : s->trap,
- vector, error);
+ exinfo_t got;
- user ? exec_user_void(s->fn) : s->fn();
+ got = user ? exec_user(insn->fn[i]) : insn->fn[i]();
- check();
+ if ( exp != got )
+ xtf_failure(" Fail (Force%c, Addr%c): expected %pe %s, got %pe %s\n",
+ i & 1 ? '+' : '-',
+ i & 2 ? '+' : '-',
+ _p(exp), exp & EXINFO_TRAP ? "trap" : "fault",
+ _p(got), got & EXINFO_TRAP ? "trap" : "fault");
- /* Avoid 'force' and 'forcered' stubs if FEP isn't available. */
+ /* Avoid FEP stubs if FEP isn't available. */
if ( i == 1 && !xtf_has_fep )
break;
}
idt[X86_EXC_OF].dpl = dpl;
}
-#define TRAP(V) EXINFO_SYM(V, 0)
+#define TRAP(V) EXINFO_SYM(V, 0) | EXINFO_TRAP
#define FAULT(V) EXINFO_SYM(V, 0)
#define FAULT_EC(V, ...) EXINFO_SYM(V, EXC_EC_SYM(__VA_ARGS__))
set_idt_entries_present(true);
set_idt_entries_dpl(3);
- xtf_exlog_start();
-
cpl0_tests();
cpl3_tests();
- xtf_exlog_stop();
-
xtf_success(NULL);
}