blob: 39dd596453682acacde99cf57696289eeb421ecc [file] [log] [blame]
/*
* Test some powerpc instructions
*/
#include <libcflat.h>
#include <asm/processor.h>
static int verbose;
static int volatile is_invalid;
static int volatile alignment;
static void program_check_handler(struct pt_regs *regs, void *opaque)
{
int *data = opaque;
if (verbose) {
printf("Detected invalid instruction %#018lx: %08x\n",
regs->nip, *(uint32_t*)regs->nip);
}
/* the result is bit 16 to 19 of SRR1
* bit 0: SRR0 contains the address of the next instruction
* bit 1: Trap
* bit 2: Privileged instruction
* bit 3: Illegal instruction
* bit 4: FP enabled exception type
*/
*data = regs->msr >> 16;
regs->nip += 4;
}
static void alignment_handler(struct pt_regs *regs, void *opaque)
{
int *data = opaque;
if (verbose) {
printf("Detected alignment exception %#018lx: %08x\n",
regs->nip, *(uint32_t*)regs->nip);
}
*data = 1;
regs->nip += 4;
}
static void test_illegal(void)
{
report_prefix_push("invalid");
is_invalid = 0;
asm volatile (".long 0");
report(is_invalid == 8, "exception"); /* illegal instruction */
report_prefix_pop();
}
static void test_64bit(void)
{
uint64_t msr;
report_prefix_push("64bit");
asm("mfmsr %[msr]": [msr] "=r" (msr));
report(msr & 0x8000000000000000UL, "detected");
report_prefix_pop();
}
/*
* Test 'Load String Word Immediate' instruction
*/
static void test_lswi(void)
{
int i;
char addr[128];
uint64_t regs[32];
report_prefix_push("lswi");
/* fill memory with sequence */
for (i = 0; i < 128; i++)
addr[i] = 1 + i;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
/*
* lswi is supposed to cause an alignment exception in little endian
* mode, but to be able to check this, we also have to specify the
* opcode without mnemonic here since newer versions of GCC refuse
* "lswi" when compiling in little endian mode.
*/
alignment = 0;
asm volatile ("mr r12,%[addr];"
".long 0x7d6c24aa;" /* lswi r11,r12,4 */
"std r11,0(%[regs]);"
:: [addr] "r" (addr), [regs] "r" (regs)
: "r11", "r12", "memory");
report(alignment, "alignment");
#else
/* check incomplete register filling */
asm volatile ("li r12,-1;"
"mr r11, r12;"
"lswi r11, %[addr], %[len];"
"std r11, 0*8(%[regs]);"
"std r12, 1*8(%[regs]);"
::
[len] "i" (3),
[addr] "b" (addr),
[regs] "r" (regs)
:
"r11", "r12", "memory");
report(regs[0] == 0x01020300 && regs[1] == (uint64_t)-1, "partial");
/* check NB = 0 ==> 32 bytes. */
asm volatile ("li r19,-1;"
"mr r11, r19; mr r12, r19; mr r13, r19;"
"mr r14, r19; mr r15, r19; mr r16, r19;"
"mr r17, r19; mr r18, r19;"
"lswi r11, %[addr], %[len];"
"std r11, 0*8(%[regs]);"
"std r12, 1*8(%[regs]);"
"std r13, 2*8(%[regs]);"
"std r14, 3*8(%[regs]);"
"std r15, 4*8(%[regs]);"
"std r16, 5*8(%[regs]);"
"std r17, 6*8(%[regs]);"
"std r18, 7*8(%[regs]);"
"std r19, 8*8(%[regs]);"
::
[len] "i" (0),
[addr] "b" (addr),
[regs] "r" (regs)
:
/* as 32 is the number of bytes,
* we should modify 32/4 = 8 regs, from r11 to r18
* We check r19 is unmodified by filling it with 1s
* before the instruction.
*/
"r11", "r12", "r13", "r14", "r15", "r16", "r17",
"r18", "r19", "memory");
report(regs[0] == 0x01020304 && regs[1] == 0x05060708 &&
regs[2] == 0x090a0b0c && regs[3] == 0x0d0e0f10 &&
regs[4] == 0x11121314 && regs[5] == 0x15161718 &&
regs[6] == 0x191a1b1c && regs[7] == 0x1d1e1f20 &&
regs[8] == (uint64_t)-1, "length");
/* check wrap around to r0 */
asm volatile ("li r31,-1;"
"mr r0, r31;"
"lswi r31, %[addr], %[len];"
"std r31, 0*8(%[regs]);"
"std r0, 1*8(%[regs]);"
::
[len] "i" (8),
[addr] "b" (addr),
[regs] "r" (regs)
:
/* modify two registers from r31, wrap around to r0 */
"r31", "r0", "memory");
report(regs[0] == 0x01020304 && regs[1] == 0x05060708,
"wrap around to r0");
/* check wrap around doesn't break RA */
asm volatile ("mr r29,r1\n"
"li r31,-1\n"
"mr r0,r31\n"
"mr r1, %[addr]\n"
".long 0x7fe154aa\n" /* lswi r31, r1, 10 */
"std r31, 0*8(%[regs])\n"
"std r0, 1*8(%[regs])\n"
"std r1, 2*8(%[regs])\n"
"mr r1,r29\n"
::
[addr] "r" (addr),
[regs] "r" (regs)
:
/* loading three registers from r31 wraps around to r1,
* r1 is saved to r29, as adding it to the clobber
* list doesn't protect it
*/
"r0", "r29", "r31", "memory");
/* doc says it is invalid, real proc stops when it comes to
* overwrite the register.
* In all the cases, the register must stay untouched
*/
report(regs[2] == (uint64_t)addr, "Don't overwrite Ra");
#endif
report_prefix_pop();
}
/*
* lswx: Load String Word Indexed X-form
*
* lswx RT,RA,RB
*
* EA = (RA|0) + RB
* n = XER
*
* Load n bytes from address EA into (n / 4) consecutive registers,
* throught RT -> RT + (n / 4) - 1.
* - Data are loaded into 4 low order bytes of registers (Word).
* - The unfilled bytes are set to 0.
* - The sequence of registers wraps around to GPR0.
* - if n == 0, content of RT is undefined
* - RT <= RA or RB < RT + (n + 4) is invalid or result is undefined
* - RT == RA == 0 is invalid
*
* For lswx in little-endian mode, an alignment interrupt always occurs.
*
*/
static void test_lswx(void)
{
int i;
char addr[128];
uint64_t regs[32];
report_prefix_push("lswx");
/* fill memory with sequence */
for (i = 0; i < 128; i++)
addr[i] = 1 + i;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
/*
* lswx is supposed to cause an alignment exception in little endian
* mode, but to be able to check this, we also have to specify the
* opcode without mnemonic here since newer versions of GCC refuse
* "lswx" when compiling in little endian mode.
*/
alignment = 0;
asm volatile ("mtxer %[len];"
"mr r11,%[addr];"
".long 0x7d805c2a;" /* lswx r12,0,r11 */
"std r12,0(%[regs]);"
:: [len]"r"(4), [addr]"r"(addr), [regs]"r"(regs)
: "r11", "r12", "memory");
report(alignment, "alignment");
#else
/* check incomplete register filling */
asm volatile ("mtxer %[len];"
"li r12,-1;"
"mr r11, r12;"
"lswx r11, 0, %[addr];"
"std r11, 0*8(%[regs]);"
"std r12, 1*8(%[regs]);"
::
[len] "r" (3),
[addr] "r" (addr),
[regs] "r" (regs)
:
"xer", "r11", "r12", "memory");
report(regs[0] == 0x01020300 && regs[1] == (uint64_t)-1, "partial");
/* check an old know bug: the number of bytes is used as
* the number of registers, so try 32 bytes.
*/
asm volatile ("mtxer %[len];"
"li r19,-1;"
"mr r11, r19; mr r12, r19; mr r13, r19;"
"mr r14, r19; mr r15, r19; mr r16, r19;"
"mr r17, r19; mr r18, r19;"
"lswx r11, 0, %[addr];"
"std r11, 0*8(%[regs]);"
"std r12, 1*8(%[regs]);"
"std r13, 2*8(%[regs]);"
"std r14, 3*8(%[regs]);"
"std r15, 4*8(%[regs]);"
"std r16, 5*8(%[regs]);"
"std r17, 6*8(%[regs]);"
"std r18, 7*8(%[regs]);"
"std r19, 8*8(%[regs]);"
::
[len] "r" (32),
[addr] "r" (addr),
[regs] "r" (regs)
:
/* as 32 is the number of bytes,
* we should modify 32/4 = 8 regs, from r11 to r18
* We check r19 is unmodified by filling it with 1s
* before the instruction.
*/
"xer", "r11", "r12", "r13", "r14", "r15", "r16", "r17",
"r18", "r19", "memory");
report(regs[0] == 0x01020304 && regs[1] == 0x05060708 &&
regs[2] == 0x090a0b0c && regs[3] == 0x0d0e0f10 &&
regs[4] == 0x11121314 && regs[5] == 0x15161718 &&
regs[6] == 0x191a1b1c && regs[7] == 0x1d1e1f20 &&
regs[8] == (uint64_t)-1, "length");
/* check wrap around to r0 */
asm volatile ("mtxer %[len];"
"li r31,-1;"
"mr r0, r31;"
"lswx r31, 0, %[addr];"
"std r31, 0*8(%[regs]);"
"std r0, 1*8(%[regs]);"
::
[len] "r" (8),
[addr] "r" (addr),
[regs] "r" (regs)
:
/* modify two registers from r31, wrap around to r0 */
"xer", "r31", "r0", "memory");
report(regs[0] == 0x01020304 && regs[1] == 0x05060708,
"wrap around to r0");
/* check wrap around to r0 over RB doesn't break RB */
asm volatile ("mtxer %[len];"
"mr r29,r1;"
"li r31,-1;"
"mr r1,r31;"
"mr r0, %[addr];"
"lswx r31, 0, r0;"
"std r31, 0*8(%[regs]);"
"std r0, 1*8(%[regs]);"
"std r1, 2*8(%[regs]);"
"mr r1,r29;"
::
[len] "r" (12),
[addr] "r" (addr),
[regs] "r" (regs)
:
/* loading three registers from r31 wraps around to r1,
* r1 is saved to r29, as adding it to the clobber
* list doesn't protect it
*/
"xer", "r31", "r0", "r29", "memory");
/* doc says it is invalid, real proc stops when it comes to
* overwrite the register.
* In all the cases, the register must stay untouched
*/
report(regs[1] == (uint64_t)addr, "Don't overwrite Rb");
#endif
report_prefix_pop();
}
int main(int argc, char **argv)
{
int i;
handle_exception(0x700, program_check_handler, (void *)&is_invalid);
handle_exception(0x600, alignment_handler, (void *)&alignment);
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
verbose = 1;
}
}
report_prefix_push("emulator");
test_64bit();
test_illegal();
test_lswx();
test_lswi();
report_prefix_pop();
return report_summary();
}