blob: 880d69d74845f2216609a48a0ff0951c717d20a6 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <unittest/unittest.h>
#include "register-set.h"
void regs_fill_test_values(zx_general_regs_t* regs) {
for (uint32_t index = 0; index < sizeof(*regs); ++index) {
((uint8_t*)regs)[index] = index + 1;
}
// Set various flags bits that will read back the same.
#if defined(__x86_64__)
// Here we set all flag bits that are modifiable from user space or
// that are not modifiable but are expected to read back as 1, with the
// exception of the trap flag (bit 8, which would interfere with
// execution if we set it).
//
// Note that setting the direction flag (bit 10) helps test whether the
// kernel correctly handles taking an interrupt when that flag is set
// (see MG-998).
regs->rflags =
(1 << 0) | // CF: carry flag
(1 << 1) | // Reserved, always 1
(1 << 2) | // PF: parity flag
(1 << 4) | // AF: adjust flag
(1 << 6) | // ZF: zero flag
(1 << 7) | // SF: sign flag
(1 << 9) | // IF: interrupt enable flag (set by kernel)
(1 << 10) | // DF: direction flag
(1 << 11) | // OF: overflow flag
(1 << 14) | // NT: nested task flag
(1 << 18) | // AC: alignment check flag
(1 << 21); // ID: used for testing for CPUID support
#elif defined(__aarch64__)
// Only set the 4 flag bits that are readable and writable by the
// instructions "msr nzcv, REG" and "mrs REG, nzcv".
regs->cpsr = 0xf0000000;
#endif
}
bool regs_expect_eq(zx_general_regs_t* regs1, zx_general_regs_t* regs2) {
BEGIN_HELPER;
#define CHECK_REG(FIELD) EXPECT_EQ(regs1->FIELD, regs2->FIELD, "Reg " #FIELD)
#if defined(__x86_64__)
CHECK_REG(rax);
CHECK_REG(rbx);
CHECK_REG(rcx);
CHECK_REG(rdx);
CHECK_REG(rsi);
CHECK_REG(rdi);
CHECK_REG(rbp);
CHECK_REG(rsp);
CHECK_REG(r8);
CHECK_REG(r9);
CHECK_REG(r10);
CHECK_REG(r11);
CHECK_REG(r12);
CHECK_REG(r13);
CHECK_REG(r14);
CHECK_REG(r15);
CHECK_REG(rip);
CHECK_REG(rflags);
#elif defined(__aarch64__)
for (int regnum = 0; regnum < 30; ++regnum) {
char name[10];
snprintf(name, sizeof(name), "Reg r[%d]", regnum);
EXPECT_EQ(regs1->r[regnum], regs2->r[regnum], name);
}
CHECK_REG(lr);
CHECK_REG(sp);
CHECK_REG(pc);
CHECK_REG(cpsr);
#else
# error Unsupported architecture
#endif
#undef CHECK_REG
END_HELPER;
}
// spin_with_regs() function.
#if defined(__x86_64__)
static_assert(offsetof(zx_general_regs_t, rax) == 8*0, "");
static_assert(offsetof(zx_general_regs_t, rbx) == 8*1, "");
static_assert(offsetof(zx_general_regs_t, rcx) == 8*2, "");
static_assert(offsetof(zx_general_regs_t, rdx) == 8*3, "");
static_assert(offsetof(zx_general_regs_t, rsi) == 8*4, "");
static_assert(offsetof(zx_general_regs_t, rdi) == 8*5, "");
static_assert(offsetof(zx_general_regs_t, rbp) == 8*6, "");
static_assert(offsetof(zx_general_regs_t, rsp) == 8*7, "");
static_assert(offsetof(zx_general_regs_t, r8) == 8*8, "");
static_assert(offsetof(zx_general_regs_t, r9) == 8*9, "");
static_assert(offsetof(zx_general_regs_t, r10) == 8*10, "");
static_assert(offsetof(zx_general_regs_t, r11) == 8*11, "");
static_assert(offsetof(zx_general_regs_t, r12) == 8*12, "");
static_assert(offsetof(zx_general_regs_t, r13) == 8*13, "");
static_assert(offsetof(zx_general_regs_t, r14) == 8*14, "");
static_assert(offsetof(zx_general_regs_t, r15) == 8*15, "");
static_assert(offsetof(zx_general_regs_t, rip) == 8*16, "");
static_assert(offsetof(zx_general_regs_t, rflags) == 8*17, "");
static_assert(sizeof(zx_general_regs_t) == 8*18, "");
__asm__(".pushsection .text, \"ax\", @progbits\n"
".global spin_with_regs\n"
"spin_with_regs:\n"
// Set flags using POPF. Note that we use POPF rather than SAHF
// because POPF is able to set more flags than SAHF.
"pushq 8*17(%rdi)\n"
"popfq\n"
// Load general purpose registers.
"movq 8*0(%rdi), %rax\n"
"movq 8*1(%rdi), %rbx\n"
"movq 8*2(%rdi), %rcx\n"
"movq 8*3(%rdi), %rdx\n"
"movq 8*4(%rdi), %rsi\n"
// Skip assigning rdi here and assign it last.
"movq 8*6(%rdi), %rbp\n"
"movq 8*7(%rdi), %rsp\n"
"movq 8*8(%rdi), %r8\n"
"movq 8*9(%rdi), %r9\n"
"movq 8*10(%rdi), %r10\n"
"movq 8*11(%rdi), %r11\n"
"movq 8*12(%rdi), %r12\n"
"movq 8*13(%rdi), %r13\n"
"movq 8*14(%rdi), %r14\n"
"movq 8*15(%rdi), %r15\n"
"movq 8*5(%rdi), %rdi\n"
".global spin_with_regs_spin_address\n"
"spin_with_regs_spin_address:\n"
"jmp spin_with_regs_spin_address\n"
".popsection\n");
#elif defined(__aarch64__)
static_assert(offsetof(zx_general_regs_t, r[0]) == 8*0, "");
static_assert(offsetof(zx_general_regs_t, r[1]) == 8*1, "");
static_assert(offsetof(zx_general_regs_t, lr) == 8*30, "");
static_assert(offsetof(zx_general_regs_t, sp) == 8*31, "");
static_assert(offsetof(zx_general_regs_t, pc) == 8*32, "");
static_assert(offsetof(zx_general_regs_t, cpsr) == 8*33, "");
static_assert(sizeof(zx_general_regs_t) == 8*34, "");
__asm__(".pushsection .text, \"ax\", %progbits\n"
".global spin_with_regs\n"
"spin_with_regs:\n"
// Load sp via a temporary register.
"ldr x1, [x0, #8*31]\n"
"mov sp, x1\n"
// Load NZCV flags, a subset of the PSTATE/CPSR register.
"ldr x1, [x0, #8*33]\n"
"msr nzcv, x1\n"
// Load general purpose registers.
// Skip assigning x0 and x1 here and assign them last.
"ldp x2, x3, [x0, #8*2]\n"
"ldp x4, x5, [x0, #8*4]\n"
"ldp x6, x7, [x0, #8*6]\n"
"ldp x8, x9, [x0, #8*8]\n"
"ldp x10, x11, [x0, #8*10]\n"
"ldp x12, x13, [x0, #8*12]\n"
"ldp x14, x15, [x0, #8*14]\n"
"ldp x16, x17, [x0, #8*16]\n"
"ldp x18, x19, [x0, #8*18]\n"
"ldp x20, x21, [x0, #8*20]\n"
"ldp x22, x23, [x0, #8*22]\n"
"ldp x24, x25, [x0, #8*24]\n"
"ldp x26, x27, [x0, #8*26]\n"
"ldp x28, x29, [x0, #8*28]\n"
"ldr x30, [x0, #8*30]\n"
"ldp x0, x1, [x0]\n"
".global spin_with_regs_spin_address\n"
"spin_with_regs_spin_address:\n"
"b spin_with_regs_spin_address\n"
".popsection\n");
#else
# error Unsupported architecture
#endif
// save_regs_and_exit_thread() function.
#if defined(__x86_64__)
__asm__(".pushsection .text,\"ax\", @progbits\n"
".global save_regs_and_exit_thread\n"
"save_regs_and_exit_thread:\n"
"movq %rax, 8*0(%rsp)\n"
"movq %rbx, 8*1(%rsp)\n"
"movq %rcx, 8*2(%rsp)\n"
"movq %rdx, 8*3(%rsp)\n"
"movq %rsi, 8*4(%rsp)\n"
"movq %rdi, 8*5(%rsp)\n"
"movq %rbp, 8*6(%rsp)\n"
"movq %rsp, 8*7(%rsp)\n"
"movq %r8, 8*8(%rsp)\n"
"movq %r9, 8*9(%rsp)\n"
"movq %r10, 8*10(%rsp)\n"
"movq %r11, 8*11(%rsp)\n"
"movq %r12, 8*12(%rsp)\n"
"movq %r13, 8*13(%rsp)\n"
"movq %r14, 8*14(%rsp)\n"
"movq %r15, 8*15(%rsp)\n"
// Save the flags register.
"pushfq\n"
"popq %rax\n"
"movq %rax, 8*17(%rsp)\n"
// Fill out the rip field with known value.
"leaq save_regs_and_exit_thread(%rip), %rax\n"
"movq %rax, 8*16(%rsp)\n"
"call zx_thread_exit@PLT\n"
"ud2\n"
".popsection\n");
#elif defined(__aarch64__)
__asm__(".pushsection .text, \"ax\", %progbits\n"
".global save_regs_and_exit_thread\n"
"save_regs_and_exit_thread:\n"
"stp x0, x1, [sp, #8*0]\n"
"stp x2, x3, [sp, #8*2]\n"
"stp x4, x5, [sp, #8*4]\n"
"stp x6, x7, [sp, #8*6]\n"
"stp x8, x9, [sp, #8*8]\n"
"stp x10, x11, [sp, #8*10]\n"
"stp x12, x13, [sp, #8*12]\n"
"stp x14, x15, [sp, #8*14]\n"
"stp x16, x17, [sp, #8*16]\n"
"stp x18, x19, [sp, #8*18]\n"
"stp x20, x21, [sp, #8*20]\n"
"stp x22, x23, [sp, #8*22]\n"
"stp x24, x25, [sp, #8*24]\n"
"stp x26, x27, [sp, #8*26]\n"
"stp x28, x29, [sp, #8*28]\n"
"str x30, [sp, #8*30]\n"
// Save the sp register.
"mov x0, sp\n"
"str x0, [sp, #8*31]\n"
// Fill out the pc field with known value.
"adr x0, save_regs_and_exit_thread\n"
"str x0, [sp, #8*32]\n"
// Save NZCV flags, a subset of the PSTATE/CPSR register.
"mrs x0, nzcv\n"
"str x0, [sp, #8*33]\n"
"bl zx_thread_exit\n"
"brk 0\n"
".popsection\n");
#else
# error Unsupported architecture
#endif