blob: 720df0b23342c9fde4169fd50927c9fbd2292af0 [file] [edit]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Arm bare-metal hermetic test utilities.
///
//===----------------------------------------------------------------------===//
#include "hdr/stdint_proxy.h"
#include "hdr/types/size_t.h"
#include "hdr/types/ssize_t.h"
#include "hdr/types/struct_timespec.h"
// Redirect stdout/stderr, time and exit from libc tests to semihosting
namespace {
// Semihosting constants and semihosting call wrapper.
// https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst
constexpr uint32_t SYS_OPEN = 0x01;
constexpr uint32_t SYS_WRITE = 0x05;
constexpr uint32_t SYS_CLOCK = 0x10;
constexpr uint32_t SYS_TIME = 0x11;
constexpr uint32_t SYS_EXIT = 0x18;
constexpr uint32_t OPENMODE_W = 4;
constexpr uint32_t ADP_Stopped_ApplicationExit = 0x20026;
constexpr uint32_t ADP_Stopped_RunTimeErrorUnknown = 0x20023;
#if defined(__thumb__) // T32
#if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M'
#define SEMIHOST_INSTRUCTION "bkpt #0xAB"
#else
#define SEMIHOST_INSTRUCTION "svc 0xab"
#endif
#else // A32
#define SEMIHOST_INSTRUCTION "svc 0x123456"
#endif
long semihosting_call(long val, const void *ptr) {
register long v __asm__("r0") = val;
register const void *p __asm__("r1") = ptr;
__asm__ __volatile__(SEMIHOST_INSTRUCTION
: "+r"(v), "+r"(p)
:
: "memory", "cc");
return v;
}
} // namespace
extern "C" {
struct __llvm_libc_stdio_cookie {
int handle;
};
struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
static void stdio_open(struct __llvm_libc_stdio_cookie *cookie, size_t mode) {
const char std_stream_name[] = ":tt";
size_t args[] = {
reinterpret_cast<size_t>(std_stream_name),
mode,
sizeof(std_stream_name) - 1UL,
};
cookie->handle = semihosting_call(SYS_OPEN, args);
}
void _platform_init(void) {
stdio_open(&__llvm_libc_stdout_cookie, OPENMODE_W);
stdio_open(&__llvm_libc_stderr_cookie, OPENMODE_W);
}
void __llvm_libc_exit(int status) {
uint32_t semihosting_status = (status == 0) ? ADP_Stopped_ApplicationExit
: ADP_Stopped_RunTimeErrorUnknown;
semihosting_call(SYS_EXIT,
reinterpret_cast<const void *>(semihosting_status));
__builtin_unreachable(); // This semihosting call does not return.
}
ssize_t __llvm_libc_stdio_write(struct __llvm_libc_stdio_cookie *cookie,
const char *buf, size_t size) {
size_t args[] = {
static_cast<size_t>(cookie->handle),
reinterpret_cast<size_t>(buf),
size,
};
ssize_t retval = semihosting_call(SYS_WRITE, args);
if (retval >= 0)
retval = size - retval;
return retval;
}
bool __llvm_libc_timespec_get_active(struct timespec *ts) {
long retval = semihosting_call(SYS_CLOCK, 0);
if (retval == -1)
return false;
// Semihosting uses centiseconds.
ts->tv_sec = (retval / 100);
ts->tv_nsec = (retval % 100) * (1'000'000'000 / 100);
return true;
}
bool __llvm_libc_timespec_get_utc(struct timespec *ts) {
long retval = semihosting_call(SYS_TIME, 0);
// Semihosting uses seconds.
ts->tv_sec = retval;
ts->tv_nsec = 0;
return true;
}
} // extern "C"