| //===----------------------------------------------------------------------===// |
| // |
| // 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" |