blob: 45201b8cfb42b4715ca8cd14a38cd15f0cc25df4 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstddef>
#include <cstdint>
#include <asm/unistd.h>
// This test examines the initial extended processor state when entering a new process.
// This means it must start at _start and not depend on any of the usual initialization logic
// performed by libc that may clobber this state before we have a chance to examine it. Thus
// it has some assembly helpers to issue syscalls standalone.
// Generic syscall with 4 arguments.
intptr_t syscall4(intptr_t syscall_number, intptr_t arg1, intptr_t arg2, intptr_t arg3,
intptr_t arg4) {
intptr_t ret;
#if defined(__x86_64__)
register intptr_t r10 asm("r10") = arg4;
__asm__ volatile("syscall;"
: "=a"(ret)
: "a"(syscall_number), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10)
: "rcx", "r11", "memory");
#elif defined(__arm__)
register intptr_t r0 asm("r0") = arg1;
register intptr_t r1 asm("r1") = arg2;
register intptr_t r2 asm("r2") = arg3;
register intptr_t r3 asm("r3") = arg4;
register intptr_t number asm("r7") = syscall_number;
__asm__ volatile("svc #0"
: "=r"(ret)
: "0"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(number)
: "memory");
#elif defined(__aarch64__)
register intptr_t x0 asm("x0") = arg1;
register intptr_t x1 asm("x1") = arg2;
register intptr_t x2 asm("x2") = arg3;
register intptr_t x3 asm("x3") = arg4;
register intptr_t number asm("x8") = syscall_number;
__asm__ volatile("svc #0"
: "=r"(ret)
: "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(number)
: "memory");
#elif defined(__riscv)
register intptr_t a0 asm("a0") = arg1;
register intptr_t a1 asm("a1") = arg2;
register intptr_t a2 asm("a2") = arg3;
register intptr_t a3 asm("a3") = arg4;
register intptr_t number asm("a7") = syscall_number;
__asm__ volatile("ecall"
: "=r"(ret)
: "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(number)
: "memory");
#else
#error Unsupported architecture
#endif
return ret;
}
#define SYSCCALL4(number, arg1, arg2, arg3, arg4, ...) \
syscall4(number, (intptr_t)(arg1), (intptr_t)(arg2), (intptr_t)(arg3), (intptr_t)(arg4))
#define SYSCALL(number, ...) SYSCCALL4(number, __VA_ARGS__, 0, 0, 0, 0)
void exit_group(int exit_code) { SYSCALL(__NR_exit_group, exit_code); }
void exit_success() { exit_group(0); }
size_t strlen(const char* str) {
const char* end = str;
while (*end)
++end;
return end - str;
}
void write_stderr(const char* str, size_t len) { SYSCALL(__NR_write, 2, str, len); }
void fail(const char* msg, int exit_code) {
write_stderr(msg, strlen(msg));
exit_group(exit_code);
}
extern "C" void _start() {
#if defined(__x86_64__)
constexpr size_t kXmmRegisterWidth = 128;
std::byte buffer[kXmmRegisterWidth * 16];
#define MOVUPS_XMM_TO_ADDR(reg, addr) asm("movups %%xmm" #reg ", %0" : : "m"(addr) : "memory");
MOVUPS_XMM_TO_ADDR(0, buffer[0 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(1, buffer[1 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(2, buffer[2 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(3, buffer[3 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(4, buffer[4 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(5, buffer[5 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(6, buffer[6 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(7, buffer[7 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(8, buffer[8 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(9, buffer[9 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(10, buffer[10 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(11, buffer[11 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(12, buffer[12 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(13, buffer[13 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(14, buffer[14 * kXmmRegisterWidth]);
MOVUPS_XMM_TO_ADDR(15, buffer[15 * kXmmRegisterWidth]);
#undef MOVUPS_XMM_TO_ADDR
for (size_t i = 0; i < kXmmRegisterWidth * 16; ++i) {
if (buffer[i] != std::byte{0}) {
int register_number = static_cast<int>(i) / kXmmRegisterWidth;
char message[] = "XMM00\n";
message[4] = '0' + register_number % 10;
message[3] = '0' + (register_number / 10) % 10;
fail(message, static_cast<int>(i / kXmmRegisterWidth) + 1);
}
}
#elif defined(__arm__)
constexpr size_t kQRegisterWidth = 64;
std::byte d_register_buffer[16 * kQRegisterWidth];
uint32_t fpscr = 0;
__asm__ volatile(
"vstr d0, [%1, #(0 * 8)]\n"
"vstr d1, [%1, #(1 * 8)]\n"
"vstr d2, [%1, #(2 * 8)]\n"
"vstr d3, [%1, #(3 * 8)]\n"
"vstr d4, [%1, #(4 * 8)]\n"
"vstr d5, [%1, #(5 * 8)]\n"
"vstr d6, [%1, #(6 * 8)]\n"
"vstr d7, [%1, #(7 * 8)]\n"
"vstr d8, [%1, #(8 * 8)]\n"
"vstr d9, [%1, #(9 * 8)]\n"
"vstr d10, [%1, #(10 * 8)]\n"
"vstr d11, [%1, #(11 * 8)]\n"
"vstr d12, [%1, #(12 * 8)]\n"
"vstr d13, [%1, #(13 * 8)]\n"
"vstr d14, [%1, #(14 * 8)]\n"
"vstr d15, [%1, #(15 * 8)]\n"
"vmrs %0, fpscr\n"
: "=r"(fpscr)
: "r"(d_register_buffer)
: "memory");
for (size_t i = 0; i < 16 * kQRegisterWidth; ++i) {
if (d_register_buffer[i] != std::byte{0}) {
int register_number = static_cast<int>(i / kQRegisterWidth);
char message[] = "D00\n";
message[2] = '0' + register_number % 10;
message[1] = '0' + (register_number / 10) % 10;
fail(message, register_number + 1);
}
}
if (fpscr != 0) {
fail("fpscr\n", 16);
}
#elif defined(__aarch64__)
constexpr size_t kQRegisterWidth = 128;
std::byte q_register_buffer[32 * kQRegisterWidth];
uint64_t fpcr = 0;
uint64_t fpsr = 0;
__asm__ volatile(
"stp q0, q1, [%2, #(0 * 32)]\n"
"stp q2, q3, [%2, #(1 * 32)]\n"
"stp q4, q5, [%2, #(2 * 32)]\n"
"stp q6, q7, [%2, #(3 * 32)]\n"
"stp q8, q9, [%2, #(4 * 32)]\n"
"stp q10, q11, [%2, #(5 * 32)]\n"
"stp q12, q13, [%2, #(6 * 32)]\n"
"stp q14, q15, [%2, #(7 * 32)]\n"
"stp q16, q17, [%2, #(8 * 32)]\n"
"stp q18, q19, [%2, #(9 * 32)]\n"
"stp q20, q21, [%2, #(10 * 32)]\n"
"stp q22, q23, [%2, #(11 * 32)]\n"
"stp q24, q25, [%2, #(12 * 32)]\n"
"stp q26, q27, [%2, #(13 * 32)]\n"
"stp q28, q29, [%2, #(14 * 32)]\n"
"stp q30, q31, [%2, #(15 * 32)]\n"
"mrs %0, fpcr\n"
"mrs %1, fpsr\n"
: "=r"(fpcr), "=r"(fpsr)
: "r"(q_register_buffer)
: "memory");
for (size_t i = 0; i < 32 * kQRegisterWidth; ++i) {
if (q_register_buffer[i] != std::byte{0}) {
int register_number = static_cast<int>(i / kQRegisterWidth);
char message[] = "Q00\n";
message[2] = '0' + register_number % 10;
message[1] = '0' + (register_number / 10) % 10;
fail(message, register_number + 1);
}
}
if (fpcr != 0) {
fail("fpcr\n", 33);
}
if (fpsr != 0) {
fail("fpsr\n", 34);
}
#elif defined(__riscv)
uint64_t fp_registers[32];
uint32_t fcsr;
__asm__ volatile(
"fld f0, 0 * 8(%[regs])\n"
"fld f1, 1 * 8(%[regs])\n"
"fld f2, 2 * 8(%[regs])\n"
"fld f3, 3 * 8(%[regs])\n"
"fld f4, 4 * 8(%[regs])\n"
"fld f5, 5 * 8(%[regs])\n"
"fld f6, 6 * 8(%[regs])\n"
"fld f7, 7 * 8(%[regs])\n"
"fld f8, 8 * 8(%[regs])\n"
"fld f9, 9 * 8(%[regs])\n"
"fld f10, 10 * 8(%[regs])\n"
"fld f11, 11 * 8(%[regs])\n"
"fld f12, 12 * 8(%[regs])\n"
"fld f13, 13 * 8(%[regs])\n"
"fld f14, 14 * 8(%[regs])\n"
"fld f15, 15 * 8(%[regs])\n"
"fld f16, 16 * 8(%[regs])\n"
"fld f17, 17 * 8(%[regs])\n"
"fld f18, 18 * 8(%[regs])\n"
"fld f19, 19 * 8(%[regs])\n"
"fld f20, 20 * 8(%[regs])\n"
"fld f21, 21 * 8(%[regs])\n"
"fld f23, 23 * 8(%[regs])\n"
"fld f24, 24 * 8(%[regs])\n"
"fld f25, 25 * 8(%[regs])\n"
"fld f26, 26 * 8(%[regs])\n"
"fld f27, 27 * 8(%[regs])\n"
"fld f28, 28 * 8(%[regs])\n"
"fld f29, 29 * 8(%[regs])\n"
"fld f30, 30 * 8(%[regs])\n"
"fld f31, 31 * 8(%[regs])\n"
"frcsr %[fcsr]\n"
: [fcsr] "=r"(fcsr)
: [regs] "r"(fp_registers)
: "memory");
for (size_t i = 0; i < 32; ++i) {
if (fp_registers[i] != 0) {
char message[] = "F00\n";
char register_number = static_cast<char>(i);
message[2] = '0' + register_number % 10;
message[1] = '0' + register_number / 10;
fail(message, register_number + 1);
}
}
if (fcsr != 0) {
fail("fcsr\n", 33);
}
#else
#error "unimplemented"
#endif
exit_success();
}