Files
esp-idf/components/vfs/vfs_linux_default_coop.c
Guillaume Souchere 9835daba70 feat(freertos/vfs): add Linux cooperative syscall dispatch with VFS FD registration
Rework the syscall interposition architecture so FreeRTOS works
standalone (without VFS) and VFS optionally overrides with strong
symbols.  All kernel FDs are registered with VFS via
esp_vfs_register_fd_with_local_fd, so the application only sees
VFS-allocated FD numbers, preventing numerical collisions between
kernel FDs and VFS-internal FD slots.

FreeRTOS side:
- Create linux_port_coop_internal.h with LINUX_COOP_IO_LOOP,
  LINUX_COOP_RESOLVE, linux_coop_yield and FD state table functions
- Export cooperative I/O primitives (freertos_linux_coop_read/write/
  open/close/fcntl/select/pread/pwrite/readv/writev/recv/send/
  recvfrom/sendto/recvmsg/sendmsg/connect/accept/pselect/poll/
  socket/socketpair/pipe/pipe2/dup/dup2/syscalls_init)
- Define weak POSIX symbols calling through to the cooperative
  primitives; keep nanosleep/sleep/usleep as strong (not FD-related)
- Refactor freertos_linux_coop_syscalls.h into a pure public API header

VFS side:
- Register the Linux host FS with esp_vfs_register_fs_with_id() as a
  proper VFS driver; register stdin/stdout/stderr at init (priority 99)
- Rewrite vfs_linux.c with strong POSIX symbols dispatching through
  esp_vfs_*
- Add strong overrides for FD-creating syscalls (open, pipe, pipe2,
  socket, socketpair, dup, dup2, accept) that register returned FDs
  with VFS
- Add strong overrides for FD-translating syscalls (readv, writev,
  recv, send, recvfrom, sendto, recvmsg, sendmsg, connect, pselect,
  poll) that translate VFS FD to kernel FD
- Delete vfs_coop_syscalls.c (absorbed into FreeRTOS weak + VFS strong)
- Update CMakeLists.txt: remove vfs_coop_syscalls.c, add linker hook
2026-06-04 11:36:34 +02:00

159 lines
4.8 KiB
C

/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief Linux target VFS host filesystem registration + init
*
* Registers the cooperative libc ops (backed by freertos_linux_coop_*)
* as a VFS filesystem using esp_vfs_register_fs_with_id, so that
* file descriptors registered against this VFS ID are dispatched
* through the cooperative syscall layer.
*
* stdin/stdout/stderr are registered with VFS here, and all other
* kernel FDs are registered by the strong POSIX overrides in vfs_linux.c.
*
* Non-blocking syscalls (lseek, fstat, ioctl, fsync) are resolved directly
* via dlsym and bypass the cooperative interposition layer because they
* complete synchronously and never return EAGAIN/EWOULDBLOCK.
*/
#include <dlfcn.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/stat.h>
#include "esp_vfs.h"
#include "esp_private/freertos_linux_coop_syscalls.h"
#include "esp_private/startup_internal.h"
/* VFS ID for the Linux host filesystem — used by vfs_linux.c to register FDs */
esp_vfs_id_t s_linux_host_vfs_id = -1;
/* Real libc entry points for non-blocking syscalls, resolved at startup */
static off_t (*real_lseek)(int, off_t, int);
static int (*real_fstat)(int, struct stat *);
static int (*real_fsync)(int);
typedef int (*real_ioctl_fn_t)(int, unsigned long, ...);
static real_ioctl_fn_t real_ioctl;
static ssize_t default_read(void *ctx, int fd, void *dst, size_t size)
{
(void)ctx;
return freertos_linux_coop_read(fd, dst, size);
}
static ssize_t default_write(void *ctx, int fd, const void *data, size_t size)
{
(void)ctx;
return freertos_linux_coop_write(fd, data, size);
}
static ssize_t default_pread(void *ctx, int fd, void *dst, size_t size, off_t offset)
{
(void)ctx;
return freertos_linux_coop_pread(fd, dst, size, offset);
}
static ssize_t default_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset)
{
(void)ctx;
return freertos_linux_coop_pwrite(fd, src, size, offset);
}
static int default_open(void *ctx, const char *path, int flags, int mode)
{
(void)ctx;
return freertos_linux_coop_open(path, flags, mode);
}
static int default_close(void *ctx, int fd)
{
(void)ctx;
return freertos_linux_coop_close(fd);
}
static int default_fcntl(void *ctx, int fd, int cmd, int arg)
{
(void)ctx;
return freertos_linux_coop_fcntl(fd, cmd, arg);
}
/* lseek never blocks — call real libc directly, no cooperative retry needed */
static off_t default_lseek(void *ctx, int fd, off_t offset, int whence)
{
(void)ctx;
return real_lseek(fd, offset, whence);
}
/* fstat never blocks — call real libc directly, no cooperative retry needed */
static int default_fstat(void *ctx, int fd, struct stat *st)
{
(void)ctx;
return real_fstat(fd, st);
}
/* ioctl never blocks — call real libc directly, no cooperative retry needed */
static int default_ioctl(void *ctx, int fd, int cmd, va_list args)
{
(void)ctx;
void *arg = va_arg(args, void *);
return real_ioctl(fd, (unsigned long)cmd, arg);
}
/* fsync never blocks — call real libc directly, no cooperative retry needed */
static int default_fsync(void *ctx, int fd)
{
(void)ctx;
return real_fsync(fd);
}
static const esp_vfs_fs_ops_t s_linux_host_ops = {
.read_p = default_read,
.write_p = default_write,
.pread_p = default_pread,
.pwrite_p = default_pwrite,
.open_p = default_open,
.close_p = default_close,
.fcntl_p = default_fcntl,
.lseek_p = default_lseek,
.fstat_p = default_fstat,
.ioctl_p = default_ioctl,
.fsync_p = default_fsync,
};
ESP_SYSTEM_INIT_FN(init_vfs_linux_coop, CORE, BIT(0), 99)
{
/* Resolve real libc symbols for non-blocking syscalls */
real_lseek = dlsym(RTLD_NEXT, "lseek");
real_fstat = dlsym(RTLD_NEXT, "fstat");
real_ioctl = dlsym(RTLD_NEXT, "ioctl");
real_fsync = dlsym(RTLD_NEXT, "fsync");
freertos_linux_coop_syscalls_init();
/* Register the Linux host FS with VFS (id-based, no path prefix) */
esp_err_t err = esp_vfs_register_fs_with_id(&s_linux_host_ops,
ESP_VFS_FLAG_CONTEXT_PTR,
NULL,
&s_linux_host_vfs_id);
if (err != ESP_OK) {
return err;
}
/* Register stdin/stdout/stderr so apps see them as VFS FDs 0, 1, 2 */
int fd;
esp_vfs_register_fd_with_local_fd(s_linux_host_vfs_id, STDIN_FILENO, true, &fd);
esp_vfs_register_fd_with_local_fd(s_linux_host_vfs_id, STDOUT_FILENO, true, &fd);
esp_vfs_register_fd_with_local_fd(s_linux_host_vfs_id, STDERR_FILENO, true, &fd);
return ESP_OK;
}
void esp_vfs_include_linux_coop_init(void)
{
// Linker hook function, exists to make the linker examine this file
}