blob: 0948cb4e386f1daef2eaec2a1341b6464bb13aa9 [file] [log] [blame]
// Copyright 2016 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 <errno.h>
#include <stdio.h>
#include <sys/uio.h>
#include <threads.h>
#include <unistd.h>
#include <zircon/compiler.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/log.h>
// output via debuglog syscalls
static zx_handle_t log_handle;
#define LOGBUF_MAX (ZX_LOG_RECORD_MAX - sizeof(zx_log_record_t))
static mtx_t linebuffer_lock = MTX_INIT;
static char linebuffer[LOGBUF_MAX] __TA_GUARDED(linebuffer_lock);
static size_t linebuffer_size __TA_GUARDED(linebuffer_lock);
// Flushes and resets linebuffer.
static void flush_linebuffer_locked(void) __TA_REQUIRES(linebuffer_lock) {
zx_debuglog_write(log_handle, 0, linebuffer, linebuffer_size);
linebuffer_size = 0;
}
static void log_write(const void* data, size_t len) {
mtx_lock(&linebuffer_lock);
// printf calls write multiple times within a print, but each debuglog write
// is a separate record, so each inserts a logical newline. To avoid
// inappropriate breaking, do a version of _IOLBF here. A write of len == 0
// indicates an fflush.
if (len == 0) {
flush_linebuffer_locked();
}
for (size_t i = 0; i < len; ++i) {
if (linebuffer_size == sizeof(linebuffer)) {
flush_linebuffer_locked();
}
char c = ((const char*)data)[i];
linebuffer[linebuffer_size++] = c;
if (c == '\n') {
flush_linebuffer_locked();
}
}
mtx_unlock(&linebuffer_lock);
}
// libc init and io stubs
// The reason these are here is that the "core" tests intentionally do not
// use fdio. See ./README.md.
static zx_handle_t root_resource;
static zx_handle_t mmio_root_resource;
static zx_handle_t system_root_resource;
__EXPORT
void __libc_extensions_init(uint32_t count, zx_handle_t handle[], uint32_t info[]) {
for (unsigned n = 0; n < count; n++) {
if (info[n] == PA_HND(PA_RESOURCE, 0)) {
root_resource = handle[n];
handle[n] = 0;
info[n] = 0;
}
if (info[n] == PA_HND(PA_MMIO_RESOURCE, 0)) {
mmio_root_resource = handle[n];
handle[n] = 0;
info[n] = 0;
}
if (info[n] == PA_HND(PA_SYSTEM_RESOURCE, 0)) {
system_root_resource = handle[n];
handle[n] = 0;
info[n] = 0;
}
}
if (root_resource == ZX_HANDLE_INVALID) {
static const char kStandaloneMsg[] =
"*** Standalone core-tests must run directly from userboot ***\n";
zx_debug_write(kStandaloneMsg, sizeof(kStandaloneMsg) - 1);
__builtin_trap();
} else {
if (zx_debuglog_create(root_resource, 0, &log_handle) != ZX_OK) {
zx_process_exit(-2);
}
static const char kStartMsg[] = "*** Running standalone Zircon core tests ***\n";
zx_debuglog_write(log_handle, 0, kStartMsg, sizeof(kStartMsg) - 1);
}
}
__EXPORT
zx_handle_t get_root_resource(void) { return root_resource; }
__EXPORT
zx_handle_t get_mmio_root_resource(void) { return mmio_root_resource; }
__EXPORT
zx_handle_t get_system_root_resource(void) { return system_root_resource; }
__EXPORT
ssize_t write(int fd, const void* data, size_t count) {
if ((fd == 1) || (fd == 2)) {
log_write(data, count);
}
return count;
}
__EXPORT
ssize_t readv(int fd, const struct iovec* iov, int num) { return 0; }
__EXPORT
ssize_t writev(int fd, const struct iovec* iov, int num) {
ssize_t count = 0;
ssize_t r;
while (num > 0) {
if (iov->iov_len != 0) {
r = write(fd, iov->iov_base, iov->iov_len);
if (r < 0) {
return count ? count : r;
}
if ((size_t)r < iov->iov_len) {
return count + r;
}
count += r;
}
iov++;
num--;
}
return count;
}
__EXPORT
off_t lseek(int fd, off_t offset, int whence) {
errno = ENOSYS;
return -1;
}
__EXPORT
int isatty(int fd) { return 1; }