Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/zephyr/posix/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ extern "C" {
/* File related operations */
int close(int file);
ssize_t write(int file, const void *buffer, size_t count);
ssize_t pwrite(int file, const void *buffer, size_t count, off_t offset);
ssize_t read(int file, void *buffer, size_t count);
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
off_t lseek(int file, off_t offset, int whence);
int fsync(int fd);
int ftruncate(int fd, off_t length);
Expand Down
32 changes: 20 additions & 12 deletions include/zephyr/sys/fdtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>

/* FIXME: use k_off_t and k_ssize_t to avoid the POSIX->Zephyr->POSIX dependency cycle */
#ifdef CONFIG_NEWLIB_LIBC
#ifndef _OFF_T_DECLARED
typedef _off_t off_t;
#define _OFF_T_DECLARED
#endif
#ifndef _SSIZE_T_DECLARED
typedef _ssize_t ssize_t;
#define _SSIZE_T_DECLARED
#endif
#endif

#include <stdio.h>

#ifdef CONFIG_PICOLIBC
#define ZVFS_O_APPEND 0x0400
#define ZVFS_O_CREAT 0x0040
Expand Down Expand Up @@ -60,18 +74,6 @@
extern "C" {
#endif

/* FIXME: use k_off_t and k_ssize_t to avoid the POSIX->Zephyr->POSIX dependency cycle */
#ifdef CONFIG_NEWLIB_LIBC
#ifndef _OFF_T_DECLARED
typedef __off_t off_t;
#define _OFF_T_DECLARED
#endif
#ifndef _SSIZE_T_DECLARED
typedef _ssize_t ssize_t;
#define _SSIZE_T_DECLARED
#endif
#endif

/**
* File descriptor virtual method table.
* Currently all operations beyond read/write/close go thru ioctl method.
Expand Down Expand Up @@ -261,6 +263,12 @@ __syscall int zvfs_select(int nfds, struct zvfs_fd_set *ZRESTRICT readfds,
struct zvfs_fd_set *ZRESTRICT errorfds,
const struct timespec *ZRESTRICT timeout, const void *ZRESTRICT sigmask);

void zvfs_libc_file_alloc_cb(int fd, const char *mode, FILE *fp);
int zvfs_libc_file_alloc(int fd, const char *mode, FILE **fp, k_timeout_t timeout);
void zvfs_libc_file_free(FILE *fp);
int zvfs_libc_file_get_fd(FILE *fp);
FILE *zvfs_libc_file_from_fd(int fd);

/**
* Request codes for fd_op_vtable.ioctl().
*
Expand Down
1 change: 1 addition & 0 deletions lib/libc/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_THRD
source/thrd/tss.c
)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_REMOVE source/stdio/remove.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_FCLOSE source/stdio/fclose.c)

# Prevent compiler from optimizing calloc into an infinite recursive call
zephyr_library_compile_options($<TARGET_PROPERTY:compiler,no_builtin_malloc>)
8 changes: 7 additions & 1 deletion lib/libc/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ config COMMON_LIBC_THRD
depends on DYNAMIC_THREAD
# Note: the POSIX_API dependency is only necessary until common elements
# of C11 threads and POSIX API can be abstracted out to a common library.
depends on POSIX_API
depends on POSIX_THREADS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In version 2 of this change set, it was required to avoid pulling in unnecessary dependencies which resulted in some issue on some platform.

In any case, it's a more accurate refinement of the dependency.

default y
help
Common implementation of C11 <threads.h> API.
Expand All @@ -120,3 +120,9 @@ config COMMON_LIBC_REMOVE
default y if FILE_SYSTEM
help
Common implementation of remove().

config COMMON_LIBC_FCLOSE
bool "Common C library fclose"
default y if ZVFS && !MINIMAL_LIBC
help
Enable the common C library implementation of fclose()
25 changes: 25 additions & 0 deletions lib/libc/common/source/stdio/fclose.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2025 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/sys/fdtable.h>
#include <unistd.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <unistd.h>


int fclose(FILE *stream)
{
int fd;

fflush(stream);
fd = zvfs_libc_file_get_fd(stream);

if (!fd) {
return EOF;
}
if (close(fd)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this would be more appropriate here, so that POSIX is not unintentionally pulled into the libc matters.

Suggested change
if (close(fd)) {
if (zvfs_close(fd)) {

return EOF;
}

return 0;
}
22 changes: 22 additions & 0 deletions lib/libc/minimal/source/stdout/stdout_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,28 @@ static inline size_t z_vrfy_zephyr_fwrite(const void *ZRESTRICT ptr,
#include <zephyr/syscalls/zephyr_fwrite_mrsh.c>
#endif

int z_impl_zephyr_write_stdout(const void *buffer, int nbytes)
{
const char *buf = buffer;

for (int i = 0; i < nbytes; i++) {
if (*(buf + i) == '\n') {
_stdout_hook('\r');
}
_stdout_hook(*(buf + i));
}
return nbytes;
}

#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zephyr_write_stdout(const void *buf, int nbytes)
{
K_OOPS(K_SYSCALL_MEMORY_READ(buf, nbytes));
return z_impl_zephyr_write_stdout((const void *)buf, nbytes);
}
#include <zephyr/syscalls/zephyr_write_stdout_mrsh.c>
#endif

size_t fwrite(const void *ZRESTRICT ptr, size_t size, size_t nitems,
FILE *ZRESTRICT stream)
{
Expand Down
2 changes: 2 additions & 0 deletions lib/libc/newlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
zephyr_library()
zephyr_library_sources(libc-hooks.c)

zephyr_library_sources_ifdef(CONFIG_ZVFS zvfs_libc.c)

# Do not allow LTO when compiling libc-hooks.c file
set_source_files_properties(libc-hooks.c PROPERTIES COMPILE_OPTIONS $<TARGET_PROPERTY:compiler,prohibit_lto>)

Expand Down
8 changes: 8 additions & 0 deletions lib/libc/newlib/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,12 @@ config NEWLIB_LIBC_USE_POSIX_LIMITS_H
Disable this option if your toolchain's newlib already includes all mandatory POSIX
limits.

if ZVFS

config ZVFS_LIBC_FILE_SIZE
default 184 if 64BIT
default 104

endif # ZVFS

endif # NEWLIB_LIBC
6 changes: 6 additions & 0 deletions lib/libc/newlib/libc-hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ static int (*_stdout_hook)(int) = _stdout_hook_default;
void __stdout_hook_install(int (*hook)(int))
{
_stdout_hook = hook;
#ifdef CONFIG_ZVFS
stdout->_file = 1; /* STDOUT_FILENO */
#endif
}

static unsigned char _stdin_hook_default(void)
Expand All @@ -181,6 +184,9 @@ static unsigned char (*_stdin_hook)(void) = _stdin_hook_default;
void __stdin_hook_install(unsigned char (*hook)(void))
{
_stdin_hook = hook;
#ifdef CONFIG_ZVFS
stdin->_file = 0; /* STDIN_FILENO */
#endif
}

int z_impl_zephyr_read_stdin(char *buf, int nbytes)
Expand Down
82 changes: 82 additions & 0 deletions lib/libc/newlib/zvfs_libc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2024 Tenstorrent AI ULC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#include <zephyr/sys/fdtable.h>
#include <zephyr/toolchain.h>

BUILD_ASSERT(CONFIG_ZVFS_LIBC_FILE_SIZE >= sizeof(__FILE),
"CONFIG_ZVFS_LIBC_FILE_SIZE must at least match size of FILE object");
BUILD_ASSERT(CONFIG_ZVFS_LIBC_FILE_ALIGN == __alignof(__FILE),
"CONFIG_ZVFS_LIBC_FILE_SIZE must match alignment of FILE object");

static int z_libc_sflags(const char *mode)
{
int ret = 0;

switch (mode[0]) {
case 'r':
ret = ZVFS_O_RDONLY;
break;

case 'w':
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_TRUNC;
break;

case 'a':
ret = ZVFS_O_WRONLY | ZVFS_O_CREAT | ZVFS_O_APPEND;
break;
default:
return 0;
}

while (*++mode) {
switch (*mode) {
case '+':
ret |= ZVFS_O_RDWR;
break;
case 'x':
ret |= ZVFS_O_EXCL;
break;
default:
break;
}
}

return ret;
}

void zvfs_libc_file_alloc_cb(int fd, const char *mode, FILE *fp)
{
/*
* These symbols have conflicting declarations in upstream headers.
* Use uintptr_t and a cast to assign cleanly.
*/
extern uintptr_t _close_r;
extern uintptr_t _lseek_r;
extern uintptr_t _read_r;
extern uintptr_t _write_r;

__ASSERT_NO_MSG(mode != NULL);
__ASSERT_NO_MSG(fp != NULL);

fp->_flags = z_libc_sflags(mode);
fp->_file = fd;
fp->_cookie = (void *)fp;

*(uintptr_t *)&fp->_read = (uintptr_t)&_read_r;
*(uintptr_t *)&fp->_write = (uintptr_t)&_write_r;
*(uintptr_t *)&fp->_seek = (uintptr_t)&_lseek_r;
*(uintptr_t *)&fp->_close = (uintptr_t)&_close_r;
}

int zvfs_libc_file_get_fd(FILE *fp)
{
return fp->_file;
}
2 changes: 2 additions & 0 deletions lib/libc/picolibc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ zephyr_library_sources(
stdio.c
)

zephyr_library_sources_ifdef(CONFIG_ZVFS zvfs_libc.c)

zephyr_library_compile_options($<TARGET_PROPERTY:compiler,prohibit_lto>)

# define __LINUX_ERRNO_EXTENSIONS__ so we get errno defines like -ESHUTDOWN
Expand Down
8 changes: 8 additions & 0 deletions lib/libc/picolibc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,12 @@ config PICOLIBC_GLOBAL_ERRNO

endif # PICOLIBC_USE_MODULE

if ZVFS

config ZVFS_LIBC_FILE_SIZE
default 144 if 64BIT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major: This seems a pretty tight assumption about the picolibc implementation (same for newlib)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also accurate. And also easily adjustable.

default 80

endif # ZVFS

endif # PICOLIBC
66 changes: 56 additions & 10 deletions lib/libc/picolibc/stdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@
*/

#include "picolibc-hooks.h"
#include "stdio-bufio.h"

static LIBC_DATA int (*_stdout_hook)(int);
static int _stdout_hook_default(int c)
{
(void)(c); /* Prevent warning about unused argument */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a macro for this


return EOF;
}

static LIBC_DATA int (*_stdout_hook)(int) = _stdout_hook_default;

int z_impl_zephyr_fputc(int a, FILE *out)
{
(*_stdout_hook)(a);
return 0;
return ((out == stdout) || (out == stderr)) ? _stdout_hook(a) : EOF;
}

#ifdef CONFIG_USERSPACE
Expand All @@ -24,31 +31,70 @@ static inline int z_vrfy_zephyr_fputc(int c, FILE *stream)

static int picolibc_put(char a, FILE *f)
{
zephyr_fputc(a, f);
return 0;
return zephyr_fputc(a, f);
}

static LIBC_DATA FILE __stdout = FDEV_SETUP_STREAM(picolibc_put, NULL, NULL, 0);
static LIBC_DATA FILE __stdin = FDEV_SETUP_STREAM(NULL, NULL, NULL, 0);
#ifndef CONFIG_ZVFS
static LIBC_DATA FILE __stdout = FDEV_SETUP_STREAM(picolibc_put, NULL, NULL, _FDEV_SETUP_WRITE);
static LIBC_DATA FILE __stdin = FDEV_SETUP_STREAM(NULL, NULL, NULL, _FDEV_SETUP_READ);
#endif

#ifdef __strong_reference
#define STDIO_ALIAS(x) __strong_reference(stdout, x);
#else
#define STDIO_ALIAS(x) FILE *const x = &__stdout;
#endif

#ifndef CONFIG_ZVFS
FILE *const stdin = &__stdin;
FILE *const stdout = &__stdout;
STDIO_ALIAS(stderr);
#endif

void __stdout_hook_install(int (*hook)(int))
{
_stdout_hook = hook;
__stdout.flags |= _FDEV_SETUP_WRITE;
#ifdef CONFIG_ZVFS
stdout->put = picolibc_put;
stdout->flags |= _FDEV_SETUP_WRITE;

struct __file_bufio *bp = (struct __file_bufio *)stdout;

bp->ptr = INT_TO_POINTER(1 /* STDOUT_FILENO */);
bp->bflags |= _FDEV_SETUP_WRITE;
#endif
}

void __stdin_hook_install(unsigned char (*hook)(void))
{
__stdin.get = (int (*)(FILE *)) hook;
__stdin.flags |= _FDEV_SETUP_READ;
stdin->get = (int (*)(FILE *))hook;
#ifdef CONFIG_ZVFS
stdin->flags |= _FDEV_SETUP_READ;
struct __file_bufio *bp = (struct __file_bufio *)stdin;

bp->bflags |= _FDEV_SETUP_READ;
bp->ptr = INT_TO_POINTER(0 /* STDIN_FILENO */);
#endif
}

int z_impl_zephyr_write_stdout(const void *buffer, int nbytes)
{
const char *buf = buffer;

for (int i = 0; i < nbytes; i++) {
if (*(buf + i) == '\n') {
_stdout_hook('\r');
}
_stdout_hook(*(buf + i));
}
return nbytes;
}

#ifdef CONFIG_USERSPACE
static inline int z_vrfy_zephyr_write_stdout(const void *buf, int nbytes)
{
K_OOPS(K_SYSCALL_MEMORY_READ(buf, nbytes));
return z_impl_zephyr_write_stdout((const void *)buf, nbytes);
}
#include <zephyr/syscalls/zephyr_write_stdout_mrsh.c>
#endif
Loading