diff --git a/components/lwp/SConscript b/components/lwp/SConscript index 19da1baf1f68..475f31397ab5 100644 --- a/components/lwp/SConscript +++ b/components/lwp/SConscript @@ -57,6 +57,7 @@ if not GetDepend(['LWP_USING_RUNTIME']): group = DefineGroup('lwProcess', src, depend = ['RT_USING_SMART'], CPPPATH = CPPPATH) +group = group + SConscript(os.path.join('syscall', 'SConscript')) group = group + SConscript(os.path.join('vdso', 'SConscript')) group = group + SConscript(os.path.join('utest', 'SConscript')) Return('group') diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c deleted file mode 100644 index a871f71b513e..000000000000 --- a/components/lwp/lwp_syscall.c +++ /dev/null @@ -1,11638 +0,0 @@ -/* - * Copyright (c) 2006-2025 RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-06-10 Bernard first version - * 2021-02-03 lizhirui add limit condition for network syscall and add 64-bit arch support - * 2021-02-06 lizhirui fix some bugs - * 2021-02-12 lizhirui add 64-bit support for sys_brk - * 2021-02-20 lizhirui fix some warnings - * 2023-03-13 WangXiaoyao Format & fix syscall return value - * 2023-07-06 Shell adapt the signal API, and clone, fork to new implementation of lwp signal - * 2023-07-27 Shell Move tid_put() from lwp_free() to sys_exit() - * 2023-11-16 xqyjlj fix some syscalls (about sched_*, get/setpriority) - * 2023-11-17 xqyjlj add process group and session support - * 2023-11-30 Shell Fix sys_setitimer() and exit(status) - */ -#define __RT_IPC_SOURCE__ -#define _GNU_SOURCE - -/* RT-Thread System call */ -#include -#include -#include - -#include -#include - -#define DBG_TAG "lwp.syscall" -#define DBG_LVL DBG_INFO -#include - -#include "syscall_generic.h" - -#include "libc_musl.h" -#include "lwp_internal.h" -#ifdef ARCH_MM_MMU -#include -#include -#include -#endif - -#include -#include - -#ifdef RT_USING_DFS -#include -#include -#include -#include -#include -#ifdef RT_USING_DFS_V2 -#include -#endif -#include -#include /* rename() */ -#include -#include /* statfs() */ -#include -#include -#ifdef RT_USING_MUSLLIBC -#include -#endif -#endif - -#include "mqueue.h" - -#ifdef RT_USING_SAL -#include -#include - -#include -#include -#include - -#endif /* RT_USING_SAL */ - -#if (defined(RT_USING_SAL) && defined(SAL_USING_POSIX)) -#include - -#define SYSCALL_NET(f) f -#else -#define SYSCALL_NET(f) SYSCALL_SIGN(sys_notimpl) -#endif /* (defined(RT_USING_SAL) && defined(SAL_USING_POSIX)) */ - -#if defined(RT_USING_DFS) && defined(ARCH_MM_MMU) -#define SYSCALL_USPACE(f) f -#else -#define SYSCALL_USPACE(f) SYSCALL_SIGN(sys_notimpl) -#endif /* defined(RT_USING_DFS) && defined(ARCH_MM_MMU) */ - -#include "lwp_ipc_internal.h" -#include - -#include - -#ifndef GRND_NONBLOCK -#define GRND_NONBLOCK 0x0001 -#endif /* GRND_NONBLOCK */ - -#ifndef GRND_RANDOM -#define GRND_RANDOM 0x0002 -#endif /*GRND_RANDOM */ - -#ifndef RT_USING_POSIX_TIMER -#error "No definition RT_USING_POSIX_TIMER" -#endif /* RT_USING_POSIX_TIMER */ - -#ifndef RT_USING_POSIX_CLOCK -#error "No definition RT_USING_POSIX_CLOCK" -#endif /* RT_USING_POSIX_CLOCK */ - -void lwp_cleanup(struct rt_thread *tid); - -#ifdef ARCH_MM_MMU -#define ALLOC_KERNEL_STACK_SIZE 5120 - -static void *kmem_get(size_t size) -{ - return rt_malloc(size); -} - -static void kmem_put(void *kptr) -{ - rt_free(kptr); -} -#else /* ARCH_MM_MMU */ -#define ALLOC_KERNEL_STACK_SIZE 1536 -#define ALLOC_KERNEL_STACK_SIZE_MIN 1024 -#define ALLOC_KERNEL_STACK_SIZE_MAX 4096 - -extern void set_user_context(void *stack); -#endif /* ARCH_MM_MMU */ - -#ifdef RT_USING_SAL - /* The same socket option is defined differently in the user interfaces and the - * implementation. The options should be converted in the kernel. */ -#include "lwp_sys_socket.h" - -static void convert_sockopt(int *level, int *optname) -{ - if (*level == INTF_SOL_SOCKET) - { - *level = IMPL_SOL_SOCKET; - - switch (*optname) - { - case INTF_SO_REUSEADDR: - *optname = IMPL_SO_REUSEADDR; - break; - case INTF_SO_KEEPALIVE: - *optname = IMPL_SO_KEEPALIVE; - break; - case INTF_SO_BROADCAST: - *optname = IMPL_SO_BROADCAST; - break; - case INTF_SO_ACCEPTCONN: - *optname = IMPL_SO_ACCEPTCONN; - break; - case INTF_SO_DONTROUTE: - *optname = IMPL_SO_DONTROUTE; - break; - case INTF_SO_LINGER: - *optname = IMPL_SO_LINGER; - break; - case INTF_SO_OOBINLINE: - *optname = IMPL_SO_OOBINLINE; - break; - case INTF_SO_REUSEPORT: - *optname = IMPL_SO_REUSEPORT; - break; - case INTF_SO_SNDBUF: - *optname = IMPL_SO_SNDBUF; - break; - case INTF_SO_RCVBUF: - *optname = IMPL_SO_RCVBUF; - break; - case INTF_SO_SNDLOWAT: - *optname = IMPL_SO_SNDLOWAT; - break; - case INTF_SO_RCVLOWAT: - *optname = IMPL_SO_RCVLOWAT; - break; - case INTF_SO_SNDTIMEO: - *optname = IMPL_SO_SNDTIMEO; - break; - case INTF_SO_RCVTIMEO: - *optname = IMPL_SO_RCVTIMEO; - break; - case INTF_SO_ERROR: - *optname = IMPL_SO_ERROR; - break; - case INTF_SO_TYPE: - *optname = IMPL_SO_TYPE; - break; - case INTF_SO_NO_CHECK: - *optname = IMPL_SO_NO_CHECK; - break; - case INTF_SO_BINDTODEVICE: - *optname = IMPL_SO_BINDTODEVICE; - break; - case INTF_SO_TIMESTAMPNS: - *optname = IMPL_SO_TIMESTAMPNS; - break; - case INTF_SO_TIMESTAMPING: - *optname = IMPL_SO_TIMESTAMPING; - break; - case INTF_SO_SELECT_ERR_QUEUE: - *optname = IMPL_SO_SELECT_ERR_QUEUE; - break; - - /* - * SO_DONTLINGER (*level = ((int)(~SO_LINGER))), - * SO_USELOOPBACK (*level = 0x0040) and - * SO_CONTIMEO (*level = 0x1009) are not supported for now. - */ - default: - *optname = 0; - break; - } - return; - } - - if (*level == INTF_IPPROTO_IP) - { - *level = IMPL_IPPROTO_IP; - - switch (*optname) - { - case INTF_IP_TTL: - *optname = IMPL_IP_TTL; - break; - case INTF_IP_TOS: - *optname = IMPL_IP_TOS; - break; - case INTF_IP_MULTICAST_TTL: - *optname = IMPL_IP_MULTICAST_TTL; - break; - case INTF_IP_MULTICAST_IF: - *optname = IMPL_IP_MULTICAST_IF; - break; - case INTF_IP_MULTICAST_LOOP: - *optname = IMPL_IP_MULTICAST_LOOP; - break; - case INTF_IP_ADD_MEMBERSHIP: - *optname = IMPL_IP_ADD_MEMBERSHIP; - break; - case INTF_IP_DROP_MEMBERSHIP: - *optname = IMPL_IP_DROP_MEMBERSHIP; - break; - default: - break; - } - } - - if (*level == INTF_IPPROTO_TCP) - { - *level = IMPL_IPPROTO_TCP; - - switch (*optname) - { - case INTF_TCP_NODELAY: - *optname = IMPL_TCP_NODELAY; - break; - case INTF_TCP_KEEPALIVE: - *optname = IMPL_TCP_KEEPALIVE; - break; - case INTF_TCP_KEEPIDLE: - *optname = IMPL_TCP_KEEPIDLE; - break; - case INTF_TCP_KEEPINTVL: - *optname = IMPL_TCP_KEEPINTVL; - break; - case INTF_TCP_KEEPCNT: - *optname = IMPL_TCP_KEEPCNT; - break; - default: - break; - } - return; - } - - if (*level == INTF_IPPROTO_IPV6) - { - *level = IMPL_IPPROTO_IPV6; - - switch (*optname) - { - case INTF_IPV6_V6ONLY: - *optname = IMPL_IPV6_V6ONLY; - break; - default: - break; - } - return; - } -} -#endif /* RT_USING_SAL */ - -#if defined(RT_USING_LWIP) || defined(SAL_USING_UNET) || defined(RT_USING_SAL) -static void sockaddr_tolwip(const struct musl_sockaddr *std, struct sockaddr *lwip) -{ - if (std && lwip) - { - lwip->sa_len = sizeof(*lwip); - lwip->sa_family = (sa_family_t)std->sa_family; - memcpy(lwip->sa_data, std->sa_data, sizeof(lwip->sa_data)); - } -} - -static void sockaddr_tomusl(const struct sockaddr *lwip, struct musl_sockaddr *std) -{ - if (std && lwip) - { - std->sa_family = (uint16_t)lwip->sa_family; - memcpy(std->sa_data, lwip->sa_data, sizeof(std->sa_data)); - } -} -#endif - -static void _crt_thread_entry(void *parameter) -{ - rt_thread_t tid; - rt_size_t user_stack; - - tid = rt_thread_self(); - - user_stack = (rt_size_t)tid->user_stack + tid->user_stack_size; - user_stack &= ~7; /* align 8 */ - -#ifdef ARCH_MM_MMU - arch_crt_start_umode(parameter, tid->user_entry, (void *)user_stack, (char *)tid->stack_addr + tid->stack_size); -#else - set_user_context((void *)user_stack); - arch_start_umode(parameter, tid->user_entry, ((struct rt_lwp *)tid->lwp)->data_entry, (void *)user_stack); -#endif /* ARCH_MM_MMU */ -} - -/** - * @brief Terminates all threads in the current thread group. - * - * This system call ends execution for all threads within the same thread group, - * releasing resources such as memory and file descriptors. It is typically used - * in multithreaded environments to ensure a clean exit for the entire process. - * - * @param value The exit code to be returned to the parent process. - * @return sysret_t: return value of the system call execution. - */ -sysret_t sys_exit_group(int value) -{ - sysret_t rc = 0; - lwp_status_t lwp_status; - struct rt_lwp *lwp = lwp_self(); - - if (lwp) - { - lwp_status = LWP_CREATE_STAT_EXIT(value); - lwp_exit(lwp, lwp_status); - } - else - { - LOG_E("Can't find matching process of current thread"); - rc = -EINVAL; - } - - return rc; -} - -/** - * @brief Terminates the calling thread and exits the process. - * - * This system call ends the execution of the calling thread and the process - * it belongs to. - * - * @param status The exit code to be returned to the parent process. - * A value of 0 typically indicates successful execution, while - * non-zero values indicate an error or specific exit condition. - * @return sysret_t: return value of the system call execution. - * - * @note Unlike `sys_exit_group`, which terminates all threads in a thread group, - * `sys_exit` only terminates the calling thread and the process. - */ -sysret_t sys_exit(int status) -{ - sysret_t rc = 0; - rt_thread_t tid; - - tid = rt_thread_self(); - if (tid && tid->lwp) - { - lwp_thread_exit(tid, status); - } - else - { - LOG_E("Can't find matching process of current thread"); - rc = -EINVAL; - } - - return rc; -} - -/** - * @brief Reads data from a file descriptor into a buffer. - * - * This system call reads up to `nbyte` bytes of data from the file descriptor - * specified by `fd` into the buffer pointed to by `buf`. - * - * @param fd The file descriptor to read from. This should be a valid file - * descriptor obtained through system calls like `open()` or `socket()`. - * @param buf A pointer to the buffer where the read data will be stored. The buffer - * must have enough space to accommodate up to `nbyte` bytes. - * @param nbyte The maximum number of bytes to read. If the file contains fewer - * bytes than `nbyte`, only the available bytes will be read. - * @return The number of bytes actually read, which may be less than `nbyte` if fewer - * bytes are available or if the end of the file is reached. Returns `0` if - * the end of the file is encountered. On error, returns `errno`. - * @warning Ensure the buffer `buf` has sufficient space to hold the requested number - * of bytes, as failing to do so may result in undefined behavior. - */ -ssize_t sys_read(int fd, void *buf, size_t nbyte) -{ -#ifdef ARCH_MM_MMU - void *kmem = RT_NULL; - ssize_t ret = -1; - - if (nbyte) - { - if (!lwp_user_accessable((void *)buf, nbyte)) - { - return -EFAULT; - } - - kmem = kmem_get(nbyte); - if (!kmem) - { - return -ENOMEM; - } - } - - ret = read(fd, kmem, nbyte); - if (ret > 0) - { - if (ret != lwp_put_to_user(buf, kmem, ret)) - return -EFAULT; - } - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmem); - - return ret; -#else - if (!lwp_user_accessable((void *)buf, nbyte)) - { - return -EFAULT; - } - ssize_t ret = read(fd, buf, nbyte); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Writes data from a buffer to a file descriptor. - * - * This system call writes up to `nbyte` bytes of data from the buffer pointed - * to by `buf` to the file descriptor specified by `fd`. - * - * @param fd The file descriptor to write to. This should be a valid file - * descriptor obtained through system calls like `open()` or `socket()`. - * @param buf A pointer to the buffer containing the data to be written. The - * buffer must remain valid and accessible for the duration of the - * write operation. - * @param nbyte The number of bytes to write from the buffer. If the file descriptor - * refers to a device or a socket, the actual number of bytes written - * may be less than `nbyte`. - * @return The number of bytes actually written. This may be less than `nbyte` if - * the underlying resource cannot accept more data at the moment. On error, - * returns `errno`. - * - * @warning Ensure the buffer `buf` contains at least `nbyte` valid bytes to - * avoid undefined behavior. Additionally, verify that `fd` is writable - * to prevent errors. - * - * @see sys_read(), write() - */ -ssize_t sys_write(int fd, const void *buf, size_t nbyte) -{ -#ifdef ARCH_MM_MMU - void *kmem = RT_NULL; - ssize_t ret = -1; - - if (nbyte) - { - if (!lwp_user_accessable((void *)buf, nbyte)) - { - return -EFAULT; - } - - kmem = kmem_get(nbyte); - if (!kmem) - { - return -ENOMEM; - } - - lwp_get_from_user(kmem, (void *)buf, nbyte); - } - - ret = write(fd, kmem, nbyte); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmem); - - return ret; -#else - if (!lwp_user_accessable((void *)buf, nbyte)) - { - return -EFAULT; - } - ssize_t ret = write(fd, buf, nbyte); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Repositions the file offset of the open file descriptor. - * - * This system call sets the file offset for the open file descriptor `fd` - * to a new position based on the `offset` and `whence` parameters. It is used - * for seeking within files, enabling random access to file content. - * - * @param fd The file descriptor whose offset is to be modified. The descriptor - * must refer to a file capable of seeking (e.g., regular files, but - * not pipes or sockets). - * @param offset The new offset value relative to the position specified by `whence`. - * Can be a positive or negative value, depending on the seek direction. - * @param whence The reference point for the new offset. Must be one of the following: - * - `SEEK_SET`: Set the offset to `offset` bytes from the beginning of the file. - * - `SEEK_CUR`: Set the offset to its current position plus `offset`. - * - `SEEK_END`: Set the offset to the size of the file plus `offset`. - * @return On success, returns the resulting offset location as measured in bytes - * from the beginning of the file. On failure, returns `errno`. - * - * @warning Ensure the file descriptor `fd` supports seeking. Using this function - * on non-seekable file descriptors (e.g., pipes or sockets) will result in an error. - * - * @see sys_read(), sys_write(), lseek() - */ -size_t sys_lseek(int fd, size_t offset, int whence) -{ - ssize_t ret = lseek(fd, offset, whence); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Opens or creates a file, returning a file descriptor. - * - * This system call opens the file specified by `name` with the specified - * access mode and flags. If the file does not exist and the `O_CREAT` flag - * is provided, it will create the file with the specified mode. - * - * @param name The path to the file to be opened. This can be an absolute or - * relative path. - * @param flag Flags controlling how the file is opened. Common values include: - * - `O_RDONLY`: Open the file for read-only access. - * - `O_WRONLY`: Open the file for write-only access. - * - `O_RDWR`: Open the file for both reading and writing. - * - `O_CREAT`: Create the file if it does not exist (requires a mode argument). - * - `O_TRUNC`: Truncate the file to zero length if it exists. - * - `O_APPEND`: Open the file in append mode. - * @param ... Optional. If the `O_CREAT` flag is specified, an additional - * `mode_t` argument must be provided, defining the file permissions - * (e.g., `0644` for user-read/write and group/world-read). - * @return On success, returns the file descriptor. - * On failure, returns `errno`. - * - * @note The file descriptor returned can be used with other system calls like - * `sys_read()`, `sys_write()`, and `sys_close()`. - * - * @warning When using the `O_CREAT` flag, ensure to provide the `mode` argument - * to avoid undefined behavior. Additionally, verify that the process - * has the necessary permissions to access or create the file. - * - * @see sys_close(), sys_read(), sys_write(), open() - */ -sysret_t sys_open(const char *name, int flag, ...) -{ -#ifdef ARCH_MM_MMU - int ret = -1; - rt_size_t len = 0; - char *kname = RT_NULL; - mode_t mode = 0; - - if (!lwp_user_accessable((void *)name, 1)) - { - return -EFAULT; - } - - len = lwp_user_strlen(name); - if (!len) - { - return -EINVAL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return -ENOMEM; - } - - if ((flag & O_CREAT) || (flag & O_TMPFILE) == O_TMPFILE) - { - va_list ap; - va_start(ap, flag); - mode = va_arg(ap, mode_t); - va_end(ap); - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return -EINVAL; - } - - ret = open(kname, flag, mode); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kname); - - return ret; -#else - int ret; - mode_t mode = 0; - - if (!lwp_user_accessable((void *)name, 1)) - { - return -EFAULT; - } - - if ((flag & O_CREAT) || (flag & O_TMPFILE) == O_TMPFILE) - { - va_list ap; - va_start(ap, flag); - mode = va_arg(ap, mode_t); - va_end(ap); - } - - ret = open(name, flag, mode); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Opens or creates a file relative to a directory file descriptor. - * - * This system call opens the file specified by `name`, relative to the directory - * indicated by `dirfd`, with the specified flags and mode. It provides more - * flexibility than `sys_open` for handling files in specific directory contexts. - * - * @param dirfd The file descriptor of the directory relative to which the file is opened. - * Special values include: - * - `AT_FDCWD`: Use the current working directory as the base. - * @param name The path to the file to be opened. If `name` is absolute, `dirfd` is ignored. - * @param flag Flags controlling how the file is opened. Common values include: - * - `O_RDONLY`: Open the file for read-only access. - * - `O_WRONLY`: Open the file for write-only access. - * - `O_RDWR`: Open the file for both reading and writing. - * - `O_CREAT`: Create the file if it does not exist. - * - `O_TRUNC`: Truncate the file to zero length if it exists. - * - `O_APPEND`: Open the file in append mode. - * - `O_EXCL`: Ensure the file is created exclusively (used with `O_CREAT`). - * @param mode The permissions to set if the file is created (e.g., `0644` for user read/write - * and group/world read). This parameter is ignored unless `O_CREAT` is specified. - * @return On success, returns the file descriptor. - * On failure, returns `errno`. - * - * @note The `sys_openat` system call is particularly useful for implementing secure - * directory traversal and operations, as it avoids race conditions when working - * with relative paths. - * - * @warning Ensure that `dirfd` is a valid directory file descriptor if a relative - * `name` is provided. Combining `O_CREAT` and `mode` requires proper handling - * to avoid unintended permission settings. - * - * @see sys_open(), sys_read(), sys_write(), sys_close() - */ -sysret_t sys_openat(int dirfd, const char *name, int flag, mode_t mode) -{ -#ifdef ARCH_MM_MMU - int ret = -1; - rt_size_t len = 0; - char *kname = RT_NULL; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return -EINVAL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return -ENOMEM; - } - - lwp_get_from_user(kname, (void *)name, len + 1); - ret = openat(dirfd, kname, flag, mode); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kname); - - return ret; -#else - if (!lwp_user_accessable((void *)name, 1)) - { - return -EFAULT; - } - int ret = openat(dirfd, name, flag, mode); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Closes a file descriptor. - * - * This system call closes the file descriptor specified by `fd`, releasing any - * resources associated with it. Once closed, the file descriptor can no longer - * be used for operations such as reading or writing, and it may be reassigned - * by subsequent calls to functions like `sys_open()`. - * - * @param fd The file descriptor to be closed. This must be a valid open file descriptor. - * @return On success, returns 0. On failure, returns `errno`. - * - * @note Closing a file descriptor that is already closed results in an error. - * Additionally, if the file descriptor refers to a file or resource shared - * among multiple processes or threads, only the reference count is decremented, - * and the resource itself is not released until all references are closed. - * - * @warning Always ensure `fd` is valid before calling this function. Attempting - * to close an invalid or already closed descriptor may lead to undefined - * behavior or errors. - * - * @see sys_open(), sys_read(), sys_write(), close() - */ -sysret_t sys_close(int fd) -{ - int ret = close(fd); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Performs control operations on a file descriptor. - * - * This system call allows a program to manipulate the behavior of a device or file - * associated with the file descriptor `fd` by issuing a control command (`cmd`). - * The function provides an interface to interact with device drivers, such as modifying - * the settings of a device or performing custom operations. - * - * @param fd The file descriptor representing the device or file on which the ioctl - * command will be performed. This should be a valid, open file descriptor. - * @param cmd The control command to be executed. The exact behavior depends on the - * device and the command. Commands are usually defined by the device driver - * or the kernel and can vary widely. - * @param data A pointer to a buffer used for passing data to and from the command. - * The contents of the buffer depend on the specific command. For input - * commands, it may contain the data to be written to the device. For output - * commands, it may hold the data returned by the device. - * @return On success, returns 0. On failure, returns `errno`. - * - * @note The actual functionality of `sys_ioctl` depends heavily on the specific `cmd` - * and the type of device associated with the file descriptor `fd`. Each device - * may have a different set of valid ioctl commands. - * - * @warning Ensure that `fd` refers to a valid file descriptor for a device that supports - * ioctl commands. Providing an invalid command or using an unsupported device - * may lead to undefined behavior or errors. - * - * @see sys_open(), sys_read(), sys_write(), ioctl() - */ -sysret_t sys_ioctl(int fd, unsigned long cmd, void *data) -{ -#ifdef ARCH_MM_MMU - if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) - { - struct flock lock; - int ret; - - if (data == RT_NULL || - !lwp_user_accessable(data, sizeof(lock)) || - lwp_get_from_user(&lock, data, sizeof(lock)) != sizeof(lock)) - { - return -EFAULT; - } - - ret = ioctl(fd, cmd, &lock); - if (ret >= 0 && cmd == F_GETLK && - lwp_put_to_user(data, &lock, sizeof(lock)) != sizeof(lock)) - { - return -EFAULT; - } - return ret < 0 ? GET_ERRNO() : ret; - } -#endif - int ret = ioctl(fd, cmd, data); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Retrieves information about a file associated with a file descriptor. - * - * This system call retrieves the status information about the file referred to - * by the file descriptor `file` and stores it in the structure pointed to by `buf`. - * It is typically used to obtain metadata such as file size, permissions, type, - * and timestamps. - * - * @param file The file descriptor referring to an open file. It must be a valid file - * descriptor returned by `sys_open()` or similar system calls. - * @param buf A pointer to a `struct stat` that will be populated with the file's - * status information, including attributes like size, access times, - * permissions, and more. - * @return On success, returns `0`. On failure, returns `errno` to indicate the error. - * - * @note The structure `struct stat` typically includes the following fields: - * - `st_size`: Size of the file in bytes. - * - `st_mode`: File type and permissions. - * - `st_mtime`: Last modification time. - * - `st_atime`: Last access time. - * - `st_ctime`: Time of last status change. - * - * @warning Ensure that `file` is a valid, open file descriptor. Invalid file - * descriptors or unsupported file types may lead to errors when using - * this function. - * - * @see sys_open(), sys_close(), stat(), fstat() - */ -sysret_t sys_fstat(int file, struct stat *buf) -{ -#ifdef ARCH_MM_MMU - int ret = -1; - struct stat statbuff = { 0 }; - - if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) - { - return -EFAULT; - } - else - { - ret = fstat(file, &statbuff); - - if (ret == 0) - { - lwp_put_to_user(buf, &statbuff, sizeof statbuff); - } - else - { - ret = GET_ERRNO(); - } - - return ret; - } -#else - if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) - { - return -EFAULT; - } - int ret = fstat(file, buf); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Monitors multiple file descriptors to see if they are ready for I/O. - * - * This system call monitors the file descriptors specified in `fds` for any - * I/O events such as readiness for reading, writing, or exceptional conditions. - * It waits up to the specified timeout and returns with information about which - * file descriptors are ready for the requested operations. - * - * @param fds An array of `struct pollfd` structures, each specifying a file descriptor - * to be monitored and the events to be checked for. On return, each structure - * will contain the result of the poll operation for that file descriptor. - * @param nfds The number of elements in the `fds` array. - * @param timeout The maximum time (in milliseconds) to wait for events. If `timeout` - * is `-1`, the call will block indefinitely. If `timeout` is `0`, - * the call will return immediately. - * @return On success, returns the number of file descriptors with events that are ready, - * or `0` if the timeout expired with no events. On error, returns `-1` and sets `errno`. - * - * @note The `pollfd` structure used for each file descriptor contains the following fields: - * - `fd`: The file descriptor to be monitored. - * - `events`: The events to monitor (e.g., `POLLIN`, `POLLOUT`). - * - `revents`: The events that actually occurred. - * - * @warning Ensure that `fds` contains valid file descriptors. Invalid descriptors or - * unsupported types (such as sockets) may result in errors. Also, be mindful - * of the timeout behavior - passing `0` will cause an immediate return, and passing - * `-1` will block indefinitely. - * - * @see sys_select(), poll(), select() - */ -sysret_t sys_poll(struct pollfd *fds, nfds_t nfds, int timeout) -{ - int ret = -1; -#ifdef ARCH_MM_MMU - struct pollfd *kfds = RT_NULL; - - if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds)) - { - return -EFAULT; - } - - kfds = (struct pollfd *)kmem_get(nfds * sizeof *kfds); - if (!kfds) - { - return -ENOMEM; - } - - lwp_get_from_user(kfds, fds, nfds * sizeof *kfds); - - ret = poll(kfds, nfds, timeout); - if (ret > 0) - { - lwp_put_to_user(fds, kfds, nfds * sizeof *kfds); - } - - kmem_put(kfds); - return ret; -#else - if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds)) - { - return -EFAULT; - } - ret = poll(fds, nfds, timeout); - return ret; -#endif /* ARCH_MM_MMU */ -} - -/** - * @brief Monitors multiple file descriptors for readiness to perform I/O operations. - * - * This system call allows a program to monitor multiple file descriptors to see if - * they are ready for reading, writing, or have exceptional conditions. It waits - * for one or more of the file descriptors to become ready or for the specified - * timeout to expire. - * - * @param nfds The highest-numbered file descriptor in any of the sets, plus one. - * This is the number of file descriptors to monitor. - * @param readfds A pointer to an `fd_set` structure specifying the file descriptors - * to be checked for readability. If a file descriptor is ready to - * read, it will be set in the returned `fd_set`. - * @param writefds A pointer to an `fd_set` structure specifying the file descriptors - * to be checked for writability. If a file descriptor is ready to - * write, it will be set in the returned `fd_set`. - * @param exceptfds A pointer to an `fd_set` structure specifying the file descriptors - * to be checked for exceptional conditions (e.g., out-of-band data). - * If a file descriptor has exceptional conditions, it will be set in - * the returned `fd_set`. - * @param timeout A pointer to a `struct timeval` that specifies the maximum time - * to wait for an event. If `NULL`, the call blocks indefinitely. If - * `timeout` is `0`, the call will return immediately. - * @return On success, returns the number of file descriptors that are ready for - * the requested operations, or `0` if the timeout expired without any events. - * On error, returns `-1` and sets `errno`. - * - * @note The `fd_set` structures should be initialized using the `FD_ZERO` macro - * and populated using the `FD_SET` macro. After the call, the `fd_set` structures - * will contain the file descriptors that are ready for the requested operations. - * - * @warning Ensure that the `fd_set` structures are properly initialized and that - * `nfds` correctly reflects the number of file descriptors to monitor. - * Passing invalid file descriptors or incorrect `nfds` can lead to undefined behavior. - * - * @see sys_poll(), sys_read(), sys_write(), poll(), select() - */ -sysret_t sys_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) -{ -#ifdef ARCH_MM_MMU - int ret = -1; - fd_set *kreadfds = RT_NULL, *kwritefds = RT_NULL, *kexceptfds = RT_NULL; - struct timeval ktimeout, *ptimeout = RT_NULL; - - if (readfds) - { - if (!lwp_user_accessable((void *)readfds, sizeof *readfds)) - { - SET_ERRNO(EFAULT); - goto quit; - } - kreadfds = (fd_set *)kmem_get(sizeof *kreadfds); - if (!kreadfds) - { - SET_ERRNO(ENOMEM); - goto quit; - } - lwp_get_from_user(kreadfds, readfds, sizeof *kreadfds); - } - if (writefds) - { - if (!lwp_user_accessable((void *)writefds, sizeof *writefds)) - { - SET_ERRNO(EFAULT); - goto quit; - } - kwritefds = (fd_set *)kmem_get(sizeof *kwritefds); - if (!kwritefds) - { - SET_ERRNO(ENOMEM); - goto quit; - } - lwp_get_from_user(kwritefds, writefds, sizeof *kwritefds); - } - if (exceptfds) - { - if (!lwp_user_accessable((void *)exceptfds, sizeof *exceptfds)) - { - SET_ERRNO(EFAULT); - goto quit; - } - kexceptfds = (fd_set *)kmem_get(sizeof *kexceptfds); - if (!kexceptfds) - { - SET_ERRNO(EINVAL); - goto quit; - } - lwp_get_from_user(kexceptfds, exceptfds, sizeof *kexceptfds); - } - if (timeout) - { - if (!lwp_user_accessable((void *)timeout, sizeof *timeout)) - { - SET_ERRNO(EFAULT); - goto quit; - } - lwp_get_from_user(&ktimeout, timeout, sizeof ktimeout); - ptimeout = &ktimeout; - } - - ret = select(nfds, kreadfds, kwritefds, kexceptfds, ptimeout); - if (kreadfds) - { - lwp_put_to_user(readfds, kreadfds, sizeof *kreadfds); - } - if (kwritefds) - { - lwp_put_to_user(writefds, kwritefds, sizeof *kwritefds); - } - if (kexceptfds) - { - lwp_put_to_user(exceptfds, kexceptfds, sizeof *kexceptfds); - } -quit: - if (ret < 0) - { - ret = GET_ERRNO(); - } - - if (kreadfds) - { - kmem_put(kreadfds); - } - if (kwritefds) - { - kmem_put(kwritefds); - } - if (kexceptfds) - { - kmem_put(kexceptfds); - } - return ret; -#else - int ret; - - if (!lwp_user_accessable((void *)readfds, sizeof *readfds)) - { - return -EFAULT; - } - if (!lwp_user_accessable((void *)writefds, sizeof *writefds)) - { - return -EFAULT; - } - if (!lwp_user_accessable((void *)exceptfds, sizeof *exceptfds)) - { - return -EFAULT; - } - ret = select(nfds, readfds, writefds, exceptfds, timeout); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Removes a file or symbolic link from the filesystem. - * - * This system call deletes the file or symbolic link specified by `pathname`. - * After the call, the file will be unlinked from the filesystem, meaning it will - * no longer be accessible via its pathname. If the file is still open, it will - * remain available to processes that have it open until all file descriptors are - * closed. If the file is a regular file and has no other hard links, it will be - * removed from the disk once all references to it are closed. - * - * @param pathname The path to the file or symbolic link to be removed. It can be - * an absolute or relative path. - * @return On success, returns `0`. On failure, returns `errno` to indicate - * the error. - * - * @note If the file is currently being used by a process (i.e., the file descriptor - * is still open), it will not be immediately removed from the disk. The actual - * removal occurs once the file is no longer in use. Symbolic links are removed - * as soon as the call is executed. - * - * @warning Ensure that the file or link to be removed exists and that the `pathname` - * is correctly specified. The operation will fail if the file is open and - * in use by other processes or if the pathname does not exist. - * - * @see sys_open(), sys_remove(), unlink() - */ -sysret_t sys_unlink(const char *pathname) -{ -#ifdef ARCH_MM_MMU - int ret = -1; - rt_size_t len = 0; - char *kname = RT_NULL; - - len = lwp_user_strlen(pathname); - if (!len) - { - return -EINVAL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kname, (void *)pathname, len + 1) != (len + 1)) - { - kmem_put(kname); - return -EINVAL; - } - ret = unlink(kname); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kname); - return ret; -#else - int ret = 0; - ret = unlink(pathname); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Suspends execution for a specified amount of time. - * - * This system call suspends the execution of the calling thread for the duration - * specified by the `rqtp` argument. The `rqtp` argument is a pointer to a `struct timespec` - * that defines the sleep time in seconds and nanoseconds. If the sleep is interrupted by a signal, - * the function may return early with the remaining time in `rmtp`, which is an optional argument. - * - * @param rqtp A pointer to a `struct timespec` that specifies the requested sleep time. - * The structure contains two fields: `tv_sec` (seconds) and `tv_nsec` (nanoseconds). - * @param rmtp A pointer to a `struct timespec` where the remaining time will be stored if the - * sleep is interrupted by a signal. This can be `NULL` if the remaining time is not needed. - * @return On success, returns `0`. On failure, returns `errno` to indicate the error. - * - * @note The `timespec` structure has two fields: - * - `tv_sec`: The number of whole seconds to sleep. - * - `tv_nsec`: The number of nanoseconds to sleep after the seconds. This value should be - * in the range [0, 1,000,000,000) nanoseconds. - * - * @warning Ensure that the values in `rqtp` are within valid ranges. A `tv_nsec` value greater than - * or equal to 1,000,000,000 will result in an error. If `rmtp` is provided, the caller - * must ensure the buffer is large enough to store the remaining time. - * - * @see sys_sleep(), clock_nanosleep(), nanosleep() - */ -sysret_t sys_nanosleep(const struct timespec *rqtp, struct timespec *rmtp) -{ - int ret = 0; - LOG_D("sys_nanosleep\n"); - if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) - return -EFAULT; - -#ifdef ARCH_MM_MMU - struct timespec rqtp_k; - struct timespec rmtp_k; - - lwp_get_from_user(&rqtp_k, (void *)rqtp, sizeof rqtp_k); - ret = nanosleep(&rqtp_k, &rmtp_k); - if ((ret != -1 || rt_get_errno() == EINTR) && rmtp && lwp_user_accessable((void *)rmtp, sizeof *rmtp)) - { - lwp_put_to_user(rmtp, (void *)&rmtp_k, sizeof rmtp_k); - if (ret != 0) - return -EINTR; - } -#else - if (rmtp) - { - if (!lwp_user_accessable((void *)rmtp, sizeof *rmtp)) - return -EFAULT; - ret = nanosleep(rqtp, rmtp); - } -#endif - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Retrieves the current time and timezone information. - * - * This system call retrieves the current time in seconds and microseconds since - * the Unix epoch (1970-01-01 00:00:00 UTC) and stores it in the `tp` argument. - * It also retrieves the timezone information, if requested, and stores it in the - * `tzp` argument. The time returned is the local time of the system unless - * UTC is specified. - * - * @param tp A pointer to a `struct timeval` where the current time will be stored. - * The `struct timeval` contains two fields: - * - `tv_sec`: The number of seconds since the Unix epoch. - * - `tv_usec`: The number of microseconds past `tv_sec`. - * @param tzp A pointer to a `struct timezone` where the timezone information will - * be stored. This structure contains two fields: - * - `tz_minuteswest`: The number of minutes west of UTC. - * - `tz_dsttime`: A flag indicating the type of daylight saving time (DST) adjustment. - * If `tzp` is `NULL`, timezone information is not provided. - * @return On success, returns `0`. On failure, returns error code. - * - * @note The `struct timeval` represents the current time in seconds and microseconds, - * while `struct timezone` provides information on the timezone relative to UTC - * and any daylight saving adjustments. - * - * @warning Ensure that `tp` and `tzp` are valid pointers before calling the function. - * If you don't need timezone information, you can pass `NULL` for `tzp`. - * - * @see sys_time(), time(), gettimeofday() - */ -sysret_t sys_gettimeofday(struct timeval *tp, struct timezone *tzp) -{ -#ifdef ARCH_MM_MMU - struct timeval t_k; - - if (tp) - { - if (!lwp_user_accessable((void *)tp, sizeof *tp)) - { - return -EFAULT; - } - - t_k.tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; - t_k.tv_usec = (rt_tick_get() % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND); - - lwp_put_to_user(tp, (void *)&t_k, sizeof t_k); - } -#else - if (tp) - { - if (!lwp_user_accessable((void *)tp, sizeof *tp)) - { - return -EFAULT; - } - tp->tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; - tp->tv_usec = (rt_tick_get() % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND); - } -#endif - - return 0; -} - -/** - * @brief Sets the system's current time and timezone information. - * - * @param tv A pointer to a `struct timeval` that contains the new system time to set. - * The `struct timeval` consists of: - * - `tv_sec`: The number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC). - * - `tv_usec`: The number of microseconds past `tv_sec`. - * @param tzp A pointer to a `struct timezone` that contains the new timezone settings. - * The `struct timezone` consists of: - * - `tz_minuteswest`: The number of minutes west of UTC. - * - `tz_dsttime`: The type of daylight saving time adjustment. - * If `tzp` is `NULL`, the timezone information is not changed. - * @return On success, returns `0`. On failure, returns error code. - * - * @note haven't supported now. - * - * @see sys_gettimeofday(), settimeofday(), time() - */ -sysret_t sys_settimeofday(const struct timeval *tv, const struct timezone *tzp) -{ - return 0; -} - -/** - * @brief Replaces the current process with a new process. - * - * This system call loads a new program into the current process's address space, - * replacing the current program with the one specified by `filename`. It passes the - * arguments `argv` and environment variables `envp` to the new program. If the execution - * is successful, the current process is completely replaced, and no code after the - * `sys_exec` call is executed. If an error occurs, the system call returns, and the - * current process continues executing. - * - * @param filename The path to the executable file to be run. This can be an absolute - * or relative path. - * @param argc The number of arguments passed to the new program. This includes the - * executable name itself as the first argument. - * @param argv An array of strings (character pointers), where each string is an argument - * passed to the new program. The first element (`argv[0]`) is conventionally - * the name of the executable. - * @param envp An array of strings representing the environment variables for the new - * process. The array is terminated by a `NULL` pointer. - * @return This function does not return on success, as the current process is replaced. - * On failure, it returns error code to indicate the error. - * - * @note The new process inherits most of the attributes of the current process, such - * as file descriptors, unless explicitly modified. It is important that `argv` - * and `envp` are properly formatted, and the `filename` points to a valid executable. - * - * @warning If `filename` is invalid or not an executable, or if the arguments or - * environment variables are incorrectly set, the system call will fail and - * return `-1`. Ensure that the executable file is accessible and that `argv` - * and `envp` are properly constructed. - * - * @see execve(), execvp(), execv(), execle() - */ -sysret_t sys_exec(char *filename, int argc, char **argv, char **envp) -{ - int ret = 0; - int len = 0; - char *kfilename = RT_NULL; - - len = lwp_user_strlen(filename); - if (len <= 0) - { - return -EFAULT; - } - - kfilename = (char *)kmem_get(len + 1); - if (!kfilename) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kfilename, (void *)filename, len + 1) != (len + 1)) - { - kmem_put(kfilename); - return -EFAULT; - } - - ret = lwp_execve(kfilename, 0, argc, argv, envp); - - kmem_put(kfilename); - - return ret; -} - -/** - * @brief Sends a signal to a process or a group of processes. - * - * This system call sends the signal specified by `signo` to the process or process group - * identified by `pid`. If `pid` is positive, the signal is sent to the process with the - * specified process ID. If `pid` is `0`, the signal is sent to all processes in the - * same process group as the caller. If `pid` is `-1`, the signal is sent to all processes - * except for the caller. If `pid` is less than `-1`, the signal is sent to all processes - * in the process group with the process group ID equal to `-pid`. - * - * @param pid The process ID or process group ID to which the signal is to be sent. - * - A positive value sends the signal to the process with that ID. - * - `0` sends the signal to all processes in the same process group as the caller. - * - `-1` sends the signal to all processes except the caller. - * - A negative value sends the signal to all processes in the process group - * with the process group ID equal to `-pid`. - * @param signo The signal to send. This is an integer value that specifies the signal - * type. Common signal values include: - * - `SIGTERM` (terminate process) - * - `SIGKILL` (force kill process) - * - `SIGSTOP` (suspend process) - * - `SIGCONT` (resume process) - * @return On success, returns `0`. On failure, it returns error code to indicate the error. - * - * @note Signals are a mechanism for inter-process communication, allowing processes - * to send notifications or requests to other processes. The behavior of signals - * depends on the signal type and how the receiving process handles them. - * - * @warning The `signo` value must be a valid signal number. Passing an invalid signal - * number or an invalid `pid` may result in an error. Additionally, some signals - * (e.g., `SIGKILL`) cannot be caught or ignored by processes. - * - * @see signal(), killpg(), raise() - */ -sysret_t sys_kill(int pid, int signo) -{ - rt_err_t kret = 0; - sysret_t sysret; - struct rt_lwp *lwp = RT_NULL; - - /* handling the semantics of sys_kill */ - if (pid > 0) - { - /** - * Brief: Match the pid and send signal to the lwp if found - * Note: Critical Section - * - pid tree (READ. since the lwp is fetch from the pid tree, it must stay there) - */ - lwp_pid_lock_take(); - lwp = lwp_from_pid_raw_locked(pid); - if (lwp) - { - lwp_ref_inc(lwp); - lwp_pid_lock_release(); - } - else - { - lwp_pid_lock_release(); - kret = -RT_ENOENT; - } - - if (lwp) - { - kret = lwp_signal_kill(lwp, signo, SI_USER, 0); - lwp_ref_dec(lwp); - } - } - else if (pid < -1 || pid == 0) - { - pid_t pgid = 0; - rt_processgroup_t group; - - if (pid == 0) - { - /** - * sig shall be sent to all processes (excluding an unspecified set - * of system processes) whose process group ID is equal to the process - * group ID of the sender, and for which the process has permission to - * send a signal. - */ - pgid = lwp_pgid_get_byprocess(lwp_self()); - } - else - { - /** - * sig shall be sent to all processes (excluding an unspecified set - * of system processes) whose process group ID is equal to the absolute - * value of pid, and for which the process has permission to send a signal. - */ - pgid = -pid; - } - - group = lwp_pgrp_find(pgid); - if (group != RT_NULL) - { - PGRP_LOCK(group); - kret = lwp_pgrp_signal_kill(group, signo, SI_USER, 0); - PGRP_UNLOCK(group); - } - else - { - kret = -ECHILD; - } - } - else if (pid == -1) - { - /** - * sig shall be sent to all processes (excluding an unspecified set - * of system processes) for which the process has permission to send - * that signal. - */ - kret = lwp_signal_kill_all(signo, SI_USER, 0); - } - - switch (kret) - { - case -RT_ENOENT: - case -ECHILD: - sysret = -ESRCH; - break; - case -RT_EINVAL: - sysret = -EINVAL; - break; - case -RT_ENOSYS: - sysret = -ENOSYS; - break; - - /** - * kill() never returns ENOMEM, so return normally to caller. - * IEEE Std 1003.1-2017 says the kill() function is successful - * if the process has permission to send sig to any of the - * processes specified by pid. - */ - case -RT_ENOMEM: - default: - sysret = 0; - } - return sysret; -} - -/** - * @brief Retrieves the process ID of the calling process. - * - * This system call returns the process ID (PID) of the calling process. The PID is a - * unique identifier assigned by the operating system to each running process. It can - * be used to refer to or manipulate the process in subsequent system calls. - * - * @return The PID of the calling process. On failure, returns `-1` and sets `errno` - * to indicate the error (although this function typically does not fail). - * - * @note The PID returned by this function is a positive integer, and it remains - * unique during the lifetime of the process. This value can be used to manage - * the process, including sending signals or querying process information. - * - * @warning This function does not take any arguments and is typically used when - * a process needs to obtain its own PID for management or logging purposes. - * - * @see getppid(), getuid(), getgid() - */ -sysret_t sys_getpid(void) -{ - return lwp_getpid(); -} - -/** - * @brief Retrieves the parent process ID of the calling process. - * - * This system call returns the process ID (PID) of the parent process of the calling process. - * The parent process is the process that created the calling process, typically through a - * system call like `fork()`. This information can be useful for process management and - * for determining the hierarchy of processes. - * - * @return The PID of the parent process. If the calling process has no parent (for example, - * the init process), it typically returns `1` as the PID of the system's "root" process. - * On failure, returns `-1` and sets `errno` to indicate the error (although this function - * typically does not fail). - * - * @note The `getppid()` function is commonly used when a process needs to know which - * process is responsible for it, or for managing relationships between parent and child processes. - * - * @warning This function does not take any arguments and is typically used when the - * process requires information about its parent. It should not be confused with `getpid()`, - * which retrieves the PID of the calling process itself. - * - * @see getpid(), getuid(), getgid() - */ -sysret_t sys_getppid(void) -{ - rt_lwp_t process; - - process = lwp_self(); - if (process->parent == RT_NULL) - { - LOG_E("%s: process %d has no parent process", __func__, lwp_to_pid(process)); - return 0; - } - else - { - return lwp_to_pid(process->parent); - } -} - -/** - * @brief Retrieves the priority of a process or process group. - * - * This system call returns the priority of a process or a process group, depending on - * the value of the `which` argument. - * - * @param which The entity for which the priority is being requested. Possible values include: - * - `PRIO_PROCESS`: Retrieves the priority of the process specified by `who`. - * - `PRIO_PGRP`: Retrieves the priority of the process group specified by `who`. (Currently not supported.) - * - `PRIO_USER`: Retrieves the priority of the user specified by `who` (the user ID). (Currently not supported.) - * @param who The ID of the process, process group, or user. The meaning of this parameter - * depends on the value of `which`: - * - When `which` is `PRIO_PROCESS`, `who` is the process ID (PID) of the process. - * - When `which` is `PRIO_PGRP`(Currently not supported.), `who` is the process group ID (PGID). - * - When `which` is `PRIO_USER`(Currently not supported.), `who` is the user ID (UID) of the user whose - * priority is being queried. - * @return On success, returns the priority value (a negative integer indicates a lower - * priority). - * - * @warning Ensure that `which` and `who` are correctly specified to avoid errors. - * Invalid values for `which` or `who` may result in an error, and the function - * will return `-1`. - * - * @see setpriority(), getpid(), getppid() - */ -sysret_t sys_getpriority(int which, id_t who) -{ - long prio = 0xff; - - if (which == PRIO_PROCESS) - { - struct rt_lwp *lwp = RT_NULL; - - lwp_pid_lock_take(); - lwp = lwp_from_pid_locked(who); - - if (lwp) - { - rt_thread_t thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); - prio = RT_SCHED_PRIV(thread).current_priority; - } - - lwp_pid_lock_release(); - } - - return prio; -} - -/** - * @brief Sets the priority of a process, process group, or user. - * - * This system call sets the priority of a process, process group, or user, depending - * on the value of the `which` argument. The priority is used by the operating system - * for scheduling processes. Lower numerical values represent higher priority in many - * systems, and the priority range may be system-specific. - * - * @param which The entity for which the priority is being set. Possible values include: - * - `PRIO_PROCESS`: Sets the priority of the process specified by `who`. - * - `PRIO_PGRP`: Sets the priority of the process group specified by `who`.(Currently not supported.) - * - `PRIO_USER`: Sets the priority of the user specified by `who` (the user ID).(Currently not supported.) - * @param who The ID of the process, process group, or user for which the priority is being set. - * The meaning of this parameter depends on the value of `which`: - * - When `which` is `PRIO_PROCESS`, `who` is the process ID (PID) of the process. - * - When `which` is `PRIO_PGRP`(Currently not supported.) , `who` is the process group ID (PGID). - * - When `which` is `PRIO_USER`(Currently not supported.) , `who` is the user ID (UID) of the user whose priority - * is being set. - * @param prio The priority value to set. - * @return On success, returns `0`. On failure, returns `-1`. - * - * @note This function modifies the priority of the specified process, process group, or user. - * The priority value is system-specific and may vary based on the system's scheduling - * policies. Ensure that the specified priority is within the acceptable range. - * - * @warning Ensure that `which`, `who`, and `prio` are valid. Invalid values for these - * parameters can result in errors, and the system call will return `-1`. - * - * @see getpriority(), getpid(), getppid() - */ -sysret_t sys_setpriority(int which, id_t who, int prio) -{ - if (which == PRIO_PROCESS) - { - struct rt_lwp *lwp = RT_NULL; - - lwp_pid_lock_take(); - lwp = lwp_from_pid_locked(who); - - if (lwp && prio >= 0 && prio < RT_THREAD_PRIORITY_MAX) - { - rt_list_t *list; - rt_thread_t thread; - for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next) - { - thread = rt_list_entry(list, struct rt_thread, sibling); - rt_thread_control(thread, RT_THREAD_CTRL_RESET_PRIORITY, &prio); - } - lwp_pid_lock_release(); - return 0; - } - else - { - lwp_pid_lock_release(); - } - } - - return -1; -} - -/** - * @brief Creates a semaphore. - * - * This system call creates a new semaphore with the specified `name` and initializes - * its value. The semaphore is used for synchronizing access to shared resources in - * concurrent programming. The semaphore's behavior is defined by the value (`value`) - * and any flags (`flag`) that specify additional properties or settings. - * - * @param name The name of the semaphore. This is a string used to identify the semaphore - * in the system. The name must be unique if the system requires it for reference. - * @param value The initial value of the semaphore. The value typically represents the - * number of available resources or tokens. The semaphore will be initialized - * with this value. - * @param flag The flag that specifies the attributes or properties of the semaphore. - * Flags can be used to define various characteristics of the semaphore, - * such as whether it is binary or counting, whether it is shared, etc. - * - * @return On success, returns a handle or reference to the created semaphore. On failure, - * returns `NULL`. - * - * @note Semaphores are commonly used in multithreading and multiprocessing environments - * to synchronize access to shared resources and ensure proper order of execution. - * The behavior of the semaphore depends on its type and the system's implementation. - * - * @warning Ensure that the `name` is unique and appropriate for your system's naming - * conventions. Invalid values for `value` or `flag` may lead to errors in semaphore - * creation. - * - * @see sem_wait(), sem_post(), sem_destroy() - */ -rt_sem_t sys_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) -{ - int len = 0; - char *kname = RT_NULL; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return RT_NULL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return RT_NULL; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return RT_NULL; - } - - rt_sem_t sem = rt_sem_create(kname, value, flag); - if (lwp_user_object_add(lwp_self(), (rt_object_t)sem) != 0) - { - rt_sem_delete(sem); - sem = NULL; - } - - kmem_put(kname); - - return sem; -} - -/** - * @brief Deletes a semaphore and releases associated resources. - * - * This system call deletes an existing semaphore identified by the given handle `sem`. - * It releases any resources associated with the semaphore and ensures that it is no longer - * available for further synchronization operations. After deletion, the semaphore handle - * is invalid and should not be used. - * - * @param sem The semaphore handle to be deleted. This handle is obtained when the semaphore - * is created using `sys_sem_create()` or similar functions. - * - * @return On success, returns `0`. On failure, returns error code. - * - * @note Deleting a semaphore is a critical operation that should be performed only when - * the semaphore is no longer needed and no tasks are currently waiting or posting - * to it. Deleting a semaphore while it is still in use may lead to undefined behavior. - * - * @warning Ensure that no tasks are using or blocking on the semaphore before deleting it. - * Using a deleted semaphore will result in undefined behavior and potential system errors. - * - * @see sys_sem_create(), sem_wait(), sem_post() - */ -sysret_t sys_sem_delete(rt_sem_t sem) -{ - return lwp_user_object_delete(lwp_self(), (rt_object_t)sem); -} - -/** - * @brief Attempts to take (acquire) a semaphore. - * - * This system call attempts to acquire a semaphore, blocking the calling task until the - * semaphore becomes available or the specified timeout expires. If the semaphore is already - * available, the calling task will immediately take it and proceed. If the semaphore is not - * available, the task will block until the semaphore becomes available or the timeout period - * has elapsed. The `time` parameter specifies the maximum amount of time the task will block - * waiting for the semaphore. - * - * @param sem The semaphore handle to be taken. This handle is obtained when the semaphore - * is created using `sys_sem_create()` or similar functions. - * @param time The maximum time to wait for the semaphore to become available. If the semaphore - * is not acquired within this time period, the function will return with an error. - * A value of `0` means no waiting (non-blocking), while a negative value may indicate - * an infinite wait, depending on the system's implementation. - * - * @return On success, returns `0` if the semaphore was successfully taken. On failure, returns error code. - * - * @warning Ensure that the semaphore handle is valid and has been properly created before - * calling this function. If the semaphore is deleted or invalid, the behavior of - * this function is undefined. - * - * @see sys_sem_create(), sys_sem_delete(), sem_post() - */ -sysret_t sys_sem_take(rt_sem_t sem, rt_int32_t time) -{ - return rt_sem_take_interruptible(sem, time); -} - -/** - * @brief Releases a semaphore and wakes up any waiting tasks. - * - * This system call releases a semaphore, incrementing its value and allowing any tasks - * that are blocked (waiting) on the semaphore to proceed. If there are tasks waiting for - * the semaphore, one of them will be unblocked and allowed to take the semaphore. - * The release operation does not block the calling task, and it will return immediately. - * - * @param sem The semaphore handle to be released. This handle is obtained when the semaphore - * is created using `sys_sem_create()` or similar functions. - * - * @return On success, returns `0`. On failure, returns error code. - * - * @warning Ensure that the semaphore handle is valid before calling this function. If the semaphore - * is deleted or invalid, the behavior of this function is undefined. - * - * @see sys_sem_create(), sys_sem_delete(), sys_sem_take() - */ -sysret_t sys_sem_release(rt_sem_t sem) -{ - return rt_sem_release(sem); -} - -/** - * @brief Creates a mutex. - * - * This system call creates a new mutex with the specified `name` and initializes it - * with the given `flag`. The mutex is used for synchronizing access to shared resources - * between tasks. Mutexes are typically used to ensure that only one task can access - * a critical section of code or a resource at a time. The `flag` parameter allows - * for setting certain properties of the mutex, such as whether it is recursive or whether - * it can be used by multiple tasks. - * - * @param name The name of the mutex. This is a string used to uniquely identify the mutex - * within the system. The name must be unique if the system requires it. - * @param flag The flag that specifies the attributes or properties of the mutex. Flags can - * define various mutex behaviors, such as whether the mutex is recursive (allows - * the same task to lock it multiple times) or whether it can be shared across - * different parts of the system. - * - * @return On success, returns a handle to the created mutex. On failure, returns `-RT_NULL`. - * - * @note Mutexes are typically used to prevent race conditions and ensure mutual exclusion - * when multiple tasks or threads are accessing shared resources. - * - * @warning Ensure that the `name` is unique and appropriate according to the system's - * naming conventions. Invalid values for `flag` or `name` may result in errors. - * - * @see sys_mutex_delete(), sys_mutex_lock(), sys_mutex_unlock() - */ -rt_mutex_t sys_mutex_create(const char *name, rt_uint8_t flag) -{ - int len = 0; - char *kname = RT_NULL; - rt_mutex_t mutex = RT_NULL; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return RT_NULL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return RT_NULL; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return RT_NULL; - } - - mutex = rt_mutex_create(kname, flag); - if (mutex == RT_NULL) - return RT_NULL; - - if (lwp_user_object_add(lwp_self(), (rt_object_t)mutex) != 0) - { - rt_mutex_delete(mutex); - mutex = RT_NULL; - } - - kmem_put(kname); - - return mutex; -} - -/** - * @brief Deletes a mutex and releases associated resources. - * - * This system call deletes an existing mutex identified by the given handle `mutex`. - * It releases any resources associated with the mutex and ensures that it is no longer - * available for further synchronization operations. After deletion, the mutex handle - * becomes invalid and should not be used. This operation also ensures that no tasks - * can block or attempt to lock the mutex after it has been deleted. - * - * @param mutex The mutex handle to be deleted. This handle is obtained when the mutex - * is created using `sys_mutex_create()` or similar functions. - * - * @return On success, returns `0`. On failure, returns error code. - * - * @warning Ensure that the mutex handle is valid before calling this function. Deleting - * an already deleted or invalid mutex will result in undefined behavior. - * - * @see sys_mutex_create(), sys_mutex_lock(), sys_mutex_unlock() - */ -sysret_t sys_mutex_delete(rt_mutex_t mutex) -{ - return lwp_user_object_delete(lwp_self(), (rt_object_t)mutex); -} - -/** - * @brief Attempts to acquire (lock) a mutex. - * - * This system call attempts to acquire a mutex, blocking the calling task until the - * mutex becomes available or the specified timeout expires. If the mutex is available, - * the calling task will immediately acquire it and proceed. If the mutex is already locked - * by another task, the calling task will block until the mutex is released or the timeout - * period elapses. The `time` parameter specifies the maximum amount of time the task will - * block while waiting for the mutex. - * - * @param mutex The mutex handle to be locked. This handle is obtained when the mutex is - * created using `sys_mutex_create()` or similar functions. - * @param time The maximum time to wait for the mutex to become available. If the mutex - * is not acquired within this time period, the function will return an error. - * A value of `0` means no waiting (non-blocking), while a negative value may - * indicate an infinite wait. - * - * @return On success, returns `0` if the mutex was successfully acquired. On failure, returns error code. - * - * @warning Ensure that the mutex handle is valid and has been properly created before - * calling this function. If the mutex is deleted or invalid, the behavior of - * this function is undefined. - * - * @see sys_mutex_create(), sys_mutex_delete(), sys_mutex_unlock() - */ -sysret_t sys_mutex_take(rt_mutex_t mutex, rt_int32_t time) -{ - return rt_mutex_take_interruptible(mutex, time); -} - -/** - * @brief Releases (unlocks) a mutex. - * - * This system call releases a mutex that was previously acquired (locked) by the calling task. - * If any other task is waiting for the mutex, one of them will be unblocked and allowed to - * acquire the mutex. The release operation does not block the calling task, and it will return - * immediately after unlocking the mutex. - * - * @param mutex The mutex handle to be released. This handle is obtained when the mutex is - * created using `sys_mutex_create()` or similar functions. - * - * @return On success, returns `0`. On failure, returns error code. - * - * @warning Ensure that the mutex handle is valid before calling this function. Attempting - * to release a mutex that has not been locked or is already released can result - * in undefined behavior. - * - * @see sys_mutex_create(), sys_mutex_delete(), sys_mutex_take() - */ -sysret_t sys_mutex_release(rt_mutex_t mutex) -{ - return rt_mutex_release(mutex); -} - -#ifdef ARCH_MM_MMU -/** - * @brief Adjusts the end of the data segment (heap) of the calling process. - * - * This system call changes the location of the program break, which defines the end of - * the data segment (heap) of the calling process. It provides a unified interface for adjusting - * the heap size. The `addr` parameter specifies the new location for the program break. - * - * @param addr The new program break location. If `addr` is `NULL`, the current program - * break is returned. If a valid address is provided, the program break will - * be adjusted to this address. The address must be page-aligned, and changes - * must stay within the process's allowed address space. - * - * @return On success, returns the new program break. - * - * @note This function is typically used to manage heap space in a process. The program - * break adjustment can impact memory allocations and deallocations in the heap. - * - * @warning The `addr` must be a valid address within the process's allocated memory - * space. Attempting to set an invalid program break address may result in - * undefined behavior or memory corruption. - * - * @see malloc(), free(), sys_sbrk() - */ -rt_base_t sys_brk(void *addr) -{ - return lwp_brk(addr); -} - -/** - * @brief Maps a file or device into memory. - * - * This system call implements the `mmap2` system call, which maps a file or device into memory. - * It provides a way for processes to access file contents directly in memory, bypassing - * the need for explicit read or write operations. This function supports advanced memory - * mapping options such as shared or private mappings, specific protection flags, and - * offset alignment. - * - * @param addr The starting address for the memory mapping. If `NULL`, the kernel chooses - * the address. If not `NULL`, the address must be page-aligned and meet - * platform-specific alignment constraints. - * @param length The length of the memory mapping in bytes. This value is rounded up to - * the nearest page size. - * @param prot The memory protection flags. Possible values include: - * - `PROT_READ`: Pages can be read. - * - `PROT_WRITE`: Pages can be written. - * - `PROT_EXEC`: Pages can be executed. - * - `PROT_NONE`: Pages cannot be accessed. - * These flags can be combined using bitwise OR (`|`). - * @param flags Flags that determine the type and behavior of the mapping. Possible values include: - * - `MAP_SHARED`: Updates are visible to other processes that map this file. - * - `MAP_PRIVATE`: Updates are not visible to other processes and are not written to the file. - * - `MAP_FIXED`: Use the exact address specified in `addr`. - * - `MAP_ANONYMOUS`: The mapping is not backed by a file and uses zero-initialized memory. - * @param fd The file descriptor of the file to map. If `MAP_ANONYMOUS` is set in `flags`, - * `fd` is ignored and should be set to `-1`. - * @param pgoffset The offset in the file where the mapping starts, specified in pages (not bytes). - * This allows finer-grained control over the starting point of the mapping. - * - * @return On success, returns a pointer to the mapped memory region. - * - * @warning Ensure that the combination of `addr`, `length`, `prot`, `flags`, and `fd` is valid. - * Improper use may lead to undefined behavior or memory access violations. - * - * @see mmap(), munmap(), msync() - */ -void *sys_mmap2(void *addr, size_t length, int prot, - int flags, int fd, size_t pgoffset) -{ - sysret_t rc = 0; - long offset = 0; - - /* aligned for user addr */ - if ((rt_base_t)addr & ARCH_PAGE_MASK) - { - if (flags & MAP_FIXED) - rc = -EINVAL; - else - { - offset = (char *)addr - (char *)RT_ALIGN_DOWN((rt_base_t)addr, ARCH_PAGE_SIZE); - length += offset; - addr = (void *)RT_ALIGN_DOWN((rt_base_t)addr, ARCH_PAGE_SIZE); - } - } - - if (rc == 0) - { - /* fix parameter passing (both along have same effect) */ - if (fd == -1 || flags & MAP_ANONYMOUS) - { - fd = -1; - /* MAP_SHARED has no effect and treated as nothing */ - flags &= ~MAP_SHARED; - flags |= MAP_PRIVATE | MAP_ANONYMOUS; - } - rc = (sysret_t)lwp_mmap2(lwp_self(), addr, length, prot, flags, fd, pgoffset); - } - - return rc < 0 ? (char *)rc : (char *)rc + offset; -} - -/** - * @brief Unmaps a memory region previously mapped with mmap or mmap2. - * - * This system call implements the `munmap` system call, which removes a mapping - * for a region of memory that was previously created using `mmap` or `mmap2`. - * - * @param addr The starting address of the memory region to unmap. This address - * must be page-aligned and refer to a region previously mapped. - * @param length The length of the memory region to unmap, in bytes. This value - * is rounded up to the nearest page size internally if needed. - * - * @return On success, returns `0`. On failure, returns error code. - * - * @warning - * - Ensure the specified memory region corresponds to a valid, active mapping. - * Providing invalid parameters may result in undefined behavior. - * - Unmapping a region that is still in use by another thread or process can - * cause concurrency issues or data corruption. - * - * @see mmap(), mmap2(), msync() - */ -sysret_t sys_munmap(void *addr, size_t length) -{ - return lwp_munmap(lwp_self(), addr, length); -} - -/** - * @brief Changes the size or location of an existing memory mapping. - * - * This system call implements the `mremap` system call, allowing the resizing - * or relocation of a previously created memory mapping. It is typically used - * to dynamically adjust memory allocation for mapped regions without unmapping - * and remapping them explicitly. - * - * @param old_address The starting address of the existing memory mapping to be resized - * or relocated. This must be the address returned by a previous - * `mmap` or `mremap` call. - * @param old_size The current size of the memory mapping, in bytes. It must match - * the size of the original mapping. - * @param new_size The new desired size of the memory mapping, in bytes. The size will - * be rounded up to the nearest page size if necessary. - * @param flags Options to control the behavior of the remapping. Possible values include: - * - `MREMAP_MAYMOVE`: Allows the kernel to move the mapping to a new - * address if the current region cannot be resized in place. - * - `MREMAP_FIXED`: Requires the mapping to be relocated to the - * specified `new_address`. This flag must be used with caution. - * @param new_address If `MREMAP_FIXED` is set in `flags`, this specifies the address - * for the new mapping. Otherwise, it is ignored. - * - * @return On success, returns a pointer to the resized or relocated memory mapping. - * - * @warning - * - Ensure that the `old_address` and `old_size` correspond to a valid, existing mapping. - * - * @see mmap(), munmap(), msync() - */ -void *sys_mremap(void *old_address, size_t old_size, - size_t new_size, int flags, void *new_address) -{ - return lwp_mremap(lwp_self(), old_address, old_size, new_size, flags, new_address); -} - -sysret_t sys_madvise(void *addr, size_t len, int behav) -{ - return -ENOSYS; -} -#endif - -/** - * @brief Creates an event object for inter-thread or inter-process communication. - * - * @param name A string representing the name of the event. If `NULL`, the event will - * be created without a name. Named events can be identified and accessed - * globally if supported by the system. - * @param flag Specifies the behavior of the event. Possible values include: - * - `RT_IPC_FLAG_FIFO`: Events are handled in a first-in, first-out order. - * - `RT_IPC_FLAG_PRIO`: Events are handled in priority order. - * - * @return On success, returns a handle (`rt_event_t`) to the created event. On failure, - * returns `-RT_NULL` to indicate that the event could not be created. - * - * @note - * - The event object must be explicitly deleted using `sys_event_delete` when it is - * no longer needed to free system resources. - * - * @warning - * - Ensure that sufficient system resources (e.g., memory) are available to create - * the event. If resources are insufficient, the function will fail. - * - Named events can potentially lead to naming conflicts if multiple events with - * the same name are created. Use unique names to avoid such issues. - * - * @see sys_event_delete(), sys_event_send(), sys_event_recv() - */ -rt_event_t sys_event_create(const char *name, rt_uint8_t flag) -{ - int len = 0; - rt_event_t event = RT_NULL; - char *kname = RT_NULL; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return RT_NULL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return RT_NULL; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return RT_NULL; - } - - event = rt_event_create(kname, flag); - if (lwp_user_object_add(lwp_self(), (rt_object_t)event) != 0) - { - rt_event_delete(event); - event = NULL; - } - - kmem_put(kname); - - return event; -} - -/** - * @brief Deletes a system event object. - * - * This system call removes the specified system event object, releasing - * any resources associated with it. After deletion, the event object - * should not be used. - * - * @param[in] event The handle to the event object to be deleted. - * Must be a valid `rt_event_t` object. - * - * @return sysret_t Returns a status code indicating the result of the - * operation: - * - `0` if the operation was successful. - * - An appropriate error code otherwise. - * - * @note Ensure that the event is no longer being accessed by any - * threads or tasks before calling this function. - * - * @warning Deleting an event that is in use or invalid may lead to - * undefined behavior. - * @see sys_event_create(), sys_event_send(), sys_event_recv() - */ -sysret_t sys_event_delete(rt_event_t event) -{ - return lwp_user_object_delete(lwp_self(), (rt_object_t)event); -} - -/** - * @brief Sends an event to the specified event object. - * - * This system call sends an event to the specified event object, setting - * the specified bits in the event object's set. The event can be used - * to signal other threads or tasks that a particular condition has - * occurred. - * - * @param[in] event The handle to the event object to which the event - * will be sent. Must be a valid `rt_event_t` object. - * @param[in] set The bits to set in the event object's set. The bits - * are specified as a bitmask, where each bit represents - * a different event condition. - * - * @return sysret_t Returns a status code indicating the result of the - * operation: - * - `0` if the operation was successful. - * - An appropriate error code otherwise. - * - * @note The event object must be created before sending events to it. - * - * @warning Ensure that the event object is valid and has been created - * before calling this function. - * @see sys_event_create(), sys_event_recv() - */ -sysret_t sys_event_send(rt_event_t event, rt_uint32_t set) -{ - return rt_event_send(event, set); -} - -/** - * @brief Receives an event from the specified event object. - * - * This system call waits for an event to be received from the specified - * event object. The function blocks until the specified event bits are - * set in the event object's set or the specified timeout period has - * elapsed. If the event is received, the function returns the set of - * bits that were set in the event object. - * - * @param[in] event The handle to the event object from which the event - * will be received. Must be a valid `rt_event_t` object. - * @param[in] set The bits to wait for in the event object's set. The - * bits are specified as a bitmask, where each bit - * represents a different event condition. - * @param[in] opt The options for receiving the event. Possible values - * include: - * - `EV_EVENT_ANY`: Wait for any of the specified bits - * to be set. - * - `EV_EVENT_ALL`: Wait for all of the specified bits - * to be set. - * @param[in] timeout The maximum time to wait for the event to be - * received, in milliseconds. A value of `0` means - * no waiting (non-blocking), while a negative value - * may indicate an infinite wait. - * @param[out] recved A pointer to a variable that will receive the set - * of bits that were set in the event object. If the - * event is received, this variable will be updated - * with the set of bits. - * - * @return sysret_t Returns a status code indicating the result of the - * operation: - * - `0` if the operation was successful. - * - An appropriate error code otherwise. - * - * @note The event object must be created before receiving events from it. - * - * @warning Ensure that the event object is valid and has been created - * before calling this function. - * @see sys_event_create(), sys_event_send() - */ -sysret_t sys_event_recv(rt_event_t event, - rt_uint32_t set, - rt_uint8_t opt, - rt_int32_t timeout, - rt_uint32_t *recved) -{ - int ret = 0; - rt_uint32_t krecved; - - if ((recved != NULL) && !lwp_user_accessable((void *)recved, sizeof(rt_uint32_t *))) - { - return -EFAULT; - } - - ret = rt_event_recv(event, set, opt, timeout, &krecved); - if ((ret == RT_EOK) && recved) - { - lwp_put_to_user((void *)recved, &krecved, sizeof(rt_uint32_t *)); - } - - return ret; -} - -/** - * @brief Creates a mailbox for inter-thread or inter-process communication. - * - * This system call creates a mailbox object with the specified name and size. - * The mailbox is used to exchange messages between threads or tasks in a - * synchronized manner. The mailbox can be used to send and receive messages - * of a specified size. - * - * @param[in] name A string representing the name of the mailbox. If `NULL`, - * the mailbox will be created without a name. Named mailboxes - * can be identified and accessed globally if supported by the system. - * @param[in] size The size of the mailbox, which determines the maximum number - * of messages that can be stored in the mailbox at any given time. - * @param[in] flag The behavior of the mailbox. Possible values include: - * - `RT_IPC_FLAG_FIFO`: Messages are handled in a first-in, first-out order. - * - `RT_IPC_FLAG_PRIO`: Messages are handled in priority order. - * - * @return rt_mailbox_t Returns a handle to the created mailbox object. - * On failure, returns `-RT_NULL`. - * - * @note - * - The mailbox object must be explicitly deleted using `sys_mb_delete` when it is - * no longer needed to free system resources. - * - * @warning - * - Ensure that sufficient system resources (e.g., memory) are available to create - * the mailbox. If resources are insufficient, the function will fail. - * - Named mailboxes can potentially lead to naming conflicts if multiple mailboxes - * with the same name are created. Use unique names to avoid such issues. - * - * @see sys_mb_delete(), sys_mb_send(), sys_mb_recv() - */ -rt_mailbox_t sys_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) -{ - int len = 0; - rt_mailbox_t mb = RT_NULL; - char *kname = RT_NULL; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return RT_NULL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return RT_NULL; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return RT_NULL; - } - - mb = rt_mb_create(kname, size, flag); - if (lwp_user_object_add(lwp_self(), (rt_object_t)mb) != 0) - { - rt_mb_delete(mb); - mb = NULL; - } - - kmem_put(kname); - - return mb; -} - -/** - * @brief Deletes a mailbox object. - * - * This system call deletes the specified mailbox object, releasing any resources - * associated with it. After deletion, the mailbox object should not be used. - * - * @param[in] mb The handle to the mailbox object to be deleted. - * Must be a valid `rt_mailbox_t` object. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0` if the operation was successful. - * - An appropriate error code otherwise. - * - * @note Ensure that the mailbox is no longer being accessed by any threads or tasks - * before calling this function. - * - * @warning Deleting a mailbox that is in use or invalid may lead to undefined behavior. - * - * @see sys_mb_create(), sys_mb_send(), sys_mb_recv() - */ -sysret_t sys_mb_delete(rt_mailbox_t mb) -{ - return lwp_user_object_delete(lwp_self(), (rt_object_t)mb); -} - -/** - * @brief Sends a message to a mailbox object. - * - * This system call posts a message (a single value) to the specified mailbox. - * If the mailbox is full, the function will return an error code immediately without waiting time. - * - * @param[in] mb The handle to the mailbox object where the message - * will be sent. Must be a valid `rt_mailbox_t` object. - * @param[in] value The value to be sent to the mailbox. Typically, this - * is a pointer or an integral value that represents - * the message content. - * - * @return sysret_t Returns a status code indicating the result of the - * operation: - * - `0` if the message was successfully sent. - * - `-RT_EFULL` if the mailbox is full. - * - An appropriate error code for other failures. - * - * @note Ensure the mailbox has been properly initialized before calling - * this function. Sending messages to an uninitialized or invalid - * mailbox may result in undefined behavior. - * - * @warning If the mailbox is full and the function blocks, ensure - * proper handling to avoid potential deadlocks. - * - * @see sys_mb_create(), sys_mb_recv(), sys_mb_send_wait() - */ -sysret_t sys_mb_send(rt_mailbox_t mb, rt_ubase_t value) -{ - return rt_mb_send(mb, value); -} - -/** - * @brief Sends a message to a mailbox object with a timeout. - * - * This system call attempts to post a message (a single value) to the specified mailbox. - * If the mailbox is full, the function will wait for a specified timeout period - * for space to become available. If the timeout expires before the message is sent, - * the function returns an error. - * - * @param[in] mb The handle to the mailbox object where the message - * will be sent. Must be a valid `rt_mailbox_t` object. - * @param[in] value The value to be sent to the mailbox. Typically, this - * is a pointer or an integral value representing the - * message content. - * @param[in] timeout The maximum time to wait for space to become available - * in the mailbox, in milliseconds. - * - a negative value can be used to wait indefinitely. - * - `0` can be used for non-blocking behavior. - * - * @return sysret_t Returns a status code indicating the result of the - * operation: - * - `0` if the message was successfully sent. - * - `-RT_ETIMEOUT` if the operation timed out. - * - `-RT_EFULL` if the mailbox is full and `timeout` is `0`. - * - An appropriate error code for other failures. - * - * @note Ensure the mailbox has been properly initialized before calling this function. - * Passing an uninitialized or invalid mailbox handle may result in undefined behavior. - * - * @warning Using a negative value without appropriate logic may lead to indefinite blocking, - * potentially causing deadlocks. - * @see sys_mb_send() - */ -sysret_t sys_mb_send_wait(rt_mailbox_t mb, - rt_ubase_t value, - rt_int32_t timeout) -{ - return rt_mb_send_wait(mb, value, timeout); -} - -/** - * @brief Receives a message from a mailbox with a timeout. - * - * This system call attempts to receive a message from the specified mailbox. - * If the mailbox is empty, the function will wait for a specified timeout - * period for a message to arrive. If no message is received within the timeout, - * an error is returned. - * - * @param[in] mb The handle to the mailbox object from which the message - * is to be received. Must be a valid `rt_mailbox_t` object. - * @param[out] value Pointer to a variable where the received message will - * be stored. Must not be NULL. - * @param[in] timeout The maximum time to wait for a message, in milliseconds. - * - a negative value: Wait indefinitely until a message is available. - * - `0`: Non-blocking mode. If no message is available, return immediately. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The message was successfully received. - * - `-RT_ETIMEOUT`: The operation timed out before a message was received. - * - Other error codes may indicate failures. - * - * @note Ensure the mailbox is properly initialized and the `value` pointer - * is valid before calling this function. Passing an invalid mailbox - * or NULL `value` pointer may lead to undefined behavior. - * - * @warning Using a negative value without proper safeguards may cause - * indefinite blocking, potentially resulting in deadlocks. - */ -sysret_t sys_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) -{ - int ret = 0; - rt_ubase_t *kvalue; - - if (!lwp_user_accessable((void *)value, sizeof(rt_ubase_t *))) - { - return -EFAULT; - } - - kvalue = kmem_get(sizeof(rt_ubase_t *)); - if (kvalue == RT_NULL) - { - return -ENOMEM; - } - - ret = rt_mb_recv(mb, (rt_ubase_t *)kvalue, timeout); - if (ret == RT_EOK) - { - lwp_put_to_user(value, kvalue, sizeof(rt_ubase_t *)); - } - - kmem_put(kvalue); - - return ret; -} - -rt_weak int syslog_ctrl(int type, char *buf, int len) -{ - return -EINVAL; -} - -/** - * @brief Sends a log message to the system log. - * - * This system call sends a message to the system log for recording or debugging purposes. - * - * @param[in] type The type of the log message, typically representing the severity - * or category (e.g., debug, info, warning, error). This should be - * a valid predefined log type. - * @param[in] buf A pointer to a buffer containing the log message. Must be a - * null-terminated string and a valid pointer. - * @param[in] len The length of the log message to be sent, excluding the null - * terminator. If the length exceeds the system log's limit, - * the message may be truncated. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The log message was successfully recorded. - * - Other error codes may indicate additional failures. - * - * @note Ensure the log type is a valid predefined value supported by the system. - * The buffer must remain valid and accessible during the execution of this function. - * - * @warning Sending excessively large or frequent log messages may impact system performance. - */ -sysret_t sys_syslog(int type, char *buf, int len) -{ - char *tmp; - int ret = -1; - - if (!lwp_user_accessable((void *)buf, len)) - { - return -EFAULT; - } - - tmp = (char *)rt_malloc(len); - if (!tmp) - { - return -ENOMEM; - } - - ret = syslog_ctrl(type, tmp, len); - lwp_put_to_user(buf, tmp, len); - rt_free(tmp); - - return ret; -} - -/** - * @brief Creates a message queue. - * - * This system call creates a new message queue with a specified name, message size, - * maximum number of messages, and associated flags. The message queue allows - * messages of a given size to be sent and received between tasks or threads. - * - * @param[in] name The name of the message queue. It should be a unique - * identifier and a null-terminated string. - * @param[in] msg_size The size of each message in the queue. This defines - * the maximum size for individual messages that can - * be sent or received. - * @param[in] max_msgs The maximum number of messages the queue can hold. - * If the queue is full, further send operations may - * block or return an error depending on the flags. - * @param[in] flag Flags that control the behavior of the message queue. - * This can specify whether the queue is blocking, - * non-blocking, or has other specific attributes. - * - * @return rt_mq_t Returns the handle to the created message queue, or `NULL` - * if the creation failed (e.g., due to invalid parameters - * or insufficient resources). - * - * @note Ensure that the message queue name is unique. The size of the messages - * and the number of messages should be chosen based on the application - * requirements. - * - * @warning Creating too many message queues or setting an overly large - * `max_msgs` may lead to resource exhaustion. - */ -rt_mq_t sys_mq_create(const char *name, - rt_size_t msg_size, - rt_size_t max_msgs, - rt_uint8_t flag) -{ - rt_mq_t mq = RT_NULL; - - int len = 0; - char *kname = RT_NULL; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return RT_NULL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return RT_NULL; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return RT_NULL; - } - - mq = rt_mq_create(kname, msg_size, max_msgs, flag); - if (lwp_user_object_add(lwp_self(), (rt_object_t)mq) != 0) - { - rt_mq_delete(mq); - mq = NULL; - } - - kmem_put(kname); - - return mq; -} - -/** - * @brief Deletes a message queue. - * - * This system call deletes the specified message queue and releases any resources - * associated with it. After calling this function, the message queue handle - * becomes invalid and should not be used. - * - * @param[in] mq The handle to the message queue to be deleted. - * Must be a valid `rt_mq_t` object. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The message queue was successfully deleted. - * - An appropriate error code for other failures. - * - * @note Ensure that no tasks or threads are using the message queue before - * deleting it to avoid undefined behavior or data loss. - * - * @warning Deleting an active message queue that is being used by tasks or - * threads may lead to resource leaks or corruption. Ensure proper - * synchronization before deletion. - */ -sysret_t sys_mq_delete(rt_mq_t mq) -{ - return lwp_user_object_delete(lwp_self(), (rt_object_t)mq); -} - -/** - * @brief Sends a message to a message queue. - * - * This system call sends a message to the specified message queue. The message - * is copied into the queue's buffer. If the queue is full, the behavior will - * depend on the flags set during queue creation (e.g., whether it is blocking - * or non-blocking). - * - * @param[in] mq The handle to the message queue where the message will be sent. - * Must be a valid `rt_mq_t` object. - * @param[in] buffer A pointer to the message data to be sent. This must not be NULL. - * @param[in] size The size of the message to be sent, in bytes. This should be - * less than or equal to the maximum message size defined when - * the queue was created. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The message was successfully sent to the queue. - * - An appropriate error code for other failures. - * - * @note Ensure that the message size does not exceed the maximum allowed message - * size when the queue was created. Passing an invalid queue handle or buffer - * may result in undefined behavior. - * - * @warning Sending messages to a full queue in blocking mode may cause the calling - * task or thread to block indefinitely if not properly handled. - */ -sysret_t sys_mq_send(rt_mq_t mq, void *buffer, rt_size_t size) -{ - int ret = 0; - void *kbuffer = RT_NULL; - - if (!lwp_user_accessable((void *)buffer, size)) - { - return -EFAULT; - } - - kbuffer = kmem_get(size); - if (kbuffer == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kbuffer, buffer, size) != size) - { - kmem_put(kbuffer); - return -EINVAL; - } - - ret = rt_mq_send(mq, kbuffer, size); - - kmem_put(kbuffer); - - return ret; -} - -/** - * @brief Sends an urgent message to a message queue. - * - * This system call sends a message to the specified message queue with higher priority, - * meaning it will be placed at the front of the queue, bypassing normal message - * order. The message is copied into the queue's buffer. If the queue is full, - * the behavior will depend on the flags set during queue creation (e.g., whether - * it is blocking or non-blocking). - * - * @param[in] mq The handle to the message queue where the message will be sent. - * Must be a valid `rt_mq_t` object. - * @param[in] buffer A pointer to the message data to be sent. This must not be NULL. - * @param[in] size The size of the message to be sent, in bytes. This should be - * less than or equal to the maximum message size defined when - * the queue was created. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The urgent message was successfully sent to the queue. - * - `-RT_EFULL`: The queue is full and the message could not be sent. - * - An appropriate error code for other failures. - * - * @note Ensure that the message size does not exceed the maximum allowed message - * size when the queue was created. The urgent message will be processed before - * other normal messages in the queue. - * - * @warning Sending urgent messages to a full queue in blocking mode may cause the - * calling task or thread to block indefinitely if not properly handled. - */ -sysret_t sys_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size) -{ - int ret = 0; - void *kbuffer = RT_NULL; - - if (!lwp_user_accessable((void *)buffer, size)) - { - return -EFAULT; - } - - kbuffer = kmem_get(size); - if (kbuffer == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kbuffer, buffer, size) != size) - { - kmem_put(kbuffer); - return -EINVAL; - } - - ret = rt_mq_urgent(mq, kbuffer, size); - - kmem_put(kbuffer); - - return ret; -} - -/** - * @brief Receives a message from a message queue. - * - * This system call attempts to receive a message from the specified message queue. - * The message is copied into the provided buffer. If no message is available - * in the queue, the function will block for a specified timeout period before - * returning. If the timeout expires without receiving a message, an error is returned. - * - * @param[in] mq The handle to the message queue from which the message - * will be received. Must be a valid `rt_mq_t` object. - * @param[out] buffer A pointer to the buffer where the received message will - * be stored. Must not be NULL and large enough to hold the - * message. - * @param[in] size The size of the buffer, in bytes. This should be at least - * the size of a message in the queue. - * @param[in] timeout The maximum time to wait for a message, in milliseconds. - * - a negative value: Wait indefinitely until a message is available. - * - `0`: Non-blocking mode. If no message is available, return immediately. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The message was successfully received. - * - `-RT_ETIMEOUT`: The operation timed out before a message was received. - * - Other error codes may indicate additional failures. - * - * @note Ensure the buffer is large enough to store the message received from the queue. - * If the buffer is too small, the function may fail or behave unexpectedly. - * - * @warning Using a negative value without proper safeguards may cause indefinite - * blocking, potentially resulting in deadlocks if no message is received. - */ -sysret_t sys_mq_recv(rt_mq_t mq, - void *buffer, - rt_size_t size, - rt_int32_t timeout) -{ - int ret = 0; - void *kbuffer = RT_NULL; - - if (!lwp_user_accessable((void *)buffer, size)) - { - return -EFAULT; - } - - kbuffer = kmem_get(size); - if (kbuffer == RT_NULL) - { - return -ENOMEM; - } - - ret = rt_mq_recv(mq, kbuffer, size, timeout); - if (ret > 0) - lwp_put_to_user((void *)buffer, (void *)kbuffer, ret); - - kmem_put(kbuffer); - - return ret; -} - -static void timer_timeout_callback(void *parameter) -{ - rt_sem_t sem = (rt_sem_t)parameter; - rt_sem_release(sem); -} - -/** - * @brief Creates a timer. - * - * This system call creates a timer that can be used to trigger events or - * actions after a specified timeout or interval. The timer will operate in a - * real-time manner and can be configured to trigger once or repeatedly. The - * created timer can be started, stopped, or deleted as required. - * - * @param[in] name The name of the timer. It should be a unique string identifier. - * The name is used for debugging and logging purposes. - * @param[in] data A pointer to user-defined data that will be passed to the timer's - * callback function when it is triggered. This may be used to carry - * context or other necessary information for the callback. - * @param[in] period The timer period in milliseconds. The timer will trigger every - * `period` milliseconds, or after the specified timeout. - * @param[in] flag Flags that control the behavior of the timer. These can specify - * whether the timer is one-shot (triggers once) or periodic - * (triggers repeatedly). - * - * @return rt_timer_t Returns the handle to the created timer, or `NULL` if the creation failed. - * - * @note The timer callback function must be implemented to handle the event triggered - * by the timer. Ensure that the `data` parameter, if used, is properly handled - * in the callback. - * - * @warning Ensure the timer's period and flags are configured correctly to avoid - * undesired behavior, especially if the timer is periodic. - */ -rt_timer_t sys_rt_timer_create(const char *name, - void *data, - rt_tick_t time, - rt_uint8_t flag) -{ - int len = 0; - char *kname = RT_NULL; - rt_timer_t timer = RT_NULL; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return RT_NULL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return RT_NULL; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return RT_NULL; - } - - timer = rt_timer_create(kname, timer_timeout_callback, (void *)data, time, flag); - if (lwp_user_object_add(lwp_self(), (rt_object_t)timer) != 0) - { - rt_timer_delete(timer); - timer = NULL; - } - - kmem_put(kname); - - return timer; -} - -/** - * @brief Deletes a timer. - * - * This system call deletes the specified timer and releases any resources - * associated with it. Once the timer is deleted, its handle becomes invalid, - * and any further operations on the timer should be avoided. - * - * @param[in] timer The handle to the timer to be deleted. Must be a valid `rt_timer_t` object. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The timer was successfully deleted. - * - Other error codes may indicate additional failures. - * - * @note Ensure that the timer is not active or being used before deleting it to avoid - * any unexpected behavior or resource leaks. - * - * @warning Deleting an active timer may lead to undefined behavior, especially - * if the timer is in the middle of triggering or executing its callback. - */ -sysret_t sys_rt_timer_delete(rt_timer_t timer) -{ - return lwp_user_object_delete(lwp_self(), (rt_object_t)timer); -} - -/** - * @brief Starts a timer. - * - * This system call starts the specified timer, causing it to begin counting down - * based on its configured period. Once the timer reaches the set period, it triggers the - * associated callback function. The behavior depends on whether the timer is configured - * as one-shot or periodic. - * - * @param[in] timer The handle to the timer to be started. Must be a valid `rt_timer_t` object. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The timer was successfully started. - * - Other error codes may indicate additional failures. - * - * @note Ensure that the timer has been created and is in a valid state before attempting to start it. - * - * @warning Starting a timer that is already running may lead to undefined behavior. Ensure that the - * timer is stopped or not in use before starting it. - */ -sysret_t sys_rt_timer_start(rt_timer_t timer) -{ - return rt_timer_start(timer); -} - -/** - * @brief Stops a timer. - * - * This system call stops the specified timer, halting its countdown and - * preventing it from triggering further callbacks. If the timer is periodic, - * stopping it will prevent further periodic triggers until it is started again. - * - * @param[in] timer The handle to the timer to be stopped. Must be a valid `rt_timer_t` object. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The timer was successfully stopped. - * - Other error codes may indicate additional failures. - * - * @note Ensure that the timer is in a valid state before attempting to stop it. - * Stopping an inactive or already stopped timer may not have any effect. - * - * @warning Stopping a timer that is actively triggering or in use may disrupt its expected - * behavior. Ensure proper synchronization or state management when stopping - * the timer during active use. - */ -sysret_t sys_rt_timer_stop(rt_timer_t timer) -{ - return rt_timer_stop(timer); -} - -/** - * @brief Controls various properties of a timer. - * - * This system call provides control over various aspects of a timer, such as - * modifying its configuration, querying its status, or changing its behavior. - * The specific behavior is determined by the command (`cmd`) and any associated arguments (`arg`). - * - * @param[in] timer The handle to the timer to be controlled. Must be a valid `rt_timer_t` object. - * @param[in] cmd The command to execute. The meaning of this parameter depends on the command value. - * Common commands might include modifying the timer period, changing its callback, - * or querying its current state. - * @param[in] arg A pointer to any additional arguments needed for the command. The type and content - * of this argument depend on the specific command being executed. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The timer control operation was successful. - * - Other error codes may indicate additional failures. - * - * @note Ensure that the provided command (`cmd`) is valid for the specific timer implementation. - * Incorrect commands or arguments may lead to unexpected behavior or errors. - * - * @warning Using invalid or unsupported commands may cause undefined behavior or crashes. - */ -sysret_t sys_rt_timer_control(rt_timer_t timer, int cmd, void *arg) -{ - return rt_timer_control(timer, cmd, arg); -} - -/* MUSL compatible */ -struct ksigevent -{ - union sigval sigev_value; - int sigev_signo; - int sigev_notify; - int sigev_tid; -}; - -/* to protect unsafe implementation in current rt-smart toolchain */ -RT_STATIC_ASSERT(sigevent_compatible, offsetof(struct ksigevent, sigev_tid) == offsetof(struct sigevent, sigev_notify_function)); - -/** - * @brief Creates a per-process timer. - * - * This system call creates a new timer associated with the specified clock, and - * initializes the timer with the provided event notification attributes. - * Once created, the timer can be started, stopped, or controlled as needed. - * The timer will trigger when the specified expiration time or interval is reached. - * - * @param[in] clockid The clock to be used for the timer. Common clock values include: - * - `CLOCK_REALTIME`: System real-time clock. - * - `CLOCK_MONOTONIC`: Monotonic clock that cannot be set and is not affected by system time changes. - * - Other clock IDs can be used depending on the platform and requirements. - * @param[in] sevp A pointer to a `sigevent` structure that specifies how the process - * should be notified when the timer expires. This can include notification - * types such as signal delivery, thread notification, or posting to a queue. - * @param[out] timerid A pointer to a `timer_t` variable where the created timer's ID will be stored. - * The timer ID will be used for subsequent timer operations (e.g., starting, stopping). - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The timer was successfully created. - * - Other error codes may indicate additional failures. - * - * @warning Ensure that the provided `sigevent` structure is properly configured, as invalid or - * unsupported notification types may result in unexpected behavior. - */ -sysret_t sys_timer_create(clockid_t clockid, struct sigevent * restrict sevp, timer_t * restrict timerid) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - struct sigevent sevp_k; - timer_t timerid_k; - int utimer; - - if (sevp == NULL) - { - sevp_k.sigev_notify = SIGEV_SIGNAL; - sevp_k.sigev_signo = SIGALRM; - sevp = &sevp_k; - } - else - { - /* clear extra bytes if any */ - if (sizeof(struct ksigevent) < sizeof(struct sigevent)) - memset(&sevp_k, 0, sizeof(sevp_k)); - - /* musl passes `struct ksigevent` to kernel, we shoule only get size of that bytes */ - if (!lwp_get_from_user(&sevp_k, (void *)sevp, sizeof(struct ksigevent))) - { - return -EINVAL; - } - } - - ret = _SYS_WRAP(timer_create(clockid, &sevp_k, &timerid_k)); - - if (ret != -RT_ERROR) - { - utimer = (rt_ubase_t)timerid_k; - if (!lwp_put_to_user(sevp, (void *)&sevp_k, sizeof(struct ksigevent)) || - !lwp_put_to_user(timerid, (void *)&utimer, sizeof(utimer))) - ret = -EINVAL; - } -#else - ret = _SYS_WRAP(timer_create(clockid, sevp, timerid)); -#endif - return ret; -} - -/** - * @brief Deletes a timer. - * - * This system call deletes the specified timer and releases any resources associated - * with it. Once the timer is deleted, it can no longer be used, and any further - * operations on the timer (such as starting or stopping) will result in an error. - * - * @param[in] timerid The ID of the timer to be deleted. This is the timer handle - * returned by `sys_timer_create`. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The timer was successfully deleted. - * - Other error codes may indicate additional failures. - * - * @note After calling this function, the timer ID becomes invalid, and it should - * not be used in any further operations. - * - * @warning Make sure the timer is not active or in use when attempting to delete it, - * as deleting an active timer may cause unexpected behavior or resource leaks. - */ -sysret_t sys_timer_delete(timer_t timerid) -{ - int ret = timer_delete(timerid); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Changes the time or interval of an existing timer. - * - * This system call modifies the expiration time or interval of a previously created - * timer. It can either set a new expiration time for the timer or update the - * interval for periodic timers. The timer can be started or modified based on - * the flags provided. The current (old) timer settings can be retrieved if - * requested. - * - * @param[in] timerid The ID of the timer to modify. This is the timer handle - * returned by `sys_timer_create`. - * @param[in] flags Flags that control the behavior of the operation. Common - * values include: - * - `TIMER_ABSTIME`: Specifies that `new_value` contains - * an absolute time. Otherwise, it is treated as relative. - * @param[in] new_value A pointer to the `itimerspec` structure specifying - * the new time settings for the timer. The structure includes: - * - `it_value`: The initial expiration time (relative or absolute). - * - `it_interval`: The period for periodic timers. - * @param[out] old_value A pointer to an `itimerspec` structure where the previous - * timer settings will be stored. If NULL, the old value is ignored. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The timer time/interval was successfully updated. - * - Other error codes may indicate additional failures. - * - * @warning Modifying a timer that is currently active can cause timing-related issues - * if not handled correctly. Make sure the timer is in an appropriate state - * before making changes. - */ -sysret_t sys_timer_settime(timer_t timerid, int flags, - const struct itimerspec * restrict new_value, - struct itimerspec * restrict old_value) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - struct itimerspec new_value_k; - struct itimerspec old_value_k; - - if (!lwp_get_from_user(&new_value_k, (void *)new_value, sizeof(*new_value)) || - (old_value && !lwp_get_from_user(&old_value_k, (void *)old_value, sizeof(*old_value)))) - { - return -EFAULT; - } - - ret = timer_settime(timerid, flags, &new_value_k, &old_value_k); - lwp_put_to_user(old_value, (void *)&old_value_k, sizeof old_value_k); - -#else - ret = timer_settime(timerid, flags, new_value, old_value); -#endif - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Retrieves the current time and interval of a timer. - * - * This system call fetches the current expiration time (`it_value`) and interval (`it_interval`) - * of a previously created timer. It allows the caller to determine the current state of the timer, - * whether it is one-shot or periodic, and the remaining time before expiration. - * - * @param[in] timerid The ID of the timer to query. This is the timer handle - * returned by `sys_timer_create`. - * @param[out] curr_value A pointer to an `itimerspec` structure where the current timer values - * will be stored. This structure includes: - * - `it_value`: The remaining time until the timer expires. - * - `it_interval`: The interval between subsequent expirations (for periodic timers). - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The current time/interval was successfully retrieved. - * - Other error codes may indicate additional failures. - * - * @warning Ensure that the timer ID is valid before calling this function, as invalid timer IDs - * will result in errors. - */ -sysret_t sys_timer_gettime(timer_t timerid, struct itimerspec *curr_value) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - - struct itimerspec curr_value_k; - lwp_get_from_user(&curr_value_k, (void *)curr_value, sizeof curr_value_k); - ret = timer_gettime(timerid, &curr_value_k); - lwp_put_to_user(curr_value, (void *)&curr_value_k, sizeof curr_value_k); -#else - ret = timer_gettime(timerid, curr_value); -#endif - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Retrieves the overrun count for a periodic timer. - * - * This system call retrieves the number of times a periodic timer has "overrun." An overrun occurs - * when a timer expires before the previous expiration has been acknowledged or handled. For periodic - * timers, this indicates how many times the timer's expiration has been missed due to delayed processing - * or handling. - * - * @param[in] timerid The ID of the timer to query. This is the timer handle - * returned by `sys_timer_create`. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - `0`: The overrun count was successfully retrieved. - * - Other error codes may indicate additional failures. - * - * @warning Ensure that the timer ID is valid before calling this function, as invalid timer IDs - * will result in errors. - */ -sysret_t sys_timer_getoverrun(timer_t timerid) -{ - int ret = 0; - ret = timer_getoverrun(timerid); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Creates a new thread. - * - * This system call creates a new thread within the calling process. The newly created - * thread starts executing the function specified by the `arg` argument, which typically - * contains the necessary arguments or function pointer for the thread's execution. - * - * @param[in] arg An array of arguments that will be passed to the function executed - * by the new thread. This can include function pointers, structures, - * or any necessary data the thread will need to execute its work. - * - * @return rt_thread_t Returns a handle to the newly created thread. If the thread - * creation fails, `NULL` is returned. - * - * @warning Ensure that the system has sufficient resources to create a new thread. - * Thread creation failures can occur if system limits are exceeded or resources - * are unavailable. - */ -rt_thread_t sys_thread_create(void *arg[]) -{ - void *user_stack = 0; - struct rt_lwp *lwp = 0; - rt_thread_t thread = RT_NULL; - int tid = 0; - - lwp = rt_thread_self()->lwp; - lwp_ref_inc(lwp); -#ifdef ARCH_MM_MMU - user_stack = lwp_map_user(lwp, 0, (size_t)arg[3], 0); - if (!user_stack) - { - goto fail; - } - if ((tid = lwp_tid_get()) == 0) - { - goto fail; - } - thread = rt_thread_create((const char *)arg[0], - _crt_thread_entry, - (void *)arg[2], - ALLOC_KERNEL_STACK_SIZE, - (rt_uint8_t)(size_t)arg[4], - (rt_uint32_t)(rt_size_t)arg[5]); - if (!thread) - { - goto fail; - } - -#ifdef RT_USING_SMP - RT_SCHED_CTX(thread).bind_cpu = lwp->bind_cpu; -#endif - thread->cleanup = lwp_cleanup; - thread->user_entry = (void (*)(void *))arg[1]; - thread->user_stack = (void *)user_stack; - thread->user_stack_size = (rt_size_t)arg[3]; -#else - rt_uint32_t kstack_size = (rt_uint32_t)arg[7]; - if (kstack_size < ALLOC_KERNEL_STACK_SIZE_MIN) - { - /* When kstack size is 0, the default size of the kernel stack is used */ - kstack_size = kstack_size ? ALLOC_KERNEL_STACK_SIZE_MIN : ALLOC_KERNEL_STACK_SIZE; - } - else if (kstack_size > ALLOC_KERNEL_STACK_SIZE_MAX) - { - kstack_size = ALLOC_KERNEL_STACK_SIZE_MAX; - } - - user_stack = (void *)arg[3]; - if ((!user_stack) || ((rt_uint32_t)arg[6] == RT_NULL)) - { - goto fail; - } - - if ((tid = lwp_tid_get()) == 0) - { - goto fail; - } - - thread = rt_thread_create((const char *)arg[0], _crt_thread_entry, (void *)arg[2], kstack_size, (rt_uint8_t)(size_t)arg[5], (rt_uint32_t)arg[6]); - if (!thread) - { - goto fail; - } - thread->cleanup = lwp_cleanup; - thread->user_entry = (void (*)(void *))arg[1]; - thread->user_stack = (void *)user_stack; - thread->user_stack_size = (uint32_t)arg[4]; - rt_memset(thread->user_stack, '#', thread->user_stack_size); -#endif /* ARCH_MM_MMU */ - - thread->lwp = (void *)lwp; - thread->tid = tid; - lwp_tid_set_thread(tid, thread); - - if (lwp->debug) - { - rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void *)0); - } - - LWP_LOCK(lwp); - rt_list_insert_after(&lwp->t_grp, &thread->sibling); - LWP_UNLOCK(lwp); - - return thread; - -fail: - lwp_tid_put(tid); - if (lwp) - { - lwp_ref_dec(lwp); - } - return RT_NULL; -} - -#ifdef ARCH_MM_MMU - -long _sys_clone(void *arg[]) -{ - struct rt_lwp *lwp = 0; - rt_thread_t thread = RT_NULL; - rt_thread_t self = RT_NULL; - int tid = 0; - rt_err_t err; - - unsigned long flags = 0; - void *user_stack = RT_NULL; - int *new_tid = RT_NULL; - void *tls = RT_NULL; - /* - musl call flags (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND - | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS - | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED); - */ - - /* check args */ - if (!lwp_user_accessable(arg, sizeof(void *[SYS_CLONE_ARGS_NR]))) - { - return -EFAULT; - } - - flags = (unsigned long)(size_t)arg[0]; - if ((flags & (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD | CLONE_SYSVSEM)) != (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD | CLONE_SYSVSEM)) - { - return -EINVAL; - } - - user_stack = arg[1]; - new_tid = (int *)arg[2]; - tls = (void *)arg[3]; - - if ((flags & CLONE_PARENT_SETTID) == CLONE_PARENT_SETTID) - { - if (!lwp_user_accessable(new_tid, sizeof(int))) - { - return -EFAULT; - } - } - - self = rt_thread_self(); - lwp = self->lwp; - lwp_ref_inc(lwp); - if (!user_stack) - { - SET_ERRNO(EINVAL); - goto fail; - } - if ((tid = lwp_tid_get()) == 0) - { - SET_ERRNO(ENOMEM); - goto fail; - } - - thread = rt_thread_create(self->parent.name, - RT_NULL, - RT_NULL, - self->stack_size, - RT_SCHED_PRIV(self).init_priority, - RT_SCHED_PRIV(self).init_tick); - if (!thread) - { - goto fail; - } - -#ifdef RT_USING_SMP - RT_SCHED_CTX(self).bind_cpu = lwp->bind_cpu; -#endif - thread->cleanup = lwp_cleanup; - thread->user_entry = RT_NULL; - thread->user_stack = RT_NULL; - thread->user_stack_size = 0; - thread->lwp = (void *)lwp; - thread->tid = tid; - - if ((flags & CLONE_SETTLS) == CLONE_SETTLS) - { - thread->thread_idr = tls; - } - if ((flags & CLONE_PARENT_SETTID) == CLONE_PARENT_SETTID) - { - *new_tid = (int)(tid); - } - if ((flags & CLONE_CHILD_CLEARTID) == CLONE_CHILD_CLEARTID) - { - thread->clear_child_tid = (int *)arg[4]; - } - - if (lwp->debug) - { - rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void *)0); - } - - LWP_LOCK(lwp); - rt_list_insert_after(&lwp->t_grp, &thread->sibling); - LWP_UNLOCK(lwp); - - /* copy origin stack */ - lwp_memcpy(thread->stack_addr, self->stack_addr, thread->stack_size); - lwp_tid_set_thread(tid, thread); - arch_set_thread_context(arch_clone_exit, - (void *)((char *)thread->stack_addr + thread->stack_size), - user_stack, &thread->sp); - /* new thread never reach there */ - rt_thread_startup(thread); - return (long)tid; -fail: - err = GET_ERRNO(); - RT_ASSERT(err < 0); - - lwp_tid_put(tid); - if (thread) - { - rt_thread_delete(thread); - } - if (lwp) - { - lwp_ref_dec(lwp); - } - return (long)err; -} - -/** - * @brief Creates a new process or thread (clone). - * - * This system call creates a new process or thread by duplicating the calling process. - * The new process/thread begins execution by calling the function specified in the - * `arg[]` array, which typically contains the necessary arguments or function pointer. - * It is used to implement process/thread creation in the system and is often a lower-level - * operation in process management. - * - * @param[in] arg An array of arguments passed to the new process or thread. This could - * include function pointers, structures, or any necessary data the - * new process/thread will need to execute its work. - * - * @return long Returns a status code or the process/thread ID of the newly created - * process/thread. On success, it may return a positive value (such as a - * thread ID). On failure, a negative value indicating the error is returned. - * - * @warning Be cautious when using this function as improper management of process/thread - * creation can lead to resource exhaustion, deadlocks, or other synchronization issues. - */ -rt_weak long sys_clone(void *arg[]) -{ - return _sys_clone(arg); -} - -static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src) -{ -#ifdef ARCH_MM_MMU - dst->end_heap = src->end_heap; -#endif - dst->lwp_type = src->lwp_type; - dst->text_entry = src->text_entry; - dst->text_size = src->text_size; - dst->data_entry = src->data_entry; - dst->data_size = src->data_size; - dst->args = src->args; - dst->background = src->background; - dst->tty = src->tty; - - /* terminal API */ - dst->term_ctrlterm = src->term_ctrlterm; - - rt_memcpy(dst->cmd, src->cmd, RT_NAME_MAX); - if (src->exe_file) - { - if (dst->exe_file) - { - rt_free(dst->exe_file); - } - dst->exe_file = strndup(src->exe_file, DFS_PATH_MAX); - } - - rt_memcpy(&dst->signal.sig_action, &src->signal.sig_action, sizeof(dst->signal.sig_action)); - rt_memcpy(&dst->signal.sig_action_mask, &src->signal.sig_action_mask, sizeof(dst->signal.sig_action_mask)); - rt_memcpy(&dst->signal.sig_action_nodefer, &src->signal.sig_action_nodefer, sizeof(dst->signal.sig_action_nodefer)); - rt_memcpy(&dst->signal.sig_action_onstack, &src->signal.sig_action_onstack, sizeof(dst->signal.sig_action_onstack)); - rt_memcpy(&dst->signal.sig_action_restart, &dst->signal.sig_action_restart, sizeof(dst->signal.sig_action_restart)); - rt_memcpy(&dst->signal.sig_action_siginfo, &dst->signal.sig_action_siginfo, sizeof(dst->signal.sig_action_siginfo)); - rt_memcpy(&dst->signal.sig_action_nocldstop, &dst->signal.sig_action_nocldstop, sizeof(dst->signal.sig_action_nocldstop)); - rt_memcpy(&dst->signal.sig_action_nocldwait, &dst->signal.sig_action_nocldwait, sizeof(dst->signal.sig_action_nocldwait)); - rt_strcpy(dst->working_directory, src->working_directory); -} - -static int lwp_copy_files(struct rt_lwp *dst, struct rt_lwp *src) -{ - struct dfs_fdtable *dst_fdt; - struct dfs_fdtable *src_fdt; - - src_fdt = &src->fdt; - dst_fdt = &dst->fdt; - /* init fds */ - dst_fdt->fds = rt_calloc(src_fdt->maxfd, sizeof(void *)); - if (dst_fdt->fds) - { - struct dfs_file *d_s; - int i; - - dst_fdt->maxfd = src_fdt->maxfd; - - dfs_file_lock(); - /* dup files */ - for (i = 0; i < src_fdt->maxfd; i++) - { - d_s = fdt_get_file(src_fdt, i); - if (d_s) - { - dst_fdt->fds[i] = d_s; - d_s->ref_count++; - } - } - dfs_file_unlock(); - return 0; - } - return -RT_ERROR; -} - -sysret_t _sys_fork(void) -{ - int tid = 0; - sysret_t falival = 0; - struct rt_lwp *lwp = RT_NULL; - struct rt_lwp *self_lwp = RT_NULL; - rt_thread_t thread = RT_NULL; - rt_thread_t self_thread = RT_NULL; - void *user_stack = RT_NULL; - rt_processgroup_t group; - - /* new lwp */ - lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID); - if (!lwp) - { - SET_ERRNO(ENOMEM); - goto fail; - } - - /* new tid */ - if ((tid = lwp_tid_get()) == 0) - { - SET_ERRNO(ENOMEM); - goto fail; - } - - /* user space init */ - if (lwp_user_space_init(lwp, 1) != 0) - { - SET_ERRNO(ENOMEM); - goto fail; - } - - self_lwp = lwp_self(); - - /* copy address space of process from this proc to forked one */ - if (lwp_fork_aspace(lwp, self_lwp) != 0) - { - SET_ERRNO(ENOMEM); - goto fail; - } - - /* copy lwp struct data */ - lwp_struct_copy(lwp, self_lwp); - - /* copy files */ - if (lwp_copy_files(lwp, self_lwp) != 0) - { - SET_ERRNO(ENOMEM); - goto fail; - } - - /* create thread */ - self_thread = rt_thread_self(); - - thread = rt_thread_create(self_thread->parent.name, - RT_NULL, - RT_NULL, - self_thread->stack_size, - RT_SCHED_PRIV(self_thread).init_priority, - RT_SCHED_PRIV(self_thread).init_tick); - if (!thread) - { - SET_ERRNO(ENOMEM); - goto fail; - } - - thread->cleanup = self_thread->cleanup; - thread->user_entry = self_thread->user_entry; - thread->user_stack = self_thread->user_stack; - thread->user_stack_size = self_thread->user_stack_size; - thread->signal.sigset_mask = self_thread->signal.sigset_mask; - thread->thread_idr = self_thread->thread_idr; - thread->clear_child_tid = self_thread->clear_child_tid; - thread->lwp = (void *)lwp; - thread->tid = tid; - - LWP_LOCK(self_lwp); - /* add thread to lwp process */ - rt_list_insert_after(&lwp->t_grp, &thread->sibling); - LWP_UNLOCK(self_lwp); - - lwp_children_register(self_lwp, lwp); - - /* set pgid and sid */ - group = lwp_pgrp_find(lwp_pgid_get_byprocess(self_lwp)); - if (group) - { - lwp_pgrp_insert(group, lwp); - } - else - { - LOG_W("the process group of pid: %d cannot be found", lwp_pgid_get_byprocess(self_lwp)); - } - - /* copy kernel stack context from self thread */ - lwp_memcpy(thread->stack_addr, self_thread->stack_addr, self_thread->stack_size); - lwp_tid_set_thread(tid, thread); - - /* duplicate user objects */ - lwp_user_object_dup(lwp, self_lwp); - - user_stack = arch_get_user_sp(); - arch_set_thread_context(arch_fork_exit, - (void *)((char *)thread->stack_addr + thread->stack_size), - user_stack, &thread->sp); - - rt_thread_startup(thread); - return lwp_to_pid(lwp); -fail: - falival = GET_ERRNO(); - - if (tid != 0) - { - lwp_tid_put(tid); - } - if (thread) - { - rt_thread_delete(thread); - } - if (lwp) - { - lwp_ref_dec(lwp); - } - return falival; -} - -/* arm needs to wrap fork/clone call to preserved lr & caller saved regs */ - -/** - * @brief Creates a child process by duplicating the calling process. - * - * This system call creates a new child process by duplicating the calling process. The - * new child process is a copy of the parent process, except for the returned value. - * The child process starts executing from the point of the fork, but the return value - * differs between the parent and child: the parent receives the process ID (PID) of the - * child, and the child receives a return value of 0. - * - * @return sysret_t Returns a status code indicating the result of the operation: - * - A positive value (the child's PID) is returned to the parent. - * - A value of 0 is returned to the child process. - * - A negative value indicates an error (e.g., resource limits exceeded). - * - * @note This function is commonly used in operating systems to create new processes. - * - * @warning Be aware that improper management of child processes (such as failing to handle - * process termination or excessive forking) can lead to resource exhaustion or - * other system issues. Ensure proper process handling in the parent and child - * processes to avoid leaks and inconsistencies. - */ -rt_weak sysret_t sys_fork(void) -{ - return _sys_fork(); -} - -rt_weak sysret_t sys_vfork(void) -{ - return sys_fork(); -} - -#define _swap_lwp_data(lwp_used, lwp_new, type, member) \ - do \ - { \ - type tmp; \ - tmp = lwp_used->member; \ - lwp_used->member = lwp_new->member; \ - lwp_new->member = tmp; \ - } while (0) - -/** - * @brief Executes a program in the current process. - * - * This system call replaces the current process image with a new program specified by the - * `path` argument. It loads the program located at the given `path` and passes the arguments - * (`argv`) and environment variables (`envp`) to it. This effectively replaces the calling - * process with a new one, and if successful, it never returns. If there is an error, the current - * process continues to execute, and an error code is returned. - * - * @param[in] path The path to the executable file to be executed. This should be an - * absolute or relative file path to the program. - * @param[in] argv An array of arguments to pass to the new program. The first element - * (`argv[0]`) should typically be the name of the program, and the array - * should be terminated with a `NULL` pointer. - * @param[in] envp An array of environment variables to pass to the new program. This is - * typically in the form of `key=value` strings, with the array terminated - * by a `NULL` pointer. - * - * @return sysret_t Returns a status code: - * - `0`: The program was successfully executed (this value is - * never returned since the process is replaced). - * - Other error codes may indicate issues with the program execution - * - * @note If `execve` is successful, it does not return to the calling function. The process - * image is replaced by the new program. - */ -sysret_t sys_execve(const char *path, char * const argv[], char * const envp[]) -{ - rt_err_t error = -1; - size_t len; - struct rt_lwp *new_lwp = NULL; - struct rt_lwp *lwp; - int uni_thread; - rt_thread_t thread; - struct process_aux *aux; - struct lwp_args_info args_info; - char *kpath = RT_NULL; - - lwp = lwp_self(); - thread = rt_thread_self(); - uni_thread = 1; - - LWP_LOCK(lwp); - if (lwp->t_grp.prev != &thread->sibling) - { - uni_thread = 0; - } - if (lwp->t_grp.next != &thread->sibling) - { - uni_thread = 0; - } - LWP_UNLOCK(lwp); - - if (!uni_thread) - { - return -EINVAL; - } - - len = lwp_user_strlen(path); - if (len <= 0) - { - return -EFAULT; - } - - kpath = rt_malloc(len + 1); - if (!kpath) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kpath, (void *)path, len) != len) - { - rt_free(kpath); - return -EFAULT; - } - kpath[len] = '\0'; - - if (access(kpath, X_OK) != 0) - { - error = rt_get_errno(); - rt_free(kpath); - return (sysret_t)error; - } - - /* setup args */ - error = lwp_args_init(&args_info); - if (error) - { - rt_free(kpath); - return -ENOMEM; - } - - if (argv) - { - error = lwp_args_put_argv(&args_info, (void *)argv); - if (error) - { - error = -EFAULT; - goto quit; - } - } - - if (envp) - { - error = lwp_args_put_envp(&args_info, (void *)envp); - if (error) - { - error = -EFAULT; - goto quit; - } - } - - /* alloc new lwp to operation */ - new_lwp = lwp_create(LWP_CREATE_FLAG_NONE); - if (!new_lwp) - { - error = -ENOMEM; - goto quit; - } - - error = lwp_user_space_init(new_lwp, 0); - if (error != 0) - { - error = -ENOMEM; - goto quit; - } - - /* file is a script ? */ - path = kpath; - while (1) - { - error = lwp_args_load_script(&args_info, path); - if (error != 0) - { - break; - } - path = lwp_args_get_argv_0(&args_info); - } - - /* now load elf */ - if ((aux = lwp_argscopy(new_lwp, &args_info)) == NULL) - { - error = -ENOMEM; - goto quit; - } - error = lwp_load(path, new_lwp, RT_NULL, 0, aux); - if (error == RT_EOK) - { - int off = 0; - int last_backslash = 0; - - /* clear all user objects */ - lwp_user_object_clear(lwp); - - /* find last \ or / to get base name */ - while (1) - { - char c = path[off++]; - - if (c == '\0') - { - break; - } - if (c == '\\' || c == '/') - { - last_backslash = off; - } - } - - /** - * Set thread name and swap the data of lwp and new_lwp. - * Since no other threads can access the lwp field, it't uneccessary to - * take a lock here - */ - RT_ASSERT(rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling) == thread); - - strncpy(thread->parent.name, path + last_backslash, RT_NAME_MAX - 1); - strncpy(lwp->cmd, new_lwp->cmd, RT_NAME_MAX); - rt_free(lwp->exe_file); - lwp->exe_file = strndup(new_lwp->exe_file, DFS_PATH_MAX); - -#ifdef ARCH_MM_MMU - _swap_lwp_data(lwp, new_lwp, struct rt_aspace *, aspace); - - _swap_lwp_data(lwp, new_lwp, size_t, end_heap); -#endif - _swap_lwp_data(lwp, new_lwp, uint8_t, lwp_type); - _swap_lwp_data(lwp, new_lwp, void *, text_entry); - _swap_lwp_data(lwp, new_lwp, uint32_t, text_size); - _swap_lwp_data(lwp, new_lwp, void *, data_entry); - _swap_lwp_data(lwp, new_lwp, uint32_t, data_size); - - _swap_lwp_data(lwp, new_lwp, void *, args); - - lwp_thread_signal_detach(&thread->signal); - rt_memset(&thread->signal.sigset_mask, 0, sizeof(thread->signal.sigset_mask)); - - lwp_signal_detach(&lwp->signal); - lwp_signal_init(&lwp->signal); - - /* to do: clsoe files with flag CLOEXEC, recy sub-thread */ - - lwp_aspace_switch(thread); - - lwp_ref_dec(new_lwp); - arch_start_umode(lwp->args, - lwp->text_entry, - (void *)USER_STACK_VEND, - (char *)thread->stack_addr + thread->stack_size); - /* never reach here */ - } - error = -EINVAL; -quit: - if (kpath) - { - rt_free(kpath); - } - lwp_args_detach(&args_info); - if (new_lwp) - { - lwp_ref_dec(new_lwp); - } - return error; -} -#endif /* ARCH_MM_MMU */ - -/** - * @brief Deletes a thread. - * - * This system call is used to delete an existing thread. The specified thread is terminated, - * and its resources are released. If the thread is currently running, it will be forcefully - * terminated. The thread identifier (`thread`) refers to the thread that is to be deleted. - * - * @param[in] thread The identifier of the thread to be deleted. - * - * @return sysret_t Returns a status code: - * - `SYSRET_OK`: The thread was successfully deleted. - * - Other error codes may indicate additional failures. - * - * @note This function should be used carefully, as forcefully terminating a thread may - * lead to resource leaks or inconsistent state if the thread is performing critical - * operations at the time of termination. - * - * @warning Ensure that the thread being deleted has completed its necessary operations - * and that there are no outstanding resources or critical tasks before deleting - * it. Otherwise, it might lead to undefined behavior or resource leaks. - */ -sysret_t sys_thread_delete(rt_thread_t thread) -{ -#ifdef ARCH_MM_MMU - return rt_thread_delete(thread); -#else - sysret_t ret = 0; - - if (thread->parent.type != RT_Object_Class_Thread) - { - ret = -EINVAL; - goto __exit; - } - - ret = rt_thread_delete(thread); - - if (rt_thread_self() == thread) - { - rt_schedule(); - } - -__exit: - return ret; -#endif -} - -/** - * @brief Starts a previously created thread. - * - * This system call is used to start a thread that was created but has not yet started running. - * It initiates the thread's execution, allowing it to begin performing its assigned task. - * The `thread` parameter refers to the thread that is to be started. - * - * @param[in] thread The identifier of the thread to be started. - * - * @return sysret_t Returns a status code: - * - `0`: The thread was successfully started. - * - Other error codes may indicate additional failures. - * - * @warning Ensure that the thread has been correctly initialized and is in a valid state - * before calling this function to avoid undefined behavior. Improper initialization - * could lead to issues such as the thread not running as expected. - */ -sysret_t sys_thread_startup(rt_thread_t thread) -{ - return rt_thread_startup(thread); -} - -/** - * @brief Retrieves the identifier of the current thread. - * - * This system call returns the thread identifier of the currently executing thread. It allows - * a thread to obtain its own identifier, which can be useful for various thread management - * tasks such as self-identification, logging, or checking the thread's status. - * - * @return rt_thread_t The identifier of the current thread. - * If no thread is currently executing, a null or invalid thread ID - * might be returned depending on the system's implementation. - * - * @note This function is typically used when a thread needs to identify itself, especially - * in cases where thread management is performed dynamically or the thread identifier - * is required for synchronization or debugging purposes. - * - * @warning Be aware that in environments where there is no concept of threads or if the - * current context is not a thread (e.g., during interrupt handling or early system - * initialization), the return value might be invalid. - */ -rt_thread_t sys_thread_self(void) -{ - return rt_thread_self(); -} - -/* sys channel */ - -/** - * @brief Opens a communication channel. - * - * This system call is used to open a communication channel with a specified name and set of flags. - * The channel allows for inter-process or inter-thread communication, depending on the underlying system. - * The `name` parameter specifies the name of the channel, while the `flags` parameter allows - * configuration of the channel's behavior (e.g., read/write permissions, blocking or non-blocking mode). - * - * @param[in] name The name of the communication channel to be opened. - * @param[in] flags The flags to configure the behavior of the channel. These flags may - * define various properties such as access mode (e.g., read-only, write-only) - * or synchronization mode (e.g., blocking, non-blocking). - * - * @return sysret_t Returns a status code: - * - `0`: The channel was successfully opened. - * - Other error codes may indicate issues with the channel opening process. - * - * @warning Ensure that the correct flags are passed to configure the channel as required, - * as improper configuration might lead to access issues, data loss, or undefined behavior. - */ -sysret_t sys_channel_open(const char *name, int flags) -{ - rt_size_t ret = 0; - char *kname = RT_NULL; - int len = 0; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return -EFAULT; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return -EFAULT; - } - - ret = lwp_channel_open(FDT_TYPE_LWP, kname, flags); - - kmem_put(kname); - - return ret; -} - -/** - * @brief Closes an open communication channel. - * - * This system call is used to close an already open communication channel specified by the file descriptor `fd`. - * After closing, the channel can no longer be used for communication, and any resources associated with - * the channel will be released. This function is necessary for proper resource management, ensuring that - * system resources (e.g., memory or file handles) are freed when no longer needed. - * - * @param[in] fd The file descriptor of the communication channel to be closed. - * - * @return sysret_t Returns a status code: - * - `SYSRET_OK`: The channel was successfully closed. - * - Other error codes may indicate issues with the channel closing process. - * - * @note This function should be called after communication is finished and the channel is no longer - * needed, to release any system resources associated with it. - * - * @warning Calling this function on an invalid or already closed file descriptor may lead to - * undefined behavior or errors. Ensure that the file descriptor is valid and that the - * channel is not already closed before attempting to close it. - */ -sysret_t sys_channel_close(int fd) -{ - return lwp_channel_close(FDT_TYPE_LWP, fd); -} - -/** - * @brief Sends a message through a communication channel. - * - * This system call is used to send a message through a specified communication channel identified - * by the file descriptor `fd`. The message to be sent is provided in the `data` parameter. - * It allows inter-process or inter-thread communication by transmitting the given message over - * the open channel. - * - * @param[in] fd The file descriptor of the communication channel to send the message to. - * @param[in] data The message data to be sent. This parameter is typically a structure - * containing the message content and metadata. - * - * @return sysret_t Returns a status code: - * - `0`: The message was successfully sent. - * - Other error codes may indicate issues with the message sending process. - * - * @note Ensure the channel is open and properly configured for sending messages before - * calling this function. Additionally, confirm that the `data` structure is valid and - * initialized with the appropriate content. - * - * @warning Failure to verify the channel's readiness or the validity of the data may lead - * to errors, data loss, or undefined behavior. - */ -sysret_t sys_channel_send(int fd, rt_channel_msg_t data) -{ - rt_size_t ret = 0; - rt_channel_msg_t kdata = RT_NULL; - - if (!lwp_user_accessable((void *)data, sizeof(*data))) - { - return -EFAULT; - } - - kdata = kmem_get(sizeof(*data)); - if (kdata == RT_NULL) - return -ENOMEM; - - if (lwp_get_from_user(kdata, data, sizeof(*kdata)) != sizeof(*kdata)) - { - kmem_put(kdata); - return -EFAULT; - } - - ret = lwp_channel_send(FDT_TYPE_LWP, fd, kdata); - - kmem_put(kdata); - - return ret; -} - -/** - * @brief Sends a message through a communication channel and waits for a response with a timeout. - * - * This system call sends a message (`data`) through a specified communication channel identified by the file descriptor `fd`. - * It then waits for a response (`data_ret`) within the specified timeout period. This is a synchronous operation - * commonly used in request-response communication patterns between processes or threads. - * - * @param[in] fd The file descriptor of the communication channel to send the message to. - * @param[in] data The message data to be sent. This is typically a structure containing - * the message content and metadata. - * @param[out] data_ret The buffer to store the response message received from the channel. - * @param[in] time The timeout period (in milliseconds) to wait for the response. - * If set to a negative value, the function will wait indefinitely. - * - * @return sysret_t Returns a status code: - * - `0`: The message was successfully sent, and a response was received. - * - Other error codes may indicate issues with the communication process. - * - * @note This function combines sending and receiving operations into a single atomic action. - * It is useful for scenarios requiring synchronous communication with a defined timeout - * to handle cases where a response may not be immediately available. - * - * @warning Ensure that the channel is open and properly configured for bidirectional communication. - * Verify that the `data` structure is valid and initialized, and the `data_ret` buffer is large - * enough to store the expected response to avoid memory issues or data corruption. - */ -sysret_t sys_channel_send_recv_timeout(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time) -{ - rt_size_t ret = 0; - rt_channel_msg_t kdata = RT_NULL; - rt_channel_msg_t kdata_ret = RT_NULL; - - if (!lwp_user_accessable((void *)data, sizeof(*data))) - { - return -EFAULT; - } - - kdata = kmem_get(sizeof(*data)); - if (kdata == RT_NULL) - return -ENOMEM; - - if (lwp_get_from_user(kdata, data, sizeof(*kdata)) != sizeof(*kdata)) - { - kmem_put(kdata); - return -EFAULT; - } - - kdata_ret = kmem_get(sizeof(*data_ret)); - if (kdata_ret == RT_NULL) - return -ENOMEM; - - ret = lwp_channel_send_recv_timeout(FDT_TYPE_LWP, fd, kdata, kdata_ret, time); - - lwp_put_to_user(data_ret, kdata_ret, sizeof(*kdata_ret)); - kmem_put(kdata); - kmem_put(kdata_ret); - - return ret; -} - -/** - * @brief Sends a reply message through a communication channel. - * - * This system call is used to send a reply (`data`) through a communication channel identified - * by the file descriptor `fd`. It is typically called in response to a received request - * within a request-response communication pattern. The reply is sent to the requesting entity - * through the same channel. - * - * @param[in] fd The file descriptor of the communication channel to send the reply to. - * @param[in] data The reply message to be sent. This is typically a structure containing - * the reply content and metadata. - * - * @return sysret_t Returns a status code: - * - `0`: The reply was successfully sent. - * - Other error codes may indicate issues with the reply sending process. - * - * @note This function is usually called in a server or responder context, where a request - * is received, processed, and the result is sent back to the requester. Ensure that - * the channel is open and configured to send replies before calling this function. - * - * @warning Ensure the `data` structure is valid and properly initialized before sending. - * Sending invalid or corrupted data may lead to unexpected behavior or communication failures. - */ -sysret_t sys_channel_reply(int fd, rt_channel_msg_t data) -{ - rt_size_t ret = 0; - rt_channel_msg_t kdata = RT_NULL; - - if (!lwp_user_accessable((void *)data, sizeof(*data))) - { - return -EFAULT; - } - - kdata = kmem_get(sizeof(*data)); - if (kdata == RT_NULL) - return -ENOMEM; - - if (lwp_get_from_user(kdata, data, sizeof(*kdata)) != sizeof(*data)) - { - kmem_put(kdata); - return -EFAULT; - } - - ret = lwp_channel_reply(FDT_TYPE_LWP, fd, kdata); - - kmem_put(kdata); - - return ret; -} - -/** - * @brief Receives a message from a communication channel with a timeout. - * - * This system call attempts to receive a message from a specified communication channel identified - * by the file descriptor `fd`. The received message is stored in the `data` buffer. If no message - * is available within the specified timeout period, the function returns with a timeout status. - * - * @param[in] fd The file descriptor of the communication channel to receive the message from. - * @param[out] data The buffer to store the received message. This parameter is typically a - * structure containing the message content and metadata. - * @param[in] time The timeout period (in milliseconds) to wait for a message. - * A negative value indicates that the function will wait indefinitely. - * - * @return sysret_t Returns a status code: - * - `0`: A message was successfully received. - * - `-ETIMEDOUT`: The operation timed out before a message was received. - * - Other error codes may indicate issues with the message receiving process. - * - * @note This function is useful in scenarios where blocking indefinitely is not desirable, - * allowing the caller to specify a timeout for receiving messages. It is commonly - * used in event-driven or time-sensitive communication systems. - * - * @warning Ensure that the channel is open and configured for receiving messages before calling - * this function. The `data` buffer must be valid and large enough to store the expected - * message to avoid memory corruption or data loss. - */ -sysret_t sys_channel_recv_timeout(int fd, rt_channel_msg_t data, rt_int32_t time) -{ - rt_size_t ret = 0; - rt_channel_msg_t kdata = RT_NULL; - - kdata = kmem_get(sizeof(*data)); - if (kdata == RT_NULL) - return -ENOMEM; - - ret = lwp_channel_recv_timeout(FDT_TYPE_LWP, fd, kdata, time); - - lwp_put_to_user(data, kdata, sizeof(*kdata)); - kmem_put(kdata); - - return ret; -} - -static struct rt_semaphore critical_lock; - -static int critical_init(void) -{ - rt_sem_init(&critical_lock, "ct_lock", 1, RT_IPC_FLAG_FIFO); - return 0; -} -INIT_DEVICE_EXPORT(critical_init); - -/** - * @brief Enters a critical section to prevent context switching or interrupts. - * - * @note Critical sections are typically used to protect shared resources or perform - * non-interruptible operations. Ensure to exit the critical section as soon as - * possible by calling `sys_exit_critical` to avoid system performance degradation - * or deadlocks. - * - * @warning Failure to exit a critical section (e.g., due to an exception or missing - * `sys_exit_critical` call) may lead to system instability or a complete halt. - */ -void sys_enter_critical(void) -{ - rt_sem_take(&critical_lock, RT_WAITING_FOREVER); -} - -/** - * @brief Exits a critical section and restores the system's previous state. - * - * @note Exiting the critical section as soon as the protected operation is completed - * is essential to avoid performance degradation or system deadlocks. Ensure - * that every call to `sys_enter_critical` is matched with a corresponding - * `sys_exit_critical` call. - * - * @warning Calling this function without a prior `sys_enter_critical` may result - * in undefined behavior or system instability. Use carefully in nested - * critical sections and ensure proper tracking of critical section depth - * if required. - */ -void sys_exit_critical(void) -{ - rt_sem_release(&critical_lock); -} - -/* syscall: "sys_log" ret: "int" args: "const char*" "size" */ -static int __sys_log_enable = 0; -static int sys_log_enable(int argc, char **argv) -{ - if (argc == 1) - { - rt_kprintf("sys_log = %d\n", __sys_log_enable); - return 0; - } - else - { - __sys_log_enable = atoi(argv[1]); - } - - return 0; -} -MSH_CMD_EXPORT_ALIAS(sys_log_enable, sys_log, sys_log 1(enable) / 0(disable)); - -/** - * @brief Logs a message to the system logging mechanism. - * - * This system call writes a log message to the system log for diagnostic or informational purposes. - * The message is specified by the `log` parameter, and its size is given by the `size` parameter. - * The logging mechanism is typically used for tracking system events, debugging, or reporting errors. - * - * @param[in] log A pointer to the message to be logged. The message should be a valid character - * array or string. - * @param[in] size The size of the log message in bytes. This specifies the number of bytes to write - * from the `log` buffer. - * - * @return sysret_t Returns a status code: - * - `0`: The log message was successfully written. - * - Other error codes may indicate issues with the logging process. - * - * @note Ensure that the `log` pointer is valid and points to a properly initialized memory buffer. - * Truncation may occur if the logging system has a size limitation. Logging should not be used - * in performance-critical paths as it may introduce latency. - * - * @warning Passing a `NULL` pointer or an invalid `size` may lead to undefined behavior. Ensure the - * logging system is properly initialized before invoking this function. - */ -sysret_t sys_log(const char *log, int size) -{ - char *klog = RT_NULL; - rt_device_t console = RT_NULL; - - if (!lwp_user_accessable((void *)log, size)) - return -EFAULT; - - klog = kmem_get(size); - if (klog == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user((void *)klog, (void *)log, size) != size) - { - kmem_put(klog); - return -EINVAL; - } - - console = rt_console_get_device(); - - if (console && __sys_log_enable) - { - rt_device_write(console, -1, klog, size); - } - - kmem_put(klog); - - return 0; -} - -/** - * @brief Retrieves information about a file or directory. - * - * This system call obtains metadata about the specified file or directory and stores it in - * the `buf` structure. The metadata includes attributes such as file size, permissions, - * ownership, and timestamps. - * - * @param[in] file A pointer to the path of the file or directory to be queried. - * The path should be a null-terminated string. - * @param[out] buf A pointer to a `struct stat` structure where the file's metadata - * will be stored. This structure must be allocated by the caller. - * - * @return sysret_t Returns a status code: - * - `0`: The operation completed successfully, and the metadata - * has been written to `buf`. - * - Other error codes may indicate issues with the file path. - * - * @note The `file` path must be valid and accessible by the calling process. Ensure that - * the `buf` pointer points to a properly allocated memory region. - * - * @warning Passing a `NULL` pointer for `file` or `buf` may result in undefined behavior. - * Check all inputs before invoking this function to avoid potential issues. - */ -sysret_t sys_stat(const char *file, struct stat *buf) -{ - int ret = 0; - size_t len; - size_t copy_len; - char *copy_path; - struct stat statbuff = { 0 }; - - if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) - { - return -EFAULT; - } - - len = lwp_user_strlen(file); - if (len <= 0) - { - return -EFAULT; - } - - copy_path = (char *)rt_malloc(len + 1); - if (!copy_path) - { - return -ENOMEM; - } - - copy_len = lwp_get_from_user(copy_path, (void *)file, len); - if (copy_len == 0) - { - rt_free(copy_path); - return -EFAULT; - } - copy_path[copy_len] = '\0'; - - ret = _SYS_WRAP(stat(copy_path, &statbuff)); - rt_free(copy_path); - - if (ret == 0) - { - lwp_put_to_user(buf, &statbuff, sizeof statbuff); - } - - return ret; -} - -/** - * @brief Retrieves metadata about a file or symbolic link. - * - * This system call obtains metadata for the specified file or symbolic link and stores - * it in the `buf` structure. Unlike `sys_stat`, if the specified path refers to a - * symbolic link, this function retrieves information about the link itself, not the - * target it points to. - * - * @param[in] file A pointer to the path of the file or symbolic link to be queried. - * The path must be a null-terminated string. - * @param[out] buf A pointer to a `struct stat` structure where the metadata will be - * stored. This structure must be allocated by the caller. - * - * @return sysret_t Returns a status code: - * - `0`: The operation completed successfully, and the metadata - * has been written to `buf`. - * - Other error codes may indicate issues with the file path. - * - * @note This function is particularly useful for handling symbolic links when you want - * to get information about the link itself rather than the target file or directory. - * The `file` path must be valid and accessible by the calling process. - * - * @warning Passing a `NULL` pointer for `file` or `buf` may result in undefined behavior. - * Always check inputs for validity before invoking this function. - */ -sysret_t sys_lstat(const char *file, struct stat *buf) -{ - int ret = 0; - size_t len; - size_t copy_len; - char *copy_path; - struct stat statbuff = { 0 }; - - if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) - { - return -EFAULT; - } - - len = lwp_user_strlen(file); - if (len <= 0) - { - return -EFAULT; - } - - copy_path = (char *)rt_malloc(len + 1); - if (!copy_path) - { - return -ENOMEM; - } - - copy_len = lwp_get_from_user(copy_path, (void *)file, len); - if (copy_len == 0) - { - rt_free(copy_path); - return -EFAULT; - } - copy_path[copy_len] = '\0'; -#ifdef RT_USING_DFS_V2 - ret = _SYS_WRAP(dfs_file_lstat(copy_path, &statbuff)); -#else - ret = _SYS_WRAP(stat(copy_path, &statbuff)); -#endif - rt_free(copy_path); - - if (ret == 0) - { - lwp_put_to_user(buf, &statbuff, sizeof statbuff); - } - - return ret; -} - -sysret_t sys_notimpl(void) -{ - return -ENOSYS; -} - -uint32_t sys_hw_interrupt_disable(void) -{ - return rt_hw_interrupt_disable(); -} - -void sys_hw_interrupt_enable(uint32_t level) -{ - rt_hw_interrupt_enable(level); -} - -#ifdef ARCH_MM_MMU -/** - * @brief Allocates or retrieves a shared memory segment. - * - * This system call allocates a new shared memory segment or retrieves an existing one - * based on the specified `key`. Shared memory allows processes to communicate by - * sharing a region of memory. - * - * @param[in] key A unique identifier for the shared memory segment. If `key` matches - * an existing segment, it will be retrieved. If `create` is set and the - * segment does not exist, a new one will be created. - * @param[in] size The size (in bytes) of the shared memory segment. If creating a new - * segment, this specifies its size. If retrieving an existing segment, - * `size` is ignored. - * @param[in] create A flag indicating whether to create the segment if it does not exist: - * - `1`: Create the shared memory segment if it does not exist. - * - `0`: Only retrieve an existing segment. - * - * @return sysret_t Returns a status code: - * - `0`: The shared memory segment was successfully created or - * retrieved. - * - Other error codes may indicate issues with the shared memory - * - * @note Shared memory segments identified by the same `key` are accessible across - * processes. Ensure proper synchronization mechanisms (e.g., semaphores) are in - * place to manage access to the shared memory. - * - * @warning Using a `NULL` or invalid `key` may result in undefined behavior. When creating - * a new segment, ensure that `size` is non-zero and meaningful. - */ -sysret_t sys_shmget(size_t key, size_t size, int create) -{ - return lwp_shmget(key, size, create); -} - -/** - * @brief Removes a shared memory segment. - * - * This system call removes the specified shared memory segment identified by its `id`. - * Once removed, the segment will no longer be accessible, and any memory associated - * with it will be freed. It is typically used to clean up shared memory resources - * when they are no longer needed. - * - * @param[in] id The identifier of the shared memory segment to be removed. This identifier - * was obtained when the segment was created or retrieved using `sys_shmget`. - * - * @return sysret_t Returns a status code: - * - `0`: The shared memory segment was successfully removed. - * - Other error codes may indicate issues with the shared memory removal. - * - * @note This function should be called only when all processes that were using the shared - * memory segment have finished accessing it. Removing the segment while it is in use - * by another process may result in undefined behavior or memory corruption. - * - * @warning Ensure that the shared memory segment is no longer needed by any process before - * calling this function to avoid premature removal and potential data loss. - */ -sysret_t sys_shmrm(int id) -{ - return lwp_shmrm(id); -} - -/** - * @brief Attaches a shared memory segment to the calling process's address space. - * - * This system call maps a shared memory segment identified by `id` into the calling - * process's address space. The segment can then be accessed using the returned virtual - * address. If the segment was previously detached or created, it will be made available - * for reading and writing. - * - * @param[in] id The identifier of the shared memory segment to be attached. - * This identifier was obtained when the segment was created - * or retrieved using `sys_shmget`. - * @param[in] shm_vaddr A pointer to the desired virtual address where the shared - * memory segment should be mapped. If `NULL`, the system will - * choose an appropriate address. - * - * @return void* Returns the virtual address where the shared memory segment - * is mapped. On success, this will be the address in the - * calling process's address space. If the attachment fails, - * `NULL` is returned. - * - * @note Once the shared memory segment is attached, it can be accessed like any - * regular memory, but it should be used with caution, especially in multi-process - * environments. Ensure that proper synchronization mechanisms (e.g., semaphores) - * are used to manage concurrent access. - * - * @warning Ensure that the shared memory segment is properly allocated and not in use - * by other processes before attaching it. Passing invalid `id` or an inaccessible - * segment may result in undefined behavior. - */ -void *sys_shmat(int id, void *shm_vaddr) -{ - return lwp_shmat(id, shm_vaddr); -} - -/** - * @brief Detaches a shared memory segment from the calling process's address space. - * - * This system call detaches the shared memory segment previously attached to the calling - * process's address space using `sys_shmat`. After calling this function, the shared - * memory will no longer be accessible via the returned address. - * - * @param[in] shm_vaddr A pointer to the virtual address where the shared memory - * segment was previously mapped. This address was returned - * by the `sys_shmat` function. - * - * @return sysret_t Returns a status code: - * - `0`: The shared memory segment was successfully detached. - * - Other error codes may indicate issues with the detachment process. - * - * @note It is important to ensure that no processes are using the shared memory segment - * before detaching it. Detaching the segment while it is still being accessed - * may lead to undefined behavior. - * - * @warning Ensure that `shm_vaddr` corresponds to a valid attached address returned - * by `sys_shmat`. Passing an incorrect or uninitialized address may result - * in undefined behavior. - */ -sysret_t sys_shmdt(void *shm_vaddr) -{ - return lwp_shmdt(shm_vaddr); -} -#elif defined RT_LWP_USING_SHM - -/** - * @brief Allocates a new shared memory segment. - * - * This system call allocates a new shared memory segment of the specified size and - * returns a pointer to the allocated memory. The allocated segment can be used for - * inter-process communication or shared memory access. The segment is typically - * managed by the system, and it can be accessed by other processes with appropriate - * synchronization. - * - * @param[in] size The size (in bytes) of the shared memory segment to be allocated. - * - * @return void* Returns a pointer to the allocated shared memory segment. - * If the allocation fails, `RT_NULL` is returned. - * - * @note The allocated shared memory segment can be used like regular memory, - * but it should be accessed with proper synchronization mechanisms (e.g., - * semaphores or mutexes) to avoid race conditions or data corruption in - * multi-process environments. - * - * @warning Ensure that the `size` parameter is a valid, non-zero value, and that - * there is enough system memory to allocate the requested size. - * Failure to do so may result in errors or resource exhaustion. - */ -void *sys_shm_alloc(int size) -{ - if (size < 0) - { - return RT_NULL; - } - return lwp_shm_alloc((rt_size_t)size); -} - -/** - * @brief Retains an existing shared memory segment. - * - * This system call retains an existing shared memory segment that was previously - * allocated or attached, ensuring it remains available for further use. It is - * typically used to increment the reference count or to ensure the segment is - * not removed or deallocated while it is still being used. - * - * @param[in] mem A pointer to the shared memory segment to retain. This should be - * the address previously returned by a memory allocation or - * attachment function such as `sys_shmat` or `sys_shm_alloc`. - * - * @return void* Returns the original pointer `mem` if the retention is successful. - * If the operation fails, `NULL` is returned. - * - * @note This function does not affect the actual content of the shared memory segment, - * but ensures the segment is not removed or deallocated while still in use. - * It is generally used in conjunction with `sys_shmdt` or `sys_shmrm` to ensure - * proper resource management. - * - * @warning Ensure that the provided pointer `mem` corresponds to a valid shared - * memory segment that is currently allocated or attached. Passing an - * invalid or uninitialized pointer may result in undefined behavior. - */ -void *sys_shm_retain(void *mem) -{ - if (!lwp_user_accessable(mem, sizeof(void *))) - { - return RT_NULL; - } - return lwp_shm_retain(mem); -} - -/** - * @brief Frees a previously allocated shared memory segment. - * - * This system call frees a shared memory segment that was previously allocated or - * retained using functions like `sys_shm_alloc` or `sys_shm_retain`. It ensures - * that the shared memory is properly deallocated, making it available for future - * allocations or for removal from the system. - * - * @param[in] mem A pointer to the shared memory segment to be freed. This should be - * the address returned by a memory allocation or retention function. - * - * @return sysret_t Returns a status code: - * - `0`: The shared memory segment was successfully freed. - * - Other error codes may indicate issues with the memory deallocation. - * - * @note After calling this function, the memory will be deallocated, and any subsequent - * access to the freed memory will result in undefined behavior. Ensure that no - * processes are using the shared memory before calling this function. - * - * @warning Ensure that `mem` corresponds to a valid shared memory segment that was - * previously allocated or retained. Passing an invalid or already freed - * pointer may result in undefined behavior. - */ -sysret_t sys_shm_free(void *mem) -{ - if (!lwp_user_accessable(mem, sizeof(void *))) - { - return -EFAULT; - } - lwp_shm_free(mem); - return 0; -} -#endif - -/* device interfaces */ - -/** - * @brief Initializes a device. - * - * This system call initializes the specified device, preparing it for use. Device initialization - * typically involves setting up necessary hardware configurations, registering device drivers, - * and ensuring that the device is in a ready state for further operations. This function should - * be called before interacting with the device. - * - * @param[in] dev A pointer to the device structure that represents the device to be initialized. - * This structure contains device-specific configuration and state information. - * - * @return sysret_t Returns a status code: - * - `0`: The device was successfully initialized. - * - Other error codes may indicate issues with the device initialization process. - * - * @note This function is typically called once during system startup or when a device is - * first accessed. It ensures that all necessary setup steps are completed before - * the device can be used. - * - * @warning Ensure that the device passed to this function is valid and properly configured - * before initialization. Initializing an invalid or improperly configured device - * may result in unpredictable behavior. - */ -sysret_t sys_device_init(rt_device_t dev) -{ - return rt_device_init(dev); -} - -/** - * @brief Registers a device with the system. - * - * This system call registers a device with the system, making it available for interaction - * by the operating system or other components. Registration typically involves associating - * the device with a name and setting up the necessary flags for the device's behavior. - * - * @param[in] dev A pointer to the device structure that represents the device to be registered. - * This structure contains the device's configuration, capabilities, and state. - * @param[in] name A string representing the name by which the device will be identified in the system. - * This name is used for device lookup and reference. - * @param[in] flags A set of flags that configure the behavior of the device, such as enabling - * or disabling certain features, or specifying the device's mode of operation. - * - * @return sysret_t Returns a status code: - * - `0`: The device was successfully registered. - * - Other error codes may indicate issues with the device registration process. - * - * @note This function should be called after the device has been initialized (via `sys_device_init`) - * and before the device is used by the system or other components. - * - * @warning Ensure that the `name` provided is unique and not already in use by another device in the system. - * Passing invalid `dev` or `flags` may result in unexpected behavior or failure of device registration. - */ -sysret_t sys_device_register(rt_device_t dev, const char *name, rt_uint16_t flags) -{ - return rt_device_register(dev, name, flags); -} - -/** - * @brief Controls a device by sending a command. - * - * This system call sends a control command to the specified device, allowing the system or other - * components to modify the device's behavior or state. The command is specified by the `cmd` - * parameter, and the arguments for the command are passed via the `arg` parameter. - * - * @param[in] dev A pointer to the device structure representing the device to be controlled. - * The device must have been previously registered and initialized. - * @param[in] cmd The control command to be sent to the device. The meaning and behavior of the - * command are device-specific and depend on the device type. - * @param[in] arg A pointer to the arguments required by the command. The type and content of - * the arguments are determined by the command. Some commands may not require - * arguments, in which case `arg` can be `NULL`. - * - * @return sysret_t Returns a status code: - * - `0`: The command was successfully executed on the device. - * - Other error codes may indicate issues with the command execution or device control. - * - * @note The set of available commands (`cmd`) and the expected argument types (`arg`) are specific - * to each device. Refer to the device documentation for the supported commands and argument - * formats. - * - * @warning Ensure that the `dev` pointer is valid and points to a correctly initialized device. - * Providing an invalid device or incorrect command may result in undefined behavior. - */ -sysret_t sys_device_control(rt_device_t dev, int cmd, void *arg) -{ - return rt_device_control(dev, cmd, arg); -} - -/** - * @brief Finds a device by its name. - * - * This system call searches for a device that has been registered with the system using the - * specified name. If the device exists, a pointer to the device structure is returned, - * allowing further interaction with the device. If no device with the specified name is found, - * a `NULL` pointer is returned. - * - * @param[in] name The name of the device to search for. This name must match the name used - * during device registration (e.g., via `sys_device_register`). - * - * @return rt_device_t Returns a pointer to the device structure if the device is found. - * Returns `NULL` if no device with the specified name exists. - * - * @note The device must have been previously registered with the system using `sys_device_register`. - * - * @warning Ensure that the provided `name` is a valid string and corresponds to a registered device. - * Passing an invalid or non-registered name will result in `NULL` being returned. - */ -rt_device_t sys_device_find(const char *name) -{ - return rt_device_find(name); -} - -/** - * @brief Opens a device for use. - * - * This system call opens the specified device, making it ready for interaction with the system - * or other components. The device must have been previously registered and initialized. - * The `oflag` parameter specifies the open mode, which may determine how the device is accessed - * (e.g., read, write, or exclusive access). - * - * @param[in] dev A pointer to the device structure representing the device to be opened. - * The device must be registered and initialized before being opened. - * @param[in] oflag The open flags that determine the mode of access to the device. These flags - * may specify read, write, or other modes of operation, depending on the device's capabilities. - * - * @return sysret_t Returns a status code: - * - `0`: The device was successfully opened. - * - Other error codes may indicate issues with the device opening process. - * - * @note The open flags (`oflag`) should be set according to the device's capabilities. For example, - * some devices may support read or write operations, while others may only support one of them. - * Check the device documentation for supported flags. - * - * @warning Ensure that the device pointer (`dev`) is valid and that the device has been initialized - * properly. Incorrect flags or attempting to open a device that is already in use may result - * in errors or undefined behavior. - */ -sysret_t sys_device_open(rt_device_t dev, rt_uint16_t oflag) -{ - return rt_device_open(dev, oflag); -} - -/** - * @brief Closes an open device. - * - * This system call closes an open device, releasing any resources or locks associated with it - * and making it unavailable for further interaction until it is opened again. The device must - * have been previously opened using `sys_device_open`. After calling this function, any further - * attempts to interact with the device will result in an error unless the device is opened again. - * - * @param[in] dev A pointer to the device structure representing the device to be closed. - * The device must be open before it can be closed. - * - * @return sysret_t Returns a status code: - * - `0`: The device was successfully closed. - * - Other error codes may indicate issues with the device closing process. - * - * @note This function should be called when the device is no longer needed or before shutting - * down the system to release device resources properly. - * - * @warning Ensure that the device has been opened before calling this function. Calling this function - * on an uninitialized or already closed device may result in undefined behavior or errors. - */ -sysret_t sys_device_close(rt_device_t dev) -{ - return rt_device_close(dev); -} - -/** - * @brief Reads data from an open device. - * - * This system call reads data from the specified device, starting at the given position and - * storing the data in the provided buffer. The device must be open before this function can be called. - * The amount of data to read is determined by the `size` parameter. - * - * @param[in] dev A pointer to the device structure representing the device to read from. - * The device must be open and ready for reading. - * @param[in] pos The position within the device from which to begin reading. For devices that - * support seeking, this value is used to specify the starting point. - * @param[out] buffer A pointer to the buffer where the read data will be stored. This buffer should - * be large enough to hold the specified amount of data. - * @param[in] size The number of bytes to read from the device. This value determines how much - * data is read into the buffer. - * - * @return rt_ssize_t Returns the number of bytes actually read from the device: - * - On success: The number of bytes read, which may be less than `size` if - * the end of the device is reached or other factors limit the read. - * - On failure: A negative error code. - * - * @note The device must be open and in a readable state before calling this function. - * The `pos` parameter allows seeking within the device if it supports such operations. - * - * @warning Ensure that the buffer provided is large enough to hold the data being read. - * Providing an insufficient buffer may result in undefined behavior. - */ -rt_ssize_t sys_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) -{ - return rt_device_read(dev, pos, buffer, size); -} - -/** - * @brief Writes data to an open device. - * - * This system call writes data to the specified device, starting at the given position and using - * the provided buffer. The device must be open and ready for writing. The amount of data to write - * is determined by the `size` parameter. - * - * @param[in] dev A pointer to the device structure representing the device to write to. - * The device must be open and ready for writing. - * @param[in] pos The position within the device where the writing should start. For devices - * that support seeking, this value specifies the location to begin writing. - * @param[in] buffer A pointer to the buffer containing the data to be written to the device. - * This buffer should contain the data to be written and should be large enough - * to accommodate the specified size. - * @param[in] size The number of bytes to write to the device. This value indicates how much - * data should be written from the buffer to the device. - * - * @return rt_ssize_t Returns the number of bytes actually written to the device: - * - On success: The number of bytes written, which may be less than `size` - * if there is an issue with the device or the data was truncated. - * - On failure: A negative error code. - * - * @note The device must be open and in a writable state before calling this function. - * The `pos` parameter allows seeking within the device if it supports such operations. - * - * @warning Ensure that the buffer provided contains valid data and that it is large enough - * to fit the amount of data specified by `size`. Providing an insufficient buffer - * or invalid data may lead to undefined behavior. - */ -rt_ssize_t sys_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) -{ - return rt_device_write(dev, pos, buffer, size); -} - -#ifdef RT_USING_SAL -/* network interfaces */ - -union lwp_sockaddr_buffer -{ - struct sockaddr address; - struct sockaddr_un unix_address; -}; - -static int lwp_sockaddr_from_user(union lwp_sockaddr_buffer *kernel_address, - const struct musl_sockaddr *user_address, - socklen_t length) -{ - uint16_t family; - struct musl_sockaddr musl_address; - - if (user_address == RT_NULL || length < sizeof(family) || - length > sizeof(*kernel_address)) - { - return -EINVAL; - } - if (!lwp_user_accessable((void *)user_address, length)) - { - return -EFAULT; - } - - rt_memset(kernel_address, 0, sizeof(*kernel_address)); - lwp_get_from_user(&family, (void *)user_address, sizeof(family)); - if (family == AF_UNIX || family == AF_NETLINK) - { - lwp_get_from_user(kernel_address, (void *)user_address, length); - } - else - { - if (length > sizeof(musl_address)) - { - return -EINVAL; - } - rt_memset(&musl_address, 0, sizeof(musl_address)); - lwp_get_from_user(&musl_address, (void *)user_address, length); - sockaddr_tolwip(&musl_address, &kernel_address->address); - } - return 0; -} - -static socklen_t lwp_sockaddr_to_user( - struct musl_sockaddr *user_address, socklen_t user_length, - const union lwp_sockaddr_buffer *kernel_address, - socklen_t kernel_length) -{ - socklen_t actual_length; - socklen_t copy_length; - struct musl_sockaddr musl_address; - - if (kernel_address->unix_address.sa_family == AF_UNIX) - { - actual_length = kernel_length; - copy_length = user_length < actual_length ? user_length : actual_length; - lwp_put_to_user(user_address, (void *)kernel_address, copy_length); - } - else - { - rt_memset(&musl_address, 0, sizeof(musl_address)); - sockaddr_tomusl(&kernel_address->address, &musl_address); - actual_length = sizeof(musl_address); - copy_length = user_length < actual_length ? user_length : actual_length; - lwp_put_to_user(user_address, &musl_address, copy_length); - } - return actual_length; -} - -/** - * @brief Accepts a connection on a socket. - * - * This system call is used to accept a pending connection request on a socket. It extracts the - * first connection request from the incoming queue, creates a new socket for the connection, - * and stores the address information of the remote peer in the provided address structure. - * The original socket must be a listening socket that has been previously bound and set to listen - * for incoming connections. - * - * @param[in] socket The listening socket file descriptor that is waiting for incoming connections. - * This socket must be in a listening state, created using `sys_socket` and - * bound to an address using `sys_bind`. - * @param[out] addr A pointer to a `musl_sockaddr` structure where the address of the connecting - * client will be stored. The structure is populated with information such as - * the client's IP address and port number. - * @param[in,out] addrlen A pointer to a socklen_t variable that specifies the size of the `addr` - * structure on input. On output, it will contain the actual size of the - * address returned in `addr`. - * - * @return sysret_t Returns a socket descriptor for the new connection on success, or - * `SYSRET_ERROR` on failure: - * - On success: A new socket descriptor that can be used for further - * communication with the connected peer. - * - On failure: A negative error code indicating the reason for the failure. - * - * @note The socket passed as `socket` must be in a listening state and ready to accept incoming - * connections. The `addr` structure is populated with the client's address details, which can - * be used for further operations, such as identifying the client. - * - * @warning Ensure that the `addr` structure is sufficiently large to hold the address information. - * The `addrlen` parameter must be set to the size of the `musl_sockaddr` structure before - * calling this function. Calling the function with an incorrectly sized `addrlen` may lead - * to buffer overflows or undefined behavior. - */ -sysret_t sys_accept(int socket, struct musl_sockaddr *addr, socklen_t *addrlen) -{ - int ret = -1; - union lwp_sockaddr_buffer kernel_address; - socklen_t uaddrlen; - socklen_t kaddrlen; - - if (addr) - { - if (!lwp_user_accessable(addrlen, sizeof(socklen_t))) - { - return -EFAULT; - } - lwp_get_from_user(&uaddrlen, addrlen, sizeof(socklen_t)); - if (!uaddrlen) - { - return -EINVAL; - } - - if (!lwp_user_accessable(addr, uaddrlen)) - { - return -EFAULT; - } - } - - rt_memset(&kernel_address, 0, sizeof(kernel_address)); - kaddrlen = sizeof(kernel_address); - ret = accept(socket, &kernel_address.address, &kaddrlen); - if (ret >= 0) - { - if (addr) - { - kaddrlen = lwp_sockaddr_to_user(addr, uaddrlen, - &kernel_address, kaddrlen); - lwp_put_to_user(addrlen, &kaddrlen, sizeof(socklen_t)); - } - } - return ret; -} - -/** - * @brief Binds a socket to a local address. - * - * This system call binds a socket to a specific local address and port. The socket must be created - * using `sys_socket` before it can be bound. The `bind` operation allows the application to associate - * a socket with a specific address, such as an IP address and port number, which can then be used - * for sending or receiving data. - * - * @param[in] socket The socket descriptor to which the address will be bound. This socket must - * be created using `sys_socket` and should not already be bound to another address. - * @param[in] name A pointer to a `musl_sockaddr` structure that contains the address to which - * the socket will be bound. This can represent an IP address and port number. - * @param[in] namelen The size of the `musl_sockaddr` structure in bytes. This value should be set - * to the actual size of the `name` structure before calling this function. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note The `socket` must be created before calling this function, and the address provided in `name` - * should be a valid local address for the socket. This operation is typically used for server-side - * sockets that listen for incoming connections or for any socket that needs to specify its local - * address before communication. - * - * @warning If the specified address is already in use by another socket or if the socket type is incompatible - * with the address, the function will return an error. Ensure that the address is not in use and is - * valid for the socket type. - */ -sysret_t sys_bind(int socket, const struct musl_sockaddr *name, socklen_t namelen) -{ - int ret; - union lwp_sockaddr_buffer kernel_address; - - ret = lwp_sockaddr_from_user(&kernel_address, name, namelen); - if (ret < 0) - { - return ret; - } - ret = bind(socket, &kernel_address.address, namelen); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Shuts down part of a full-duplex connection. - * - * This system call disables further send or receive operations on a socket. The `how` parameter - * specifies which part of the connection should be shut down. This operation is typically used - * when the application no longer needs to send or receive data, but wants to keep the connection open - * for other purposes (e.g., receiving shutdown acknowledgment). - * - * @param[in] socket The socket descriptor to be shut down. This socket must be an open socket that - * has been created using `sys_socket`. - * @param[in] how The shutdown behavior: - * - `SHUT_RD`: Disables further receives on the socket. The application can no longer - * receive data from this socket. - * - `SHUT_WR`: Disables further sends on the socket. The application can no longer - * send data through this socket. - * - `SHUT_RDWR`: Disables both sends and receives on the socket. The socket is - * fully shut down for communication. - * - * @return sysret_t Returns `0` on success, or a negative error code (e.g., `SYSRET_ERROR`) on failure. - * - * @note The `socket` should be open and in a connected state when calling this function. The `how` - * parameter determines which part of the connection is to be shut down, allowing for finer control - * over communication termination. - * - * @warning Once a socket is shut down with `SHUT_RD`, no further data can be received on it. Similarly, - * after `SHUT_WR`, no more data can be sent. Calling `sys_shutdown` with incompatible `how` values - * or on an invalid socket may result in errors. - */ -sysret_t sys_shutdown(int socket, int how) -{ - return shutdown(socket, how); -} - -/** - * @brief Retrieves the address of the peer connected to a socket. - * - * This system call retrieves the address of the peer (remote endpoint) that is connected to the specified - * socket. The socket must be connected (i.e., for stream-oriented protocols such as TCP). The address - * of the peer is stored in the `name` structure, and the size of the structure is updated in `namelen`. - * - * @param[in] socket The socket descriptor of the connected socket. This socket must be in a connected - * state (e.g., after a successful `sys_connect` or `sys_accept` call). - * @param[out] name A pointer to a `musl_sockaddr` structure where the address of the peer will be - * stored. This structure will be populated with the peer's IP address and port number. - * @param[in,out] namelen A pointer to a `socklen_t` variable that specifies the size of the `name` structure - * on input. On output, it will contain the actual size of the address returned in `name`. - * - * @return sysret_t Returns `SYSRET_OK` on success, or a negative error code on failure. - * - * @note The `socket` must be in a connected state when calling this function. The `name` structure will - * contain the peer's address information, which can be used for logging, debugging, or further - * communication. - * - * @warning Ensure that the `name` structure is sufficiently large to hold the peer's address information. - * The `namelen` parameter must be set to the size of the `name` structure before calling this function. - * Failing to provide a correctly sized `namelen` could result in buffer overflows or undefined behavior. - */ -sysret_t sys_getpeername(int socket, struct musl_sockaddr *name, socklen_t *namelen) -{ - int ret = -1; - union lwp_sockaddr_buffer kernel_address; - socklen_t unamelen; - socklen_t knamelen; - - if (!lwp_user_accessable(namelen, sizeof(socklen_t))) - { - return -EFAULT; - } - lwp_get_from_user(&unamelen, namelen, sizeof(socklen_t)); - if (!unamelen) - { - return -EINVAL; - } - - if (!lwp_user_accessable(name, unamelen)) - { - return -EFAULT; - } - - rt_memset(&kernel_address, 0, sizeof(kernel_address)); - knamelen = sizeof(kernel_address); - ret = getpeername(socket, &kernel_address.address, &knamelen); - - if (ret == 0) - { - knamelen = lwp_sockaddr_to_user(name, unamelen, - &kernel_address, knamelen); - lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t)); - } - else - { - ret = GET_ERRNO(); - } - - return ret; -} - -/** - * @brief Retrieves the local address of the socket. - * - * This system call retrieves the local address (local endpoint) that is bound to the specified socket. - * The socket must be created and, if necessary, bound to a local address using `sys_bind`. The address - * of the local endpoint is stored in the `name` structure, and the size of the structure is updated in `namelen`. - * - * @param[in] socket The socket descriptor of the socket. This socket must be created and, if required, - * bound to a local address. - * @param[out] name A pointer to a `musl_sockaddr` structure where the local address of the socket will - * be stored. This structure will be populated with the local IP address and port number. - * @param[in,out] namelen A pointer to a `socklen_t` variable that specifies the size of the `name` structure - * on input. On output, it will contain the actual size of the address returned in `name`. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note The `socket` must be created and, if needed, bound to a local address using `sys_bind`. - * The `name` structure will contain the local address of the socket, which can be used for logging, - * debugging, or further communication. - * - * @warning Ensure that the `name` structure is sufficiently large to hold the local address information. - * The `namelen` parameter must be set to the size of the `name` structure before calling this function. - * Failing to provide a correctly sized `namelen` could result in buffer overflows or undefined behavior. - */ -sysret_t sys_getsockname(int socket, struct musl_sockaddr *name, socklen_t *namelen) -{ - int ret = -1; - union lwp_sockaddr_buffer kernel_address; - socklen_t unamelen; - socklen_t knamelen; - - if (!lwp_user_accessable(namelen, sizeof(socklen_t))) - { - return -EFAULT; - } - lwp_get_from_user(&unamelen, namelen, sizeof(socklen_t)); - if (!unamelen) - { - return -EINVAL; - } - - if (!lwp_user_accessable(name, unamelen)) - { - return -EFAULT; - } - - rt_memset(&kernel_address, 0, sizeof(kernel_address)); - knamelen = sizeof(kernel_address); - ret = getsockname(socket, &kernel_address.address, &knamelen); - if (ret == 0) - { - knamelen = lwp_sockaddr_to_user(name, unamelen, - &kernel_address, knamelen); - lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t)); - } - else - { - ret = GET_ERRNO(); - } - return ret; -} - -/** - * @brief Retrieves the value of a socket option. - * - * This system call retrieves the current value of a socket option for the specified socket. The socket - * options allow fine-grained control over various aspects of socket behavior, such as timeouts, buffering, - * and connection settings. The option value is stored in the `optval` buffer, and the size of the buffer - * is specified by the `optlen` parameter. - * - * @param[in] socket The socket descriptor for which the option value is being retrieved. The socket - * must be valid and open. - * @param[in] level The level at which the option is defined. Typically, this is `SOL_SOCKET` for general - * socket options, or a protocol-specific level (e.g., `IPPROTO_TCP` for TCP options). - * @param[in] optname The option name. This specifies which socket option to retrieve (e.g., `SO_RCVBUF` - * for receive buffer size or `SO_RCVBUF` for send buffer size). - * @param[out] optval A pointer to a buffer where the option value will be stored. The buffer's type depends - * on the option being retrieved. - * @param[in,out] optlen A pointer to a `socklen_t` variable that specifies the size of the `optval` buffer on - * input. On output, it will contain the actual size of the option value retrieved. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note The `socket` must be valid and open when calling this function. The `level` and `optname` parameters - * define the specific option to be retrieved. The `optval` buffer will contain the option value after - * the function call, and `optlen` will be updated to reflect the size of the retrieved value. - * - * @warning Ensure that the `optval` buffer is large enough to hold the value for the specified option. - * The `optlen` parameter must be set to the correct size of the buffer before calling this function. - * Failing to provide a correctly sized buffer could result in undefined behavior or buffer overflows. - */ -sysret_t sys_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen) -{ - int ret = 0; - socklen_t koptlen = 0; - void *koptval = RT_NULL; - - if (!lwp_user_accessable((void *)optlen, sizeof(uint32_t))) - return -EFAULT; - - if (lwp_get_from_user(&koptlen, optlen, sizeof(uint32_t)) != sizeof(uint32_t)) - { - return -EINVAL; - } - - if (!lwp_user_accessable((void *)optval, koptlen)) - return -EFAULT; - - koptval = kmem_get(koptlen); - if (koptval == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(koptval, optval, koptlen) != koptlen) - { - kmem_put(koptval); - return -EINVAL; - } - - convert_sockopt(&level, &optname); - ret = getsockopt(socket, level, optname, koptval, &koptlen); - - lwp_put_to_user((void *)optval, koptval, koptlen); - lwp_put_to_user((void *)optlen, &koptlen, sizeof(uint32_t)); - - kmem_put(koptval); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Sets the value of a socket option. - * - * This system call sets a socket option for the specified socket. Socket options control various aspects - * of socket behavior, such as timeouts, buffer sizes, or connection parameters. The option value is - * provided in the `optval` buffer, and its length is specified by the `optlen` parameter. - * - * @param[in] socket The socket descriptor for which the option is being set. The socket must be valid and open. - * @param[in] level The level at which the option is defined. This can be `SOL_SOCKET` for general socket options, - * or a protocol-specific level (e.g., `IPPROTO_TCP` for TCP options). - * @param[in] optname The option name. This specifies which socket option to set (e.g., `SO_RCVBUF` for receive buffer size). - * @param[in] optval A pointer to the buffer that contains the option value to set. The format of this buffer depends - * on the option being set. - * @param[in] optlen The size of the `optval` buffer. This specifies the length of the data to be used when setting - * the option value. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure: - * - * @note The `socket` must be valid and open. The `level` and `optname` parameters define the specific option to be set, - * and the `optval` buffer should contain the appropriate value for that option. The `optlen` parameter must - * match the size of the `optval` buffer. - * - * @warning Ensure that the `optval` buffer contains valid data for the specified option. The `optlen` parameter must be - * set to the correct size of the `optval` buffer before calling this function. Failing to provide a correctly - * sized buffer could result in undefined behavior or errors. - */ -sysret_t sys_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen) -{ - int ret; - void *koptval = RT_NULL; - - if (!lwp_user_accessable((void *)optval, optlen)) - return -EFAULT; - - koptval = kmem_get(optlen); - if (koptval == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(koptval, (void *)optval, optlen) != optlen) - { - kmem_put(koptval); - return -EINVAL; - } - - convert_sockopt(&level, &optname); - ret = setsockopt(socket, level, optname, koptval, optlen); - - kmem_put(koptval); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Establishes a connection to a remote socket. - * - * This system call attempts to establish a connection to a remote socket specified by the `name` parameter. - * It is typically used for client-side socket operations to connect to a server. The connection is established - * by using the specified socket descriptor and the address of the remote server. - * - * @param[in] socket The socket descriptor for the connection attempt. The socket must be valid and in a - * `SOCK_STREAM` or `SOCK_DGRAM` state, depending on the protocol. - * @param[in] name A pointer to a `sockaddr` structure containing the address of the remote host. This address - * must include the correct IP address and port number for the connection. - * @param[in] namelen The size of the `sockaddr` structure pointed to by `name`. This should match the size - * of the address structure (e.g., `sizeof(struct sockaddr_in)` for IPv4). - * - * @return sysret_t Returns `0` on success, or a negative error code on failure: - * - * @note This function may block if the connection is being established, depending on the socket's configuration. - * For non-blocking sockets, it will return immediately, even if the connection has not been established. - * - * @warning The `socket` must be of a valid type (e.g., `SOCK_STREAM` for TCP or `SOCK_DGRAM` for UDP). - * The `name` parameter must point to a valid `sockaddr` structure that contains a correctly formatted address. - */ -sysret_t sys_connect(int socket, const struct musl_sockaddr *name, socklen_t namelen) -{ - int ret; - union lwp_sockaddr_buffer kernel_address; - - ret = lwp_sockaddr_from_user(&kernel_address, name, namelen); - if (ret < 0) - { - return ret; - } - ret = connect(socket, &kernel_address.address, namelen); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Prepares a socket to accept incoming connection requests. - * - * This system call marks a socket as a passive socket, indicating that it will be used to accept - * incoming connection requests. It is typically used on server-side sockets after binding a - * local address and port using `sys_bind()`. - * - * @param[in] socket The socket descriptor to be set up for listening. The socket must be valid - * and of type `SOCK_STREAM` (for TCP connections). - * @param[in] backlog The maximum number of pending connections that can be queued. This value - * is system-dependent and may be limited to a lower maximum. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note Before calling this function, the socket must be bound to a local address and port using - * `sys_bind()`. Only sockets of type `SOCK_STREAM` can be used for listening. The actual - * maximum queue length may be less than the specified `backlog` value, depending on system limits. - * - * @warning Ensure that the socket is properly initialized, bound, and of the correct type before calling this function. - * Failure to do so will result in undefined behavior or errors. - */ -sysret_t sys_listen(int socket, int backlog) -{ - return listen(socket, backlog); -} - -#define MUSLC_MSG_OOB 0x0001 -#define MUSLC_MSG_PEEK 0x0002 -#define MUSLC_MSG_CTRUNC 0x0008 -#define MUSLC_MSG_DONTWAIT 0x0040 -#define MUSLC_MSG_WAITALL 0x0100 -#define MUSLC_MSG_MORE 0x8000 - -static int netflags_muslc_2_lwip(int flags) -{ - int flgs = 0; - - if (flags & MUSLC_MSG_PEEK) - { - flgs |= MSG_PEEK; - } - if (flags & MUSLC_MSG_WAITALL) - { - flgs |= MSG_WAITALL; - } - if (flags & MUSLC_MSG_OOB) - { - flgs |= MSG_OOB; - } - if (flags & MUSLC_MSG_DONTWAIT) - { - flgs |= MSG_DONTWAIT; - } - if (flags & MUSLC_MSG_MORE) - { - flgs |= MSG_MORE; - } - if (flags & MSG_ERRQUEUE) - { - flgs |= MSG_ERRQUEUE; - } - - return flgs; -} - -static int netflags_lwip_2_muslc(int flags) -{ - int flgs = 0; - - if (flags & MSG_CTRUNC) - { - flgs |= MUSLC_MSG_CTRUNC; - } - return flgs; -} - -static void cmsg_level_muslc_2_lwip(struct msghdr *message) -{ - struct cmsghdr *cmsg; - - for_each_cmsghdr(cmsg, message) - { - if (!CMSG_OK(message, cmsg)) - { - break; - } - if (cmsg->cmsg_level == INTF_SOL_SOCKET) - { - cmsg->cmsg_level = IMPL_SOL_SOCKET; - } - } -} - -static void cmsg_level_lwip_2_muslc(struct msghdr *message) -{ - struct cmsghdr *cmsg; - - for_each_cmsghdr(cmsg, message) - { - if (!CMSG_OK(message, cmsg)) - { - break; - } - if (cmsg->cmsg_level == IMPL_SOL_SOCKET) - { - cmsg->cmsg_level = INTF_SOL_SOCKET; - } - } -} - -#ifdef ARCH_MM_MMU -static int copy_msghdr_from_user(struct msghdr *kmsg, - struct musl_msghdr *umsg, - struct iovec **out_iov, void **out_msg_control, - void **out_msg_name, void **out_buffer, - socklen_t *out_user_name_length, - rt_bool_t receive) -{ - int index; - size_t iovs_size; - struct musl_msghdr user_message; - struct iovec *uiov, *kiov; - size_t iovs_buffer_size = 0; - char *iovs_buffer; - char *buffer_cursor; - - if (!lwp_user_accessable(umsg, sizeof(user_message))) - { - return -EFAULT; - } - - lwp_get_from_user(&user_message, umsg, sizeof(user_message)); - kmsg->msg_name = user_message.msg_name; - kmsg->msg_namelen = user_message.msg_namelen; - kmsg->msg_iov = user_message.msg_iov; - kmsg->msg_iovlen = user_message.msg_iovlen; - kmsg->msg_control = user_message.msg_control; - kmsg->msg_controllen = user_message.msg_controllen; - kmsg->msg_flags = user_message.msg_flags; - - if (out_user_name_length != RT_NULL) - { - *out_user_name_length = kmsg->msg_namelen; - } - - if (kmsg->msg_iovlen < 0 || - (size_t)kmsg->msg_iovlen > SIZE_MAX / sizeof(*kmsg->msg_iov) || - (!receive && - kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer))) - { - return -EINVAL; - } - if (receive) - { - if (kmsg->msg_name == RT_NULL) - { - kmsg->msg_namelen = 0; - } - else if (kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer)) - { - kmsg->msg_namelen = sizeof(union lwp_sockaddr_buffer); - } - if (kmsg->msg_control == RT_NULL) - { - kmsg->msg_controllen = 0; - } - } - if (kmsg->msg_name != RT_NULL && - !lwp_user_accessable(kmsg->msg_name, kmsg->msg_namelen)) - { - return -EFAULT; - } - if (kmsg->msg_control != RT_NULL && - !lwp_user_accessable(kmsg->msg_control, kmsg->msg_controllen)) - { - return -EFAULT; - } - if (kmsg->msg_control == RT_NULL) - { - if (kmsg->msg_controllen != 0) - { - return -EFAULT; - } - } - if (kmsg->msg_name == RT_NULL) - { - kmsg->msg_namelen = 0; - } - - iovs_size = sizeof(*kmsg->msg_iov) * kmsg->msg_iovlen; - if (!lwp_user_accessable(kmsg->msg_iov, iovs_size)) - { - return -EFAULT; - } - - /* user and kernel */ - kiov = kmem_get(iovs_size * 2); - if (!kiov) - { - return -ENOMEM; - } - - uiov = (void *)kiov + iovs_size; - lwp_get_from_user(uiov, kmsg->msg_iov, iovs_size); - - if (out_iov) - { - *out_iov = uiov; - } - kmsg->msg_iov = kiov; - - for (index = 0; index < kmsg->msg_iovlen; ++index) - { - /* - * We MUST check we can copy data to user after socket done in uiov - * otherwise we will be lost the messages from the network! - */ - if (!lwp_user_accessable(uiov->iov_base, uiov->iov_len)) - { - kmem_put(kmsg->msg_iov); - - return -EPERM; - } - - if (SIZE_MAX - iovs_buffer_size < uiov->iov_len) - { - kmem_put(kmsg->msg_iov); - return -EINVAL; - } - iovs_buffer_size += uiov->iov_len; - kiov->iov_len = uiov->iov_len; - - ++kiov; - ++uiov; - } - - /* msg_iov and msg_control */ - if (SIZE_MAX - iovs_buffer_size < kmsg->msg_controllen || - SIZE_MAX - iovs_buffer_size - kmsg->msg_controllen < - kmsg->msg_namelen) - { - kmem_put(kmsg->msg_iov); - return -EINVAL; - } - iovs_buffer = kmem_get(iovs_buffer_size + kmsg->msg_controllen + - kmsg->msg_namelen); - - if (!iovs_buffer) - { - kmem_put(kmsg->msg_iov); - - return -ENOMEM; - } - - *out_buffer = iovs_buffer; - buffer_cursor = iovs_buffer; - kiov = kmsg->msg_iov; - - for (index = 0; index < kmsg->msg_iovlen; ++index) - { - kiov->iov_base = buffer_cursor; - buffer_cursor += kiov->iov_len; - ++kiov; - } - - *out_msg_control = kmsg->msg_control; - if (kmsg->msg_control != RT_NULL) - { - kmsg->msg_control = buffer_cursor; - buffer_cursor += kmsg->msg_controllen; - } - *out_msg_name = kmsg->msg_name; - if (kmsg->msg_name != RT_NULL && kmsg->msg_namelen != 0) - { - kmsg->msg_name = buffer_cursor; - } - else - { - kmsg->msg_name = RT_NULL; - kmsg->msg_namelen = 0; - } - - return 0; -} -#endif /* ARCH_MM_MMU */ - -/** - * @brief Receives a message from a socket using a message header structure. - * - * This system call is used to receive data and associated metadata from a socket. - * It supports both stream-oriented and message-oriented sockets and allows control - * over the behavior of the receiving operation via the `flags` parameter. - * - * @param[in] socket The socket descriptor from which the message will be received. - * The socket must be in a valid and connected state for stream-oriented - * sockets or bound for message-oriented sockets. - * @param[out] msg A pointer to an `msghdr` structure that specifies message buffers and - * will receive the incoming data. The structure also stores ancillary - * data for advanced socket operations. - * @param[in] flags Modifiers that control the behavior of the receive operation. Common - * flags include: - * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue. - * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning. - * - `MSG_DONTWAIT`: Perform a non-blocking receive operation. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note The `msghdr` structure should be initialized properly, including setting up buffers for - * receiving the data. The function can return fewer bytes than expected depending on socket - * type and flags. For stream-oriented sockets, partial data may be received. - * - * @warning The `socket` must be valid and in an appropriate state. If the socket is non-blocking - * and no data is available, the function will return immediately. - * - * @see sys_sendmsg() - */ -sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags) -{ - int flgs, ret = -1; - int error = 0; - struct msghdr kmsg; -#ifdef ARCH_MM_MMU - int index; - size_t remaining; - socklen_t user_name_length; - void *buffer; - void *msg_control; - void *msg_name; - struct iovec *uiov, *kiov; -#endif - - if (!msg) - { - return -EPERM; - } - - flgs = netflags_muslc_2_lwip(flags); - -#ifdef ARCH_MM_MMU - ret = copy_msghdr_from_user(&kmsg, msg, &uiov, &msg_control, - &msg_name, &buffer, &user_name_length, - RT_TRUE); - - if (ret < 0) - { - return ret; - } - ret = recvmsg(socket, &kmsg, flgs); - if (ret < 0) - { - error = GET_ERRNO(); - } - - if (ret < 0) - { - goto _free_res; - } - - kiov = kmsg.msg_iov; - remaining = (size_t)ret; - - for (index = 0; index < kmsg.msg_iovlen && remaining != 0; ++index) - { - size_t copy_length = kiov->iov_len; - - if (copy_length > remaining) - { - copy_length = remaining; - } - lwp_put_to_user(uiov->iov_base, kiov->iov_base, copy_length); - remaining -= copy_length; - - ++kiov; - ++uiov; - } - - if (msg_control != RT_NULL && kmsg.msg_controllen != 0) - { - cmsg_level_lwip_2_muslc(&kmsg); - lwp_put_to_user(msg_control, kmsg.msg_control, - kmsg.msg_controllen); - } - if (msg_name != RT_NULL && kmsg.msg_name != RT_NULL) - { - socklen_t name_length; - - name_length = lwp_sockaddr_to_user( - (struct musl_sockaddr *)msg_name, user_name_length, - (const union lwp_sockaddr_buffer *)kmsg.msg_name, - kmsg.msg_namelen); - lwp_put_to_user(&msg->msg_namelen, &name_length, - sizeof(name_length)); - } - kmsg.msg_flags = netflags_lwip_2_muslc(kmsg.msg_flags); - lwp_put_to_user(&msg->msg_flags, &kmsg.msg_flags, sizeof(kmsg.msg_flags)); - lwp_put_to_user(&msg->msg_controllen, &kmsg.msg_controllen, - sizeof(kmsg.msg_controllen)); - -_free_res: - kmem_put(buffer); - kmem_put(kmsg.msg_iov); -#else - kmsg.msg_name = msg->msg_name; - kmsg.msg_namelen = msg->msg_namelen; - kmsg.msg_iov = msg->msg_iov; - kmsg.msg_iovlen = msg->msg_iovlen; - kmsg.msg_control = msg->msg_control; - kmsg.msg_controllen = msg->msg_controllen; - kmsg.msg_flags = msg->msg_flags; - - ret = recvmsg(socket, &kmsg, flgs); - if (ret < 0) - { - error = GET_ERRNO(); - } - - if (!ret) - { - msg->msg_flags = kmsg.msg_flags; - } -#endif /* ARCH_MM_MMU */ - - if (ret >= 0) - { - return ret; - } - return error != 0 ? error : ret; -} - -/** - * @brief Receives data from a socket, optionally capturing the source address. - * - * This system call is used to receive data from a socket. For connectionless sockets, it also - * retrieves the address of the sender. It supports both blocking and non-blocking operations - * depending on the socket configuration and the specified flags. - * - * @param[in] socket The socket descriptor from which the data will be received. The socket - * must be valid and appropriately configured (e.g., connected or bound). - * @param[out] mem A pointer to the buffer where the received data will be stored. - * @param[in] len The maximum number of bytes to receive. This defines the size of the `mem` buffer. - * @param[in] flags Modifiers that control the behavior of the receive operation. Common - * flags include: - * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue. - * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning. - * - `MSG_DONTWAIT`: Perform a non-blocking receive operation. - * @param[out] from A pointer to a `musl_sockaddr` structure that will hold the address of - * the sender. This parameter can be `NULL` if the sender's address is not needed. - * @param[in,out] fromlen A pointer to a `socklen_t` variable indicating the size of the `from` buffer. - * On return, it will be updated with the actual size of the sender's address. - * This parameter is ignored if `from` is `NULL`. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure: - * - * @note For stream-oriented sockets, this function behaves like `sys_recv()`, and the `from` and - * `fromlen` parameters are ignored. For datagram-oriented sockets, the function fills `from` - * with the address of the sender. - * - * @warning The `socket` must be valid and configured for receiving data. If the `mem` buffer is - * smaller than the received data, excess data may be discarded. In non-blocking mode, - * if no data is available, the function returns immediately. - * - * @see sys_sendto(), sys_recv() - */ -sysret_t sys_recvfrom(int socket, void *mem, size_t len, int flags, - struct musl_sockaddr *from, socklen_t *fromlen) -{ - int flgs = 0; - int ret = -1; - socklen_t user_from_length = 0; - socklen_t kernel_from_length = 0; - union lwp_sockaddr_buffer kernel_address; -#ifdef ARCH_MM_MMU - void *kmem = RT_NULL; -#endif - - flgs = netflags_muslc_2_lwip(flags); - if (from != RT_NULL) - { - if (fromlen == RT_NULL || - !lwp_user_accessable(fromlen, sizeof(*fromlen))) - { - return -EFAULT; - } - lwp_get_from_user(&user_from_length, fromlen, - sizeof(user_from_length)); - if (user_from_length == 0 || - !lwp_user_accessable(from, user_from_length)) - { - return -EFAULT; - } - rt_memset(&kernel_address, 0, sizeof(kernel_address)); - kernel_from_length = sizeof(kernel_address); - } -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)mem, len)) - { - return -EFAULT; - } - - kmem = kmem_get(len); - if (!kmem) - { - return -ENOMEM; - } - - if (from) - { - ret = recvfrom(socket, kmem, len, flgs, - &kernel_address.address, &kernel_from_length); - } - else - { - ret = recvfrom(socket, kmem, len, flgs, NULL, NULL); - } - - if (ret >= 0) - { - if (ret > 0) - { - lwp_put_to_user(mem, kmem, ret); - } - if (from != RT_NULL) - { - kernel_from_length = lwp_sockaddr_to_user( - from, user_from_length, &kernel_address, - kernel_from_length); - lwp_put_to_user(fromlen, &kernel_from_length, - sizeof(kernel_from_length)); - } - } - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmem); - - return ret; -#else - if (from) - { - ret = recvfrom(socket, mem, len, flgs, - &kernel_address.address, &kernel_from_length); - if (ret >= 0) - { - kernel_from_length = lwp_sockaddr_to_user( - from, user_from_length, &kernel_address, - kernel_from_length); - lwp_put_to_user(fromlen, &kernel_from_length, - sizeof(kernel_from_length)); - } - } - else - { - ret = recvfrom(socket, mem, len, flgs, NULL, NULL); - } - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Receives data from a connected socket. - * - * This system call is used to receive data from a socket that is in a connected state. - * It supports various flags to modify the behavior of the receive operation. - * - * @param[in] socket The socket descriptor from which the data will be received. - * The socket must be in a valid and connected state. - * @param[out] mem A pointer to the buffer where the received data will be stored. - * @param[in] len The maximum number of bytes to receive. This defines the size of the `mem` buffer. - * @param[in] flags Modifiers that control the behavior of the receive operation. Common flags include: - * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue. - * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning. - * - `MSG_DONTWAIT`: Perform a non-blocking receive operation. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note The function is designed for connected sockets, such as stream-oriented sockets (e.g., TCP). - * For datagram-oriented sockets (e.g., UDP), use `sys_recvfrom()` to capture the sender's address if needed. - * - * @warning The `socket` must be valid and in a connected state. If the `mem` buffer is smaller than the - * received data, excess data may be discarded. For non-blocking sockets, the function returns - * immediately if no data is available. - * - * @see sys_send(), sys_recvfrom() - */ -sysret_t sys_recv(int socket, void *mem, size_t len, int flags) -{ - int flgs = 0; - int ret; - void *kmem = RT_NULL; - - if (!lwp_user_accessable((void *)mem, len)) - return -EFAULT; - - kmem = kmem_get(len); - if (kmem == RT_NULL) - { - return -ENOMEM; - } - - flgs = netflags_muslc_2_lwip(flags); - ret = recvfrom(socket, kmem, len, flgs, NULL, NULL); - - if (ret > 0) - { - lwp_put_to_user((void *)mem, kmem, ret); - } - kmem_put(kmem); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Sends a message from a socket using scatter-gather I/O. - * - * This system call sends data from multiple buffers specified in the `msghdr` structure. It is particularly - * useful for advanced socket operations requiring ancillary data or scatter-gather I/O. - * - * @param[in] socket The socket descriptor through which the data will be sent. The socket must be - * valid and, for connection-oriented sockets, in a connected state. - * @param[in] msg A pointer to a `msghdr` structure that specifies the message to be sent. This structure includes: - * - `msg_name`: Optional address of the target (used for connectionless sockets like UDP). - * - `msg_namelen`: Length of the address in `msg_name`. - * - `msg_iov`: An array of `iovec` structures pointing to the data buffers. - * - `msg_iovlen`: The number of elements in the `msg_iov` array. - * - `msg_control`: Optional ancillary data (e.g., file descriptors). - * - `msg_controllen`: Length of the ancillary data. - * - `msg_flags`: Flags for the message (e.g., end-of-record markers). - * @param[in] flags Flags that modify the behavior of the send operation. Common flags include: - * - `MSG_DONTWAIT`: Perform a non-blocking send operation. - * - `MSG_EOR`: Indicates the end of a record. - * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note This function is versatile and supports both connection-oriented (e.g., TCP) and - * connectionless (e.g., UDP) sockets. For simpler use cases, consider using `sys_send()` or `sys_sendto()`. - * - * @warning The `socket` must be configured correctly for the intended communication. For non-blocking sockets, - * the function may return immediately if the send buffer is full. Ancillary data in `msg_control` - * must be formatted correctly to avoid undefined behavior. - * - * @see sys_send(), sys_sendto(), sys_recvmsg() - */ -sysret_t sys_sendmsg(int socket, const struct musl_msghdr *msg, int flags) -{ - int flgs, ret = -1; - int error = 0; - struct msghdr kmsg; -#ifdef ARCH_MM_MMU - int index; - void *buffer; - void *msg_control; - void *msg_name; - struct iovec *uiov, *kiov; -#endif - if (!msg) - { - return -EPERM; - } - - flgs = netflags_muslc_2_lwip(flags); - -#ifdef ARCH_MM_MMU - ret = copy_msghdr_from_user(&kmsg, (struct musl_msghdr *)msg, &uiov, - &msg_control, &msg_name, &buffer, RT_NULL, - RT_FALSE); - - if (ret < 0) - { - return ret; - } - kiov = kmsg.msg_iov; - - for (index = 0; index < kmsg.msg_iovlen; ++index) - { - lwp_get_from_user(kiov->iov_base, uiov->iov_base, kiov->iov_len); - - ++kiov; - ++uiov; - } - - if (msg_control != RT_NULL && kmsg.msg_controllen != 0) - { - lwp_get_from_user(kmsg.msg_control, msg_control, - kmsg.msg_controllen); - cmsg_level_muslc_2_lwip(&kmsg); - } - if (msg_name != RT_NULL && kmsg.msg_namelen != 0) - { - union lwp_sockaddr_buffer kernel_address; - - ret = lwp_sockaddr_from_user(&kernel_address, - (struct musl_sockaddr *)msg_name, - kmsg.msg_namelen); - if (ret < 0) - { - kmem_put(buffer); - kmem_put(kmsg.msg_iov); - return ret; - } - rt_memcpy(kmsg.msg_name, &kernel_address, kmsg.msg_namelen); - } - - ret = sendmsg(socket, &kmsg, flgs); - if (ret < 0) - { - error = GET_ERRNO(); - } - - kmem_put(buffer); - kmem_put(kmsg.msg_iov); -#else - kmsg.msg_name = msg->msg_name; - kmsg.msg_namelen = msg->msg_namelen; - kmsg.msg_iov = msg->msg_iov; - kmsg.msg_iovlen = msg->msg_iovlen; - kmsg.msg_control = msg->msg_control; - kmsg.msg_controllen = msg->msg_controllen; - kmsg.msg_flags = msg->msg_flags; - - ret = sendmsg(socket, &kmsg, flgs); - if (ret < 0) - { - error = GET_ERRNO(); - } - -#endif /* ARCH_MM_MMU */ - - if (ret >= 0) - { - return ret; - } - return error != 0 ? error : ret; -} - -/** - * @brief Sends data to a specific address using a socket. - * - * This system call is used to send data from a socket to a specified address. It is commonly - * used with connectionless sockets (e.g., UDP) but can also be used with connection-oriented - * sockets if the destination address needs to be overridden. - * - * @param[in] socket The socket descriptor used for sending data. It must be valid and - * properly configured for communication. - * @param[in] dataptr A pointer to the buffer containing the data to be sent. - * @param[in] size The size, in bytes, of the data to be sent. - * @param[in] flags Flags that modify the behavior of the send operation. Common flags include: - * - `MSG_DONTWAIT`: Perform a non-blocking send operation. - * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors. - * @param[in] to A pointer to a `musl_sockaddr` structure that specifies the destination address. - * This parameter can be `NULL` if the socket is connection-oriented. - * @param[in] tolen The length of the address structure pointed to by `to`. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note For connectionless sockets (e.g., UDP), `to` and `tolen` must specify a valid destination address. - * For connection-oriented sockets (e.g., TCP), these parameters can be ignored if the connection - * is already established. - * - * @warning Ensure that the buffer size (`size`) matches the expected size for the data protocol in use. - * For non-blocking sockets, this function may return immediately if the send buffer is full. - * - * @see sys_send(), sys_sendmsg(), sys_recvfrom() - */ -sysret_t sys_sendto(int socket, const void *dataptr, size_t size, int flags, - const struct musl_sockaddr *to, socklen_t tolen) -{ - int flgs = 0; - int ret = -1; - union lwp_sockaddr_buffer kernel_address; -#ifdef ARCH_MM_MMU - void *kmem = RT_NULL; -#endif - - flgs = netflags_muslc_2_lwip(flags); - if (to != RT_NULL) - { - ret = lwp_sockaddr_from_user(&kernel_address, to, tolen); - if (ret < 0) - { - return ret; - } - } -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)dataptr, size)) - { - return -EFAULT; - } - - kmem = kmem_get(size); - if (!kmem) - { - return -ENOMEM; - } - - lwp_get_from_user(kmem, (void *)dataptr, size); - - if (to) - { - ret = sendto(socket, kmem, size, flgs, - &kernel_address.address, tolen); - } - else - { - ret = sendto(socket, kmem, size, flgs, NULL, tolen); - } - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmem); - - return ret; -#else - if (to) - { - ret = sendto(socket, dataptr, size, flgs, - &kernel_address.address, tolen); - } - else - { - ret = sendto(socket, dataptr, size, flgs, NULL, tolen); - } - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Sends data over a connected socket. - * - * This system call sends data from the specified buffer through a connected socket. It is typically - * used with connection-oriented sockets (e.g., TCP) but can also work with connectionless sockets - * (e.g., UDP) if a connection is established using `sys_connect()`. - * - * @param[in] socket The socket descriptor used for sending data. It must be a valid socket - * and, for connection-oriented sockets, in a connected state. - * @param[in] dataptr A pointer to the buffer containing the data to be sent. - * @param[in] size The size, in bytes, of the data to be sent. - * @param[in] flags Flags that modify the behavior of the send operation. Common flags include: - * - `MSG_DONTWAIT`: Perform a non-blocking send operation. - * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note For connection-oriented sockets, the socket must already be connected using `sys_connect()` or - * `sys_accept()`. For connectionless sockets, use `sys_sendto()` to specify the destination address. - * - * @warning If the socket is in non-blocking mode and the send buffer is full, this function may return - * immediately with an error. Ensure that the size of the data (`size`) matches the protocol's - * expectations to avoid truncation or overflow issues. - * - * @see sys_sendto(), sys_sendmsg(), sys_recv() - */ -sysret_t sys_send(int socket, const void *dataptr, size_t size, int flags) -{ - int flgs = 0; - int ret = 0; - void *kdataptr = RT_NULL; - - if (!lwp_user_accessable((void *)dataptr, size)) - return -EFAULT; - - kdataptr = kmem_get(size); - if (kdataptr == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kdataptr, (void *)dataptr, size) != size) - { - kmem_put(kdataptr); - return -EINVAL; - } - - flgs = netflags_muslc_2_lwip(flags); - ret = sendto(socket, kdataptr, size, flgs, NULL, 0); - - kmem_put(kdataptr); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Creates a new socket for communication. - * - * This system call creates a new socket endpoint for communication and returns a file descriptor - * that can be used for subsequent socket operations, such as binding, connecting, or data transfer. - * - * @param[in] domain Specifies the protocol family to be used for the socket. Common values include: - * - `AF_INET`: IPv4 Internet protocols. - * - `AF_INET6`: IPv6 Internet protocols. - * - `AF_UNIX`: Local communication using UNIX domain sockets. - * @param[in] type Specifies the type of socket to be created. Common values include: - * - `SOCK_STREAM`: Provides sequenced, reliable, two-way, connection-based byte streams. - * - `SOCK_DGRAM`: Supports datagrams (connectionless, unreliable messages of fixed maximum length). - * - `SOCK_RAW`: Provides raw network protocol access. - * @param[in] protocol Specifies the specific protocol to be used. Typically set to `0` to use the default - * protocol for the specified domain and type. - * - * @return sysret_t Returns the socket file descriptor on success, or a negative error code on failure. - * - * @note The returned socket descriptor must be closed using `sys_close()` to release system resources. - * - * @warning Ensure the combination of `domain`, `type`, and `protocol` is valid. Invalid combinations - * may result in errors. For example, specifying `AF_INET` with `SOCK_DGRAM` is valid for UDP, - * but with `SOCK_STREAM` it is used for TCP. - * - * @see sys_bind(), sys_connect(), sys_accept(), sys_close() - */ -sysret_t sys_socket(int domain, int type, int protocol) -{ - int fd = -1; - int nonblock = 0; - /* not support SOCK_CLOEXEC type */ - if (type & SOCK_CLOEXEC) - { - type &= ~SOCK_CLOEXEC; - } - if (type & SOCK_NONBLOCK) - { - nonblock = 1; - type &= ~SOCK_NONBLOCK; - } - - fd = socket(domain, type, protocol); - if (fd < 0) - { - goto out; - } - if (nonblock) - { - fcntl(fd, F_SETFL, O_NONBLOCK); - } - -out: - return (fd < 0 ? GET_ERRNO() : fd); -} - -/** - * @brief Creates a pair of connected sockets. - * - * This system call creates two connected sockets that can be used for bidirectional communication - * between processes or threads. The sockets are returned as file descriptors in the `fd` array. - * - * @param[in] domain Specifies the protocol family to be used for the sockets. Common values include: - * - `AF_UNIX`: Local communication using UNIX domain sockets. - * @param[in] type Specifies the type of socket to be created. Common values include: - * - `SOCK_STREAM`: Provides sequenced, reliable, two-way, connection-based byte streams. - * - `SOCK_DGRAM`: Supports datagrams (connectionless, unreliable messages of fixed maximum length). - * @param[in] protocol Specifies the specific protocol to be used. Typically set to `0` to use the default - * protocol for the specified domain and type. - * @param[out] fd An array of two integers where the connected socket descriptors will be stored. - * After a successful call: - * - `fd[0]`: The first socket descriptor. - * - `fd[1]`: The second socket descriptor. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note The sockets in the pair are connected and can be used for inter-process communication - * (IPC) or between threads in the same process. - * - * @warning Ensure the `domain`, `type`, and `protocol` combination is valid. This function is typically - * supported only for `AF_UNIX` domain. - * - * @see sys_socket(), sys_close() - */ -sysret_t sys_socketpair(int domain, int type, int protocol, int fd[2]) -{ -#ifdef RT_USING_SAL - int ret = 0; - int k_fd[2]; - - if (!lwp_user_accessable((void *)fd, sizeof(int[2]))) - { - return -EFAULT; - } - - ret = socketpair(domain, type, protocol, k_fd); - - if (ret == 0) - { - lwp_put_to_user(fd, k_fd, sizeof(int[2])); - } - - return ret; -#else - return -ELIBACC; -#endif -} - -/** - * @brief Closes an open socket. - * - * This system call is used to close a previously opened socket. Once the socket is closed, it is no longer - * valid for any further operations, such as sending, receiving, or other socket-related functions. - * - * @param[in] socket The socket descriptor to be closed. This descriptor must be a valid socket that was - * previously created with `sys_socket()` or related functions. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note Once a socket is closed, any attempts to use the socket for communication will result in an error. - * The system will release any resources associated with the socket. - * - * @warning Make sure that no data is being transferred or pending on the socket before closing it. - * Closing an active socket might lead to data loss. - * - * @see sys_socket(), sys_shutdown() - */ -sysret_t sys_closesocket(int socket) -{ - sysret_t ret; - ret = closesocket(socket); - return ret; -} - -#endif - -/** - * @brief Finds a thread by its name. - * - * This system call is used to search for a thread based on its name. It returns a reference to the - * thread if found, otherwise it returns `RT_NULL`. The name comparison is case-sensitive. - * - * @param[in] name The name of the thread to search for. This should be a valid string that - * uniquely identifies the thread within the system. - * - * @return rt_thread_t The thread object corresponding to the given name, or `RT_NULL` if no - * matching thread was found. - * - * @note The thread name is typically assigned when the thread is created, and it can be used to - * identify threads in the system. - * - * @warning Ensure that the `name` string is correctly set and unique within the system to avoid - * unexpected behavior. - * - * @see sys_thread_create(), sys_thread_delete() - */ -rt_thread_t sys_thread_find(char *name) -{ - int len = 0; - char *kname = RT_NULL; - rt_thread_t thread; - - len = lwp_user_strlen(name); - if (len <= 0) - { - return RT_NULL; - } - - kname = (char *)kmem_get(len + 1); - if (!kname) - { - return RT_NULL; - } - - if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) - { - kmem_put(kname); - return RT_NULL; - } - - thread = rt_thread_find(name); - - kmem_put(kname); - - return thread; -} - -/** - * @brief Gets the current system tick count. - * - * This system call returns the current value of the system tick counter, which is typically incremented - * at a fixed interval (e.g., every millisecond or microsecond). The tick count can be used for timing - * purposes, such as measuring the elapsed time or triggering time-based events. - * - * @return rt_tick_t The current value of the system tick counter. - * - * @note The system tick counter typically wraps around after reaching its maximum value, so the - * returned tick value may reset after a certain period of time, depending on the configuration - * of the system tick timer. - * - * @warning Be cautious when using the tick value for time-based calculations, as the counter may - * overflow and wrap around. Ensure that the code handling the tick count properly accounts - * for potential overflow. - * - * @see sys_tick_init(), sys_tick_delay() - */ -rt_tick_t sys_tick_get(void) -{ - return rt_tick_get(); -} - -/** - * @brief Delays the current thread for a specified number of milliseconds. - * - * This system call puts the calling thread to sleep for a given number of milliseconds. It is a blocking - * call, meaning the thread will not execute any further instructions until the specified delay has - * passed. The delay is achieved by the system's timer mechanism. - * - * @param[in] ms The number of milliseconds to delay. The value must be a positive integer. - * - * @return sysret_t Returns `0` on success, or a negative error code on failure. - * - * @note This function is useful for introducing a fixed delay in time-sensitive applications or when - * the thread needs to be paused before continuing execution. - * - * @warning Be cautious when using this function in real-time or time-critical applications, as - * excessive delays may affect overall system performance or responsiveness. - * - * @see sys_thread_delay(), sys_thread_sleep() - */ -sysret_t sys_thread_mdelay(rt_int32_t ms) -{ - return rt_thread_mdelay(ms); -} - -struct k_sigaction -{ - void (*handler)(int); - unsigned long flags; - void (*restorer)(void); - unsigned mask[2]; -}; - -/** - * @brief Changes the action taken by the system on receiving a signal. - * - * This system call allows a process to specify how signals should be handled. It allows you to set a - * new action for a specific signal, retrieve the old action, and define the signal mask that should - * be applied during the execution of the signal handler. - * - * @param[in] sig The signal number for which the action is to be set or retrieved. - * Signal numbers are typically defined as constants (e.g., `SIGINT`, `SIGTERM`). - * @param[in] act A pointer to a `k_sigaction` structure that specifies the new action for the signal. - * If `act` is `NULL`, the signal's action is not changed. - * @param[out] oact A pointer to a `k_sigaction` structure where the old action will be stored. - * If `oact` is `NULL`, the old action is not retrieved. - * @param[in] sigsetsize The size of the `sigset_t` structure used in `k_sigaction`. This is to ensure the - * compatibility with the signal mask size. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note The `k_sigaction` structure allows you to specify the signal handler, signal mask, and flags for - * the signal action. It is important to correctly configure the handler to prevent unexpected - * behavior in the signal handling process. - * - * @warning Be cautious when modifying signal handling behavior, as incorrect configuration may result - * in unhandled signals or undefined behavior. Signal handlers should be designed to perform - * minimal, safe operations. - * - * @see sys_signal(), sys_sigprocmask() - */ -sysret_t sys_sigaction(int sig, const struct k_sigaction *act, - struct k_sigaction *oact, size_t sigsetsize) -{ - int ret = -RT_EINVAL; - struct rt_lwp *lwp; - struct lwp_sigaction kact, *pkact = RT_NULL; - struct lwp_sigaction koact, *pkoact = RT_NULL; - - if (!sigsetsize) - { - SET_ERRNO(EINVAL); - goto out; - } - if (sigsetsize > sizeof(lwp_sigset_t)) - { - sigsetsize = sizeof(lwp_sigset_t); - } - if (!act && !oact) - { - SET_ERRNO(EINVAL); - goto out; - } - if (oact) - { - if (!lwp_user_accessable((void *)oact, sizeof(*oact))) - { - SET_ERRNO(EFAULT); - goto out; - } - pkoact = &koact; - } - if (act) - { - if (!lwp_user_accessable((void *)act, sizeof(*act))) - { - SET_ERRNO(EFAULT); - goto out; - } - kact.sa_flags = act->flags; - kact.__sa_handler._sa_handler = act->handler; - lwp_memcpy(&kact.sa_mask, &act->mask, sigsetsize); - kact.sa_restorer = act->restorer; - pkact = &kact; - } - - lwp = lwp_self(); - RT_ASSERT(lwp); - ret = lwp_signal_action(lwp, sig, pkact, pkoact); -#ifdef ARCH_MM_MMU - if (ret == 0 && oact) - { - lwp_put_to_user(&oact->handler, &pkoact->__sa_handler._sa_handler, sizeof(void (*)(int))); - lwp_put_to_user(&oact->mask, &pkoact->sa_mask, sigsetsize); - lwp_put_to_user(&oact->flags, &pkoact->sa_flags, sizeof(int)); - lwp_put_to_user(&oact->restorer, &pkoact->sa_restorer, sizeof(void (*)(void))); - } -#endif /* ARCH_MM_MMU */ -out: - return (ret < 0 ? GET_ERRNO() : ret); -} - -static int mask_command_u2k[] = { - [SIG_BLOCK] = LWP_SIG_MASK_CMD_BLOCK, - [SIG_UNBLOCK] = LWP_SIG_MASK_CMD_UNBLOCK, - [SIG_SETMASK] = LWP_SIG_MASK_CMD_SET_MASK, -}; - -/** - * @brief Sets or retrieves the signal mask for the calling process. - * - * This system call allows the caller to block or unblock signals by modifying the signal mask. The signal - * mask determines which signals are blocked and which signals can be delivered to the process. The - * function can also be used to retrieve the current signal mask. - * - * @param[in] how The action to be taken on the signal mask. It can be one of the following values: - * - `SIG_BLOCK`: Add the signals in `sigset` to the current mask. - * - `SIG_UNBLOCK`: Remove the signals in `sigset` from the current mask. - * - `SIG_SETMASK`: Set the signal mask to the value in `sigset`, replacing the current mask. - * @param[in] sigset A pointer to a `sigset_t` structure that specifies the signals to be blocked or unblocked. - * This parameter is ignored when `how` is `SIG_SETMASK`, in which case `sigset` is used - * as the new signal mask. - * @param[out] oset A pointer to a `sigset_t` structure where the previous signal mask will be stored. - * If `oset` is `NULL`, the previous signal mask will not be returned. - * @param[in] size The size of the `sigset_t` structure, ensuring compatibility with the signal mask. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note Modifying the signal mask prevents signals from being delivered to the process while they are blocked. - * Once the signals are unblocked, they will be delivered to the process if they are pending. - * - * @warning Be careful when blocking signals, especially critical ones like `SIGKILL`, as it may interfere - * with the normal operation of the system. Ensure that signal masks are managed carefully to avoid - * missing important signals. - * - * @see sys_sigaction(), sys_sigpending() - */ -sysret_t sys_sigprocmask(int how, const sigset_t *sigset, sigset_t *oset, size_t size) -{ - int ret = -1; - lwp_sigset_t *pnewset = RT_NULL, *poldset = RT_NULL; -#ifdef ARCH_MM_MMU - lwp_sigset_t newset, oldset; -#endif /* ARCH_MM_MMU*/ - - if (!size) - { - return -EINVAL; - } - if (!oset && !sigset) - { - return -EINVAL; - } - if (size > sizeof(lwp_sigset_t)) - { - size = sizeof(lwp_sigset_t); - } - if (oset) - { -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)oset, size)) - { - return -EFAULT; - } - poldset = &oldset; -#else - if (!lwp_user_accessable((void *)oset, size)) - { - return -EFAULT; - } - poldset = (lwp_sigset_t *)oset; -#endif - } - if (sigset) - { -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)sigset, size)) - { - return -EFAULT; - } - lwp_get_from_user(&newset, (void *)sigset, size); - pnewset = &newset; -#else - if (!lwp_user_accessable((void *)sigset, size)) - { - return -EFAULT; - } - pnewset = (lwp_sigset_t *)sigset; -#endif /* ARCH_MM_MMU */ - } - ret = lwp_thread_signal_mask(rt_thread_self(), mask_command_u2k[how], pnewset, poldset); -#ifdef ARCH_MM_MMU - if (ret < 0) - { - return ret; - } - if (oset) - { - lwp_put_to_user(oset, poldset, size); - } -#endif /* ARCH_MM_MMU */ - return (ret < 0 ? -EFAULT : ret); -} - -/** - * @brief Retrieves the set of signals that are pending for delivery to the calling process. - * - * This system call allows a process to query the set of signals that are pending, i.e., signals that have - * been sent to the process but have not yet been delivered because the process is blocking those signals - * or has not yet handled them. The function returns the set of signals that are waiting to be delivered. - * - * @param[out] sigset A pointer to a `sigset_t` structure where the set of pending signals will be stored. - * The `sigset_t` structure will contain the signals that are pending for delivery. - * @param[in] sigsize The size of the `sigset_t` structure, used to ensure compatibility with the signal set. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note The returned signal set will contain the signals that have been sent to the process but are blocked, - * or that are waiting to be handled. These signals will be delivered once they are unblocked or the - * process handles them. - * - * @warning Be cautious when querying pending signals, as unblocking too many signals at once may lead - * to unexpected behavior or a flood of signal deliveries. It's recommended to carefully manage - * the signals the process can accept at any given time. - * - * @see sys_sigaction(), sys_sigprocmask() - */ -sysret_t sys_sigpending(sigset_t *sigset, size_t sigsize) -{ - sysret_t ret = 0; - lwp_sigset_t lwpset; - - /* Verify and Get sigset, timeout */ - if (!sigset || !lwp_user_accessable((void *)sigset, sigsize)) - { - ret = -EFAULT; - } - else - { - /* Fit sigset size to lwp set */ - if (sizeof(lwpset) < sigsize) - { - LOG_I("%s: sigsize (%lx) extends lwp sigset chunk\n", __func__, sigsize); - sigsize = sizeof(lwpset); - } - - lwp_thread_signal_pending(rt_thread_self(), &lwpset); - - if (!lwp_put_to_user(sigset, &lwpset, sigsize)) - RT_ASSERT(0); /* should never happened */ - } - - return ret; -} - -/** - * @brief Waits for a signal to be delivered, with a timeout. - * - * This system call allows a process to wait for one of the signals specified in the `sigset` to be delivered. - * The process will block until a signal in the set is received or the specified timeout period expires. - * If the signal is received before the timeout, information about the signal will be returned in the `siginfo_t` structure. - * If the timeout expires without any signal being delivered, the function will return with an appropriate error code. - * - * @param[in] sigset A pointer to a `sigset_t` structure that specifies the set of signals to wait for. - * The function will block until a signal in this set is received, or the timeout expires. - * @param[out] info A pointer to a `siginfo_t` structure where information about the delivered signal will be stored. - * If no signal is received before the timeout, this structure will not be filled. - * @param[in] timeout A pointer to a `timespec` structure that specifies the maximum amount of time to wait - * for a signal to be delivered. If this value is `NULL`, the function will wait indefinitely. - * @param[in] sigsize The size of the `sigset_t` structure, ensuring compatibility with the signal set. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note If the `timeout` is `NULL`, the function will block indefinitely until a signal is delivered. - * If a signal is received, the corresponding information is returned in the `siginfo_t` structure. - * The `sigset` should contain only the signals you are interested in. - * - * @warning If the timeout expires, no signal will be delivered, and the function will return a timeout error. - * Make sure to handle the timeout case correctly to prevent any unexpected behavior. - * - * @see sys_sigaction(), sys_sigtimedwait() - */ -sysret_t sys_sigtimedwait(const sigset_t *sigset, siginfo_t *info, const struct timespec *timeout, size_t sigsize) -{ - int sig; - size_t ret; - lwp_sigset_t lwpset; - siginfo_t kinfo; - struct timespec ktimeout; - struct timespec *ptimeout; - - /* for RT_ASSERT */ - RT_UNUSED(ret); - - /* Fit sigset size to lwp set */ - if (sizeof(lwpset) < sigsize) - { - LOG_I("%s: sigsize (%lx) extends lwp sigset chunk\n", __func__, sigsize); - sigsize = sizeof(lwpset); - } - else - { - /* if sigset of user is smaller, clear extra space */ - memset(&lwpset, 0, sizeof(lwpset)); - } - - /* Verify and Get sigset, timeout */ - if (!sigset || !lwp_user_accessable((void *)sigset, sigsize)) - { - return -EFAULT; - } - else - { - ret = lwp_get_from_user(&lwpset, (void *)sigset, sigsize); - RT_ASSERT(ret == sigsize); - } - - if (timeout) - { - if (!lwp_user_accessable((void *)timeout, sizeof(*timeout))) - return -EFAULT; - else - { - ret = lwp_get_from_user(&ktimeout, (void *)timeout, sizeof(*timeout)); - ptimeout = &ktimeout; - RT_ASSERT(ret == sizeof(*timeout)); - } - } - else - { - ptimeout = RT_NULL; - } - - sig = lwp_thread_signal_timedwait(rt_thread_self(), &lwpset, &kinfo, ptimeout); - - if (sig > 0 && info) - { - if (!lwp_user_accessable((void *)info, sizeof(*info))) - return -EFAULT; - else - { - ret = lwp_put_to_user(info, &kinfo, sizeof(*info)); - RT_ASSERT(ret == sizeof(*info)); - } - } - - return sig; -} - -/** - * @brief Sends a signal to a specific thread. - * - * This system call allows a process to send a signal to a specific thread within the same process. - * The signal specified by the `sig` parameter will be delivered to the thread with the ID `tid`. - * This function is similar to `kill()`, but it targets a specific thread rather than a process. - * - * @param[in] tid The thread ID to which the signal will be sent. This ID identifies the target thread within the same process. - * @param[in] sig The signal number to be sent to the specified thread. The signal can be any valid signal number. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note The `sig` parameter must be a valid signal number. It can range from `1` to `31`, or be one of the predefined signal constants like `SIGKILL`, `SIGTERM`, etc. - * If the target thread does not exist or is not eligible to receive the signal, the function will fail. - * - * @warning If an invalid signal number is provided or the target thread does not exist, an error will be returned. - * Be careful when sending signals, as some signals (e.g., `SIGKILL`) can immediately terminate the target thread. - * - * @see sys_kill(), sys_sigaction() - */ -sysret_t sys_tkill(int tid, int sig) -{ -#ifdef ARCH_MM_MMU - rt_thread_t thread; - sysret_t ret; - - /** - * Brief: Match a tid and do the kill - * - * Note: Critical Section - * - the thread (READ. may be released at the meantime; protected by locked) - */ - thread = lwp_tid_get_thread_and_inc_ref(tid); - ret = lwp_thread_signal_kill(thread, sig, SI_USER, 0); - lwp_tid_dec_ref(thread); - - return ret; -#else - return lwp_thread_kill((rt_thread_t)tid, sig); -#endif -} - -/** - * @brief Manipulates the signal mask for the current thread. - * - * This function allows a thread to modify its signal mask, which controls which signals are blocked (prevented from delivery). - * The signal mask can be modified by adding, removing, or setting the signal set as a whole, depending on the `how` parameter. - * The `sigset` specifies the signals to be manipulated, and the current signal mask before modification can be retrieved in `oset`. - * - * @param[in] how The operation to perform on the signal mask. It can be one of the following values: - * - `SIG_BLOCK`: Add the signals in `sigset` to the current mask (block those signals). - * - `SIG_UNBLOCK`: Remove the signals in `sigset` from the current mask (unblock those signals). - * - `SIG_SETMASK`: Set the signal mask to the signals specified in `sigset` (replace the current mask). - * @param[in] sigset A pointer to an `lwp_sigset_t` structure that specifies the set of signals to be manipulated. - * Signals in this set will be added, removed, or set in the current thread's signal mask based on the `how` parameter. - * @param[out] oset A pointer to an `lwp_sigset_t` structure where the previous signal mask will be stored. - * This allows the caller to restore the previous mask if needed. - * @param[in] size The size of the `lwp_sigset_t` structure. This ensures that the correct structure size is used during signal mask manipulation. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note The `sigset` structure should contain only valid signal numbers. If `how` is `SIG_SETMASK`, the entire signal mask will be replaced by the contents of `sigset`. - * If `how` is `SIG_BLOCK` or `SIG_UNBLOCK`, the signals in `sigset` will be added or removed from the current mask, respectively. - * - * @warning Be cautious when modifying the signal mask, as blocking signals can cause the thread to miss important signals. - * If a signal is blocked, it will not be delivered to the thread until it is unblocked, or the thread is explicitly made to handle it. - * - * @see sys_sigaction(), sys_thread_sigpending() - */ -sysret_t sys_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset, size_t size) -{ - int ret = -1; - lwp_sigset_t *pnewset = RT_NULL, *poldset = RT_NULL; -#ifdef ARCH_MM_MMU - lwp_sigset_t newset, oldset; -#endif /* ARCH_MM_MMU */ - - if (!size) - { - return -EINVAL; - } - if (!oset && !sigset) - { - return -EINVAL; - } - if (size != sizeof(lwp_sigset_t)) - { - return -EINVAL; - } - if (oset) - { -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)oset, size)) - { - return -EFAULT; - } - poldset = &oldset; -#else - if (!lwp_user_accessable((void *)oset, size)) - { - return -EFAULT; - } - poldset = oset; -#endif - } - if (sigset) - { -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)sigset, size)) - { - return -EFAULT; - } - lwp_get_from_user(&newset, (void *)sigset, sizeof(lwp_sigset_t)); - pnewset = &newset; -#else - if (!lwp_user_accessable((void *)sigset, size)) - { - return -EFAULT; - } - pnewset = (lwp_sigset_t *)sigset; -#endif - } - ret = lwp_thread_signal_mask(rt_thread_self(), mask_command_u2k[how], pnewset, poldset); - if (ret < 0) - { - return ret; - } -#ifdef ARCH_MM_MMU - if (oset) - { - lwp_put_to_user(oset, poldset, sizeof(lwp_sigset_t)); - } -#endif - return (ret < 0 ? -EFAULT : ret); -} - -#ifndef ARCH_MM_MMU - -/** - * @brief Sets the signal handler for a specific signal in the current thread. - * - * This function allows a thread to set a custom signal handler for a specific signal. - * When the specified signal is received, the corresponding handler function (`func`) will be invoked. - * The handler will be executed in the context of the current thread, allowing it to handle signals as needed. - * - * @param[in] sig The signal number for which the handler is being set. This should be a valid signal number. - * @param[in] func The signal handler function to be set. This function will be called when the specified signal is received. - * The signature of `func` should match the expected format for signal handlers. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note The `func` parameter must be a valid function pointer. It will be called when the signal specified by `sig` is received by the thread. - * The handler function typically has the signature: `void handler(int sig)`, where `sig` is the signal number. - * - * @warning Setting a custom signal handler may override any default actions for that signal. - * It is important to ensure that the handler function is properly designed to handle the signal appropriately. - * For example, some signals may require special handling (e.g., `SIGKILL` cannot be caught). - * - * @see sys_signal(), sys_lwp_sigaction() - */ -sysret_t sys_lwp_sighandler_set(int sig, lwp_sighandler_t func) -{ - if (!lwp_user_accessable((void *)func, sizeof(lwp_sighandler_t))) - { - return -EFAULT; - } - - lwp_sighandler_set(sig, func); - return 0; -} - -/** - * @brief Sets the signal handler for a specific signal in the target thread. - * - * This function allows setting a custom signal handler for a specific signal within a particular thread. - * When the specified signal is delivered to the thread, the corresponding handler function (`func`) will be invoked. - * The handler is executed in the context of the thread that receives the signal. - * - * @param[in] sig The signal number for which the handler is being set. This should be a valid signal number. - * @param[in] func The signal handler function to be set. This function will be called when the specified signal is delivered to the target thread. - * The handler function should have the signature `void func(int sig)`. - * - * @return sysret_t Returns `0` on success or a negative error code on failure. - * - * @note The `func` parameter must be a valid function pointer, and it will be invoked when the signal specified by `sig` is delivered to the target thread. - * The handler function typically has the signature: `void handler(int sig)`, where `sig` is the signal number. - * - * @warning Setting a custom signal handler may override any default actions for that signal. - * It is important to ensure that the handler function is properly designed to handle the signal appropriately. - * For example, some signals may require special handling (e.g., `SIGKILL` cannot be caught). - * - * @see sys_thread_sigprocmask(), sys_thread_sigaction(), sys_signal() - */ -sysret_t sys_thread_sighandler_set(int sig, lwp_sighandler_t func) -{ - if (!lwp_user_accessable((void *)func, sizeof(lwp_sighandler_t))) - { - return -EFAULT; - } - - lwp_thread_sighandler_set(sig, func); - return 0; -} -#endif /* not defined ARCH_MM_MMU */ - -/** - * @brief Waits for the termination of a child process. - * - * This function makes the calling process wait until one of its child processes terminates or until a - * specified condition is met. The function retrieves information about the terminated child process, - * including its exit status, and can also handle other process-related events, such as stopping or - * continuing execution. - * - * @param[in] pid The process ID of the child process to wait for. If `pid` is: - * - `-1`: Wait for any child process. - * - `0`: Wait for any child process in the same process group. - * - > 0: Wait for the child process with the specified process ID. - * @param[out] status A pointer to an integer where the exit status of the terminated child process will be stored. - * This value provides information about the child's termination status, such as normal exit, - * signal termination, etc. - * @param[in] options A bitmask of options that control the behavior of the function. It can include: - * - `WNOHANG`: Return immediately if no child has exited. - * - `WUNTRACED`: Report status of stopped child processes. - * - Other flags can be defined depending on the implementation. - * - * @return sysret_t Returns the process ID of the child process that terminated on success, or a negative error code on failure. - * - * @note The `status` argument provides detailed information about the termination of the child process. To interpret this - * status, macros such as `WIFEXITED()`, `WIFSIGNALED()`, and `WEXITSTATUS()` are commonly used. - * - * @warning This function should be used carefully when managing child processes, as not properly handling child processes - * may lead to zombie processes. - * - * @see sys_wait(), sys_waitid() - */ -sysret_t sys_waitpid(int32_t pid, int *status, int options) -{ - int ret = -1; -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)status, sizeof(int))) - { - return -EFAULT; - } - else - { - ret = lwp_waitpid(pid, status, options, RT_NULL); - } -#else - if (!lwp_user_accessable((void *)status, sizeof(int))) - { - return -EFAULT; - } - ret = waitpid(pid, status, options); -#endif - return ret; -} - -#if defined(RT_USING_SAL) && defined(SAL_USING_POSIX) -struct musl_addrinfo -{ - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - socklen_t ai_addrlen; - - struct musl_sockaddr *ai_addr; - char *ai_canonname; - - struct musl_addrinfo *ai_next; -}; - -/** - * @brief Resolves network addresses and service names into a list of address structures. - * - * This function provides a mechanism for resolving a host name and service name (or port number) - * into a list of address structures suitable for use with socket-based communication. The function - * can handle both IPv4 and IPv6 addresses and provides a flexible way to specify different types - * of address and service resolution options. - * - * @param[in] nodename The host name to be resolved. This can be a DNS name or an IP address in string format. - * If `nodename` is `NULL`, the function will resolve the local host. - * @param[in] servname The service name (e.g., "http") or port number to be resolved. If `servname` is `NULL`, - * the function will resolve the port number or address without any service association. - * @param[in] hints A pointer to a `struct musl_addrinfo` that provides hints for the address resolution. - * It can be used to specify criteria such as the desired address family (IPv4 or IPv6), - * socket type, protocol, and flags for resolution. - * @param[out] res A pointer to a `struct musl_addrinfo` that will be filled with the resolved address - * information. The resulting linked list will contain one or more `struct musl_sockaddr` - * structures, each representing a different address that can be used for communication. - * - * @return sysret_t Returns `0` on success, indicating that the address resolution was successful. - * On failure, returns a negative error code. - * - * @note The `res` parameter points to a linked list of resolved address structures. Each node in the list - * contains a different resolved address and can be used for socket connection purposes. - * It is important to free the memory allocated for the list after it is no longer needed, using `freeaddrinfo()`. - * - * @warning The `hints` structure allows you to specify various options for the resolution. However, incorrect - * hints may lead to unexpected or incorrect results. For example, if you request IPv6 addresses but - * the host only supports IPv4, the function may not return the expected results. - * - * @see freeaddrinfo(), sys_socket(), sys_connect(), sys_gethostbyname(), sys_gethostbyaddr() - */ -sysret_t sys_getaddrinfo(const char *nodename, - const char *servname, - const struct musl_addrinfo *hints, - struct musl_addrinfo *res) -{ - int ret = -1; - struct addrinfo *k_res = NULL; - char *k_nodename = NULL; - char *k_servname = NULL; - struct addrinfo *k_hints = NULL; -#ifdef ARCH_MM_MMU - int len = 0; -#endif - -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)res, sizeof(*res))) - { - SET_ERRNO(EFAULT); - goto exit; - } -#endif - if (nodename) - { -#ifdef ARCH_MM_MMU - len = lwp_user_strlen(nodename); - if (len <= 0) - { - SET_ERRNO(EFAULT); - goto exit; - } - - k_nodename = (char *)kmem_get(len + 1); - if (!k_nodename) - { - SET_ERRNO(ENOMEM); - goto exit; - } - - if (lwp_get_from_user(k_nodename, (void *)nodename, len + 1) != len + 1) - { - SET_ERRNO(EFAULT); - goto exit; - } -#else - k_nodename = rt_strdup(nodename); - if (!k_nodename) - { - SET_ERRNO(ENOMEM); - goto exit; - } -#endif - } - if (servname) - { -#ifdef ARCH_MM_MMU - len = lwp_user_strlen(servname); - if (len <= 0) - { - SET_ERRNO(EFAULT); - goto exit; - } - - k_servname = (char *)kmem_get(len + 1); - if (!k_servname) - { - SET_ERRNO(ENOMEM); - goto exit; - } - - if (lwp_get_from_user(k_servname, (void *)servname, len + 1) < 0) - { - SET_ERRNO(EFAULT); - goto exit; - } -#else - k_servname = rt_strdup(servname); - if (!k_servname) - { - SET_ERRNO(ENOMEM); - goto exit; - } -#endif - } - - if (hints) - { -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)hints, sizeof(*hints))) - { - SET_ERRNO(EFAULT); - goto exit; - } -#endif - k_hints = (struct addrinfo *)rt_malloc(sizeof *hints); - if (!k_hints) - { - SET_ERRNO(ENOMEM); - goto exit; - } - - rt_memset(k_hints, 0x0, sizeof(struct addrinfo)); - k_hints->ai_flags = hints->ai_flags; - k_hints->ai_family = hints->ai_family; - k_hints->ai_socktype = hints->ai_socktype; - k_hints->ai_protocol = hints->ai_protocol; - k_hints->ai_addrlen = hints->ai_addrlen; - } - - ret = sal_getaddrinfo(k_nodename, k_servname, k_hints, &k_res); - if (ret == 0) - { - /* set sockaddr */ - sockaddr_tomusl(k_res->ai_addr, res->ai_addr); - res->ai_addrlen = k_res->ai_addrlen; - - /* set up addrinfo */ - res->ai_family = k_res->ai_family; - res->ai_flags = k_res->ai_flags; - res->ai_next = NULL; - - if (hints != NULL) - { - /* copy socktype & protocol from hints if specified */ - res->ai_socktype = hints->ai_socktype; - res->ai_protocol = hints->ai_protocol; - } - - sal_freeaddrinfo(k_res); - k_res = NULL; - } - -exit: - if (ret < 0) - { - ret = GET_ERRNO(); - } -#ifdef ARCH_MM_MMU - if (k_nodename) - { - kmem_put(k_nodename); - } -#else - if (k_nodename) - { - rt_free(k_nodename); - } -#endif -#ifdef ARCH_MM_MMU - if (k_servname) - { - kmem_put(k_servname); - } -#else - if (k_servname) - { - rt_free(k_servname); - } -#endif - if (k_hints) - { - rt_free(k_hints); - } - - return ret; -} - -#define HOSTENT_BUFSZ 512 - -/** - * @brief Resolves a host name to an address, with support for specifying the address family. - * - * This function performs a lookup of the specified host name, and resolves it to an address, - * while allowing the caller to specify the desired address family (e.g., IPv4 or IPv6). It is - * a reentrant version of `gethostbyname2`, meaning it is safe for use in multi-threaded applications. - * The results are returned in a user-provided buffer to avoid memory allocation overhead. - * - * @param[in] name The host name to be resolved. This can be a DNS name or an IP address in string format. - * @param[in] af The address family to use for the resolution. Common values are: - * - `AF_INET` for IPv4 addresses. - * - `AF_INET6` for IPv6 addresses. - * @param[out] ret A pointer to a `struct hostent` where the resolved host information will be stored. - * This includes the host name, alias names, address type, and the address itself. - * @param[in] buf A buffer to store additional information required for the `struct hostent` structure. - * This is needed to ensure the reentrant behavior and avoid memory allocation. - * @param[in] buflen The size of the buffer provided. - * @param[out] result A pointer to a `struct hostent*` that will point to the resolved host entry. - * This will be set to the value of `ret` upon success. - * @param[out] err A pointer to an integer where error codes will be stored. If the function fails, - * `err` will contain a non-zero value corresponding to the error. - * - * @return sysret_t Returns `0` on success, indicating the resolution was successful. - * On failure, returns a negative error code that indicates the failure reason. - * - * @note This function is reentrant and thread-safe, meaning it does not use static memory or global state. - * It relies on the buffers provided by the caller to store the resolved data. - * - * @see gethostbyname2(), sys_gethostbyname(), sys_socket(), sys_connect(), sys_getaddrinfo() - */ -sysret_t sys_gethostbyname2_r(const char *name, int af, struct hostent *ret, - char *buf, size_t buflen, - struct hostent **result, int *err) -{ - int ret_val = -1; - int sal_ret = -1, sal_err = -1; - struct hostent sal_he, sal_tmp; - struct hostent *sal_result = NULL; - char *sal_buf = NULL; - char *k_name = NULL; - int len = 0; - -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)err, sizeof(*err))) - { - SET_ERRNO(EFAULT); - goto __exit; - } - - if (!lwp_user_accessable((void *)result, sizeof(*result)) || !lwp_user_accessable((void *)ret, sizeof(*ret)) || !lwp_user_accessable((void *)buf, buflen)) - { - /* not all arguments given */ - *err = EFAULT; - SET_ERRNO(EFAULT); - goto __exit; - } - - len = lwp_user_strlen(name); - if (len <= 0) - { - *err = EFAULT; - SET_ERRNO(EFAULT); - goto __exit; - } - - k_name = (char *)kmem_get(len + 1); - if (!k_name) - { - SET_ERRNO(ENOMEM); - goto __exit; - } - - if (lwp_get_from_user(k_name, (void *)name, len + 1) < 0) - { - SET_ERRNO(EFAULT); - goto __exit; - } -#else - k_name = rt_strdup(name); - if (k_name == NULL) - { - SET_ERRNO(ENOMEM); - goto __exit; - } -#endif - - *result = ret; - sal_buf = (char *)malloc(HOSTENT_BUFSZ); - if (sal_buf == NULL) - { - SET_ERRNO(ENOMEM); - goto __exit; - } - - /* get host by name in SAL */ - sal_ret = sal_gethostbyname_r(k_name, &sal_he, sal_buf, HOSTENT_BUFSZ, &sal_result, &sal_err); - if (sal_ret == 0) - { - int index = 0, cnt = 0; - char *ptr = buf; - - /* get counter */ - index = 0; - while (sal_he.h_addr_list[index] != NULL) - { - index++; - } - cnt = index + 1; - -#ifdef ARCH_MM_MMU - /* update user space hostent */ - lwp_put_to_user(buf, k_name, buflen - (ptr - buf)); - lwp_memcpy(&sal_tmp, &sal_he, sizeof(sal_he)); - sal_tmp.h_name = ptr; - ptr += rt_strlen(k_name); - - sal_tmp.h_addr_list = (char **)ptr; - ptr += cnt * sizeof(char *); - - index = 0; - while (sal_he.h_addr_list[index] != NULL) - { - sal_tmp.h_addr_list[index] = ptr; - lwp_memcpy(ptr, sal_he.h_addr_list[index], sal_he.h_length); - - ptr += sal_he.h_length; - index++; - } - sal_tmp.h_addr_list[index] = NULL; - lwp_put_to_user(ret, &sal_tmp, sizeof(sal_tmp)); -#else - /* update user space hostent */ - ret->h_addrtype = sal_he.h_addrtype; - ret->h_length = sal_he.h_length; - - rt_strncpy(ptr, k_name, buflen - (ptr - buf)); - ret->h_name = ptr; - ptr += strlen(k_name); - - ret->h_addr_list = (char **)ptr; - ptr += cnt * sizeof(char *); - - index = 0; - while (sal_he.h_addr_list[index] != NULL) - { - ret->h_addr_list[index] = ptr; - lwp_memcpy(ptr, sal_he.h_addr_list[index], sal_he.h_length); - - ptr += sal_he.h_length; - index++; - } - ret->h_addr_list[index] = NULL; -#endif - ret_val = 0; - } - else - { - SET_ERRNO(EINVAL); - } - -__exit: - if (ret_val < 0) - { - ret_val = GET_ERRNO(); - } - - /* release buffer */ - if (sal_buf) - { - free(sal_buf); - } -#ifdef ARCH_MM_MMU - if (k_name) - { - kmem_put(k_name); - } -#else - if (k_name) - { - free(k_name); - } -#endif - - return ret_val; -} -#endif - -/** - * @brief Gets the current working directory. - * - * This function retrieves the absolute pathname of the current working directory - * and stores it in the provided buffer. The buffer must be large enough to hold - * the directory path, including the null-terminator. If the buffer is too small, - * the function will return an error. - * - * @param[out] buf A pointer to a buffer where the current working directory - * path will be stored. The buffer should be large enough - * to hold the path, including the null-terminator. - * @param[in] size The size of the buffer in bytes. The buffer must be large enough - * to accommodate the full path. - * - * @return long On success, returns the number of bytes written to the buffer - * (not including the null-terminator). If the buffer is not large - * enough, returns a negative error code. - * - * @note The maximum path length depends on the system's configuration. - * Ensure the buffer size is sufficient to hold the entire path. - * - * @see sys_chdir(), sys_getcwd_r(), sys_realpath() - */ -long sys_getcwd(char *buf, size_t size) -{ - char *tmp = RT_NULL; - long ret = -1; - - if (!lwp_user_accessable((void *)buf, size)) - { - return ret; - } - - tmp = (char *)rt_malloc(size); - if (!tmp) - { - return ret; - } - - if (getcwd(tmp, size) != RT_NULL) - { - if (lwp_put_to_user(buf, tmp, size) > 0) - { - if (buf != RT_NULL) - ret = strlen(buf); - else - ret = -EFAULT; - } - } - - rt_free(tmp); - - return ret; -} - -/** - * @brief Changes the current working directory. - * - * This function changes the current working directory of the calling process - * to the directory specified by the given path. The path can be absolute or - * relative. If the specified path does not exist or the process does not have - * sufficient permissions, the function will return an error. - * - * @param[in] path The path to the new working directory. This can be either - * an absolute or a relative path. - * - * @return sysret_t Returns `0` on success, indicating that the working - * directory was successfully changed. On failure, returns a - * negative error code indicating the reason for failure. - * - * @note If the specified path is a relative path, it is interpreted relative - * to the current working directory. - * - * @see sys_getcwd(), sys_chdir() for changing directories, sys_opendir(), sys_stat() - */ -sysret_t sys_chdir(const char *path) -{ -#ifdef ARCH_MM_MMU - int err = 0; - int len = 0; - int errcode; - char *kpath = RT_NULL; - - len = lwp_user_strlen(path); - if (len <= 0) - { - return -EFAULT; - } - - kpath = (char *)kmem_get(len + 1); - if (!kpath) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1)) - { - kmem_put(kpath); - return -EINVAL; - } - - err = chdir(kpath); - errcode = err != 0 ? GET_ERRNO() : 0; - - kmem_put(kpath); - - return errcode; -#else - int ret = chdir(path); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Changes the current working directory to the one associated with the specified file descriptor. - * - * This function changes the current working directory of the calling process - * to the directory associated with the file descriptor `fd`. The file descriptor - * should refer to an open directory. If the file descriptor does not refer to a - * directory, or if the process lacks the necessary permissions, the function will - * return an error. - * - * @param[in] fd The file descriptor referring to the directory to which the current - * working directory should be changed. - * - * @return sysret_t Returns `0` on success, indicating the current working directory - * was successfully changed. On failure, returns a negative error code. - * - * @note The file descriptor must refer to a directory. If it refers to a file or other - * non-directory object, the operation will fail. - * - * @see sys_chdir(), sys_getcwd(), sys_opendir(), sys_open() - */ -sysret_t sys_fchdir(int fd) -{ - int errcode = -ENOSYS; -#ifdef ARCH_MM_MMU -#ifdef RT_USING_DFS_V2 - int err = -1; - struct dfs_file *d; - char *kpath; - - d = fd_get(fd); - if (!d || !d->vnode) - { - return -EBADF; - } - kpath = dfs_dentry_full_path(d->dentry); - if (!kpath) - { - return -EACCES; - } - - err = chdir(kpath); - errcode = err != 0 ? GET_ERRNO() : 0; - - kmem_put(kpath); -#endif -#endif - return errcode; -} - -/** - * @brief Creates a new directory with the specified path and mode. - * - * This function creates a new directory with the specified path and mode. - * The directory is created with the permissions specified by the `mode` parameter. - * If the directory already exists, the function will return an error. - * - * @param[in] path The path of the directory to be created. This can be an absolute - * or relative path. If the directory does not exist, it will be created. - * @param[in] mode The permissions to be set for the new directory. This parameter - * specifies the access permissions for the directory owner, group, and others. - * - * @return sysret_t Returns `0` on success, indicating that the directory was successfully - * created. On failure, returns a negative error code indicating the reason for failure. - * - * @note The `mode` parameter specifies the permissions for the new directory. The permissions - * are typically specified using the `S_IRWXU`, `S_IRWXG`, and `S_IRWXO` macros, which - * define the read, write, and execute permissions for the owner, group, and others. - * - * @see sys_rmdir(), sys_chdir(), sys_mkdirat() - */ -sysret_t sys_mkdir(const char *path, mode_t mode) -{ -#ifdef ARCH_MM_MMU - int err = 0; - int len = 0; - char *kpath = RT_NULL; - - len = lwp_user_strlen(path); - if (len <= 0) - { - return -EFAULT; - } - - kpath = (char *)kmem_get(len + 1); - if (!kpath) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1)) - { - kmem_put(kpath); - return -EINVAL; - } - - err = _SYS_WRAP(mkdir(kpath, mode)); - - kmem_put(kpath); - - return err; -#else - int ret = mkdir(path, mode); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Removes the specified directory. - * - * This function removes the directory specified by the given path. The directory - * must be empty for the operation to succeed. If the directory is not empty, the - * function will return an error. If the directory does not exist or the process - * lacks the necessary permissions, the function will also return an error. - * - * @param[in] path The path of the directory to be removed. This can be an absolute - * or relative path. The directory must be empty for the operation to succeed. - * - * @return sysret_t Returns `0` on success, indicating that the directory was successfully - * removed. On failure, returns a negative error code indicating the reason for failure. - * - * @note The directory must be empty for the operation to succeed. If the directory contains - * files or subdirectories, the operation will fail. To remove a non-empty directory, - * the contents must be deleted first. - * - * @see sys_mkdir(), sys_chdir(), sys_rmdirat() - */ -sysret_t sys_rmdir(const char *path) -{ - int err = 0; - int ret = 0; -#ifdef ARCH_MM_MMU - int len = 0; - char *kpath = RT_NULL; - - len = lwp_user_strlen(path); - if (len <= 0) - { - return -EFAULT; - } - - kpath = (char *)kmem_get(len + 1); - if (!kpath) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1)) - { - kmem_put(kpath); - return -EINVAL; - } - - ret = rmdir(kpath); - if (ret < 0) - { - err = GET_ERRNO(); - } - - kmem_put(kpath); - - return (err < 0 ? err : ret); -#else - ret = rmdir(path); - if (ret < 0) - { - err = GET_ERRNO(); - } - return (err < 0 ? err : ret); -#endif -} - -/** - * @brief Reads directory entries. - * - * This function reads the directory entries from the directory stream specified by the file descriptor `fd`. - * It stores the directory entries in the buffer pointed to by `dirp`, up to the specified number of bytes (`nbytes`). - * The entries are returned in a format compatible with the `struct libc_dirent` structure. - * - * @param[in] fd The file descriptor referring to an open directory. This descriptor must be obtained - * from a call to `sys_open()` with the appropriate flags for directory reading. - * @param[out] dirp A pointer to a buffer where the directory entries will be stored. - * The buffer must be large enough to hold at least `nbytes` bytes of directory entries. - * @param[in] nbytes The size of the buffer (`dirp`) in bytes. It limits the number of directory entries - * that can be read in a single call. The function will return as many entries as possible - * that fit within the buffer size. - * - * @return sysret_t Returns the number of bytes read on success, which may be less than `nbytes` if there - * are fewer entries in the directory or if the buffer is too small. - * On failure, returns a negative error code. - * - * @note If the function returns 0, it indicates the end of the directory stream. A negative return value - * indicates an error. The caller can use the returned number of bytes to process the entries in `dirp`. - * - * @see sys_open(), sys_close(), sys_readdir(), sys_stat() - */ -sysret_t sys_getdents(int fd, struct libc_dirent *dirp, size_t nbytes) -{ - int ret = -1; - struct dfs_file *file; - size_t cnt = (nbytes / sizeof(struct libc_dirent)); - size_t rtt_nbytes = 0; - struct dirent *rtt_dirp; - -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)dirp, sizeof(struct libc_dirent))) - { - return -EFAULT; - } -#endif - - if (cnt == 0) - { - return -EINVAL; - } - rtt_nbytes = cnt * sizeof(struct dirent); - rtt_dirp = (struct dirent *)rt_malloc(rtt_nbytes); - if (!rtt_dirp) - { - return -ENOMEM; - } - file = fd_get(fd); - ret = dfs_file_getdents(file, rtt_dirp, rtt_nbytes); - if (ret > 0) - { - size_t i = 0; - cnt = ret / sizeof(struct dirent); - for (i = 0; i < cnt; i++) - { - dirp[i].d_ino = 0; - dirp[i].d_off = i * sizeof(struct libc_dirent); - dirp[i].d_type = rtt_dirp[i].d_type; - dirp[i].d_reclen = sizeof(struct libc_dirent); - strcpy(dirp[i].d_name, rtt_dirp[i].d_name); - } - ret = cnt * sizeof(struct libc_dirent); - } - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - rt_free(rtt_dirp); - - return ret; -} - -/** - * @brief Retrieves the current value of the error code. - * - * This function returns the most recent error code set by a system call. The error code is typically set - * when a system call fails. This function allows users to retrieve the last error that occurred, helping to - * diagnose issues or handle errors in a more controlled manner. - * - * @return sysret_t The current error code. A value of `0` indicates no error, while a non-zero value - * represents the most recent error. The error codes are system-specific and can - * represent various failure conditions (e.g., `EINVAL`, `ENOMEM`, `EIO`). - * - * @note The error code returned is specific to the current thread or process and is typically updated - * each time a system call fails. The error code is persistent until it is overwritten by the next - * failed system call or explicitly reset. - * - * @see sys_set_errno(), sys_perror(), sys_strerror() - */ -sysret_t sys_get_errno(void) -{ - return rt_get_errno(); -} - -#ifdef ARCH_MM_MMU - -/** - * @brief Sets the thread-specific data area. - * - * This function is used to associate a specific area of memory with the current thread. The area pointed to - * by `p` is set as the thread's local storage. Thread-specific data is used to store data that is unique to - * each thread, allowing different threads to maintain independent state information. - * - * @param[in] p A pointer to the memory area that is to be set as the thread's local storage. This area - * will be used by the thread to store its specific data. The structure and size of the data - * area depend on the implementation and use case. - * - * @return sysret_t Returns `0` on success. On failure, it returns a negative error code. - * - * @note This function is typically used to set up thread-specific storage for managing data that should - * not be shared between threads. The data area is accessible only by the thread that set it, ensuring - * thread safety for the stored information. - * - * @see sys_get_thread_area(), sys_thread_create(), sys_thread_self() - */ -sysret_t sys_set_thread_area(void *p) -{ - rt_thread_t thread; - - thread = rt_thread_self(); - thread->thread_idr = p; - arch_set_thread_area(p); - - return 0; -} - -/** - * @brief Sets the address at which the thread ID is stored. - * - * This function sets the address of a variable that will store the thread ID for the calling thread. - * The specified address `tidptr` will hold the thread's unique identifier. This can be useful for - * managing thread-specific state or for synchronization mechanisms where the thread's ID needs to be - * shared or checked by other parts of the system. - * - * @param[in] tidptr A pointer to an integer where the thread ID will be stored. This value will - * hold the calling thread's ID, and it can be accessed to identify the thread - * later. The value of the thread ID can be used in various thread management - * operations. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function is typically used in systems that require associating a specific address with the - * thread ID, often in real-time or embedded systems where managing and accessing thread IDs is - * crucial for scheduling or resource allocation. - * - * @see sys_get_tid(), sys_thread_self(), sys_thread_create() - */ -sysret_t sys_set_tid_address(int *tidptr) -{ - rt_thread_t thread; - -#ifdef ARCH_MM_MMU - if (!lwp_user_accessable((void *)tidptr, sizeof(int))) - { - return -EFAULT; - } -#endif - thread = rt_thread_self(); - thread->clear_child_tid = tidptr; - return thread->tid; -} -#endif /* ARCH_MM_MMU */ - -/** - * @brief Gets the thread ID of the calling thread. - * - * This function returns the unique thread identifier (thread ID) for the calling thread. The thread ID - * can be used to uniquely identify the thread within the system and is typically used for debugging, - * thread management, or scheduling purposes. - * - * @return sysret_t The thread ID of the calling thread. The value is typically a positive integer - * representing the unique ID assigned to the thread. In case of failure, an error code - * may be returned. - * - * @note The thread ID returned by this function is unique within the system, and it may be used to - * reference or manipulate the specific thread associated with the ID. - * - * @see sys_set_tid_address(), sys_thread_self(), sys_thread_create() - */ -sysret_t sys_gettid(void) -{ - return rt_thread_self()->tid; -} - -/** - * @brief Checks the accessibility of a file or directory. - * - * This function checks whether the calling process has the specified access rights for the given file or directory. - * The check is performed based on the provided `mode`, which can indicate whether read, write, or execute permissions - * are required. - * - * @param[in] filename The path to the file or directory whose accessibility is being checked. - * @param[in] mode The access mode to check for. This can be a combination of the following: - * - `R_OK`: Check for read permission. - * - `W_OK`: Check for write permission. - * - `X_OK`: Check for execute permission. - * - `F_OK`: Check if the file exists. - * - * @return sysret_t Returns `0` if the specified access is allowed. On failure, returns a negative error code. - * - * @note This function does not modify the file or directory, it only checks if the specified access rights are granted. - * - * @see sys_open(), sys_stat(), sys_fstat(), sys_chmod(), sys_chown() - */ -sysret_t sys_access(const char *filename, int mode) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - rt_size_t len = 0; - char *kfilename = RT_NULL; - - len = lwp_user_strlen(filename); - if (len <= 0) - { - return -EINVAL; - } - - kfilename = (char *)kmem_get(len + 1); - if (!kfilename) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kfilename, (void *)filename, len + 1) != (len + 1)) - { - kmem_put(kfilename); - return -EFAULT; - } - - ret = access(kfilename, mode); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kfilename); - return ret; -#else - ret = access(filename, mode); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Creates a pipe, a unidirectional data channel. - * - * This function creates a pipe, which is a unidirectional data channel used for inter-process communication. - * The pipe consists of two file descriptors: one for reading from the pipe and one for writing to the pipe. - * The pipe is used for passing data between processes or threads, typically in a producer-consumer scenario. - * - * @param[out] fd An array of two integers where the file descriptors for the read and write ends of the pipe will be stored. - * - `fd[0]`: The file descriptor for reading from the pipe. - * - `fd[1]`: The file descriptor for writing to the pipe. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The pipe created by this function is typically used for simple communication between processes or threads. - * The data written to `fd[1]` can be read from `fd[0]`. After usage, both file descriptors should be closed. - * - * @see sys_read(), sys_write(), sys_close(), sys_fork(), sys_execve() - */ -sysret_t sys_pipe(int fd[2]) -{ - int ret; - int kfd[2] = { 0, 0 }; - - if (!lwp_user_accessable((void *)fd, sizeof(int[2]))) - { - return -EFAULT; - } - - ret = pipe(kfd); - - lwp_put_to_user((void *)fd, kfd, sizeof(int[2])); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Waits for a process to change state. - * - * This function suspends the execution of the calling process until one of its child processes terminates - * or a specified process (identified by `pid`) changes its state. The function returns the process ID of the child - * process that terminated, and provides information about its exit status and resource usage. - * - * @param[in] pid The process ID of the child process to wait for. The behavior of this parameter can be one of the following: - * - `pid > 0`: Wait for the child process with the specified PID. - * - `pid == 0`: Wait for any child process in the same process group. - * - `pid == -1`: Wait for any child process (default behavior). - * - `pid < -1`: Wait for any child process in the specified process group. - * - * @param[out] status A pointer to an integer where the exit status of the terminated child process will be stored. - * This status can be interpreted using macros such as `WIFEXITED` or `WIFSIGNALED`. - * - * @param[in] options Options for the wait operation, which can include: - * - `WNOHANG`: Return immediately if no child has exited. - * - `WUNTRACED`: Return if a child has stopped, but not yet terminated. - * - * @param[out] ru A pointer to a `struct rusage` where resource usage information of the child process will be stored. - * This includes information such as CPU time consumed by the child process. - * - * @return sysret_t Returns the process ID of the terminated child on success. In case of failure, a negative error code is returned. - * - * @note This function is useful for monitoring and cleaning up child processes in parent-child relationships. - * The `status` value can be further analyzed to determine if the child process terminated normally or due to a signal. - * - * @see sys_waitpid(), sys_fork(), sys_exit() - */ -sysret_t sys_wait4(pid_t pid, int *status, int options, struct rusage *ru) -{ - return lwp_waitpid(pid, status, options, ru); -} - -/** - * @brief Sets the time of a specified clock. - * - * This function sets the time of the specified clock (identified by `clk`) to the given value. The time is provided - * as a `struct timespec` containing seconds and nanoseconds. This function can be used to set the system clock or - * other specific clocks, such as monotonic or real-time clocks. - * - * @param[in] clk The clock ID for which to set the time. The clock can be one of the following: - * - `CLOCK_REALTIME`: Set the system's real-time clock. - * - `CLOCK_MONOTONIC`: Set the monotonic clock, which measures time since some unspecified starting point. - * - `CLOCK_PROCESS_CPUTIME_ID`: Set the CPU time used by the process. - * - `CLOCK_THREAD_CPUTIME_ID`: Set the CPU time used by the current thread. - * - * @param[in] ts A pointer to a `struct timespec` containing the new time to set for the specified clock. - * - `ts->tv_sec`: Seconds since the epoch (for `CLOCK_REALTIME`) or since some unspecified start point (for other clocks). - * - `ts->tv_nsec`: Nanoseconds within the current second. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function requires appropriate permissions for setting the system's clock. In some systems, only privileged users - * may change the `CLOCK_REALTIME` clock. - * - * @see sys_clock_gettime(), sys_clock_getres() - */ -sysret_t sys_clock_settime(clockid_t clk, const struct timespec *ts) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - size_t size = sizeof(struct timespec); - struct timespec *kts = NULL; - - if (!lwp_user_accessable((void *)ts, size)) - { - return -EFAULT; - } - - kts = kmem_get(size); - if (!kts) - { - return -ENOMEM; - } - - lwp_get_from_user(kts, (void *)ts, size); - ret = clock_settime(clk, kts); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kts); - - return ret; -#else - if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) - { - return -EFAULT; - } - ret = clock_settime(clk, ts); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Retrieves the current time of a specified clock. - * - * This function retrieves the current time of the specified clock (identified by `clk`) and stores it in the - * `struct timespec` pointed to by `ts`. The time is expressed in seconds and nanoseconds. The clock can be - * one of several types, such as real-time, monotonic, or process-specific clocks. - * - * @param[in] clk The clock ID for which to get the time. The clock can be one of the following: - * - `CLOCK_REALTIME`: Get the system's real-time clock. - * - `CLOCK_MONOTONIC`: Get the monotonic clock, which measures time since some unspecified starting point. - * - `CLOCK_PROCESS_CPUTIME_ID`: Get the CPU time used by the process. - * - `CLOCK_THREAD_CPUTIME_ID`: Get the CPU time used by the current thread. - * - * @param[out] ts A pointer to a `struct timespec` where the current time for the specified clock will be stored. - * - `ts->tv_sec`: Seconds since the epoch (for `CLOCK_REALTIME`) or since some unspecified start point (for other clocks). - * - `ts->tv_nsec`: Nanoseconds within the current second. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function requires appropriate permissions for retrieving certain clocks (e.g., `CLOCK_REALTIME`). - * - * @see sys_clock_settime(), sys_clock_getres() - */ -sysret_t sys_clock_gettime(clockid_t clk, struct timespec *ts) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - size_t size = sizeof(struct timespec); - struct timespec *kts = NULL; - - if (!lwp_user_accessable((void *)ts, size)) - { - return -EFAULT; - } - - kts = kmem_get(size); - if (!kts) - { - return -ENOMEM; - } - - ret = clock_gettime(clk, kts); - if (ret != -1) - lwp_put_to_user(ts, kts, size); - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kts); - - return ret; -#else - if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) - { - return -EFAULT; - } - ret = clock_gettime(clk, ts); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Suspends the execution of the calling thread for the specified time duration. - * - * This function causes the calling thread to sleep for the specified time duration, which is provided as a - * `struct timespec` containing seconds and nanoseconds. The sleep is done based on the specified clock (identified by `clk`). - * If the `flags` parameter is set to `TIMER_ABSTIME`, the specified time represents an absolute time, otherwise, - * it represents a relative time interval. - * - * @param[in] clk The clock ID for which to perform the sleep. The clock can be one of the following: - * - `CLOCK_REALTIME`: Use the system's real-time clock. - * - `CLOCK_MONOTONIC`: Use the monotonic clock, which measures time since some unspecified starting point. - * - `CLOCK_PROCESS_CPUTIME_ID`: Use the CPU time used by the process. - * - `CLOCK_THREAD_CPUTIME_ID`: Use the CPU time used by the current thread. - * - * @param[in] flags The sleep behavior flags. The possible flags are: - * - `0`: The sleep duration is relative to the current time. - * - `TIMER_ABSTIME`: The sleep duration is absolute (measured from the specified clock). - * - * @param[in] rqtp A pointer to a `struct timespec` containing the requested sleep time. - * - `rqtp->tv_sec`: Seconds to sleep. - * - `rqtp->tv_nsec`: Nanoseconds to sleep (0 ≤ `rqtp->tv_nsec` < 1 billion). - * - * @param[out] rmtp A pointer to a `struct timespec` where the remaining time will be stored if the sleep is interrupted. - * If the sleep completes successfully, `rmtp` will not be modified. - * - `rmtp->tv_sec`: Remaining seconds. - * - `rmtp->tv_nsec`: Remaining nanoseconds. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function can be interrupted by signals. In that case, the remaining time is returned in `rmtp`. - * If the sleep is not interrupted, the function returns `SYSRET_OK` when the requested time has passed. - * - * @see sys_clock_gettime(), sys_clock_settime(), sys_nanosleep() - */ -sysret_t sys_clock_nanosleep(clockid_t clk, int flags, const struct timespec *rqtp, struct timespec *rmtp) -{ - int ret = 0; - LOG_D("sys_nanosleep\n"); - if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) - return -EFAULT; - -#ifdef ARCH_MM_MMU - struct timespec rqtp_k; - struct timespec rmtp_k; - - lwp_get_from_user(&rqtp_k, (void *)rqtp, sizeof rqtp_k); - ret = clock_nanosleep(clk, flags, &rqtp_k, &rmtp_k); - if ((ret != -1 || rt_get_errno() == EINTR) && rmtp && lwp_user_accessable((void *)rmtp, sizeof *rmtp)) - { - lwp_put_to_user(rmtp, (void *)&rmtp_k, sizeof rmtp_k); - if (ret != 0) - return -EINTR; - } -#else - if (rmtp) - { - if (!lwp_user_accessable((void *)rmtp, sizeof *rmtp)) - return -EFAULT; - ret = clock_nanosleep(clk, flags, rqtp, rmtp); - } -#endif - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Get the resolution of the specified clock. - * - * This function retrieves the resolution of the specified clock, which is the smallest time interval that the clock can measure. - * The resolution is returned in a `struct timespec` which contains seconds and nanoseconds. - * - * @param[in] clk The clock ID for which to get the resolution. The clock can be one of the following: - * - `CLOCK_REALTIME`: System's real-time clock. - * - `CLOCK_MONOTONIC`: Monotonic clock (measures time since an unspecified point). - * - `CLOCK_PROCESS_CPUTIME_ID`: CPU time consumed by the current process. - * - `CLOCK_THREAD_CPUTIME_ID`: CPU time consumed by the current thread. - * - * @param[out] ts A pointer to a `struct timespec` where the clock's resolution will be stored. - * - `ts->tv_sec`: The number of seconds in the resolution. - * - `ts->tv_nsec`: The number of nanoseconds in the resolution (0 ≤ `ts->tv_nsec` < 1 billion). - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The resolution may be zero for some clocks, depending on the system's configuration. - * A clock's resolution determines the smallest unit of time the clock can measure. - * - * @see sys_clock_gettime(), sys_clock_settime(), sys_clock_nanosleep() - */ -sysret_t sys_clock_getres(clockid_t clk, struct timespec *ts) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - struct timespec kts; - size_t size = sizeof(struct timespec); - - if (!lwp_user_accessable((void *)ts, size)) - { - return -EFAULT; - } - - ret = clock_getres(clk, &kts); - - if (ret != -1) - lwp_put_to_user(ts, &kts, size); -#else - if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) - { - return -EFAULT; - } - ret = clock_getres(clk, ts); -#endif - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Rename or move a file or directory. - * - * This function renames or moves a file or directory from `oldpath` to `newpath`. - * If the `newpath` exists, it may be overwritten depending on the system's file system behavior and - * the permissions of the files involved. - * - * @param[in] oldpath The current path of the file or directory to rename or move. - * @param[in] newpath The new path or name to which the file or directory should be renamed or moved. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note If `oldpath` and `newpath` refer to different file systems, the behavior may vary, - * as the operation might involve copying and removing files instead of simply renaming them. - * The success of the operation also depends on the permissions of the source and destination. - * - * @see sys_unlink(), sys_mkdir(), sys_access() - */ -sysret_t sys_rename(const char *oldpath, const char *newpath) -{ - int ret = -1; -#ifdef ARCH_MM_MMU - int err; - - err = lwp_user_strlen(oldpath); - if (err <= 0) - { - return -EFAULT; - } - - err = lwp_user_strlen(newpath); - if (err <= 0) - { - return -EFAULT; - } -#endif - ret = rename(oldpath, newpath); - return (ret < 0 ? GET_ERRNO() : ret); -} - -typedef unsigned long long rlim_t; - -struct rlimit -{ - rlim_t rlim_cur; - rlim_t rlim_max; -}; - -#define RLIMIT_CPU 0 -#define RLIMIT_FSIZE 1 -#define RLIMIT_DATA 2 -#define RLIMIT_STACK 3 -#define RLIMIT_CORE 4 -#define RLIMIT_RSS 5 -#define RLIMIT_NPROC 6 -#define RLIMIT_NOFILE 7 -#define RLIMIT_MEMLOCK 8 -#define RLIMIT_AS 9 - -sysret_t sys_prlimit64(pid_t pid, - unsigned int resource, - const struct rlimit *new_rlim, - struct rlimit *old_rlim) -{ - return -ENOSYS; -} - -/** - * @brief Get resource limits. - * - * This function retrieves the current resource limits for the specified resource type. - * The resource limit specifies the maximum value for a particular resource that a process or thread can use. - * The limits are returned in an array `rlim` where: - * - `rlim[0]` represents the soft limit (the current value). - * - `rlim[1]` represents the hard limit (the maximum allowable value). - * - * @param[in] resource The resource for which to get the limits. It can be one of the following: - * - `RLIMIT_NOFILE`: Maximum number of file descriptors. - * - * @param[out] rlim An array to store the resource limits. The array should have at least 2 elements: - * - `rlim[0]`: The soft limit for the resource. - * - `rlim[1]`: The hard limit for the resource. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The limits returned by `sys_getrlimit` are subject to system constraints and may vary across different systems. - * - * @see sys_setrlimit(), sys_getrlimit64(), sys_getrusage() - */ -sysret_t sys_getrlimit(unsigned int resource, unsigned long rlim[2]) -{ - int ret = -1; - unsigned long krlim[2] = { 0, 0 }; - - if (!lwp_user_accessable((void *)rlim, sizeof(unsigned long[2]))) - { - return -EFAULT; - } - - if (lwp_get_from_user(krlim, rlim, sizeof(unsigned long[2])) != sizeof(unsigned long[2])) - { - return -EINVAL; - } - - switch (resource) - { - case RLIMIT_NOFILE: - { - struct dfs_fdtable *fdt = dfs_fdtable_get(); - - dfs_file_lock(); - krlim[0] = fdt->maxfd; - dfs_file_unlock(); - krlim[1] = DFS_FD_MAX; - ret = 0; - } - break; - default: - return -EINVAL; - break; - } - - lwp_put_to_user((void *)rlim, krlim, sizeof(unsigned long[2])); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -sysret_t sys_setrlimit(unsigned int resource, struct rlimit *rlim) -{ - return -ENOSYS; -} - -/** - * @brief Get random data from the kernel's random number generator. - * - * This function retrieves cryptographically secure random data from the kernel's random number generator - * and stores it in the buffer provided by the user. The data can be used for cryptographic operations or - * other applications requiring randomization. - * - * @param[out] buf A pointer to the buffer where the random data will be stored. The buffer must be large - * enough to hold the requested amount of random data. - * @param[in] buflen The number of bytes of random data to retrieve. This value must be a positive integer. - * @param[in] flags Optional flags to modify the behavior of the random data retrieval. Possible values may include: - * - `GRND_RANDOM`: Requests random data from the system's non-blocking random source. - * - `GRND_NONBLOCK`: Instructs the function to return immediately even if insufficient entropy is available. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note - * - If the `GRND_NONBLOCK` flag is not set and there is insufficient entropy in the system's random pool, - * this function may block until enough entropy is available. - * - The function returns cryptographically secure random data, suitable for use in secure applications. - * - * @see sys_random(), sys_getentropy() - */ -sysret_t sys_getrandom(void *buf, size_t buflen, unsigned int flags) -{ - int ret = -1; - int count = 0; - void *kmem = RT_NULL; - rt_device_t rd_dev = RT_NULL; - - if (flags & GRND_RANDOM) - rd_dev = rt_device_find("random"); - else - rd_dev = rt_device_find("urandom"); - - if (rd_dev == RT_NULL) - { - return -EFAULT; - } - - if (rt_device_open(rd_dev, RT_DEVICE_OFLAG_RDONLY) != RT_EOK) - { - return -EFAULT; - } - - if (!lwp_user_accessable(buf, buflen)) - { - rt_device_close(rd_dev); - return -EFAULT; - } - -#ifdef ARCH_MM_MMU - kmem = kmem_get(buflen); - if (!kmem) - { - rt_device_close(rd_dev); - return -ENOMEM; - } - - while (count < buflen) - { - ret = rt_device_read(rd_dev, count, (char *)kmem + count, buflen - count); - if (ret <= 0) - break; - count += ret; - } - rt_device_close(rd_dev); - - ret = count; - if (count > 0) - { - ret = lwp_put_to_user(buf, kmem, count); - } - kmem_put(kmem); -#else - while (count < buflen) - { - ret = rt_device_read(rd_dev, count, (char *)kmem + count, buflen - count); - if (ret <= 0) - break; - count += ret; - } - rt_device_close(rd_dev); - - ret = count; -#endif - return ret; -} - -/** - * @brief Read the value of a symbolic link. - * - * This function reads the value of a symbolic link and stores it in the provided buffer. The value is the - * path to which the symbolic link points. If the symbolic link is too long to fit in the provided buffer, - * the function returns the number of bytes needed to store the entire path (not including the terminating null byte). - * - * @param[in] path The path of the symbolic link to read. - * @param[out] buf A buffer where the symbolic link's target will be stored. The buffer must be large enough - * to hold the path of the symbolic link. - * @param[in] bufsz The size of the buffer `buf`. It specifies the maximum number of bytes to read. - * - * @return ssize_t The number of bytes written to `buf` (excluding the terminating null byte) on success. - * On failure, it returns a negative error code: - * - `-EINVAL`: Invalid path. - * - `-ENOMEM`: Insufficient memory to read the link. - * - `-EFAULT`: Invalid address for the `buf`. - * - * @note It will (silently) truncate the contents(to a length of bufsiz characters), - * in case the buffer is too small to hold all of the contents. - * - * @see sys_symlink(), sys_lstat() - */ -ssize_t sys_readlink(char *path, char *buf, size_t bufsz) -{ - size_t len, copy_len; - int err, rtn; - char *copy_path; - - len = lwp_user_strlen(path); - if (len <= 0) - { - return -EFAULT; - } - - if (!lwp_user_accessable(buf, bufsz)) - { - return -EINVAL; - } - - copy_path = (char *)rt_malloc(len + 1); - if (!copy_path) - { - return -ENOMEM; - } - - copy_len = lwp_get_from_user(copy_path, path, len); - copy_path[copy_len] = '\0'; - - char *link_fn = (char *)rt_malloc(DFS_PATH_MAX); - if (link_fn) - { - err = dfs_file_readlink(copy_path, link_fn, DFS_PATH_MAX); - if (err > 0) - { - buf[bufsz > err ? err : bufsz] = '\0'; - rtn = lwp_put_to_user(buf, link_fn, bufsz > err ? err : bufsz); - } - else - { - rtn = -EIO; - } - rt_free(link_fn); - } - else - { - rtn = -ENOMEM; - } - - rt_free(copy_path); - return rtn; -} - -/** - * @brief Set the CPU affinity mask of a process. - * - * This function sets the CPU affinity mask for a specified process. The affinity mask determines which CPUs the - * process is allowed to execute on. The process will be restricted to the CPUs specified in the mask. - * - * @param[in] pid The process ID of the process whose CPU affinity is to be set. If the `pid` is `0`, the - * affinity of the calling process will be modified. - * @param[in] size The size (in bytes) of the CPU set, typically `sizeof(cpu_set_t)`. - * @param[in] set A pointer to the CPU set. The CPU set is a bitmask representing which CPUs the process - * is allowed to run on. The bitmask must have enough bits to cover the number of CPUs on the system. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The CPU set is represented as a bitmask where each bit corresponds to a CPU. If the bit is set, the process - * can execute on that CPU. The number of CPUs is platform-dependent, and the size of `set` must be large enough - * to hold a bit for each CPU. - * - * @see sys_sched_getaffinity(), sys_setcpu() - */ -sysret_t sys_sched_setaffinity(pid_t pid, size_t size, void *set) -{ - void *kset = RT_NULL; - - if (size <= 0 || size > sizeof(cpu_set_t)) - { - return -EINVAL; - } - if (!lwp_user_accessable((void *)set, size)) - return -EFAULT; - - kset = kmem_get(size); - if (kset == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kset, set, size) != size) - { - kmem_put(kset); - return -EINVAL; - } - - for (int i = 0; i < size * 8; i++) - { - if (CPU_ISSET_S(i, size, kset)) - { - kmem_put(kset); - - /** - * yes it's tricky. - * But when we talk about 'pid' from GNU libc, it's the 'task-id' - * aka 'thread->tid' known in kernel. - */ - return lwp_setaffinity(pid, i); - } - } - - kmem_put(kset); - - return -1; -} - -/** - * @brief Get the CPU affinity mask of a process. - * - * This function retrieves the CPU affinity mask for a specified process. The affinity mask indicates which CPUs the - * process is allowed to execute on. The process can run on any of the CPUs represented by the bits set in the mask. - * - * @param[in] pid The process ID of the process whose CPU affinity is to be retrieved. If `pid` is `0`, the - * affinity mask of the calling process will be retrieved. - * @param[in] size The size (in bytes) of the CPU set, typically `sizeof(cpu_set_t)`. - * @param[out] set A pointer to a buffer where the CPU affinity mask will be stored. The mask is represented - * as a bitmask, where each bit corresponds to a CPU. The bit is set if the process can run on that CPU. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The CPU set is represented as a bitmask where each bit corresponds to a CPU. If the bit is set, the process - * can execute on that CPU. The number of CPUs is platform-dependent, and the size of `set` must be large enough - * to hold a bit for each CPU. - * - * @see sys_sched_setaffinity(), sys_getcpu() - */ -sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set) -{ -#ifdef ARCH_MM_MMU - LWP_DEF_RETURN_CODE(rc); - void *mask; - struct rt_lwp *lwp; - - if (size <= 0 || size > sizeof(cpu_set_t)) - { - return -EINVAL; - } - if (!lwp_user_accessable(set, size)) - { - return -EFAULT; - } - mask = kmem_get(size); - if (!mask) - { - return -ENOMEM; - } - - CPU_ZERO_S(size, mask); - - lwp_pid_lock_take(); - lwp = lwp_from_pid_locked(pid); - - if (!lwp) - { - rc = -ESRCH; - } - else - { -#ifdef RT_USING_SMP - if (lwp->bind_cpu == RT_CPUS_NR) /* not bind */ - { - for (int i = 0; i < RT_CPUS_NR; i++) - { - CPU_SET_S(i, size, mask); - } - } - else /* set bind cpu */ - { - /* TODO: only single-core bindings are now supported of rt-smart */ - CPU_SET_S(lwp->bind_cpu, size, mask); - } -#else - CPU_SET_S(0, size, mask); -#endif - - if (lwp_put_to_user(set, mask, size) != size) - rc = -EFAULT; - else - rc = size; - } - - lwp_pid_lock_release(); - - kmem_put(mask); - - LWP_RETURN(rc); -#else - return -1; -#endif -} - -/** - * @brief Retrieve system information. - * - * This function provides details about the current state of the system, such as uptime, memory usage, - * load average, and other key statistics. The information is stored in a structure pointed to by `info`. - * - * @param[out] info A pointer to a buffer where system information will be stored. The structure should - * be compatible with the format expected by the system, typically `struct sysinfo`. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The structure of `info` must be predefined and consistent with the system's expectations. - * This function does not allocate memory for `info`; the caller must provide sufficient - * memory for the structure. - */ -sysret_t sys_sysinfo(void *info) -{ -#ifdef ARCH_MM_MMU - struct sysinfo kinfo = { 0 }; - rt_size_t total_pages = 0, free_pages = 0; - - if (!lwp_user_accessable(info, sizeof(struct sysinfo))) - { - return -EFAULT; - } - - kinfo.uptime = rt_tick_get_millisecond() / 1000; - /* TODO: 1, 5, and 15 minute load averages */ - kinfo.loads[0] = kinfo.loads[1] = kinfo.loads[2] = rt_object_get_length(RT_Object_Class_Thread); - rt_page_get_info(&total_pages, &free_pages); - kinfo.totalram = total_pages; - kinfo.freeram = free_pages; - - /* TODO: implementation procfs, here is counter the lwp number */ - struct lwp_avl_struct *pids = lwp_get_pid_ary(); - for (int index = 0; index < RT_LWP_MAX_NR; index++) - { - struct rt_lwp *lwp = (struct rt_lwp *)pids[index].data; - - if (lwp) - { - kinfo.procs++; - } - } - - rt_page_high_get_info(&total_pages, &free_pages); - kinfo.totalhigh = total_pages; - kinfo.freehigh = free_pages; - kinfo.mem_unit = ARCH_PAGE_SIZE; - - if (lwp_put_to_user(info, &kinfo, sizeof(struct sysinfo)) != sizeof(struct sysinfo)) - { - return -EFAULT; - } - - return 0; -#else - return -1; -#endif -} - -static rt_bool_t _sys_sched_priority_is_valid(int priority); - -/** - * @brief Set scheduling parameters for a specific thread. - * - * This function allows setting the scheduling parameters for a thread identified by its thread ID (`tid`). - * The parameters are provided via the `param` argument, which should be a structure compatible with the - * system's scheduling policies. - * - * @param[in] tid The thread ID of the target thread for which the scheduling parameters are to be set. - * @param[in] param A pointer to a structure containing the new scheduling parameters. The structure - * typically includes fields like priority and policy. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The caller must have appropriate permissions to change the scheduling parameters of the specified thread. - * The exact structure and fields of `param` depend on the system's implementation and scheduling policies. - */ -sysret_t sys_sched_setparam(pid_t tid, void *param) -{ - struct sched_param *sched_param = RT_NULL; - rt_thread_t thread; - int ret = -1; - - if (!lwp_user_accessable(param, sizeof(struct sched_param))) - { - return -EFAULT; - } - - sched_param = kmem_get(sizeof(struct sched_param)); - if (sched_param == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(sched_param, param, sizeof(struct sched_param)) != sizeof(struct sched_param)) - { - kmem_put(sched_param); - return -EINVAL; - } - - if (!_sys_sched_priority_is_valid(sched_param->sched_priority)) - { - kmem_put(sched_param); - return -EINVAL; - } - - thread = lwp_tid_get_thread_and_inc_ref(tid); - - if (thread) - { - ret = rt_thread_control(thread, RT_THREAD_CTRL_RESET_PRIORITY, (void *)&sched_param->sched_priority); - } - - lwp_tid_dec_ref(thread); - - kmem_put(sched_param); - - return ret; -} - -/** - * @brief Relinquish the processor voluntarily. - * - * This function causes the calling thread to yield the processor, allowing other threads - * that are ready to run to execute. The thread will be placed back into the scheduler's - * ready queue and may be rescheduled according to its priority and the system's scheduling policy. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function is typically used in cooperative multitasking scenarios or when a thread - * explicitly determines it no longer needs the processor at the moment. - */ -sysret_t sys_sched_yield(void) -{ - rt_thread_yield(); - return 0; -} - -/** - * @brief Retrieve the scheduling parameters of a specific thread. - * - * This function retrieves the scheduling parameters of the thread identified by the thread ID (`tid`). - * The retrieved parameters are stored in the structure pointed to by the `param` argument. - * - * @param[in] tid The thread ID of the target thread whose scheduling parameters are to be retrieved. - * @param[out] param A pointer to a structure where the scheduling parameters will be stored. The structure - * typically includes fields like priority and policy. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The caller must have appropriate permissions to query the scheduling parameters of the specified thread. - * The exact structure and fields of `param` depend on the system's implementation and scheduling policies. - */ -sysret_t sys_sched_getparam(const pid_t tid, void *param) -{ - struct sched_param *sched_param = RT_NULL; - rt_thread_t thread; - int ret = -1; - - if (!lwp_user_accessable(param, sizeof(struct sched_param))) - { - return -EFAULT; - } - - sched_param = kmem_get(sizeof(struct sched_param)); - if (sched_param == RT_NULL) - { - return -ENOMEM; - } - - thread = lwp_tid_get_thread_and_inc_ref(tid); - - if (thread) - { - sched_param->sched_priority = RT_SCHED_PRIV(thread).current_priority; - ret = 0; - } - - lwp_tid_dec_ref(thread); - - lwp_put_to_user((void *)param, sched_param, sizeof(struct sched_param)); - kmem_put(sched_param); - - return ret; -} - -/** - * @brief Get the maximum priority for a given scheduling policy. - * - * This function retrieves the maximum priority value that can be used with the specified - * scheduling policy. - * - * @param[in] policy The scheduling policy for which to retrieve the maximum priority. - * - * @return sysret_t Returns the maximum priority value on success. On failure, returns a - * negative error code. - * - * @note The valid priority range depends on the system's configuration and the selected - * scheduling policy. The returned value represents the highest priority that can - * be assigned to a thread using the given policy. - */ -sysret_t sys_sched_get_priority_max(int policy) -{ - if (policy < 0) - { - SET_ERRNO(EINVAL); - return -rt_get_errno(); - } - return RT_THREAD_PRIORITY_MAX; -} - -/** - * @brief Get the minimum priority for a given scheduling policy. - * - * This function retrieves the minimum priority value that can be used with the specified - * scheduling policy. - * - * @param[in] policy The scheduling policy for which to retrieve the minimum priority. - * - * @return sysret_t Returns the minimum priority value on success. On failure, returns a - * negative error code. - * - * @note The valid priority range depends on the system's configuration and the selected - * scheduling policy. The returned value represents the lowest priority that can - * be assigned to a thread using the given policy. - */ -sysret_t sys_sched_get_priority_min(int policy) -{ - if (policy < 0) - { - SET_ERRNO(EINVAL); - return -rt_get_errno(); - } - return 0; -} - -static rt_bool_t _sys_sched_priority_is_valid(int priority) -{ - return (priority >= 0) && (priority < RT_THREAD_PRIORITY_MAX); -} - -#if defined(RT_USING_UTESTCASES) && defined(RT_USING_SMART) -rt_bool_t rt_utest_sys_sched_priority_is_valid(int priority) -{ - return _sys_sched_priority_is_valid(priority); -} -#endif - -/** - * @brief Set the scheduling policy and parameters for a thread. - * - * This function sets the scheduling policy and associated parameters for the specified thread. - * It allows controlling the scheduling behavior of threads. - * - * @param[in] tid The thread ID of the target thread. - * @param[in] policy The scheduling policy to be applied. Common values include: - * - `SCHED_FIFO`: First-in, first-out scheduling. - * - `SCHED_RR`: Round-robin scheduling. - * - `SCHED_OTHER`: Default or standard scheduling. - * @param[in] param Pointer to a structure containing scheduling parameters, such as priority. - * The structure type depends on the system implementation. - * - * @return sysret_t Returns 0 on success. On failure, returns a negative error code. - * - * @note This function requires appropriate permissions to modify the scheduling settings - * of another thread. For most systems, elevated privileges may be required. - * Ensure the `param` structure is properly initialized for the given `policy`. - */ -sysret_t sys_sched_setscheduler(int tid, int policy, void *param) -{ - sysret_t ret; - struct sched_param *sched_param = RT_NULL; - rt_thread_t thread = RT_NULL; - - if (!lwp_user_accessable(param, sizeof(struct sched_param))) - { - return -EFAULT; - } - - sched_param = kmem_get(sizeof(struct sched_param)); - if (sched_param == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(sched_param, param, sizeof(struct sched_param)) != sizeof(struct sched_param)) - { - kmem_put(sched_param); - return -EINVAL; - } - - if (!_sys_sched_priority_is_valid(sched_param->sched_priority)) - { - kmem_put(sched_param); - return -EINVAL; - } - - thread = lwp_tid_get_thread_and_inc_ref(tid); - ret = rt_thread_control(thread, RT_THREAD_CTRL_RESET_PRIORITY, (void *)&sched_param->sched_priority); - lwp_tid_dec_ref(thread); - - kmem_put(sched_param); - - return ret; -} - -/** - * @brief Get the scheduling policy of a thread. - * - * This function retrieves the current scheduling policy of the specified thread. - * - * @param[in] tid The thread ID of the target thread. - * - * @return sysret_t Returns the scheduling policy of the thread on success. On failure, - * returns a negative error code. - * - * @note The caller must have appropriate permissions to query the scheduling policy - * of the specified thread. - */ -sysret_t sys_sched_getscheduler(int tid) -{ - rt_thread_t thread = RT_NULL; - int rtn; - - thread = lwp_tid_get_thread_and_inc_ref(tid); - lwp_tid_dec_ref(thread); - - if (thread) - { - rtn = SCHED_RR; - } - else - { - rtn = -ESRCH; - } - - return rtn; -} - -/** - * @brief Flush the file descriptors' data to disk. - * - * This function flushes all modified data of the file associated with the specified - * file descriptor to disk, ensuring that any changes made to the file are committed - * to permanent storage. - * - * @param[in] fd The file descriptor associated with the file to be flushed. It should - * refer to an open file. - * - * @return sysret_t Returns `0` (0) on success. On failure, returns a negative - * error code. - * - * @note The `fsync` function ensures that all data written to the file is physically - * stored on disk, but it does not guarantee that all file metadata is flushed. - * To flush both data and metadata, `fdatasync` can be used. - */ -sysret_t sys_fsync(int fd) -{ - int res = fsync(fd); - if (res < 0) - res = rt_get_errno(); - return res; -} - -/** - * @brief Open a message queue. - * - * This function opens a message queue for communication between processes. - * It can create a new message queue or open an existing one, depending on the specified - * flags. - * - * @param[in] name The name of the message queue. The name should be a null-terminated - * string and is subject to system-specific naming conventions. - * @param[in] flags Flags that control the behavior of the message queue. Common flags include: - * - `O_CREAT`: Create the message queue if it does not exist. - * - `O_EXCL`: Fail if the message queue already exists. - * - `O_RDONLY`: Open the queue for reading. - * - `O_WRONLY`: Open the queue for writing. - * - `O_RDWR`: Open the queue for both reading and writing. - * @param[in] mode The mode to be applied when creating the message queue, which defines - * the permissions for the message queue (e.g., read, write). - * @param[in] attr A pointer to a `struct mq_attr` that defines the attributes of the - * message queue, such as the maximum number of messages and the size of - * each message. If `NULL`, default values are used. - * - * @return mqd_t Returns a non-negative file descriptor for the message queue on success. - * On failure, returns `-1` and sets `errno` to indicate the error. - * - * @note If the message queue is created, its attributes (such as the maximum number of - * messages and message size) must be defined in the `mq_attr` structure. If the - * `O_CREAT` flag is not specified and the queue does not exist, the function will - * return `-1`. - */ -mqd_t sys_mq_open(const char *name, int flags, mode_t mode, struct mq_attr *attr) -{ - mqd_t mqdes; - sysret_t ret = 0; -#ifdef ARCH_MM_MMU - char *kname = RT_NULL; - rt_size_t len = 0; - struct mq_attr attr_k; - - len = lwp_user_strlen(name); - if (!len) - return (mqd_t)-EINVAL; - - kname = (char *)kmem_get(len + 1); - if (!kname) - return (mqd_t)-ENOMEM; - - if (attr == NULL) - { - attr_k.mq_maxmsg = 10; - attr_k.mq_msgsize = 8192; - attr_k.mq_flags = 0; - attr = &attr_k; - } - else - { - if (!lwp_get_from_user(&attr_k, (void *)attr, sizeof(struct mq_attr))) - return -EINVAL; - } - - lwp_get_from_user(kname, (void *)name, len + 1); - mqdes = mq_open(kname, flags, mode, &attr_k); - if (mqdes == -1) - { - ret = GET_ERRNO(); - } - lwp_put_to_user(attr, &attr_k, sizeof(struct mq_attr)); - kmem_put(kname); -#else - mqdes = mq_open(name, flags, mode, attr); -#endif - if (mqdes == -1) - return (mqd_t)ret; - else - return mqdes; -} - -/** - * @brief Remove a message queue. - * - * This function removes a message queue identified by its name. If the message queue - * is open by any process, it will be removed only when all the processes close their - * file descriptors associated with the message queue. - * - * @param[in] name The name of the message queue to be removed. It should be a null-terminated - * string that conforms to system-specific naming conventions. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative - * error code. - * - * @note After a successful call, the message queue is removed from the system. However, - * the removal will not take effect until all processes close their file descriptors - * associated with the queue. The function will fail if the message queue is still - * open by other processes. - */ -sysret_t sys_mq_unlink(const char *name) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - char *kname = RT_NULL; - rt_size_t len = 0; - - len = lwp_user_strlen(name); - if (!len) - return -EINVAL; - kname = (char *)kmem_get(len + 1); - if (!kname) - return -ENOMEM; - - lwp_get_from_user(kname, (void *)name, len + 1); - ret = mq_unlink(kname); - if (ret < 0) - { - ret = GET_ERRNO(); - } - kmem_put(kname); - return ret; -#else - ret = mq_unlink(name); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Send a message to a message queue with a timeout. - * - * This function sends a message to the specified message queue, but it allows the sender - * to specify a timeout. If the message queue is full, the function will block until either - * space becomes available or the specified timeout expires. If the timeout expires without - * space being available, the function returns an error. - * - * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. - * @param[in] msg A pointer to the message to be sent. - * @param[in] len The length of the message to send. - * @param[in] prio The priority of the message (higher values indicate higher priority). - * @param[in] at A pointer to a `timespec` structure that specifies the absolute timeout. - * If the timeout expires before the message can be sent, the function returns - * an error. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error - * code. - * - * @note The function uses the `timespec` structure to specify the absolute timeout. The - * `at` parameter should indicate the time at which the operation should time out. - * If the timeout is `NULL`, the function will not apply any timeout (it will block - * indefinitely until the message is sent). - * - * @see sys_mq_send - */ -sysret_t sys_mq_timedsend(mqd_t mqd, const char *msg, size_t len, unsigned prio, const struct timespec *at) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - char *kmsg = RT_NULL; - struct timespec at_k; - - kmsg = (char *)kmem_get(len + 1); - if (!kmsg) - return -ENOMEM; - - lwp_get_from_user(&at_k, (void *)at, sizeof(struct timespec)); - lwp_get_from_user(kmsg, (void *)msg, len + 1); - ret = mq_timedsend(mqd, kmsg, len, prio, &at_k); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmsg); - - return ret; -#else - ret = mq_timedsend(mqd, msg, len, prio, at); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Receive a message from a message queue with a timeout. - * - * This function attempts to receive a message from the specified message queue, but it - * allows the receiver to specify a timeout. If the queue is empty, the function will block - * until either a message becomes available or the specified timeout expires. If the timeout - * expires without receiving a message, the function returns an error. - * - * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. - * @param[out] msg A pointer to the buffer where the received message will be stored. - * @param[in] len The maximum length of the buffer to store the received message. - * @param[out] prio A pointer to an unsigned integer that will be set to the priority - * of the received message. - * @param[in] at A pointer to a `timespec` structure that specifies the absolute timeout. - * If the timeout expires before a message is received, the function will - * return an error. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The function uses the `timespec` structure to specify the absolute timeout. The - * `at` parameter should indicate the time at which the operation should time out. - * If the timeout is `NULL`, the function will block indefinitely until a message is - * received. - * - * @see sys_mq_receive - */ -sysret_t sys_mq_timedreceive(mqd_t mqd, char * restrict msg, size_t len, unsigned * restrict prio, const struct timespec * restrict at) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - char * restrict kmsg = RT_NULL; - - struct timespec at_k; - - kmsg = (char * restrict)kmem_get(len + 1); - if (!kmsg) - return -ENOMEM; - - lwp_get_from_user(kmsg, (void *)msg, len + 1); - if (at == RT_NULL) - { - ret = mq_timedreceive(mqd, kmsg, len, prio, RT_NULL); - } - else - { - if (!lwp_get_from_user(&at_k, (void *)at, sizeof(struct timespec))) - return -EINVAL; - ret = mq_timedreceive(mqd, kmsg, len, prio, &at_k); - } - - if (ret > 0) - lwp_put_to_user(msg, kmsg, len + 1); - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmsg); - - return ret; -#else - ret = mq_timedreceive(mqd, msg, len, prio, at); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Set up asynchronous notification for a message queue. - * - * This function configures asynchronous notification for a message queue. When a message - * is available in the queue, the system can notify the calling process through a signal - * or another method specified in the `sigevent` structure. This is typically used to allow - * a process to be notified when a message arrives without having to block in the receive call. - * - * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. - * @param[in] sev A pointer to a `sigevent` structure that specifies the notification - * mechanism to be used when a message is received. It could include - * signals or other notification types such as event flags or message passing. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function enables asynchronous notification, but the specific behavior depends - * on the configuration specified in the `sev` parameter, which could involve signals - * or other forms of notification. - * - * @see sys_mq_send, sys_mq_timedreceive - */ -sysret_t sys_mq_notify(mqd_t mqd, const struct sigevent *sev) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - struct sigevent sev_k; - lwp_get_from_user(&sev_k, (void *)sev, sizeof(struct timespec)); - ret = mq_notify(mqd, &sev_k); -#else - ret = mq_notify(mqd, sev); -#endif - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Get or set attributes of a message queue. - * - * This function allows you to get or set the attributes of an existing message queue. - * If the `new` attribute structure is non-NULL, it updates the message queue with the new - * attributes. Otherwise, If the `old` attribute structure is non-NULL, it will return the current - * attributes of the message queue. - * - * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. - * @param[in] new A pointer to a `mq_attr` structure containing the new attributes to set. - * If `NULL`, the function will not modify the message queue attributes. - * @param[out] old A pointer to a `mq_attr` structure where the current message queue - * attributes will be returned. If `NULL`, the current attributes will not - * be returned. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The `mq_attr` structure contains parameters like the maximum number of messages, - * the maximum message size, and other attributes that control the behavior of the - * message queue. - * - * @see sys_mq_open, sys_mq_notify - */ -sysret_t sys_mq_getsetattr(mqd_t mqd, const struct mq_attr * restrict new, struct mq_attr * restrict old) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - size_t size = sizeof(struct mq_attr); - struct mq_attr * restrict knew = NULL; - struct mq_attr * restrict kold = NULL; - - if (new != RT_NULL) - { - if (!lwp_user_accessable((void *)new, size)) - return -EFAULT; - knew = kmem_get(size); - if (!knew) - return -ENOMEM; - lwp_get_from_user(knew, (void *)new, size); - } - - if (!lwp_user_accessable((void *)old, size)) - return -EFAULT; - kold = kmem_get(size); - if (!kold) - return -ENOMEM; - - lwp_get_from_user(kold, (void *)old, size); - ret = mq_setattr(mqd, knew, kold); - if (ret != -1) - lwp_put_to_user(old, kold, size); - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kold); - if (new != RT_NULL) - kmem_put(knew); - - return ret; -#else - ret = mq_setattr(mqd, new, old); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} - -/** - * @brief Close a message queue descriptor. - * - * This function closes a message queue descriptor. After calling this function, the - * descriptor can no longer be used to interact with the message queue. Any resources - * associated with the descriptor are released. If the message queue was opened with the - * `O_CLOEXEC` flag, it will be automatically closed when the calling process executes - * an `exec` system call. - * - * @param[in] mqd The message queue descriptor to be closed. It was previously returned - * by `sys_mq_open`. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @see sys_mq_open, sys_mq_unlink - */ -sysret_t sys_mq_close(mqd_t mqd) -{ - int ret = 0; -#ifdef ARCH_MM_MMU - ret = mq_close(mqd); -#else - ret = mq_close(mqd); -#endif - return (ret < 0 ? GET_ERRNO() : ret); -} - -#define ICACHE (1 << 0) -#define DCACHE (1 << 1) -#define BCACHE (ICACHE | DCACHE) - -/** - * @brief Flush the cache for a specified memory region. - * - * This function flushes the cache for a specified memory region. It is commonly used - * to ensure that the contents of a memory region are written back to the main memory - * or that stale cache entries are invalidated. The cache operation can be controlled - * by the `cache` parameter to determine whether to clean or invalidate the cache. - * - * @param[in] addr The starting address of the memory region to flush. - * @param[in] size The size of the memory region to flush, in bytes. - * @param[in] cache A flag to specify the cache operation: - * - `CACHE_CLEAN`: Clean the cache (write back to memory). - * - `CACHE_INVALIDATE`: Invalidate the cache (discard cached data). - * - `CACHE_FLUSH`: Both clean and invalidate the cache. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function is typically used in low-level operations or in systems where - * cache coherence between memory and cache is critical. - */ -rt_weak sysret_t sys_cacheflush(void *addr, int size, int cache) -{ - if (!lwp_user_accessable(addr, size)) - return -EFAULT; - - if (((size_t)addr < (size_t)addr + size) && - ((size_t)addr >= USER_VADDR_START) && - ((size_t)addr + size < USER_VADDR_TOP)) - { - if ((cache & DCACHE)) - { - rt_hw_cpu_dcache_clean_and_invalidate(addr, size); - } - - if ((cache & ICACHE)) - { - rt_hw_cpu_icache_invalidate(addr, size); - } - - return 0; - } - return -EFAULT; -} - -/** - * @brief Get system information. - * - * This function retrieves information about the current system, such as the system name, - * version, release, architecture, and other details. The information is stored in the - * `utsname` structure pointed to by the `uts` parameter. - * - * @param[out] uts A pointer to a `utsname` structure where the system information will - * be stored. The structure includes fields such as: - * - `sysname`: Operating system name. - * - `nodename`: Network node hostname. - * - `release`: Operating system release version. - * - `version`: Operating system version. - * - `machine`: Hardware identifier (architecture). - * - * @return sysret_t Returns `SYSRET_OK` (0) on success. On failure, returns a negative error code. - * - * @note This function provides details about the system environment, which may be useful - * for applications needing to adapt to different system configurations. - * - * @see sys_gethostname, sys_uname - */ -sysret_t sys_uname(struct utsname *uts) -{ - struct utsname utsbuff = { 0 }; - int ret = 0; - const char *machine; - - if (!lwp_user_accessable((void *)uts, sizeof(struct utsname))) - { - return -EFAULT; - } - rt_strncpy(utsbuff.sysname, "RT-Thread", sizeof(utsbuff.sysname)); - utsbuff.nodename[0] = '\0'; - ret = rt_snprintf(utsbuff.release, sizeof(utsbuff.release), "%u.%u.%u", - RT_VERSION_MAJOR, RT_VERSION_MINOR, RT_VERSION_PATCH); - if (ret < 0) - { - return -EIO; - } - ret = rt_snprintf(utsbuff.version, sizeof(utsbuff.version), "RT-Thread %u.%u.%u %s %s", - RT_VERSION_MAJOR, RT_VERSION_MINOR, RT_VERSION_PATCH, __DATE__, __TIME__); - if (ret < 0) - { - return -EIO; - } - - machine = rt_hw_cpu_arch(); - rt_strncpy(utsbuff.machine, machine, sizeof(utsbuff.machine)); - - utsbuff.domainname[0] = '\0'; - lwp_put_to_user(uts, &utsbuff, sizeof utsbuff); - return 0; -} - -/** - * @brief Get filesystem statistics. - * - * This function retrieves statistics about the filesystem at the specified path, - * storing the results in the provided `statfs` structure. It can be used to obtain - * information such as the total number of blocks, free blocks, available inodes, etc. - * - * @param[in] path The path to the filesystem to query. If the path is the root directory - * (`"/"`), it returns statistics for the root filesystem. - * @param[out] buf A pointer to a `statfs` structure where the filesystem statistics will - * be stored. This structure includes information such as: - * - `f_type`: The type of the filesystem. - * - `f_bsize`: The optimal block size for I/O operations. - * - `f_blocks`: Total number of blocks in the filesystem. - * - `f_bfree`: Number of free blocks. - * - `f_bavail`: Number of free blocks available to non-superuser. - * - `f_files`: Total number of file nodes (inodes). - * - `f_ffree`: Number of free inodes. - * - `f_favail`: Number of inodes available to non-superuser. - * - `f_flag`: Flags describing the filesystem. - * - `f_namelen`: Maximum length of a filename. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function is useful for determining the available space and file system - * characteristics of a mounted filesystem. - * - * @see sys_fstatfs - */ -sysret_t sys_statfs(const char *path, struct statfs *buf) -{ - int ret = 0; - size_t len; - size_t copy_len; - char *copy_path; - struct statfs statfsbuff = { 0 }; - - if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) - { - return -EFAULT; - } - - len = lwp_user_strlen(path); - if (len <= 0) - { - return -EFAULT; - } - - copy_path = (char *)rt_malloc(len + 1); - if (!copy_path) - { - return -ENOMEM; - } - - copy_len = lwp_get_from_user(copy_path, (void *)path, len); - if (copy_len == 0) - { - rt_free(copy_path); - return -EFAULT; - } - copy_path[copy_len] = '\0'; - - ret = _SYS_WRAP(statfs(copy_path, &statfsbuff)); - rt_free(copy_path); - - if (ret == 0) - { - lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); - } - - return ret; -} - -/** - * @brief Get extended filesystem statistics (64-bit). - * - * This function retrieves extended statistics about the filesystem at the specified path, - * using 64-bit values for larger filesystems or filesystems with a large number of blocks or inodes. - * The information is stored in the provided `statfs` structure, which includes details such as total - * blocks, free blocks, available inodes, etc. - * - * @param[in] path The path to the filesystem to query. Typically, this would be the root directory - * (`"/"`) for the root filesystem, or any other directory on the filesystem. - * @param[in] sz The size of the `statfs` structure. This parameter allows for future extensions - * of the `statfs` structure without breaking compatibility with older applications. - * @param[out] buf A pointer to a `statfs` structure where the extended filesystem statistics - * will be stored. This structure includes information such as: - * - `f_bsize`: The optimal block size for I/O operations. - * - `f_blocks`: Total number of blocks in the filesystem. - * - `f_bfree`: Number of free blocks. - * - `f_bavail`: Number of free blocks available to non-superuser. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function is particularly useful for querying large filesystems or filesystems with - * 64-bit addresses or data values, and is an extended version of the standard `sys_statfs`. - * - * @see sys_statfs - */ -sysret_t sys_statfs64(const char *path, size_t sz, struct statfs *buf) -{ - int ret = 0; - size_t len; - size_t copy_len; - char *copy_path; - struct statfs statfsbuff = { 0 }; - - if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) - { - return -EFAULT; - } - - if (sz != sizeof(struct statfs)) - { - return -EINVAL; - } - - len = lwp_user_strlen(path); - if (len <= 0) - { - return -EFAULT; - } - - copy_path = (char *)rt_malloc(len + 1); - if (!copy_path) - { - return -ENOMEM; - } - - copy_len = lwp_get_from_user(copy_path, (void *)path, len); - if (copy_len == 0) - { - rt_free(copy_path); - return -EFAULT; - } - copy_path[copy_len] = '\0'; - - ret = _SYS_WRAP(statfs(copy_path, &statfsbuff)); - rt_free(copy_path); - - if (ret == 0) - { - lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); - } - - return ret; -} - -/** - * @brief Get filesystem statistics for a file descriptor. - * - * This function retrieves statistics about the filesystem containing the file referred to by the - * file descriptor `fd`. The information is stored in the provided `statfs` structure, which includes - * details such as total blocks, free blocks, available inodes, etc. - * - * @param[in] fd The file descriptor referring to an open file. The file descriptor must be - * valid and represent a file on a mounted filesystem. - * @param[out] buf A pointer to a `statfs` structure where the filesystem statistics will be - * stored. This structure includes information such as: - * - `f_bsize`: The optimal block size for I/O operations. - * - `f_blocks`: Total number of blocks in the filesystem. - * - `f_bfree`: Number of free blocks. - * - `f_bavail`: Number of free blocks available to non-superuser. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note This function is useful for obtaining filesystem information about a specific file - * represented by its file descriptor, rather than querying the filesystem associated with - * a specific path. - * - * @see sys_statfs - */ -sysret_t sys_fstatfs(int fd, struct statfs *buf) -{ - int ret = 0; - struct statfs statfsbuff = { 0 }; - - if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) - { - return -EFAULT; - } - - ret = _SYS_WRAP(fstatfs(fd, &statfsbuff)); - - if (ret == 0) - { - lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); - } - - return ret; -} - -/** - * @brief Get 64-bit filesystem statistics for a file descriptor. - * - * This function retrieves 64-bit statistics about the filesystem containing the file referred to by - * the file descriptor `fd`. The statistics are stored in the provided `statfs` structure, which - * includes details such as total blocks, free blocks, available inodes, etc. The function differs - * from `sys_fstatfs` in that it supports 64-bit values for filesystem sizes and counts. - * - * @param[in] fd The file descriptor referring to an open file. The file descriptor must be - * valid and represent a file on a mounted filesystem. - * @param[in] sz The size of the `statfs` structure (typically used to ensure compatibility with - * different versions of the structure). - * @param[out] buf A pointer to a `statfs` structure where the filesystem statistics will be - * stored. This structure includes information such as: - * - `f_bsize`: The optimal block size for I/O operations. - * - `f_blocks`: Total number of blocks in the filesystem (64-bit). - * - `f_bfree`: Number of free blocks (64-bit). - * - `f_bavail`: Number of free blocks available to non-superuser (64-bit). - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * @note This function is particularly useful for systems supporting 64-bit filesystem statistics. - * It provides extended accuracy for large filesystems, including those with very large - * numbers of blocks or inodes. - * - * @see sys_fstatfs - */ -sysret_t sys_fstatfs64(int fd, size_t sz, struct statfs *buf) -{ - int ret = 0; - struct statfs statfsbuff = { 0 }; - - if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) - { - return -EFAULT; - } - - if (sz != sizeof(struct statfs)) - { - return -EINVAL; - } - - ret = _SYS_WRAP(fstatfs(fd, &statfsbuff)); - - if (ret == 0) - { - lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); - } - - return ret; -} - -static char *_cp_from_usr_string(char *dst, char *src, size_t length) -{ - char *rc; - size_t copied_bytes; - if (length) - { - copied_bytes = lwp_get_from_user(dst, src, length); - dst[copied_bytes] = '\0'; - rc = dst; - } - else - { - rc = RT_NULL; - } - return rc; -} - -/** - * @brief Mount a filesystem. - * - * This function mounts a filesystem onto a specified directory. It allows for mounting various - * types of filesystems, including but not limited to `ext4`, `nfs`, and `tmpfs`. The function - * allows the specification of the source device, the target mount point, and additional parameters - * such as filesystem type, mount flags, and extra data. - * - * @param[in] source The source of the filesystem, which can be a device (e.g., `/dev/sda1`) - * or a network resource (e.g., a NFS share). For certain filesystems like - * `tmpfs`, this can be `NULL`. - * @param[in] target The target directory where the filesystem will be mounted. - * This should be an existing empty directory. - * @param[in] filesystemtype The type of filesystem to mount, e.g., `ext4`, `tmpfs`, `nfs`, etc. - * @param[in] mountflags Flags that control the mount operation, such as `MS_RDONLY` for read-only - * mounts or `MS_NODEV` to prevent device files from being created. - * @param[in] data Optional data that is passed to the filesystem's mount handler. This - * may be used for setting up specific parameters for the filesystem type. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note Mounting a filesystem on an existing mount point can replace the existing filesystem at that - * location. Make sure the target directory is empty before mounting to avoid any conflicts. - */ -sysret_t sys_mount(char *source, char *target, char *filesystemtype, - unsigned long mountflags, void *data) -{ - char *kbuffer, *ksource, *ktarget, *kfs; - size_t len_source, len_target, len_fs; - char *tmp = NULL; - int ret = 0; - struct stat buf = { 0 }; - char *dev_fullpath = RT_NULL; - - len_source = source ? lwp_user_strlen(source) : 0; - if (len_source < 0) - return -EINVAL; - - len_target = target ? lwp_user_strlen(target) : 0; - if (len_target <= 0) - return -EINVAL; - - len_fs = filesystemtype ? lwp_user_strlen(filesystemtype) : 0; - if (len_fs < 0) - return -EINVAL; - - kbuffer = (char *)rt_malloc(len_source + 1 + len_target + 1 + len_fs + 1); - if (!kbuffer) - { - return -ENOMEM; - } - - /* get parameters from user space */ - ksource = kbuffer; - ktarget = ksource + len_source + 1; - kfs = ktarget + len_target + 1; - ksource = _cp_from_usr_string(ksource, source, len_source); - ktarget = _cp_from_usr_string(ktarget, target, len_target); - kfs = _cp_from_usr_string(kfs, filesystemtype, len_fs); - - if (mountflags & MS_REMOUNT) - { - ret = dfs_remount(ktarget, mountflags, data); - } - else - { - if (strcmp(kfs, "nfs") == 0) - { - tmp = ksource; - ksource = NULL; - } - if (strcmp(kfs, "tmp") == 0) - { - ksource = NULL; - } - - if (ksource && !dfs_file_stat(ksource, &buf) && S_ISBLK(buf.st_mode)) - { - dev_fullpath = dfs_normalize_path(RT_NULL, ksource); - RT_ASSERT(rt_strncmp(dev_fullpath, "/dev/", sizeof("/dev/") - 1) == 0); - ret = dfs_mount(dev_fullpath + sizeof("/dev/") - 1, ktarget, kfs, 0, tmp); - } - else - { - ret = dfs_mount(ksource, ktarget, kfs, 0, tmp); - } - - if (ret < 0) - { - ret = -rt_get_errno(); - } - } - - rt_free(kbuffer); - rt_free(dev_fullpath); - return ret; -} - -/** - * @brief Unmount a filesystem. - * - * This function unmounts a previously mounted filesystem from a specified target directory or file. - * It removes the filesystem from the system, making the resources associated with it available for - * other uses. It can also support additional flags to control the unmounting behavior. - * - * @param[in] __special_file The target directory or mount point from which the filesystem is to be - * unmounted. This should be a valid mount point that was previously mounted. - * @param[in] __flags Flags that control the unmounting behavior. Common flags include: - * - `MNT_FORCE`: Forces the unmount even if the filesystem is busy. - * - `MNT_DETACH`: Detaches the filesystem, allowing it to be unmounted - * asynchronously. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note The `MNT_FORCE` flag should be used with caution, as it may result in data loss or corruption - * if there are pending writes or active processes using the filesystem. - * - * @see sys_mount - */ -sysret_t sys_umount2(char *__special_file, int __flags) -{ - char *copy_special_file; - size_t len_special_file, copy_len_special_file; - int ret = 0; - - len_special_file = lwp_user_strlen(__special_file); - if (len_special_file <= 0) - { - return -EFAULT; - } - - copy_special_file = (char *)rt_malloc(len_special_file + 1); - if (!copy_special_file) - { - return -ENOMEM; - } - - copy_len_special_file = lwp_get_from_user(copy_special_file, __special_file, len_special_file); - copy_special_file[copy_len_special_file] = '\0'; - - ret = dfs_unmount(copy_special_file); - rt_free(copy_special_file); - - return ret; -} - -/** - * @brief Create a new hard link to an existing file. - * - * This function creates a new hard link to an existing file, making the file accessible from - * multiple filenames. The new link points to the same inode as the existing file, meaning - * both names refer to the same underlying data. The link count for the file is incremented. - * - * @param[in] existing The path to the existing file. It must be an absolute or relative path - * to a file that already exists. - * @param[in] new The path to the new link to be created. This can be a new filename or - * an existing directory where the link will be placed. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note Hard links share the same inode number, meaning changes to the data of the file via one - * link will be reflected in all hard links. Deleting any of the links will not remove the - * file data until all links are deleted. - */ -sysret_t sys_link(const char *existing, const char *new) -{ - int ret = -1; - int err = 0; -#ifdef RT_USING_DFS_V2 -#ifdef ARCH_MM_MMU - int len = 0; - char *kexisting = RT_NULL; - char *knew = RT_NULL; - - len = lwp_user_strlen(existing); - if (len <= 0) - { - return -EFAULT; - } - - kexisting = (char *)kmem_get(len + 1); - if (!kexisting) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kexisting, (void *)existing, len + 1) != (len + 1)) - { - kmem_put(kexisting); - return -EINVAL; - } - - len = lwp_user_strlen(new); - if (len <= 0) - { - kmem_put(kexisting); - return -EFAULT; - } - - knew = (char *)kmem_get(len + 1); - if (!knew) - { - kmem_put(kexisting); - return -ENOMEM; - } - - if (lwp_get_from_user(knew, (void *)new, len + 1) != (len + 1)) - { - kmem_put(knew); - kmem_put(kexisting); - return -EINVAL; - } - - ret = dfs_file_link(kexisting, knew); - if (ret < 0) - { - err = GET_ERRNO(); - } - - kmem_put(knew); - kmem_put(kexisting); -#else - ret = dfs_file_link(existing, new); -#endif -#else - SET_ERRNO(EFAULT); - err = GET_ERRNO(); -#endif - - return (err < 0 ? err : ret); -} - -/** - * @brief Create a symbolic link to an existing file or directory. - * - * This function creates a new symbolic link, which is a special file that contains a reference - * to another file or directory. The symbolic link allows for easy redirection or aliasing of - * file paths. Unlike a hard link, a symbolic link can point to a file or directory across - * different file systems. - * - * @param[in] existing The path to the existing file or directory that the symbolic link - * will refer to. It must be an absolute or relative path. - * @param[in] new The path to the new symbolic link to be created. This must be a valid - * path where the link will be placed. - * - * @return sysret_t Returns `0` on success. On failure, returns a negative error code. - * - * @note A symbolic link is distinct from a hard link in that it points to the path of the target - * file or directory, rather than directly to the inode. If the target file or directory is - * removed or moved, the symbolic link will become "broken" and will not resolve to a valid - * location. - */ -sysret_t sys_symlink(const char *existing, const char *new) -{ - int ret = -1; - int err = 0; -#ifdef ARCH_MM_MMU - - ret = lwp_user_strlen(existing); - if (ret <= 0) - { - return -EFAULT; - } - - ret = lwp_user_strlen(new); - if (ret <= 0) - { - return -EFAULT; - } -#endif -#ifdef RT_USING_DFS_V2 - ret = dfs_file_symlink(existing, new); - if (ret < 0) - { - err = GET_ERRNO(); - } -#else - SET_ERRNO(EFAULT); -#endif - return (err < 0 ? err : ret); -} - -/** - * @brief Create an event file descriptor. - * - * This function creates an eventfd, which is a file descriptor used for event notification. - * Eventfd is a simple mechanism to notify threads or processes about certain events using - * an integer counter. It is typically used for inter-process communication (IPC) or - * synchronization purposes, where one thread or process increments the counter to signal - * another thread or process. - * - * @param[in] count Initial value for the eventfd counter. It can be set to a non-zero - * value to initialize the counter to that value. - * @param[in] flags Flags that control the behavior of the eventfd. Valid flags include: - * - `EFD_CLOEXEC`: Set the close-on-exec flag for the eventfd. - * - `EFD_NONBLOCK`: Set the non-blocking flag for the eventfd. - * - * @return sysret_t On success, returns a valid file descriptor referring to the eventfd. On - * failure, returns a negative error code. - * - * @note Eventfd can be used for both counting and signaling purposes. It provides - * efficient signaling between threads or processes. - */ -sysret_t sys_eventfd2(unsigned int count, int flags) -{ - int ret; - - ret = eventfd(count, flags); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Create an epoll instance. - * - * This function creates an epoll instance, which is used for monitoring multiple file descriptors - * to see if I/O is possible on them. It allows a program to efficiently wait for events such as - * data being available for reading or space becoming available for writing. Epoll provides a scalable - * mechanism for managing large numbers of file descriptors. - * - * @param[in] flags Flags that control the behavior of the epoll instance. The valid flags include: - * - `EPOLL_CLOEXEC`: Set the close-on-exec flag for the epoll instance. - * - `EPOLL_NONBLOCK`: Set the non-blocking flag for the epoll instance. - * - * @return sysret_t On success, returns a file descriptor for the epoll instance. On failure, - * returns a negative error code. - * - * @note The `sys_epoll_create1` function is similar to `sys_epoll_create`, but it allows for - * additional control over the epoll instance creation via flags. - */ -sysret_t sys_epoll_create1(int flags) -{ - int ret; - - ret = epoll_create(flags); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Control an epoll instance. - * - * This function performs control operations on an epoll instance, such as adding, modifying, - * or removing file descriptors from the epoll instance's interest list. - * - * @param[in] fd The file descriptor of the epoll instance created using `sys_epoll_create` or `sys_epoll_create1`. - * @param[in] op The operation to perform on the epoll instance. It can be one of the following: - * - `EPOLL_CTL_ADD`: Add the specified file descriptor to the epoll interest list. - * - `EPOLL_CTL_MOD`: Modify the event mask of an existing file descriptor in the epoll interest list. - * - `EPOLL_CTL_DEL`: Remove the specified file descriptor from the epoll interest list. - * @param[in] fd2 The file descriptor to be added, modified, or removed from the epoll interest list. - * @param[in] ev A pointer to an `epoll_event` structure that describes the event to be associated with `fd2`. - * This parameter is required for `EPOLL_CTL_ADD` and `EPOLL_CTL_MOD`, but not for `EPOLL_CTL_DEL`. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note This function is typically used to manage the set of file descriptors monitored by an epoll instance. - */ -sysret_t sys_epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) -{ - int ret = 0; - struct epoll_event *kev = RT_NULL; - - if (ev) - { - if (!lwp_user_accessable((void *)ev, sizeof(struct epoll_event))) - return -EFAULT; - - kev = kmem_get(sizeof(struct epoll_event)); - if (kev == RT_NULL) - { - return -ENOMEM; - } - - if (lwp_get_from_user(kev, ev, sizeof(struct epoll_event)) != sizeof(struct epoll_event)) - { - kmem_put(kev); - return -EINVAL; - } - - ret = epoll_ctl(fd, op, fd2, kev); - - kmem_put(kev); - } - else - { - ret = epoll_ctl(fd, op, fd2, RT_NULL); - } - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Wait for events on an epoll file descriptor, with the ability to block for specific signals. - * - * This function waits for events on an epoll file descriptor. It blocks until one or more events - * are available or a timeout occurs. In addition to waiting for events, it can also block for specific signals - * as specified by the `sigs` argument. - * - * @param[in] fd The file descriptor of the epoll instance, created using `sys_epoll_create` or `sys_epoll_create1`. - * @param[out] ev A pointer to an array of `epoll_event` structures that will receive the events that have occurred. - * @param[in] cnt The maximum number of events to return. This specifies the size of the `ev` array. - * @param[in] to The timeout in milliseconds. A negative value means no timeout, and `0` means non-blocking. - * @param[in] sigs A pointer to a signal set (`sigset_t`) that specifies the signals to block during the wait. - * If `NULL`, no signals are blocked. - * @param[in] sigsetsize The size of the signal set (`sigs`) in bytes. This should be the size of `sigset_t` if `sigs` is not `NULL`. - * - * @return sysret_t On success, returns the number of events returned (may be `0` if the timeout expires with no events). - * On failure, returns a negative error code. - * - * @note This function is similar to `sys_epoll_wait`, but it also allows for signal handling, blocking the calling thread - * from receiving signals specified in the `sigs` set while waiting for events. - */ -sysret_t sys_epoll_pwait(int fd, - struct epoll_event *ev, - int cnt, - int to, - const sigset_t *sigs, - unsigned long sigsetsize) -{ - int ret = 0; - struct epoll_event *kev = RT_NULL; - sigset_t *ksigs = RT_NULL; - - if (!lwp_user_accessable((void *)ev, cnt * sizeof(struct epoll_event))) - return -EFAULT; - - kev = kmem_get(cnt * sizeof(struct epoll_event)); - if (kev == RT_NULL) - { - return -ENOMEM; - } - - if (sigs != RT_NULL) - { - if (!lwp_user_accessable((void *)sigs, sizeof(sigset_t))) - { - kmem_put(kev); - return -EFAULT; - } - - ksigs = kmem_get(sizeof(sigset_t)); - if (ksigs == RT_NULL) - { - kmem_put(kev); - return -ENOMEM; - } - - if (lwp_get_from_user(ksigs, (void *)sigs, sizeof(sigset_t)) != sizeof(sigset_t)) - { - kmem_put(kev); - kmem_put(ksigs); - return -EINVAL; - } - } - - ret = epoll_pwait(fd, kev, cnt, to, ksigs); - - if (ret > 0) - { - lwp_put_to_user((void *)ev, kev, ret * sizeof(struct epoll_event)); - } - - if (sigs != RT_NULL) - kmem_put(ksigs); - - kmem_put(kev); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Truncate a file to a specified length. - * - * This function resizes the file associated with the given file descriptor `fd` to the specified length. - * If the current size of the file is greater than the specified length, the file is truncated. If the file is smaller, - * it is extended, and the new space is initialized to zero. - * - * @param[in] fd The file descriptor referring to the file to truncate. This must be a valid open file descriptor. - * @param[in] length The new length of the file. The file will be truncated or extended to this size. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note The file descriptor `fd` must be opened with write permissions for this operation to succeed. - */ -sysret_t sys_ftruncate(int fd, size_t length) -{ - int ret; - - ret = ftruncate(fd, length); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Set file access and modification times. - * - * This function updates the access and modification times of a file or directory referenced by `__fd` or `__path`. - * If `__fd` is a valid file descriptor, the times will be applied to the file or directory it refers to. - * If `__fd` is `AT_FDCWD`, the times will be applied to the file or directory specified by `__path`. - * The `__times` argument consists of two `timespec` values: the first represents the access time, - * and the second represents the modification time. If `__times` is `NULL`, the current time is used. - * - * @param[in] __fd The file descriptor of the file or directory to modify, or `AT_FDCWD` to modify using `__path`. - * @param[in] __path The path to the file or directory to modify. Ignored if `__fd` is not `AT_FDCWD`. - * @param[in] __times An array of two `timespec` structures. The first element is the access time, and the second is the modification time. - * If `__times` is `NULL`, the current time is used for both access and modification times. - * @param[in] __flags Flags to modify behavior. Supported flags include: - * - `AT_SYMLINK_NOFOLLOW`: Do not follow symbolic links. - * - `AT_NO_AUTOMOUNT`: Do not trigger automounting. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note This function modifies both access and modification times of files, and may affect file system timestamps. - */ -sysret_t sys_utimensat(int __fd, const char *__path, const struct timespec __times[2], int __flags) -{ -#ifdef RT_USING_DFS_V2 -#ifdef ARCH_MM_MMU - int ret = -1; - rt_size_t len = 0; - char *kpath = RT_NULL; - - len = lwp_user_strlen(__path); - if (len <= 0) - { - return -EINVAL; - } - - kpath = (char *)kmem_get(len + 1); - if (!kpath) - { - return -ENOMEM; - } - - lwp_get_from_user(kpath, (void *)__path, len + 1); - ret = utimensat(__fd, kpath, __times, __flags); - - kmem_put(kpath); - - return ret; -#else - if (!lwp_user_accessable((void *)__path, 1)) - { - return -EFAULT; - } - int ret = utimensat(__fd, __path, __times, __flags); - return ret; -#endif -#else - return -1; -#endif -} - -/** - * @brief Change file permissions. - * - * This function changes the permissions of a file or directory specified by the `pathname`. - * The new permissions are specified using the `mode` argument, which is a bitwise OR of permission bits. - * The permissions apply to the file owner, group, and others, depending on the value of `mode`. - * - * @param[in] pathname The path to the file or directory whose permissions are to be changed. - * @param[in] mode The new permissions to set, represented as a bitwise OR of permission flags. - * For example, `S_IRUSR`, `S_IWUSR`, `S_IRGRP`, etc. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note If the file or directory is a symbolic link, this function will modify the permissions of the symbolic link itself, - * not the target file or directory. - */ -sysret_t sys_chmod(const char *pathname, mode_t mode) -{ - char *copy_file; - size_t len_file, copy_len_file; - struct dfs_attr attr = { 0 }; - int ret = 0; - - len_file = lwp_user_strlen(pathname); - if (len_file <= 0) - { - return -EFAULT; - } - - copy_file = (char *)rt_malloc(len_file + 1); - if (!copy_file) - { - return -ENOMEM; - } - - copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file); - - attr.st_mode = mode; - attr.ia_valid |= ATTR_MODE_SET; - - copy_file[copy_len_file] = '\0'; - ret = dfs_file_setattr(copy_file, &attr); - rt_free(copy_file); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -sysret_t sys_fchmod(int fd, mode_t mode) -{ - int ret; - - ret = fchmod(fd, mode); - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Change the ownership of a file or directory. - * - * This function changes the owner and/or group of the file or directory specified by `pathname`. - * The `owner` and `group` parameters represent the new owner and group IDs, respectively. If either - * parameter is set to `-1`, that aspect (owner or group) will remain unchanged. - * - * @param[in] pathname The path to the file or directory whose ownership is to be changed. - * @param[in] owner The new owner ID. If set to `-1`, the owner's ID is not changed. - * @param[in] group The new group ID. If set to `-1`, the group's ID is not changed. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note The caller must have the appropriate permissions (i.e., be the superuser or the file owner) - * to change the ownership of a file or directory. - */ -sysret_t sys_chown(const char *pathname, uid_t owner, gid_t group) -{ - char *copy_file; - size_t len_file, copy_len_file; - struct dfs_attr attr = { 0 }; - int ret = 0; - - len_file = lwp_user_strlen(pathname); - if (len_file <= 0) - { - return -EFAULT; - } - - copy_file = (char *)rt_malloc(len_file + 1); - if (!copy_file) - { - return -ENOMEM; - } - - copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file); - - if (owner >= 0) - { - attr.st_uid = owner; - attr.ia_valid |= ATTR_UID_SET; - } - - if (group >= 0) - { - attr.st_gid = group; - attr.ia_valid |= ATTR_GID_SET; - } - - copy_file[copy_len_file] = '\0'; - ret = dfs_file_setattr(copy_file, &attr); - rt_free(copy_file); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -#ifndef LWP_USING_RUNTIME -sysret_t lwp_teardown(struct rt_lwp *lwp, void (*cb)(void)) -{ - /* if no LWP_USING_RUNTIME configured */ - return -ENOSYS; -} -#endif - -/** - * @brief Reboot the system. - * - * This function initiates a system reboot with a specific reboot type and optional arguments. - * The reboot process is performed by passing `magic` and `magic2` as security keys to validate the - * reboot request, along with the desired reboot type (`type`) and optional argument (`arg`) that - * may contain additional parameters depending on the reboot type. - * - * @param[in] magic A magic number for security validation, typically used to verify that the caller - * has sufficient privileges (e.g., root). - * @param[in] magic2 A second magic number for additional security verification, used in combination - * with `magic` to validate the reboot request. - * @param[in] type The type of reboot to perform, such as `RB_AUTOBOOT`, `RB_HALT`, `RB_RESTART`, - * etc. The exact values may depend on the platform. - * @param[in] arg An optional argument for the reboot process. This can provide additional data, - * such as a specific shutdown procedure, depending on the type of reboot. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note This operation is typically available to the system administrator (root) and should be used - * with caution as it can cause the system to restart or shut down. - */ -sysret_t sys_reboot(int magic, int magic2, int type, void *arg) -{ - sysret_t rc; - switch (type) - { - /* Hardware reset */ - case RB_AUTOBOOT: - rc = lwp_teardown(lwp_self(), rt_hw_cpu_reset); - break; - - /* Stop system and switch power off */ - case RB_POWER_OFF: - rc = lwp_teardown(lwp_self(), rt_hw_cpu_shutdown); - break; - default: - rc = -ENOSYS; - } - - return rc; -} - -/** - * @brief Read data from a file descriptor at a specific offset. - * - * This function reads data from a file descriptor `fd` into the buffer `buf`, starting from the - * specified byte `offset`. Unlike a regular `read` call, `sys_pread64` allows the file pointer to - * remain unchanged after the read, as the operation directly accesses the file at the provided - * offset. - * - * @param[in] fd The file descriptor from which to read. It must be a valid open file descriptor. - * @param[out] buf A pointer to the buffer where the read data will be stored. - * @param[in] size The number of bytes to read from the file. - * @param[in] offset The offset in bytes from the beginning of the file to start reading from. - * - * @return ssize_t On success, returns the number of bytes read (which may be less than `size` if - * the end of the file is reached). On error, returns a negative error code. - * - * @note This function is particularly useful for random access to files, allowing for efficient - * reads from arbitrary positions without affecting the current file pointer. - */ -ssize_t sys_pread64(int fd, void *buf, int size, size_t offset) -#ifdef RT_USING_DFS_V2 -{ - ssize_t pread(int fd, void *buf, size_t len, size_t offset); -#ifdef ARCH_MM_MMU - ssize_t ret = -1; - void *kmem = RT_NULL; - - if (!size) - { - return -EINVAL; - } - - if (!lwp_user_accessable((void *)buf, size)) - { - return -EFAULT; - } - kmem = kmem_get(size); - if (!kmem) - { - return -ENOMEM; - } - - ret = pread(fd, kmem, size, offset); - if (ret > 0) - { - lwp_put_to_user(buf, kmem, ret); - } - - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmem); - - return ret; -#else - if (!lwp_user_accessable((void *)buf, size)) - { - return -EFAULT; - } - - ssize_t ret = pread(fd, kmem, size, offset); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} -#else -{ - ssize_t ret = -ENOSYS; - return (ret < 0 ? GET_ERRNO() : ret); -} -#endif - -/** - * @brief Write data to a file descriptor at a specific offset. - * - * This function writes data from the buffer `buf` to the file descriptor `fd`, starting at the - * specified byte `offset`. Unlike a regular `write` call, `sys_pwrite64` allows the file pointer to - * remain unchanged after the write, as the operation directly writes the data to the file at the - * provided offset. - * - * @param[in] fd The file descriptor to which the data will be written. It must be a valid open - * file descriptor. - * @param[in] buf A pointer to the buffer containing the data to be written. - * @param[in] size The number of bytes to write to the file. - * @param[in] offset The offset in bytes from the beginning of the file to start writing to. - * - * @return ssize_t On success, returns the number of bytes written (which may be less than `size` if - * the write operation is partial). On error, returns a negative error code. - * - * @note This function is particularly useful for random access to files, allowing for efficient - * writes to arbitrary positions without affecting the current file pointer. - */ -ssize_t sys_pwrite64(int fd, void *buf, int size, size_t offset) -#ifdef RT_USING_DFS_V2 -{ - ssize_t pwrite(int fd, const void *buf, size_t len, size_t offset); -#ifdef ARCH_MM_MMU - ssize_t ret = -1; - void *kmem = RT_NULL; - - if (!size) - { - return -EINVAL; - } - - if (!lwp_user_accessable((void *)buf, size)) - { - return -EFAULT; - } - kmem = kmem_get(size); - if (!kmem) - { - return -ENOMEM; - } - - lwp_get_from_user(kmem, (void *)buf, size); - - ret = pwrite(fd, kmem, size, offset); - if (ret < 0) - { - ret = GET_ERRNO(); - } - - kmem_put(kmem); - - return ret; -#else - if (!lwp_user_accessable((void *)buf, size)) - { - return -EFAULT; - } - - ssize_t ret = pwrite(fd, kmem, size, offset); - return (ret < 0 ? GET_ERRNO() : ret); -#endif -} -#else -{ - ssize_t ret = -ENOSYS; - return (ret < 0 ? GET_ERRNO() : ret); -} -#endif - -/** - * @brief Create a timer file descriptor. - * - * This function creates a timer that can be used to notify a process after a specified - * amount of time has passed. The timer is represented by a file descriptor, which can be - * used with functions such as `read` and `poll` to monitor and retrieve notifications - * about timer expirations. - * - * @param[in] clockid The clock to use for the timer. Possible values include: - * - `CLOCK_REALTIME`: The system's real-time clock. - * - `CLOCK_MONOTONIC`: A clock that cannot be set and is not affected by - * changes in the system time. - * @param[in] flags Flags that modify the behavior of the timer. The commonly used values are: - * - `TFD_CLOEXEC`: Set the close-on-exec flag for the file descriptor. - * - `TFD_NONBLOCK`: Set the non-blocking flag for the file descriptor. - * - * @return sysret_t On success, returns a non-negative file descriptor that refers to the timer. - * On failure, returns a negative error code. - * - * @note The timer created by this function can be used to create periodic or one-shot timers. - * The timer can be configured using `timerfd_settime` and notifications can be retrieved - * by reading from the returned file descriptor. - */ -sysret_t sys_timerfd_create(int clockid, int flags) -{ - int ret; - - ret = timerfd_create(clockid, flags); - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Set or modify the expiration time for a timer. - * - * This function is used to arm or re-arm a timer associated with a file descriptor created - * by `sys_timerfd_create`. It allows setting a new expiration time and can also return - * the previous timer configuration. - * - * @param[in] fd The file descriptor referring to the timer, which was created using - * `sys_timerfd_create`. - * @param[in] flags Flags that modify the behavior of the timer. The commonly used values are: - * - `TFD_TIMER_ABSTIME`: If set, the `new` expiration time is an absolute time. - * - `TFD_TIMER_RELATIVE`: The default behavior, where `new` expiration time is - * a relative time from now. - * @param[in] new A pointer to a `struct itimerspec` specifying the new expiration time. - * It contains two fields: - * - `it_value`: The initial expiration time of the timer. - * - `it_interval`: The period of periodic timers (if zero, the timer is one-shot). - * @param[out] old A pointer to a `struct itimerspec` where the previous timer configuration - * will be stored. This can be `NULL` if the previous configuration is not needed. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note This function allows for both one-shot and periodic timers. If the timer is periodic, - * it will continue triggering at intervals specified in `it_interval` until canceled or modified. - * When a timer expires, the associated file descriptor becomes readable, and the application - * can retrieve the expiration event by reading from the file descriptor. - */ -sysret_t sys_timerfd_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old) -{ - int ret = -1; - struct itimerspec *knew = RT_NULL; - struct itimerspec *kold = RT_NULL; - - if (new == RT_NULL) - return -EINVAL; - - if (!lwp_user_accessable((void *)new, sizeof(struct itimerspec))) - { - return -EFAULT; - } - - knew = kmem_get(sizeof(struct itimerspec)); - - if (knew) - { - lwp_get_from_user(knew, (void *)new, sizeof(struct itimerspec)); - - if (old) - { - if (!lwp_user_accessable((void *)old, sizeof(struct itimerspec))) - { - kmem_put(knew); - return -EFAULT; - } - - kold = kmem_get(sizeof(struct itimerspec)); - if (kold == RT_NULL) - { - kmem_put(knew); - return -ENOMEM; - } - } - - ret = timerfd_settime(fd, flags, knew, kold); - - if (old) - { - lwp_put_to_user(old, kold, sizeof(*kold)); - kmem_put(kold); - } - - kmem_put(knew); - } - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Retrieve the current configuration of a timer. - * - * This function is used to obtain the current expiration time and interval of a timer associated - * with a file descriptor created by `sys_timerfd_create`. It allows querying the current state - * of the timer, including the time remaining until the next expiration and the period (for periodic timers). - * - * @param[in] fd The file descriptor referring to the timer, which was created using - * `sys_timerfd_create`. - * @param[out] cur A pointer to a `struct itimerspec` where the current timer configuration will - * be stored. This structure contains: - * - `it_value`: The time remaining until the timer expires. - * - `it_interval`: The period for periodic timers (zero for one-shot timers). - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note The timer's `it_value` field will contain the time remaining until the next expiration. - * If the timer is periodic, the `it_interval` field will contain the period for the next expiration. - * If the timer has expired and there is no further interval (for one-shot timers), `it_value` will - * contain a value of `0`. - */ -sysret_t sys_timerfd_gettime(int fd, struct itimerspec *cur) -{ - int ret = -1; - struct itimerspec *kcur; - - if (cur == RT_NULL) - return -EINVAL; - - if (!lwp_user_accessable((void *)cur, sizeof(struct itimerspec))) - { - return -EFAULT; - } - - kcur = kmem_get(sizeof(struct itimerspec)); - - if (kcur) - { - lwp_get_from_user(kcur, cur, sizeof(struct itimerspec)); - ret = timerfd_gettime(fd, kcur); - lwp_put_to_user(cur, kcur, sizeof(struct itimerspec)); - kmem_put(kcur); - } - - return (ret < 0 ? GET_ERRNO() : ret); -} - -/** - * @brief Create a file descriptor for receiving signals. - * - * This function creates a file descriptor that can be used to receive signals, similar to how a - * regular file descriptor is used. The signals specified in the provided signal mask are blocked - * for the calling thread, and any matching signals will be reported through the file descriptor. - * This allows signals to be handled in a more controlled, non-interrupt-driven way by using - * standard I/O operations (e.g., `read()` or `select()`) on the file descriptor. - * - * @param[in] fd A file descriptor associated with the process, typically obtained from - * `sys_signalfd()` or a similar mechanism. If `fd` is `-1`, a new file descriptor - * will be created. - * @param[in] mask A pointer to the signal mask (`sigset_t`) that specifies the signals that - * should be received by the file descriptor. Only signals in the mask will be - * reported. - * @param[in] flags Additional flags to control the behavior of the signalfd. Common flags - * include `O_NONBLOCK` for non-blocking operation. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note The caller must call `read()` on the resulting file descriptor to actually receive signals. - * Each read returns a `struct signalfd_siginfo` containing the signal number, and additional - * information, such as the signal source and sender. - * - * @see sys_read(), sigprocmask(), sigaction(), sigsuspend(). - */ -sysret_t sys_signalfd(int fd, const sigset_t *mask, int flags) -{ - int ret = 0; - sigset_t *kmask = RT_NULL; - -#ifdef RT_USING_MUSLLIBC - if (mask == RT_NULL) - return -EINVAL; - - if (!lwp_user_accessable((void *)mask, sizeof(struct itimerspec))) - { - return -EFAULT; - } - - kmask = kmem_get(sizeof(struct itimerspec)); - - if (kmask) - { - if (lwp_get_from_user(kmask, (void *)mask, sizeof(struct itimerspec)) != sizeof(struct itimerspec)) - { - kmem_put(kmask); - return -EFAULT; - } - - ret = signalfd(fd, mask, flags); - kmem_put(kmask); - } -#endif - - return (ret < 0 ? GET_ERRNO() : ret); -} - -sysret_t sys_memfd_create() -{ - return 0; -} - -/** - * @brief Set the value of an interval timer. - * - * This function is used to set the value of a specified timer that generates periodic signals - * after the specified intervals. The timer can be configured to generate a signal when it expires, - * and it can be used for tasks like scheduling periodic events. - * - * @param[in] which Specifies which timer to set. Possible values include: - * - `ITIMER_REAL`: Timer that decrements in real time and sends `SIGALRM` when expired. - * - `ITIMER_VIRTUAL`: Timer that decrements only while the process is executing. - * - `ITIMER_PROF`: Timer that decrements while the process is executing or when it is - * executing in the kernel. - * @param[in] new A pointer to a `struct itimerspec` containing the new value for the timer. - * The `itimerspec` structure contains two `timespec` fields: `it_value` for the initial - * expiration time and `it_interval` for the interval between successive expirations. - * @param[out] old A pointer to a `struct itimerspec` where the previous timer values (before the - * timer is set) will be stored. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note If the specified timer is already active, it will be updated with the new values. The timer - * will start at the `it_value` time and will repeat at the interval specified in `it_interval`. - * If `it_interval` is set to `0`, the timer will not repeat and will only expire once. - * - * @see sys_getitimer(), sigaction(), alarm(), setitimer(). - */ -sysret_t sys_setitimer(int which, const struct itimerspec * restrict new, struct itimerspec * restrict old) -{ - sysret_t rc = 0; - rt_lwp_t lwp = lwp_self(); - struct itimerspec new_value_k; - struct itimerspec old_value_k; - - if (lwp_get_from_user(&new_value_k, (void *)new, sizeof(*new)) != sizeof(*new)) - { - return -EFAULT; - } - - rc = lwp_signal_setitimer(lwp, which, &new_value_k, &old_value_k); - if (old && lwp_put_to_user(old, (void *)&old_value_k, sizeof old_value_k) != sizeof old_value_k) - return -EFAULT; - - return rc; -} - -/** - * @brief Set the robust list for the current thread. - * - * The `sys_set_robust_list` function sets the robust list for the calling thread. The robust list is - * a list of `struct robust_list_head` elements that contain information about mutexes or other - * resources that need to be cleaned up if the thread dies unexpectedly. - * - * This function is typically used for thread safety in multi-threaded programs, where robust mutexes - * can be used to avoid leaving resources in an inconsistent state when a thread is terminated - * without properly unlocking locks or releasing resources. - * - * @param[in] head A pointer to the robust list to be set. This list contains `struct robust_list_head` - * entries which are used by the kernel to maintain information about robust mutexes. - * @param[in] len The length of the robust list (the number of entries in the list). This should correspond - * to the size of the list in memory, generally expressed in bytes. - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note The robust list should be set during the initialization of a thread or task, and it may be - * used by the kernel to track which mutexes need to be cleaned up in case of failure. - * The list may be manipulated using related functions such as `sys_get_robust_list`. - * - * @see sys_get_robust_list(), robust_mutex, pthread_mutex, set_robust_list. - */ -sysret_t sys_set_robust_list(struct robust_list_head *head, size_t len) -{ - if (len != sizeof(*head)) - return -EINVAL; - - rt_thread_self()->robust_list = head; - return 0; -} - -/** - * @brief Get the robust list for the specified thread. - * - * The `sys_get_robust_list` function retrieves the robust list associated with the given thread. - * The robust list contains `struct robust_list_head` entries used by the kernel to manage - * robust mutexes and other resources that need to be cleaned up if the thread terminates unexpectedly. - * - * This function is primarily used to retrieve the robust list for a specific thread, so that - * the caller can inspect or manipulate the robust list to handle clean-up operations in case - * the thread exits while holding resources. - * - * @param[in] tid The thread ID of the thread whose robust list is to be retrieved. - * - * @param[out] head_ptr A pointer to a pointer to `struct robust_list_head`. On success, - * this will point to the robust list for the specified thread. - * - * @param[out] len_ptr A pointer to a variable that will hold the length of the robust list - * (i.e., the number of entries in the list). - * - * @return sysret_t On success, returns `0`. On failure, returns a negative error code. - * - * @note The robust list is used to track mutexes and other resources that need to be cleaned up - * if a thread terminates without releasing the resources properly. It is typically used by - * the kernel for thread safety and resource management in multi-threaded programs. - * - * @see sys_set_robust_list(), robust_mutex, pthread_mutex, get_robust_list. - */ -sysret_t sys_get_robust_list(int tid, struct robust_list_head **head_ptr, size_t *len_ptr) -{ - rt_thread_t thread; - size_t len; - struct robust_list_head *head; - - if (!lwp_user_accessable((void *)head_ptr, sizeof(struct robust_list_head *))) - { - return -EFAULT; - } - if (!lwp_user_accessable((void *)len_ptr, sizeof(size_t))) - { - return -EFAULT; - } - - if (tid == 0) - { - thread = rt_thread_self(); - head = thread->robust_list; - } - else - { - thread = lwp_tid_get_thread_and_inc_ref(tid); - if (thread) - { - head = thread->robust_list; - lwp_tid_dec_ref(thread); - } - else - { - return -ESRCH; - } - } - - len = sizeof(*(head)); - - if (!lwp_put_to_user(len_ptr, &len, sizeof(size_t))) - return -EFAULT; - if (!lwp_put_to_user(head_ptr, &head, sizeof(struct robust_list_head *))) - return -EFAULT; - - return 0; -} - -const static struct rt_syscall_def func_table[] = { - SYSCALL_SIGN(sys_exit), /* 01 */ - SYSCALL_SIGN(sys_read), - SYSCALL_SIGN(sys_write), - SYSCALL_SIGN(sys_lseek), - SYSCALL_SIGN(sys_open), /* 05 */ - SYSCALL_SIGN(sys_close), - SYSCALL_SIGN(sys_ioctl), - SYSCALL_SIGN(sys_fstat), - SYSCALL_SIGN(sys_poll), - SYSCALL_SIGN(sys_nanosleep), /* 10 */ - SYSCALL_SIGN(sys_gettimeofday), - SYSCALL_SIGN(sys_settimeofday), - SYSCALL_SIGN(sys_exec), - SYSCALL_SIGN(sys_kill), - SYSCALL_SIGN(sys_getpid), /* 15 */ - SYSCALL_SIGN(sys_getpriority), - SYSCALL_SIGN(sys_setpriority), - SYSCALL_SIGN(sys_sem_create), - SYSCALL_SIGN(sys_sem_delete), - SYSCALL_SIGN(sys_sem_take), /* 20 */ - SYSCALL_SIGN(sys_sem_release), - SYSCALL_SIGN(sys_mutex_create), - SYSCALL_SIGN(sys_mutex_delete), - SYSCALL_SIGN(sys_mutex_take), - SYSCALL_SIGN(sys_mutex_release), /* 25 */ - SYSCALL_SIGN(sys_event_create), - SYSCALL_SIGN(sys_event_delete), - SYSCALL_SIGN(sys_event_send), - SYSCALL_SIGN(sys_event_recv), - SYSCALL_SIGN(sys_mb_create), /* 30 */ - SYSCALL_SIGN(sys_mb_delete), - SYSCALL_SIGN(sys_mb_send), - SYSCALL_SIGN(sys_mb_send_wait), - SYSCALL_SIGN(sys_mb_recv), - SYSCALL_SIGN(sys_mq_create), /* 35 */ - SYSCALL_SIGN(sys_mq_delete), - SYSCALL_SIGN(sys_mq_send), - SYSCALL_SIGN(sys_mq_urgent), - SYSCALL_SIGN(sys_mq_recv), - SYSCALL_SIGN(sys_thread_create), /* 40 */ - SYSCALL_SIGN(sys_thread_delete), - SYSCALL_SIGN(sys_thread_startup), - SYSCALL_SIGN(sys_thread_self), - SYSCALL_SIGN(sys_channel_open), - SYSCALL_SIGN(sys_channel_close), /* 45 */ - SYSCALL_SIGN(sys_channel_send), - SYSCALL_SIGN(sys_channel_send_recv_timeout), - SYSCALL_SIGN(sys_channel_reply), - SYSCALL_SIGN(sys_channel_recv_timeout), - SYSCALL_SIGN(sys_enter_critical), /* 50 */ - SYSCALL_SIGN(sys_exit_critical), - - SYSCALL_USPACE(SYSCALL_SIGN(sys_brk)), - SYSCALL_USPACE(SYSCALL_SIGN(sys_mmap2)), - SYSCALL_USPACE(SYSCALL_SIGN(sys_munmap)), -#ifdef ARCH_MM_MMU - SYSCALL_USPACE(SYSCALL_SIGN(sys_shmget)), /* 55 */ - SYSCALL_USPACE(SYSCALL_SIGN(sys_shmrm)), - SYSCALL_USPACE(SYSCALL_SIGN(sys_shmat)), - SYSCALL_USPACE(SYSCALL_SIGN(sys_shmdt)), -#else -#ifdef RT_LWP_USING_SHM - SYSCALL_SIGN(sys_shm_alloc), /* 55 */ - SYSCALL_SIGN(sys_shm_free), - SYSCALL_SIGN(sys_shm_retain), - SYSCALL_SIGN(sys_notimpl), -#else - SYSCALL_SIGN(sys_notimpl), /* 55 */ - SYSCALL_SIGN(sys_notimpl), - SYSCALL_SIGN(sys_notimpl), - SYSCALL_SIGN(sys_notimpl), -#endif /* RT_LWP_USING_SHM */ -#endif /* ARCH_MM_MMU */ - SYSCALL_SIGN(sys_device_init), - SYSCALL_SIGN(sys_device_register), /* 60 */ - SYSCALL_SIGN(sys_device_control), - SYSCALL_SIGN(sys_device_find), - SYSCALL_SIGN(sys_device_open), - SYSCALL_SIGN(sys_device_close), - SYSCALL_SIGN(sys_device_read), /* 65 */ - SYSCALL_SIGN(sys_device_write), - - SYSCALL_SIGN(sys_stat), - SYSCALL_SIGN(sys_thread_find), - - SYSCALL_NET(SYSCALL_SIGN(sys_accept)), - SYSCALL_NET(SYSCALL_SIGN(sys_bind)), /* 70 */ - SYSCALL_NET(SYSCALL_SIGN(sys_shutdown)), - SYSCALL_NET(SYSCALL_SIGN(sys_getpeername)), - SYSCALL_NET(SYSCALL_SIGN(sys_getsockname)), - SYSCALL_NET(SYSCALL_SIGN(sys_getsockopt)), - SYSCALL_NET(SYSCALL_SIGN(sys_setsockopt)), /* 75 */ - SYSCALL_NET(SYSCALL_SIGN(sys_connect)), - SYSCALL_NET(SYSCALL_SIGN(sys_listen)), - SYSCALL_NET(SYSCALL_SIGN(sys_recv)), - SYSCALL_NET(SYSCALL_SIGN(sys_recvfrom)), - SYSCALL_NET(SYSCALL_SIGN(sys_send)), /* 80 */ - SYSCALL_NET(SYSCALL_SIGN(sys_sendto)), - SYSCALL_NET(SYSCALL_SIGN(sys_socket)), - - SYSCALL_NET(SYSCALL_SIGN(sys_closesocket)), - SYSCALL_NET(SYSCALL_SIGN(sys_getaddrinfo)), - SYSCALL_NET(SYSCALL_SIGN(sys_gethostbyname2_r)), /* 85 */ - - SYSCALL_NET(SYSCALL_SIGN(sys_sendmsg)), - SYSCALL_NET(SYSCALL_SIGN(sys_recvmsg)), - SYSCALL_SIGN(sys_notimpl), /* network */ - SYSCALL_SIGN(sys_notimpl), /* network */ - SYSCALL_SIGN(sys_notimpl), /* network, 90 */ - SYSCALL_SIGN(sys_notimpl), /* network */ - SYSCALL_SIGN(sys_notimpl), /* network */ - SYSCALL_SIGN(sys_notimpl), /* network */ - -#ifdef RT_USING_DFS - SYSCALL_SIGN(sys_select), -#else - SYSCALL_SIGN(sys_notimpl), -#endif - - SYSCALL_SIGN(sys_notimpl), /* SYSCALL_SIGN(sys_hw_interrupt_disable), 95 */ - SYSCALL_SIGN(sys_notimpl), /* SYSCALL_SIGN(sys_hw_interrupt_enable) */ - - SYSCALL_SIGN(sys_tick_get), - SYSCALL_SIGN(sys_exit_group), - - SYSCALL_SIGN(sys_notimpl), /* rt_delayed_work_init */ - SYSCALL_SIGN(sys_notimpl), /* rt_work_submit, 100 */ - SYSCALL_SIGN(sys_notimpl), /* rt_wqueue_wakeup */ - SYSCALL_SIGN(sys_thread_mdelay), - SYSCALL_SIGN(sys_sigaction), - SYSCALL_SIGN(sys_sigprocmask), - SYSCALL_SIGN(sys_tkill), /* 105 */ - SYSCALL_SIGN(sys_thread_sigprocmask), -#ifdef ARCH_MM_MMU - SYSCALL_SIGN(sys_cacheflush), - SYSCALL_SIGN(sys_notimpl), - SYSCALL_SIGN(sys_notimpl), -#else - SYSCALL_SIGN(sys_notimpl), - SYSCALL_SIGN(sys_lwp_sighandler_set), - SYSCALL_SIGN(sys_thread_sighandler_set), -#endif - SYSCALL_SIGN(sys_waitpid), /* 110 */ - - SYSCALL_SIGN(sys_rt_timer_create), - SYSCALL_SIGN(sys_rt_timer_delete), - SYSCALL_SIGN(sys_rt_timer_start), - SYSCALL_SIGN(sys_rt_timer_stop), - SYSCALL_SIGN(sys_rt_timer_control), /* 115 */ - SYSCALL_SIGN(sys_getcwd), - SYSCALL_SIGN(sys_chdir), - SYSCALL_SIGN(sys_unlink), - SYSCALL_SIGN(sys_mkdir), - SYSCALL_SIGN(sys_rmdir), /* 120 */ - SYSCALL_SIGN(sys_getdents), - SYSCALL_SIGN(sys_get_errno), -#ifdef ARCH_MM_MMU - SYSCALL_SIGN(sys_set_thread_area), - SYSCALL_SIGN(sys_set_tid_address), -#else - SYSCALL_SIGN(sys_notimpl), - SYSCALL_SIGN(sys_notimpl), -#endif - SYSCALL_SIGN(sys_access), /* 125 */ - SYSCALL_SIGN(sys_pipe), - SYSCALL_SIGN(sys_clock_settime), - SYSCALL_SIGN(sys_clock_gettime), - SYSCALL_SIGN(sys_clock_getres), - SYSCALL_USPACE(SYSCALL_SIGN(sys_clone)), /* 130 */ - SYSCALL_USPACE(SYSCALL_SIGN(sys_futex)), - SYSCALL_SIGN(sys_notimpl), /* discarded: sys_pmutex */ - SYSCALL_SIGN(sys_dup), - SYSCALL_SIGN(sys_dup2), - SYSCALL_SIGN(sys_rename), /* 135 */ - SYSCALL_USPACE(SYSCALL_SIGN(sys_fork)), - SYSCALL_USPACE(SYSCALL_SIGN(sys_execve)), - SYSCALL_USPACE(SYSCALL_SIGN(sys_vfork)), - SYSCALL_SIGN(sys_gettid), - SYSCALL_SIGN(sys_prlimit64), /* 140 */ - SYSCALL_SIGN(sys_getrlimit), - SYSCALL_SIGN(sys_setrlimit), - SYSCALL_SIGN(sys_setsid), - SYSCALL_SIGN(sys_getrandom), - SYSCALL_SIGN(sys_readlink), /* 145 */ - SYSCALL_USPACE(SYSCALL_SIGN(sys_mremap)), - SYSCALL_USPACE(SYSCALL_SIGN(sys_madvise)), - SYSCALL_SIGN(sys_sched_setparam), - SYSCALL_SIGN(sys_sched_getparam), - SYSCALL_SIGN(sys_sched_get_priority_max), /* 150 */ - SYSCALL_SIGN(sys_sched_get_priority_min), - SYSCALL_SIGN(sys_sched_setscheduler), - SYSCALL_SIGN(sys_sched_getscheduler), - SYSCALL_SIGN(sys_sched_setaffinity), - SYSCALL_SIGN(sys_fsync), /* 155 */ - SYSCALL_SIGN(sys_clock_nanosleep), - SYSCALL_SIGN(sys_timer_create), - SYSCALL_SIGN(sys_timer_delete), - SYSCALL_SIGN(sys_timer_settime), - SYSCALL_SIGN(sys_timer_gettime), /* 160 */ - SYSCALL_SIGN(sys_timer_getoverrun), - SYSCALL_SIGN(sys_mq_open), - SYSCALL_SIGN(sys_mq_unlink), - SYSCALL_SIGN(sys_mq_timedsend), - SYSCALL_SIGN(sys_mq_timedreceive), /* 165 */ - SYSCALL_SIGN(sys_mq_notify), - SYSCALL_SIGN(sys_mq_getsetattr), - SYSCALL_SIGN(sys_mq_close), - SYSCALL_SIGN(sys_lstat), - SYSCALL_SIGN(sys_uname), /* 170 */ - SYSCALL_SIGN(sys_statfs), - SYSCALL_SIGN(sys_statfs64), - SYSCALL_SIGN(sys_fstatfs), - SYSCALL_SIGN(sys_fstatfs64), - SYSCALL_SIGN(sys_openat), /* 175 */ - SYSCALL_SIGN(sys_mount), - SYSCALL_SIGN(sys_umount2), - SYSCALL_SIGN(sys_link), - SYSCALL_SIGN(sys_symlink), - SYSCALL_SIGN(sys_sched_getaffinity), /* 180 */ - SYSCALL_SIGN(sys_sysinfo), - SYSCALL_SIGN(sys_chmod), - SYSCALL_SIGN(sys_reboot), - SYSCALL_SIGN(sys_sched_yield), - SYSCALL_SIGN(sys_pread64), /* 185 */ - SYSCALL_SIGN(sys_pwrite64), - SYSCALL_SIGN(sys_sigpending), - SYSCALL_SIGN(sys_sigtimedwait), - SYSCALL_SIGN(sys_notimpl), - SYSCALL_SIGN(sys_notimpl), /* 190 */ - SYSCALL_SIGN(sys_eventfd2), - SYSCALL_SIGN(sys_epoll_create1), - SYSCALL_SIGN(sys_epoll_ctl), - SYSCALL_SIGN(sys_epoll_pwait), - SYSCALL_SIGN(sys_notimpl), /* 195 */ - SYSCALL_SIGN(sys_timerfd_create), - SYSCALL_SIGN(sys_timerfd_settime), - SYSCALL_SIGN(sys_timerfd_gettime), - SYSCALL_SIGN(sys_signalfd), - SYSCALL_SIGN(sys_memfd_create), /* 200 */ - SYSCALL_SIGN(sys_ftruncate), - SYSCALL_SIGN(sys_setitimer), - SYSCALL_SIGN(sys_utimensat), -#ifdef RT_USING_POSIX_SOCKET - SYSCALL_SIGN(sys_syslog), - SYSCALL_SIGN(sys_socketpair), /* 205 */ -#else - SYSCALL_SIGN(sys_notimpl), - SYSCALL_SIGN(sys_notimpl), /* 205 */ -#endif - SYSCALL_SIGN(sys_wait4), - SYSCALL_SIGN(sys_set_robust_list), - SYSCALL_SIGN(sys_get_robust_list), - SYSCALL_SIGN(sys_setpgid), - SYSCALL_SIGN(sys_getpgid), /* 210 */ - SYSCALL_SIGN(sys_getsid), - SYSCALL_SIGN(sys_getppid), - SYSCALL_SIGN(sys_fchdir), - SYSCALL_SIGN(sys_chown), - SYSCALL_SIGN(sys_fchmod), -}; - -const void *lwp_get_sys_api(rt_uint32_t number) -{ - const void *func = (const void *)sys_notimpl; - - if (number == 0xff) - { - func = (void *)sys_log; - } - else - { - number -= 1; - if (number < sizeof(func_table) / sizeof(func_table[0])) - { - func = func_table[number].func; - } - else - { - if (__sys_log_enable) - { - LOG_I("Unimplement syscall %d", number); - } - } - } - - return func; -} - -const char *lwp_get_syscall_name(rt_uint32_t number) -{ - const char *name = "sys_notimpl"; - - if (number == 0xff) - { - name = "sys_log"; - } - else - { - number -= 1; - if (number < sizeof(func_table) / sizeof(func_table[0])) - { - name = (char *)func_table[number].name; - } - else - { - if (__sys_log_enable) - { - LOG_I("Unimplement syscall %d", number); - } - } - } - - /* skip sys_ */ - return name; -} diff --git a/components/lwp/syscall/SConscript b/components/lwp/syscall/SConscript new file mode 100644 index 000000000000..7970a7cc664c --- /dev/null +++ b/components/lwp/syscall/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') + Glob('*.cxx') + Glob('*.cc') +CPPPATH = [cwd] + +group = DefineGroup('lwProcess', src, depend = ['RT_USING_SMART'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/lwp/syscall/syscall_channel.c b/components/lwp/syscall/syscall_channel.c new file mode 100644 index 000000000000..9bb7ecbcd37f --- /dev/null +++ b/components/lwp/syscall/syscall_channel.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/* sys channel */ + +/** + * @brief Opens a communication channel. + * + * This system call is used to open a communication channel with a specified name and set of flags. + * The channel allows for inter-process or inter-thread communication, depending on the underlying system. + * The `name` parameter specifies the name of the channel, while the `flags` parameter allows + * configuration of the channel's behavior (e.g., read/write permissions, blocking or non-blocking mode). + * + * @param[in] name The name of the communication channel to be opened. + * @param[in] flags The flags to configure the behavior of the channel. These flags may + * define various properties such as access mode (e.g., read-only, write-only) + * or synchronization mode (e.g., blocking, non-blocking). + * + * @return sysret_t Returns a status code: + * - `0`: The channel was successfully opened. + * - Other error codes may indicate issues with the channel opening process. + * + * @warning Ensure that the correct flags are passed to configure the channel as required, + * as improper configuration might lead to access issues, data loss, or undefined behavior. + */ +sysret_t sys_channel_open(const char *name, int flags) +{ + rt_size_t ret = 0; + char *kname = RT_NULL; + int len = 0; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return -EFAULT; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return -EFAULT; + } + + ret = lwp_channel_open(FDT_TYPE_LWP, kname, flags); + + kmem_put(kname); + + return ret; +} + +/** + * @brief Closes an open communication channel. + * + * This system call is used to close an already open communication channel specified by the file descriptor `fd`. + * After closing, the channel can no longer be used for communication, and any resources associated with + * the channel will be released. This function is necessary for proper resource management, ensuring that + * system resources (e.g., memory or file handles) are freed when no longer needed. + * + * @param[in] fd The file descriptor of the communication channel to be closed. + * + * @return sysret_t Returns a status code: + * - `SYSRET_OK`: The channel was successfully closed. + * - Other error codes may indicate issues with the channel closing process. + * + * @note This function should be called after communication is finished and the channel is no longer + * needed, to release any system resources associated with it. + * + * @warning Calling this function on an invalid or already closed file descriptor may lead to + * undefined behavior or errors. Ensure that the file descriptor is valid and that the + * channel is not already closed before attempting to close it. + */ +sysret_t sys_channel_close(int fd) +{ + return lwp_channel_close(FDT_TYPE_LWP, fd); +} + +/** + * @brief Sends a message through a communication channel. + * + * This system call is used to send a message through a specified communication channel identified + * by the file descriptor `fd`. The message to be sent is provided in the `data` parameter. + * It allows inter-process or inter-thread communication by transmitting the given message over + * the open channel. + * + * @param[in] fd The file descriptor of the communication channel to send the message to. + * @param[in] data The message data to be sent. This parameter is typically a structure + * containing the message content and metadata. + * + * @return sysret_t Returns a status code: + * - `0`: The message was successfully sent. + * - Other error codes may indicate issues with the message sending process. + * + * @note Ensure the channel is open and properly configured for sending messages before + * calling this function. Additionally, confirm that the `data` structure is valid and + * initialized with the appropriate content. + * + * @warning Failure to verify the channel's readiness or the validity of the data may lead + * to errors, data loss, or undefined behavior. + */ +sysret_t sys_channel_send(int fd, rt_channel_msg_t data) +{ + rt_size_t ret = 0; + rt_channel_msg_t kdata = RT_NULL; + + if (!lwp_user_accessable((void *)data, sizeof(*data))) + { + return -EFAULT; + } + + kdata = kmem_get(sizeof(*data)); + if (kdata == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kdata, data, sizeof(*kdata)) != sizeof(*kdata)) + { + kmem_put(kdata); + return -EFAULT; + } + + ret = lwp_channel_send(FDT_TYPE_LWP, fd, kdata); + + kmem_put(kdata); + + return ret; +} + +/** + * @brief Sends a message through a communication channel and waits for a response with a timeout. + * + * This system call sends a message (`data`) through a specified communication channel identified by the file descriptor `fd`. + * It then waits for a response (`data_ret`) within the specified timeout period. This is a synchronous operation + * commonly used in request-response communication patterns between processes or threads. + * + * @param[in] fd The file descriptor of the communication channel to send the message to. + * @param[in] data The message data to be sent. This is typically a structure containing + * the message content and metadata. + * @param[out] data_ret The buffer to store the response message received from the channel. + * @param[in] time The timeout period (in milliseconds) to wait for the response. + * If set to a negative value, the function will wait indefinitely. + * + * @return sysret_t Returns a status code: + * - `0`: The message was successfully sent, and a response was received. + * - Other error codes may indicate issues with the communication process. + * + * @note This function combines sending and receiving operations into a single atomic action. + * It is useful for scenarios requiring synchronous communication with a defined timeout + * to handle cases where a response may not be immediately available. + * + * @warning Ensure that the channel is open and properly configured for bidirectional communication. + * Verify that the `data` structure is valid and initialized, and the `data_ret` buffer is large + * enough to store the expected response to avoid memory issues or data corruption. + */ +sysret_t sys_channel_send_recv_timeout(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time) +{ + rt_size_t ret = 0; + rt_channel_msg_t kdata = RT_NULL; + rt_channel_msg_t kdata_ret = RT_NULL; + + if (!lwp_user_accessable((void *)data, sizeof(*data))) + { + return -EFAULT; + } + + kdata = kmem_get(sizeof(*data)); + if (kdata == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kdata, data, sizeof(*kdata)) != sizeof(*kdata)) + { + kmem_put(kdata); + return -EFAULT; + } + + kdata_ret = kmem_get(sizeof(*data_ret)); + if (kdata_ret == RT_NULL) + { + return -ENOMEM; + } + + ret = lwp_channel_send_recv_timeout(FDT_TYPE_LWP, fd, kdata, kdata_ret, time); + + lwp_put_to_user(data_ret, kdata_ret, sizeof(*kdata_ret)); + kmem_put(kdata); + kmem_put(kdata_ret); + + return ret; +} + +/** + * @brief Sends a reply message through a communication channel. + * + * This system call is used to send a reply (`data`) through a communication channel identified + * by the file descriptor `fd`. It is typically called in response to a received request + * within a request-response communication pattern. The reply is sent to the requesting entity + * through the same channel. + * + * @param[in] fd The file descriptor of the communication channel to send the reply to. + * @param[in] data The reply message to be sent. This is typically a structure containing + * the reply content and metadata. + * + * @return sysret_t Returns a status code: + * - `0`: The reply was successfully sent. + * - Other error codes may indicate issues with the reply sending process. + * + * @note This function is usually called in a server or responder context, where a request + * is received, processed, and the result is sent back to the requester. Ensure that + * the channel is open and configured to send replies before calling this function. + * + * @warning Ensure the `data` structure is valid and properly initialized before sending. + * Sending invalid or corrupted data may lead to unexpected behavior or communication failures. + */ +sysret_t sys_channel_reply(int fd, rt_channel_msg_t data) +{ + rt_size_t ret = 0; + rt_channel_msg_t kdata = RT_NULL; + + if (!lwp_user_accessable((void *)data, sizeof(*data))) + { + return -EFAULT; + } + + kdata = kmem_get(sizeof(*data)); + if (kdata == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kdata, data, sizeof(*kdata)) != sizeof(*data)) + { + kmem_put(kdata); + return -EFAULT; + } + + ret = lwp_channel_reply(FDT_TYPE_LWP, fd, kdata); + + kmem_put(kdata); + + return ret; +} + +/** + * @brief Receives a message from a communication channel with a timeout. + * + * This system call attempts to receive a message from a specified communication channel identified + * by the file descriptor `fd`. The received message is stored in the `data` buffer. If no message + * is available within the specified timeout period, the function returns with a timeout status. + * + * @param[in] fd The file descriptor of the communication channel to receive the message from. + * @param[out] data The buffer to store the received message. This parameter is typically a + * structure containing the message content and metadata. + * @param[in] time The timeout period (in milliseconds) to wait for a message. + * A negative value indicates that the function will wait indefinitely. + * + * @return sysret_t Returns a status code: + * - `0`: A message was successfully received. + * - `-ETIMEDOUT`: The operation timed out before a message was received. + * - Other error codes may indicate issues with the message receiving process. + * + * @note This function is useful in scenarios where blocking indefinitely is not desirable, + * allowing the caller to specify a timeout for receiving messages. It is commonly + * used in event-driven or time-sensitive communication systems. + * + * @warning Ensure that the channel is open and configured for receiving messages before calling + * this function. The `data` buffer must be valid and large enough to store the expected + * message to avoid memory corruption or data loss. + */ +sysret_t sys_channel_recv_timeout(int fd, rt_channel_msg_t data, rt_int32_t time) +{ + rt_size_t ret = 0; + rt_channel_msg_t kdata = RT_NULL; + + kdata = kmem_get(sizeof(*data)); + if (kdata == RT_NULL) + { + return -ENOMEM; + } + + ret = lwp_channel_recv_timeout(FDT_TYPE_LWP, fd, kdata, time); + + lwp_put_to_user(data, kdata, sizeof(*kdata)); + kmem_put(kdata); + + return ret; +} + +static struct rt_semaphore critical_lock; + +static int critical_init(void) +{ + rt_sem_init(&critical_lock, "ct_lock", 1, RT_IPC_FLAG_FIFO); + return 0; +} +INIT_DEVICE_EXPORT(critical_init); + +/** + * @brief Enters a critical section to prevent context switching or interrupts. + * + * @note Critical sections are typically used to protect shared resources or perform + * non-interruptible operations. Ensure to exit the critical section as soon as + * possible by calling `sys_exit_critical` to avoid system performance degradation + * or deadlocks. + * + * @warning Failure to exit a critical section (e.g., due to an exception or missing + * `sys_exit_critical` call) may lead to system instability or a complete halt. + */ +void sys_enter_critical(void) +{ + rt_sem_take(&critical_lock, RT_WAITING_FOREVER); +} + +/** + * @brief Exits a critical section and restores the system's previous state. + * + * @note Exiting the critical section as soon as the protected operation is completed + * is essential to avoid performance degradation or system deadlocks. Ensure + * that every call to `sys_enter_critical` is matched with a corresponding + * `sys_exit_critical` call. + * + * @warning Calling this function without a prior `sys_enter_critical` may result + * in undefined behavior or system instability. Use carefully in nested + * critical sections and ensure proper tracking of critical section depth + * if required. + */ +void sys_exit_critical(void) +{ + rt_sem_release(&critical_lock); +} + +int __sys_log_enable = 0; + +/** + * @brief Implements the shell command used to toggle syscall logging. + * + * @param[in] argc The number of command arguments. + * @param[in] argv The command argument vector. + * @return int Zero on success, otherwise a command error. + */ +static int sys_log_enable(int argc, char **argv) +{ + if (argc == 1) + { + rt_kprintf("sys_log = %d\n", __sys_log_enable); + return 0; + } + else + { + __sys_log_enable = atoi(argv[1]); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(sys_log_enable, sys_log, sys_log 1(enable) / 0(disable)); + +/** + * @brief Logs a message to the system logging mechanism. + * + * This system call writes a log message to the system log for diagnostic or informational purposes. + * The message is specified by the `log` parameter, and its size is given by the `size` parameter. + * The logging mechanism is typically used for tracking system events, debugging, or reporting errors. + * + * @param[in] log A pointer to the message to be logged. The message should be a valid character + * array or string. + * @param[in] size The size of the log message in bytes. This specifies the number of bytes to write + * from the `log` buffer. + * + * @return sysret_t Returns a status code: + * - `0`: The log message was successfully written. + * - Other error codes may indicate issues with the logging process. + * + * @note Ensure that the `log` pointer is valid and points to a properly initialized memory buffer. + * Truncation may occur if the logging system has a size limitation. Logging should not be used + * in performance-critical paths as it may introduce latency. + * + * @warning Passing a `NULL` pointer or an invalid `size` may lead to undefined behavior. Ensure the + * logging system is properly initialized before invoking this function. + */ +sysret_t sys_log(const char *log, int size) +{ + char *klog = RT_NULL; + rt_device_t console = RT_NULL; + + if (!lwp_user_accessable((void *)log, size)) + { + return -EFAULT; + } + + klog = kmem_get(size); + if (klog == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user((void *)klog, (void *)log, size) != size) + { + kmem_put(klog); + return -EINVAL; + } + + console = rt_console_get_device(); + + if (console && __sys_log_enable) + { + rt_device_write(console, -1, klog, size); + } + + kmem_put(klog); + + return 0; +} diff --git a/components/lwp/syscall/syscall_core.c b/components/lwp/syscall/syscall_core.c new file mode 100644 index 000000000000..01b5a675db6b --- /dev/null +++ b/components/lwp/syscall/syscall_core.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-06-10 Bernard first version + * 2021-02-03 lizhirui add limit condition for network syscall and add 64-bit arch support + * 2021-02-06 lizhirui fix some bugs + * 2021-02-12 lizhirui add 64-bit support for sys_brk + * 2021-02-20 lizhirui fix some warnings + * 2023-03-13 WangXiaoyao Format & fix syscall return value + * 2023-07-06 Shell adapt the signal API, and clone, fork to new implementation of lwp signal + * 2023-07-27 Shell Move tid_put() from lwp_free() to sys_exit() + * 2023-11-16 xqyjlj fix some syscalls (about sched_*, get/setpriority) + * 2023-11-17 xqyjlj add process group and session support + * 2023-11-30 Shell Fix sys_setitimer() and exit(status) + */ +#include "syscall_internal.h" + +sysret_t sys_notimpl(void) +{ + return -ENOSYS; +} + +uint32_t sys_hw_interrupt_disable(void) +{ + return rt_hw_interrupt_disable(); +} + +void sys_hw_interrupt_enable(uint32_t level) +{ + rt_hw_interrupt_enable(level); +} + +#ifdef ARCH_MM_MMU +void *kmem_get(size_t size) +{ + return rt_malloc(size); +} + +void kmem_put(void *kptr) +{ + rt_free(kptr); +} +#endif /* ARCH_MM_MMU */ + +#ifdef RT_USING_SAL +/* The same socket option is defined differently in the user interfaces and the + * implementation. The options should be converted in the kernel. */ +void convert_sockopt(int *level, int *optname) +{ + if (*level == INTF_SOL_SOCKET) + { + *level = IMPL_SOL_SOCKET; + + switch (*optname) + { + case INTF_SO_REUSEADDR: + *optname = IMPL_SO_REUSEADDR; + break; + case INTF_SO_KEEPALIVE: + *optname = IMPL_SO_KEEPALIVE; + break; + case INTF_SO_BROADCAST: + *optname = IMPL_SO_BROADCAST; + break; + case INTF_SO_ACCEPTCONN: + *optname = IMPL_SO_ACCEPTCONN; + break; + case INTF_SO_DONTROUTE: + *optname = IMPL_SO_DONTROUTE; + break; + case INTF_SO_LINGER: + *optname = IMPL_SO_LINGER; + break; + case INTF_SO_OOBINLINE: + *optname = IMPL_SO_OOBINLINE; + break; + case INTF_SO_REUSEPORT: + *optname = IMPL_SO_REUSEPORT; + break; + case INTF_SO_SNDBUF: + *optname = IMPL_SO_SNDBUF; + break; + case INTF_SO_RCVBUF: + *optname = IMPL_SO_RCVBUF; + break; + case INTF_SO_SNDLOWAT: + *optname = IMPL_SO_SNDLOWAT; + break; + case INTF_SO_RCVLOWAT: + *optname = IMPL_SO_RCVLOWAT; + break; + case INTF_SO_SNDTIMEO: + *optname = IMPL_SO_SNDTIMEO; + break; + case INTF_SO_RCVTIMEO: + *optname = IMPL_SO_RCVTIMEO; + break; + case INTF_SO_ERROR: + *optname = IMPL_SO_ERROR; + break; + case INTF_SO_TYPE: + *optname = IMPL_SO_TYPE; + break; + case INTF_SO_NO_CHECK: + *optname = IMPL_SO_NO_CHECK; + break; + case INTF_SO_BINDTODEVICE: + *optname = IMPL_SO_BINDTODEVICE; + break; + case INTF_SO_TIMESTAMPNS: + *optname = IMPL_SO_TIMESTAMPNS; + break; + case INTF_SO_TIMESTAMPING: + *optname = IMPL_SO_TIMESTAMPING; + break; + case INTF_SO_SELECT_ERR_QUEUE: + *optname = IMPL_SO_SELECT_ERR_QUEUE; + break; + + /* + * SO_DONTLINGER (*level = ((int)(~SO_LINGER))), + * SO_USELOOPBACK (*level = 0x0040) and + * SO_CONTIMEO (*level = 0x1009) are not supported for now. + */ + default: + *optname = 0; + break; + } + return; + } + + if (*level == INTF_IPPROTO_IP) + { + *level = IMPL_IPPROTO_IP; + + switch (*optname) + { + case INTF_IP_TTL: + *optname = IMPL_IP_TTL; + break; + case INTF_IP_TOS: + *optname = IMPL_IP_TOS; + break; + case INTF_IP_MULTICAST_TTL: + *optname = IMPL_IP_MULTICAST_TTL; + break; + case INTF_IP_MULTICAST_IF: + *optname = IMPL_IP_MULTICAST_IF; + break; + case INTF_IP_MULTICAST_LOOP: + *optname = IMPL_IP_MULTICAST_LOOP; + break; + case INTF_IP_ADD_MEMBERSHIP: + *optname = IMPL_IP_ADD_MEMBERSHIP; + break; + case INTF_IP_DROP_MEMBERSHIP: + *optname = IMPL_IP_DROP_MEMBERSHIP; + break; + default: + break; + } + } + + if (*level == INTF_IPPROTO_TCP) + { + *level = IMPL_IPPROTO_TCP; + + switch (*optname) + { + case INTF_TCP_NODELAY: + *optname = IMPL_TCP_NODELAY; + break; + case INTF_TCP_KEEPALIVE: + *optname = IMPL_TCP_KEEPALIVE; + break; + case INTF_TCP_KEEPIDLE: + *optname = IMPL_TCP_KEEPIDLE; + break; + case INTF_TCP_KEEPINTVL: + *optname = IMPL_TCP_KEEPINTVL; + break; + case INTF_TCP_KEEPCNT: + *optname = IMPL_TCP_KEEPCNT; + break; + default: + break; + } + return; + } + + if (*level == INTF_IPPROTO_IPV6) + { + *level = IMPL_IPPROTO_IPV6; + + switch (*optname) + { + case INTF_IPV6_V6ONLY: + *optname = IMPL_IPV6_V6ONLY; + break; + default: + break; + } + return; + } +} +#endif /* RT_USING_SAL */ + +#if defined(RT_USING_LWIP) || defined(SAL_USING_UNET) || defined(RT_USING_SAL) +void sockaddr_tolwip(const struct musl_sockaddr *std, struct sockaddr *lwip) +{ + if (std && lwip) + { + lwip->sa_len = sizeof(*lwip); + lwip->sa_family = (sa_family_t)std->sa_family; + memcpy(lwip->sa_data, std->sa_data, sizeof(lwip->sa_data)); + } +} + +void sockaddr_tomusl(const struct sockaddr *lwip, struct musl_sockaddr *std) +{ + if (std && lwip) + { + std->sa_family = (uint16_t)lwip->sa_family; + memcpy(std->sa_data, lwip->sa_data, sizeof(std->sa_data)); + } +} +#endif + +void _crt_thread_entry(void *parameter) +{ + rt_thread_t tid; + rt_size_t user_stack; + + tid = rt_thread_self(); + + user_stack = (rt_size_t)tid->user_stack + tid->user_stack_size; + user_stack &= ~7; /* align 8 */ + +#ifdef ARCH_MM_MMU + arch_crt_start_umode(parameter, tid->user_entry, (void *)user_stack, (char *)tid->stack_addr + tid->stack_size); +#else + set_user_context((void *)user_stack); + arch_start_umode(parameter, tid->user_entry, ((struct rt_lwp *)tid->lwp)->data_entry, (void *)user_stack); +#endif /* ARCH_MM_MMU */ +} diff --git a/components/lwp/syscall/syscall_device.c b/components/lwp/syscall/syscall_device.c new file mode 100644 index 000000000000..33c7779609d5 --- /dev/null +++ b/components/lwp/syscall/syscall_device.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/* device interfaces */ + +/** + * @brief Initializes a device. + * + * This system call initializes the specified device, preparing it for use. Device initialization + * typically involves setting up necessary hardware configurations, registering device drivers, + * and ensuring that the device is in a ready state for further operations. This function should + * be called before interacting with the device. + * + * @param[in] dev A pointer to the device structure that represents the device to be initialized. + * This structure contains device-specific configuration and state information. + * + * @return sysret_t Returns a status code: + * - `0`: The device was successfully initialized. + * - Other error codes may indicate issues with the device initialization process. + * + * @note This function is typically called once during system startup or when a device is + * first accessed. It ensures that all necessary setup steps are completed before + * the device can be used. + * + * @warning Ensure that the device passed to this function is valid and properly configured + * before initialization. Initializing an invalid or improperly configured device + * may result in unpredictable behavior. + */ +sysret_t sys_device_init(rt_device_t dev) +{ + return rt_device_init(dev); +} + +/** + * @brief Registers a device with the system. + * + * This system call registers a device with the system, making it available for interaction + * by the operating system or other components. Registration typically involves associating + * the device with a name and setting up the necessary flags for the device's behavior. + * + * @param[in] dev A pointer to the device structure that represents the device to be registered. + * This structure contains the device's configuration, capabilities, and state. + * @param[in] name A string representing the name by which the device will be identified in the system. + * This name is used for device lookup and reference. + * @param[in] flags A set of flags that configure the behavior of the device, such as enabling + * or disabling certain features, or specifying the device's mode of operation. + * + * @return sysret_t Returns a status code: + * - `0`: The device was successfully registered. + * - Other error codes may indicate issues with the device registration process. + * + * @note This function should be called after the device has been initialized (via `sys_device_init`) + * and before the device is used by the system or other components. + * + * @warning Ensure that the `name` provided is unique and not already in use by another device in the system. + * Passing invalid `dev` or `flags` may result in unexpected behavior or failure of device registration. + */ +sysret_t sys_device_register(rt_device_t dev, const char *name, rt_uint16_t flags) +{ + return rt_device_register(dev, name, flags); +} + +/** + * @brief Controls a device by sending a command. + * + * This system call sends a control command to the specified device, allowing the system or other + * components to modify the device's behavior or state. The command is specified by the `cmd` + * parameter, and the arguments for the command are passed via the `arg` parameter. + * + * @param[in] dev A pointer to the device structure representing the device to be controlled. + * The device must have been previously registered and initialized. + * @param[in] cmd The control command to be sent to the device. The meaning and behavior of the + * command are device-specific and depend on the device type. + * @param[in] arg A pointer to the arguments required by the command. The type and content of + * the arguments are determined by the command. Some commands may not require + * arguments, in which case `arg` can be `NULL`. + * + * @return sysret_t Returns a status code: + * - `0`: The command was successfully executed on the device. + * - Other error codes may indicate issues with the command execution or device control. + * + * @note The set of available commands (`cmd`) and the expected argument types (`arg`) are specific + * to each device. Refer to the device documentation for the supported commands and argument + * formats. + * + * @warning Ensure that the `dev` pointer is valid and points to a correctly initialized device. + * Providing an invalid device or incorrect command may result in undefined behavior. + */ +sysret_t sys_device_control(rt_device_t dev, int cmd, void *arg) +{ + return rt_device_control(dev, cmd, arg); +} + +/** + * @brief Finds a device by its name. + * + * This system call searches for a device that has been registered with the system using the + * specified name. If the device exists, a pointer to the device structure is returned, + * allowing further interaction with the device. If no device with the specified name is found, + * a `NULL` pointer is returned. + * + * @param[in] name The name of the device to search for. This name must match the name used + * during device registration (e.g., via `sys_device_register`). + * + * @return rt_device_t Returns a pointer to the device structure if the device is found. + * Returns `NULL` if no device with the specified name exists. + * + * @note The device must have been previously registered with the system using `sys_device_register`. + * + * @warning Ensure that the provided `name` is a valid string and corresponds to a registered device. + * Passing an invalid or non-registered name will result in `NULL` being returned. + */ +rt_device_t sys_device_find(const char *name) +{ + return rt_device_find(name); +} + +/** + * @brief Opens a device for use. + * + * This system call opens the specified device, making it ready for interaction with the system + * or other components. The device must have been previously registered and initialized. + * The `oflag` parameter specifies the open mode, which may determine how the device is accessed + * (e.g., read, write, or exclusive access). + * + * @param[in] dev A pointer to the device structure representing the device to be opened. + * The device must be registered and initialized before being opened. + * @param[in] oflag The open flags that determine the mode of access to the device. These flags + * may specify read, write, or other modes of operation, depending on the device's capabilities. + * + * @return sysret_t Returns a status code: + * - `0`: The device was successfully opened. + * - Other error codes may indicate issues with the device opening process. + * + * @note The open flags (`oflag`) should be set according to the device's capabilities. For example, + * some devices may support read or write operations, while others may only support one of them. + * Check the device documentation for supported flags. + * + * @warning Ensure that the device pointer (`dev`) is valid and that the device has been initialized + * properly. Incorrect flags or attempting to open a device that is already in use may result + * in errors or undefined behavior. + */ +sysret_t sys_device_open(rt_device_t dev, rt_uint16_t oflag) +{ + return rt_device_open(dev, oflag); +} + +/** + * @brief Closes an open device. + * + * This system call closes an open device, releasing any resources or locks associated with it + * and making it unavailable for further interaction until it is opened again. The device must + * have been previously opened using `sys_device_open`. After calling this function, any further + * attempts to interact with the device will result in an error unless the device is opened again. + * + * @param[in] dev A pointer to the device structure representing the device to be closed. + * The device must be open before it can be closed. + * + * @return sysret_t Returns a status code: + * - `0`: The device was successfully closed. + * - Other error codes may indicate issues with the device closing process. + * + * @note This function should be called when the device is no longer needed or before shutting + * down the system to release device resources properly. + * + * @warning Ensure that the device has been opened before calling this function. Calling this function + * on an uninitialized or already closed device may result in undefined behavior or errors. + */ +sysret_t sys_device_close(rt_device_t dev) +{ + return rt_device_close(dev); +} + +/** + * @brief Reads data from an open device. + * + * This system call reads data from the specified device, starting at the given position and + * storing the data in the provided buffer. The device must be open before this function can be called. + * The amount of data to read is determined by the `size` parameter. + * + * @param[in] dev A pointer to the device structure representing the device to read from. + * The device must be open and ready for reading. + * @param[in] pos The position within the device from which to begin reading. For devices that + * support seeking, this value is used to specify the starting point. + * @param[out] buffer A pointer to the buffer where the read data will be stored. This buffer should + * be large enough to hold the specified amount of data. + * @param[in] size The number of bytes to read from the device. This value determines how much + * data is read into the buffer. + * + * @return rt_ssize_t Returns the number of bytes actually read from the device: + * - On success: The number of bytes read, which may be less than `size` if + * the end of the device is reached or other factors limit the read. + * - On failure: A negative error code. + * + * @note The device must be open and in a readable state before calling this function. + * The `pos` parameter allows seeking within the device if it supports such operations. + * + * @warning Ensure that the buffer provided is large enough to hold the data being read. + * Providing an insufficient buffer may result in undefined behavior. + */ +rt_ssize_t sys_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + return rt_device_read(dev, pos, buffer, size); +} + +/** + * @brief Writes data to an open device. + * + * This system call writes data to the specified device, starting at the given position and using + * the provided buffer. The device must be open and ready for writing. The amount of data to write + * is determined by the `size` parameter. + * + * @param[in] dev A pointer to the device structure representing the device to write to. + * The device must be open and ready for writing. + * @param[in] pos The position within the device where the writing should start. For devices + * that support seeking, this value specifies the location to begin writing. + * @param[in] buffer A pointer to the buffer containing the data to be written to the device. + * This buffer should contain the data to be written and should be large enough + * to accommodate the specified size. + * @param[in] size The number of bytes to write to the device. This value indicates how much + * data should be written from the buffer to the device. + * + * @return rt_ssize_t Returns the number of bytes actually written to the device: + * - On success: The number of bytes written, which may be less than `size` + * if there is an issue with the device or the data was truncated. + * - On failure: A negative error code. + * + * @note The device must be open and in a writable state before calling this function. + * The `pos` parameter allows seeking within the device if it supports such operations. + * + * @warning Ensure that the buffer provided contains valid data and that it is large enough + * to fit the amount of data specified by `size`. Providing an insufficient buffer + * or invalid data may lead to undefined behavior. + */ +rt_ssize_t sys_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + return rt_device_write(dev, pos, buffer, size); +} diff --git a/components/lwp/syscall/syscall_dispatch.c b/components/lwp/syscall/syscall_dispatch.c new file mode 100644 index 000000000000..60ce5e316ab6 --- /dev/null +++ b/components/lwp/syscall/syscall_dispatch.c @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "syscall_generic.h" + +#define DBG_TAG "lwp.syscall" +#define DBG_LVL DBG_INFO +#include + +#if (defined(RT_USING_SAL) && defined(SAL_USING_POSIX)) +#define SYSCALL_NET(f) f +#else +#define SYSCALL_NET(f) SYSCALL_SIGN(sys_notimpl) +#endif + +#if defined(RT_USING_DFS) && defined(ARCH_MM_MMU) +#define SYSCALL_USPACE(f) f +#else +#define SYSCALL_USPACE(f) SYSCALL_SIGN(sys_notimpl) +#endif + +extern int __sys_log_enable; +extern void sys_accept(); +extern void sys_access(); +extern void sys_api(); +extern void sys_bind(); +extern void sys_brk(); +extern void sys_cacheflush(); +extern void sys_channel_close(); +extern void sys_channel_open(); +extern void sys_channel_recv_timeout(); +extern void sys_channel_reply(); +extern void sys_channel_send(); +extern void sys_channel_send_recv_timeout(); +extern void sys_chdir(); +extern void sys_chmod(); +extern void sys_chown(); +extern void sys_clock_getres(); +extern void sys_clock_gettime(); +extern void sys_clock_nanosleep(); +extern void sys_clock_settime(); +extern void sys_clone(); +extern void sys_close(); +extern void sys_closesocket(); +extern void sys_connect(); +extern void sys_device_close(); +extern void sys_device_control(); +extern void sys_device_find(); +extern void sys_device_init(); +extern void sys_device_open(); +extern void sys_device_read(); +extern void sys_device_register(); +extern void sys_device_write(); +extern void sys_dup(); +extern void sys_dup2(); +extern void sys_enter_critical(); +extern void sys_epoll_create1(); +extern void sys_epoll_ctl(); +extern void sys_epoll_pwait(); +extern void sys_event_create(); +extern void sys_event_delete(); +extern void sys_event_recv(); +extern void sys_event_send(); +extern void sys_eventfd2(); +extern void sys_exec(); +extern void sys_execve(); +extern void sys_exit(); +extern void sys_exit_critical(); +extern void sys_exit_group(); +extern void sys_fchdir(); +extern void sys_fchmod(); +extern void sys_fork(); +extern void sys_fstat(); +extern void sys_fstatfs(); +extern void sys_fstatfs64(); +extern void sys_fsync(); +extern void sys_ftruncate(); +extern void sys_futex(); +extern void sys_get_errno(); +extern void sys_get_robust_list(); +extern void sys_getaddrinfo(); +extern void sys_getcwd(); +extern void sys_getdents(); +extern void sys_gethostbyname2_r(); +extern void sys_getpeername(); +extern void sys_getpgid(); +extern void sys_getpid(); +extern void sys_getppid(); +extern void sys_getpriority(); +extern void sys_getrandom(); +extern void sys_getrlimit(); +extern void sys_getsid(); +extern void sys_getsockname(); +extern void sys_getsockopt(); +extern void sys_gettid(); +extern void sys_gettimeofday(); +extern void sys_hw_interrupt_disable(); +extern void sys_hw_interrupt_enable(); +extern void sys_ioctl(); +extern void sys_kill(); +extern void sys_link(); +extern void sys_listen(); +extern void sys_log(); +extern void sys_log_enable(); +extern void sys_lseek(); +extern void sys_lstat(); +extern void sys_lwp_sighandler_set(); +extern void sys_madvise(); +extern void sys_mb_create(); +extern void sys_mb_delete(); +extern void sys_mb_recv(); +extern void sys_mb_send(); +extern void sys_mb_send_wait(); +extern void sys_memfd_create(); +extern void sys_mkdir(); +extern void sys_mmap2(); +extern void sys_mount(); +extern void sys_mq_close(); +extern void sys_mq_create(); +extern void sys_mq_delete(); +extern void sys_mq_getsetattr(); +extern void sys_mq_notify(); +extern void sys_mq_open(); +extern void sys_mq_recv(); +extern void sys_mq_send(); +extern void sys_mq_timedreceive(); +extern void sys_mq_timedsend(); +extern void sys_mq_unlink(); +extern void sys_mq_urgent(); +extern void sys_mremap(); +extern void sys_munmap(); +extern void sys_mutex_create(); +extern void sys_mutex_delete(); +extern void sys_mutex_release(); +extern void sys_mutex_take(); +extern void sys_nanosleep(); +extern void sys_notimpl(); +extern void sys_open(); +extern void sys_openat(); +extern void sys_pipe(); +extern void sys_pmutex(); +extern void sys_poll(); +extern void sys_pread64(); +extern void sys_prlimit64(); +extern void sys_pwrite64(); +extern void sys_read(); +extern void sys_readlink(); +extern void sys_reboot(); +extern void sys_recv(); +extern void sys_recvfrom(); +extern void sys_recvmsg(); +extern void sys_rename(); +extern void sys_rmdir(); +extern void sys_rt_timer_control(); +extern void sys_rt_timer_create(); +extern void sys_rt_timer_delete(); +extern void sys_rt_timer_start(); +extern void sys_rt_timer_stop(); +extern void sys_sched_get_priority_max(); +extern void sys_sched_get_priority_min(); +extern void sys_sched_getaffinity(); +extern void sys_sched_getparam(); +extern void sys_sched_getscheduler(); +extern void sys_sched_setaffinity(); +extern void sys_sched_setparam(); +extern void sys_sched_setscheduler(); +extern void sys_sched_yield(); +extern void sys_select(); +extern void sys_sem_create(); +extern void sys_sem_delete(); +extern void sys_sem_release(); +extern void sys_sem_take(); +extern void sys_send(); +extern void sys_sendmsg(); +extern void sys_sendto(); +extern void sys_set_robust_list(); +extern void sys_set_thread_area(); +extern void sys_set_tid_address(); +extern void sys_setitimer(); +extern void sys_setpgid(); +extern void sys_setpriority(); +extern void sys_setrlimit(); +extern void sys_setsid(); +extern void sys_setsockopt(); +extern void sys_settimeofday(); +extern void sys_shm_alloc(); +extern void sys_shm_free(); +extern void sys_shm_retain(); +extern void sys_shmat(); +extern void sys_shmdt(); +extern void sys_shmget(); +extern void sys_shmrm(); +extern void sys_shutdown(); +extern void sys_sigaction(); +extern void sys_signalfd(); +extern void sys_sigpending(); +extern void sys_sigprocmask(); +extern void sys_sigtimedwait(); +extern void sys_socket(); +extern void sys_socketpair(); +extern void sys_stat(); +extern void sys_statfs(); +extern void sys_statfs64(); +extern void sys_symlink(); +extern void sys_sysinfo(); +extern void sys_syslog(); +extern void sys_thread_create(); +extern void sys_thread_delete(); +extern void sys_thread_find(); +extern void sys_thread_mdelay(); +extern void sys_thread_self(); +extern void sys_thread_sighandler_set(); +extern void sys_thread_sigprocmask(); +extern void sys_thread_startup(); +extern void sys_tick_get(); +extern void sys_timer_create(); +extern void sys_timer_delete(); +extern void sys_timer_getoverrun(); +extern void sys_timer_gettime(); +extern void sys_timer_settime(); +extern void sys_timerfd_create(); +extern void sys_timerfd_gettime(); +extern void sys_timerfd_settime(); +extern void sys_tkill(); +extern void sys_umount2(); +extern void sys_uname(); +extern void sys_unlink(); +extern void sys_utimensat(); +extern void sys_vfork(); +extern void sys_wait4(); +extern void sys_waitpid(); +extern void sys_write(); +const static struct rt_syscall_def func_table[] = { + SYSCALL_SIGN(sys_exit), /* 01 */ + SYSCALL_SIGN(sys_read), + SYSCALL_SIGN(sys_write), + SYSCALL_SIGN(sys_lseek), + SYSCALL_SIGN(sys_open), /* 05 */ + SYSCALL_SIGN(sys_close), + SYSCALL_SIGN(sys_ioctl), + SYSCALL_SIGN(sys_fstat), + SYSCALL_SIGN(sys_poll), + SYSCALL_SIGN(sys_nanosleep), /* 10 */ + SYSCALL_SIGN(sys_gettimeofday), + SYSCALL_SIGN(sys_settimeofday), + SYSCALL_SIGN(sys_exec), + SYSCALL_SIGN(sys_kill), + SYSCALL_SIGN(sys_getpid), /* 15 */ + SYSCALL_SIGN(sys_getpriority), + SYSCALL_SIGN(sys_setpriority), + SYSCALL_SIGN(sys_sem_create), + SYSCALL_SIGN(sys_sem_delete), + SYSCALL_SIGN(sys_sem_take), /* 20 */ + SYSCALL_SIGN(sys_sem_release), + SYSCALL_SIGN(sys_mutex_create), + SYSCALL_SIGN(sys_mutex_delete), + SYSCALL_SIGN(sys_mutex_take), + SYSCALL_SIGN(sys_mutex_release), /* 25 */ + SYSCALL_SIGN(sys_event_create), + SYSCALL_SIGN(sys_event_delete), + SYSCALL_SIGN(sys_event_send), + SYSCALL_SIGN(sys_event_recv), + SYSCALL_SIGN(sys_mb_create), /* 30 */ + SYSCALL_SIGN(sys_mb_delete), + SYSCALL_SIGN(sys_mb_send), + SYSCALL_SIGN(sys_mb_send_wait), + SYSCALL_SIGN(sys_mb_recv), + SYSCALL_SIGN(sys_mq_create), /* 35 */ + SYSCALL_SIGN(sys_mq_delete), + SYSCALL_SIGN(sys_mq_send), + SYSCALL_SIGN(sys_mq_urgent), + SYSCALL_SIGN(sys_mq_recv), + SYSCALL_SIGN(sys_thread_create), /* 40 */ + SYSCALL_SIGN(sys_thread_delete), + SYSCALL_SIGN(sys_thread_startup), + SYSCALL_SIGN(sys_thread_self), + SYSCALL_SIGN(sys_channel_open), + SYSCALL_SIGN(sys_channel_close), /* 45 */ + SYSCALL_SIGN(sys_channel_send), + SYSCALL_SIGN(sys_channel_send_recv_timeout), + SYSCALL_SIGN(sys_channel_reply), + SYSCALL_SIGN(sys_channel_recv_timeout), + SYSCALL_SIGN(sys_enter_critical), /* 50 */ + SYSCALL_SIGN(sys_exit_critical), + + SYSCALL_USPACE(SYSCALL_SIGN(sys_brk)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_mmap2)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_munmap)), +#ifdef ARCH_MM_MMU + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmget)), /* 55 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmrm)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmat)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmdt)), +#else +#ifdef RT_LWP_USING_SHM + SYSCALL_SIGN(sys_shm_alloc), /* 55 */ + SYSCALL_SIGN(sys_shm_free), + SYSCALL_SIGN(sys_shm_retain), + SYSCALL_SIGN(sys_notimpl), +#else + SYSCALL_SIGN(sys_notimpl), /* 55 */ + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), +#endif /* RT_LWP_USING_SHM */ +#endif /* ARCH_MM_MMU */ + SYSCALL_SIGN(sys_device_init), + SYSCALL_SIGN(sys_device_register), /* 60 */ + SYSCALL_SIGN(sys_device_control), + SYSCALL_SIGN(sys_device_find), + SYSCALL_SIGN(sys_device_open), + SYSCALL_SIGN(sys_device_close), + SYSCALL_SIGN(sys_device_read), /* 65 */ + SYSCALL_SIGN(sys_device_write), + + SYSCALL_SIGN(sys_stat), + SYSCALL_SIGN(sys_thread_find), + + SYSCALL_NET(SYSCALL_SIGN(sys_accept)), + SYSCALL_NET(SYSCALL_SIGN(sys_bind)), /* 70 */ + SYSCALL_NET(SYSCALL_SIGN(sys_shutdown)), + SYSCALL_NET(SYSCALL_SIGN(sys_getpeername)), + SYSCALL_NET(SYSCALL_SIGN(sys_getsockname)), + SYSCALL_NET(SYSCALL_SIGN(sys_getsockopt)), + SYSCALL_NET(SYSCALL_SIGN(sys_setsockopt)), /* 75 */ + SYSCALL_NET(SYSCALL_SIGN(sys_connect)), + SYSCALL_NET(SYSCALL_SIGN(sys_listen)), + SYSCALL_NET(SYSCALL_SIGN(sys_recv)), + SYSCALL_NET(SYSCALL_SIGN(sys_recvfrom)), + SYSCALL_NET(SYSCALL_SIGN(sys_send)), /* 80 */ + SYSCALL_NET(SYSCALL_SIGN(sys_sendto)), + SYSCALL_NET(SYSCALL_SIGN(sys_socket)), + + SYSCALL_NET(SYSCALL_SIGN(sys_closesocket)), + SYSCALL_NET(SYSCALL_SIGN(sys_getaddrinfo)), + SYSCALL_NET(SYSCALL_SIGN(sys_gethostbyname2_r)), /* 85 */ + + SYSCALL_NET(SYSCALL_SIGN(sys_sendmsg)), + SYSCALL_NET(SYSCALL_SIGN(sys_recvmsg)), + SYSCALL_SIGN(sys_notimpl), /* network */ + SYSCALL_SIGN(sys_notimpl), /* network */ + SYSCALL_SIGN(sys_notimpl), /* network, 90 */ + SYSCALL_SIGN(sys_notimpl), /* network */ + SYSCALL_SIGN(sys_notimpl), /* network */ + SYSCALL_SIGN(sys_notimpl), /* network */ + +#ifdef RT_USING_DFS + SYSCALL_SIGN(sys_select), +#else + SYSCALL_SIGN(sys_notimpl), +#endif + + SYSCALL_SIGN(sys_notimpl), /* SYSCALL_SIGN(sys_hw_interrupt_disable), 95 */ + SYSCALL_SIGN(sys_notimpl), /* SYSCALL_SIGN(sys_hw_interrupt_enable) */ + + SYSCALL_SIGN(sys_tick_get), + SYSCALL_SIGN(sys_exit_group), + + SYSCALL_SIGN(sys_notimpl), /* rt_delayed_work_init */ + SYSCALL_SIGN(sys_notimpl), /* rt_work_submit, 100 */ + SYSCALL_SIGN(sys_notimpl), /* rt_wqueue_wakeup */ + SYSCALL_SIGN(sys_thread_mdelay), + SYSCALL_SIGN(sys_sigaction), + SYSCALL_SIGN(sys_sigprocmask), + SYSCALL_SIGN(sys_tkill), /* 105 */ + SYSCALL_SIGN(sys_thread_sigprocmask), +#ifdef ARCH_MM_MMU + SYSCALL_SIGN(sys_cacheflush), + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), +#else + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_lwp_sighandler_set), + SYSCALL_SIGN(sys_thread_sighandler_set), +#endif + SYSCALL_SIGN(sys_waitpid), /* 110 */ + + SYSCALL_SIGN(sys_rt_timer_create), + SYSCALL_SIGN(sys_rt_timer_delete), + SYSCALL_SIGN(sys_rt_timer_start), + SYSCALL_SIGN(sys_rt_timer_stop), + SYSCALL_SIGN(sys_rt_timer_control), /* 115 */ + SYSCALL_SIGN(sys_getcwd), + SYSCALL_SIGN(sys_chdir), + SYSCALL_SIGN(sys_unlink), + SYSCALL_SIGN(sys_mkdir), + SYSCALL_SIGN(sys_rmdir), /* 120 */ + SYSCALL_SIGN(sys_getdents), + SYSCALL_SIGN(sys_get_errno), +#ifdef ARCH_MM_MMU + SYSCALL_SIGN(sys_set_thread_area), + SYSCALL_SIGN(sys_set_tid_address), +#else + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), +#endif + SYSCALL_SIGN(sys_access), /* 125 */ + SYSCALL_SIGN(sys_pipe), + SYSCALL_SIGN(sys_clock_settime), + SYSCALL_SIGN(sys_clock_gettime), + SYSCALL_SIGN(sys_clock_getres), + SYSCALL_USPACE(SYSCALL_SIGN(sys_clone)), /* 130 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_futex)), + SYSCALL_SIGN(sys_notimpl), /* discarded: sys_pmutex */ + SYSCALL_SIGN(sys_dup), + SYSCALL_SIGN(sys_dup2), + SYSCALL_SIGN(sys_rename), /* 135 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_fork)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_execve)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_vfork)), + SYSCALL_SIGN(sys_gettid), + SYSCALL_SIGN(sys_prlimit64), /* 140 */ + SYSCALL_SIGN(sys_getrlimit), + SYSCALL_SIGN(sys_setrlimit), + SYSCALL_SIGN(sys_setsid), + SYSCALL_SIGN(sys_getrandom), + SYSCALL_SIGN(sys_readlink), /* 145 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_mremap)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_madvise)), + SYSCALL_SIGN(sys_sched_setparam), + SYSCALL_SIGN(sys_sched_getparam), + SYSCALL_SIGN(sys_sched_get_priority_max), /* 150 */ + SYSCALL_SIGN(sys_sched_get_priority_min), + SYSCALL_SIGN(sys_sched_setscheduler), + SYSCALL_SIGN(sys_sched_getscheduler), + SYSCALL_SIGN(sys_sched_setaffinity), + SYSCALL_SIGN(sys_fsync), /* 155 */ + SYSCALL_SIGN(sys_clock_nanosleep), + SYSCALL_SIGN(sys_timer_create), + SYSCALL_SIGN(sys_timer_delete), + SYSCALL_SIGN(sys_timer_settime), + SYSCALL_SIGN(sys_timer_gettime), /* 160 */ + SYSCALL_SIGN(sys_timer_getoverrun), + SYSCALL_SIGN(sys_mq_open), + SYSCALL_SIGN(sys_mq_unlink), + SYSCALL_SIGN(sys_mq_timedsend), + SYSCALL_SIGN(sys_mq_timedreceive), /* 165 */ + SYSCALL_SIGN(sys_mq_notify), + SYSCALL_SIGN(sys_mq_getsetattr), + SYSCALL_SIGN(sys_mq_close), + SYSCALL_SIGN(sys_lstat), + SYSCALL_SIGN(sys_uname), /* 170 */ + SYSCALL_SIGN(sys_statfs), + SYSCALL_SIGN(sys_statfs64), + SYSCALL_SIGN(sys_fstatfs), + SYSCALL_SIGN(sys_fstatfs64), + SYSCALL_SIGN(sys_openat), /* 175 */ + SYSCALL_SIGN(sys_mount), + SYSCALL_SIGN(sys_umount2), + SYSCALL_SIGN(sys_link), + SYSCALL_SIGN(sys_symlink), + SYSCALL_SIGN(sys_sched_getaffinity), /* 180 */ + SYSCALL_SIGN(sys_sysinfo), + SYSCALL_SIGN(sys_chmod), + SYSCALL_SIGN(sys_reboot), + SYSCALL_SIGN(sys_sched_yield), + SYSCALL_SIGN(sys_pread64), /* 185 */ + SYSCALL_SIGN(sys_pwrite64), + SYSCALL_SIGN(sys_sigpending), + SYSCALL_SIGN(sys_sigtimedwait), + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), /* 190 */ + SYSCALL_SIGN(sys_eventfd2), + SYSCALL_SIGN(sys_epoll_create1), + SYSCALL_SIGN(sys_epoll_ctl), + SYSCALL_SIGN(sys_epoll_pwait), + SYSCALL_SIGN(sys_notimpl), /* 195 */ + SYSCALL_SIGN(sys_timerfd_create), + SYSCALL_SIGN(sys_timerfd_settime), + SYSCALL_SIGN(sys_timerfd_gettime), + SYSCALL_SIGN(sys_signalfd), + SYSCALL_SIGN(sys_memfd_create), /* 200 */ + SYSCALL_SIGN(sys_ftruncate), + SYSCALL_SIGN(sys_setitimer), + SYSCALL_SIGN(sys_utimensat), +#ifdef RT_USING_POSIX_SOCKET + SYSCALL_SIGN(sys_syslog), + SYSCALL_SIGN(sys_socketpair), /* 205 */ +#else + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), /* 205 */ +#endif + SYSCALL_SIGN(sys_wait4), + SYSCALL_SIGN(sys_set_robust_list), + SYSCALL_SIGN(sys_get_robust_list), + SYSCALL_SIGN(sys_setpgid), + SYSCALL_SIGN(sys_getpgid), /* 210 */ + SYSCALL_SIGN(sys_getsid), + SYSCALL_SIGN(sys_getppid), + SYSCALL_SIGN(sys_fchdir), + SYSCALL_SIGN(sys_chown), + SYSCALL_SIGN(sys_fchmod), +}; + +const void *lwp_get_sys_api(rt_uint32_t number) +{ + const void *func = (const void *)sys_notimpl; + + if (number == 0xff) + { + func = (void *)sys_log; + } + else + { + number -= 1; + if (number < sizeof(func_table) / sizeof(func_table[0])) + { + func = func_table[number].func; + } + else + { + if (__sys_log_enable) + { + LOG_I("Unimplement syscall %d", number); + } + } + } + + return func; +} + +const char *lwp_get_syscall_name(rt_uint32_t number) +{ + const char *name = "sys_notimpl"; + + if (number == 0xff) + { + name = "sys_log"; + } + else + { + number -= 1; + if (number < sizeof(func_table) / sizeof(func_table[0])) + { + name = (char *)func_table[number].name; + } + else + { + if (__sys_log_enable) + { + LOG_I("Unimplement syscall %d", number); + } + } + } + + /* skip sys_ */ + return name; +} diff --git a/components/lwp/syscall/syscall_file.c b/components/lwp/syscall/syscall_file.c new file mode 100644 index 000000000000..1650215e6738 --- /dev/null +++ b/components/lwp/syscall/syscall_file.c @@ -0,0 +1,773 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Reads data from a file descriptor into a buffer. + * + * This system call reads up to `nbyte` bytes of data from the file descriptor + * specified by `fd` into the buffer pointed to by `buf`. + * + * @param fd The file descriptor to read from. This should be a valid file + * descriptor obtained through system calls like `open()` or `socket()`. + * @param buf A pointer to the buffer where the read data will be stored. The buffer + * must have enough space to accommodate up to `nbyte` bytes. + * @param nbyte The maximum number of bytes to read. If the file contains fewer + * bytes than `nbyte`, only the available bytes will be read. + * @return The number of bytes actually read, which may be less than `nbyte` if fewer + * bytes are available or if the end of the file is reached. Returns `0` if + * the end of the file is encountered. On error, returns `errno`. + * @warning Ensure the buffer `buf` has sufficient space to hold the requested number + * of bytes, as failing to do so may result in undefined behavior. + */ +ssize_t sys_read(int fd, void *buf, size_t nbyte) +{ +#ifdef ARCH_MM_MMU + void *kmem = RT_NULL; + ssize_t ret = -1; + + if (nbyte) + { + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + + kmem = kmem_get(nbyte); + if (!kmem) + { + return -ENOMEM; + } + } + + ret = read(fd, kmem, nbyte); + if (ret > 0) + { + if (ret != lwp_put_to_user(buf, kmem, ret)) + { + return -EFAULT; + } + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmem); + + return ret; +#else + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + ssize_t ret = read(fd, buf, nbyte); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Writes data from a buffer to a file descriptor. + * + * This system call writes up to `nbyte` bytes of data from the buffer pointed + * to by `buf` to the file descriptor specified by `fd`. + * + * @param fd The file descriptor to write to. This should be a valid file + * descriptor obtained through system calls like `open()` or `socket()`. + * @param buf A pointer to the buffer containing the data to be written. The + * buffer must remain valid and accessible for the duration of the + * write operation. + * @param nbyte The number of bytes to write from the buffer. If the file descriptor + * refers to a device or a socket, the actual number of bytes written + * may be less than `nbyte`. + * @return The number of bytes actually written. This may be less than `nbyte` if + * the underlying resource cannot accept more data at the moment. On error, + * returns `errno`. + * + * @warning Ensure the buffer `buf` contains at least `nbyte` valid bytes to + * avoid undefined behavior. Additionally, verify that `fd` is writable + * to prevent errors. + * + * @see sys_read(), write() + */ +ssize_t sys_write(int fd, const void *buf, size_t nbyte) +{ +#ifdef ARCH_MM_MMU + void *kmem = RT_NULL; + ssize_t ret = -1; + + if (nbyte) + { + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + + kmem = kmem_get(nbyte); + if (!kmem) + { + return -ENOMEM; + } + + lwp_get_from_user(kmem, (void *)buf, nbyte); + } + + ret = write(fd, kmem, nbyte); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmem); + + return ret; +#else + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + ssize_t ret = write(fd, buf, nbyte); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Repositions the file offset of the open file descriptor. + * + * This system call sets the file offset for the open file descriptor `fd` + * to a new position based on the `offset` and `whence` parameters. It is used + * for seeking within files, enabling random access to file content. + * + * @param fd The file descriptor whose offset is to be modified. The descriptor + * must refer to a file capable of seeking (e.g., regular files, but + * not pipes or sockets). + * @param offset The new offset value relative to the position specified by `whence`. + * Can be a positive or negative value, depending on the seek direction. + * @param whence The reference point for the new offset. Must be one of the following: + * - `SEEK_SET`: Set the offset to `offset` bytes from the beginning of the file. + * - `SEEK_CUR`: Set the offset to its current position plus `offset`. + * - `SEEK_END`: Set the offset to the size of the file plus `offset`. + * @return On success, returns the resulting offset location as measured in bytes + * from the beginning of the file. On failure, returns `errno`. + * + * @warning Ensure the file descriptor `fd` supports seeking. Using this function + * on non-seekable file descriptors (e.g., pipes or sockets) will result in an error. + * + * @see sys_read(), sys_write(), lseek() + */ +size_t sys_lseek(int fd, size_t offset, int whence) +{ + ssize_t ret = lseek(fd, offset, whence); + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Opens or creates a file, returning a file descriptor. + * + * This system call opens the file specified by `name` with the specified + * access mode and flags. If the file does not exist and the `O_CREAT` flag + * is provided, it will create the file with the specified mode. + * + * @param name The path to the file to be opened. This can be an absolute or + * relative path. + * @param flag Flags controlling how the file is opened. Common values include: + * - `O_RDONLY`: Open the file for read-only access. + * - `O_WRONLY`: Open the file for write-only access. + * - `O_RDWR`: Open the file for both reading and writing. + * - `O_CREAT`: Create the file if it does not exist (requires a mode argument). + * - `O_TRUNC`: Truncate the file to zero length if it exists. + * - `O_APPEND`: Open the file in append mode. + * @param ... Optional. If the `O_CREAT` flag is specified, an additional + * `mode_t` argument must be provided, defining the file permissions + * (e.g., `0644` for user-read/write and group/world-read). + * @return On success, returns the file descriptor. + * On failure, returns `errno`. + * + * @note The file descriptor returned can be used with other system calls like + * `sys_read()`, `sys_write()`, and `sys_close()`. + * + * @warning When using the `O_CREAT` flag, ensure to provide the `mode` argument + * to avoid undefined behavior. Additionally, verify that the process + * has the necessary permissions to access or create the file. + * + * @see sys_close(), sys_read(), sys_write(), open() + */ +sysret_t sys_open(const char *name, int flag, ...) +{ +#ifdef ARCH_MM_MMU + int ret = -1; + rt_size_t len = 0; + char *kname = RT_NULL; + mode_t mode = 0; + + if (!lwp_user_accessable((void *)name, 1)) + { + return -EFAULT; + } + + len = lwp_user_strlen(name); + if (!len) + { + return -EINVAL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return -ENOMEM; + } + + if ((flag & O_CREAT) || (flag & O_TMPFILE) == O_TMPFILE) + { + va_list ap; + va_start(ap, flag); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return -EINVAL; + } + + ret = open(kname, flag, mode); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kname); + + return ret; +#else + int ret; + mode_t mode = 0; + + if (!lwp_user_accessable((void *)name, 1)) + { + return -EFAULT; + } + + if ((flag & O_CREAT) || (flag & O_TMPFILE) == O_TMPFILE) + { + va_list ap; + va_start(ap, flag); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + ret = open(name, flag, mode); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Opens or creates a file relative to a directory file descriptor. + * + * This system call opens the file specified by `name`, relative to the directory + * indicated by `dirfd`, with the specified flags and mode. It provides more + * flexibility than `sys_open` for handling files in specific directory contexts. + * + * @param dirfd The file descriptor of the directory relative to which the file is opened. + * Special values include: + * - `AT_FDCWD`: Use the current working directory as the base. + * @param name The path to the file to be opened. If `name` is absolute, `dirfd` is ignored. + * @param flag Flags controlling how the file is opened. Common values include: + * - `O_RDONLY`: Open the file for read-only access. + * - `O_WRONLY`: Open the file for write-only access. + * - `O_RDWR`: Open the file for both reading and writing. + * - `O_CREAT`: Create the file if it does not exist. + * - `O_TRUNC`: Truncate the file to zero length if it exists. + * - `O_APPEND`: Open the file in append mode. + * - `O_EXCL`: Ensure the file is created exclusively (used with `O_CREAT`). + * @param mode The permissions to set if the file is created (e.g., `0644` for user read/write + * and group/world read). This parameter is ignored unless `O_CREAT` is specified. + * @return On success, returns the file descriptor. + * On failure, returns `errno`. + * + * @note The `sys_openat` system call is particularly useful for implementing secure + * directory traversal and operations, as it avoids race conditions when working + * with relative paths. + * + * @warning Ensure that `dirfd` is a valid directory file descriptor if a relative + * `name` is provided. Combining `O_CREAT` and `mode` requires proper handling + * to avoid unintended permission settings. + * + * @see sys_open(), sys_read(), sys_write(), sys_close() + */ +sysret_t sys_openat(int dirfd, const char *name, int flag, mode_t mode) +{ +#ifdef ARCH_MM_MMU + int ret = -1; + rt_size_t len = 0; + char *kname = RT_NULL; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return -EINVAL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return -ENOMEM; + } + + lwp_get_from_user(kname, (void *)name, len + 1); + ret = openat(dirfd, kname, flag, mode); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kname); + + return ret; +#else + if (!lwp_user_accessable((void *)name, 1)) + { + return -EFAULT; + } + int ret = openat(dirfd, name, flag, mode); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Closes a file descriptor. + * + * This system call closes the file descriptor specified by `fd`, releasing any + * resources associated with it. Once closed, the file descriptor can no longer + * be used for operations such as reading or writing, and it may be reassigned + * by subsequent calls to functions like `sys_open()`. + * + * @param fd The file descriptor to be closed. This must be a valid open file descriptor. + * @return On success, returns 0. On failure, returns `errno`. + * + * @note Closing a file descriptor that is already closed results in an error. + * Additionally, if the file descriptor refers to a file or resource shared + * among multiple processes or threads, only the reference count is decremented, + * and the resource itself is not released until all references are closed. + * + * @warning Always ensure `fd` is valid before calling this function. Attempting + * to close an invalid or already closed descriptor may lead to undefined + * behavior or errors. + * + * @see sys_open(), sys_read(), sys_write(), close() + */ +sysret_t sys_close(int fd) +{ + int ret = close(fd); + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Performs control operations on a file descriptor. + * + * This system call allows a program to manipulate the behavior of a device or file + * associated with the file descriptor `fd` by issuing a control command (`cmd`). + * The function provides an interface to interact with device drivers, such as modifying + * the settings of a device or performing custom operations. + * + * @param fd The file descriptor representing the device or file on which the ioctl + * command will be performed. This should be a valid, open file descriptor. + * @param cmd The control command to be executed. The exact behavior depends on the + * device and the command. Commands are usually defined by the device driver + * or the kernel and can vary widely. + * @param data A pointer to a buffer used for passing data to and from the command. + * The contents of the buffer depend on the specific command. For input + * commands, it may contain the data to be written to the device. For output + * commands, it may hold the data returned by the device. + * @return On success, returns 0. On failure, returns `errno`. + * + * @note The actual functionality of `sys_ioctl` depends heavily on the specific `cmd` + * and the type of device associated with the file descriptor `fd`. Each device + * may have a different set of valid ioctl commands. + * + * @warning Ensure that `fd` refers to a valid file descriptor for a device that supports + * ioctl commands. Providing an invalid command or using an unsupported device + * may lead to undefined behavior or errors. + * + * @see sys_open(), sys_read(), sys_write(), ioctl() + */ +sysret_t sys_ioctl(int fd, unsigned long cmd, void *data) +{ +#ifdef ARCH_MM_MMU + if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) + { + struct flock lock; + int ret; + + if (data == RT_NULL || + !lwp_user_accessable(data, sizeof(lock)) || + lwp_get_from_user(&lock, data, sizeof(lock)) != sizeof(lock)) + { + return -EFAULT; + } + + ret = ioctl(fd, cmd, &lock); + if (ret >= 0 && cmd == F_GETLK && + lwp_put_to_user(data, &lock, sizeof(lock)) != sizeof(lock)) + { + return -EFAULT; + } + return ret < 0 ? GET_ERRNO() : ret; + } +#endif + int ret = ioctl(fd, cmd, data); + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Retrieves information about a file associated with a file descriptor. + * + * This system call retrieves the status information about the file referred to + * by the file descriptor `file` and stores it in the structure pointed to by `buf`. + * It is typically used to obtain metadata such as file size, permissions, type, + * and timestamps. + * + * @param file The file descriptor referring to an open file. It must be a valid file + * descriptor returned by `sys_open()` or similar system calls. + * @param buf A pointer to a `struct stat` that will be populated with the file's + * status information, including attributes like size, access times, + * permissions, and more. + * @return On success, returns `0`. On failure, returns `errno` to indicate the error. + * + * @note The structure `struct stat` typically includes the following fields: + * - `st_size`: Size of the file in bytes. + * - `st_mode`: File type and permissions. + * - `st_mtime`: Last modification time. + * - `st_atime`: Last access time. + * - `st_ctime`: Time of last status change. + * + * @warning Ensure that `file` is a valid, open file descriptor. Invalid file + * descriptors or unsupported file types may lead to errors when using + * this function. + * + * @see sys_open(), sys_close(), stat(), fstat() + */ +sysret_t sys_fstat(int file, struct stat *buf) +{ +#ifdef ARCH_MM_MMU + int ret = -1; + struct stat statbuff = { 0 }; + + if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) + { + return -EFAULT; + } + else + { + ret = fstat(file, &statbuff); + + if (ret == 0) + { + lwp_put_to_user(buf, &statbuff, sizeof statbuff); + } + else + { + ret = GET_ERRNO(); + } + + return ret; + } +#else + if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) + { + return -EFAULT; + } + int ret = fstat(file, buf); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Monitors multiple file descriptors to see if they are ready for I/O. + * + * This system call monitors the file descriptors specified in `fds` for any + * I/O events such as readiness for reading, writing, or exceptional conditions. + * It waits up to the specified timeout and returns with information about which + * file descriptors are ready for the requested operations. + * + * @param fds An array of `struct pollfd` structures, each specifying a file descriptor + * to be monitored and the events to be checked for. On return, each structure + * will contain the result of the poll operation for that file descriptor. + * @param nfds The number of elements in the `fds` array. + * @param timeout The maximum time (in milliseconds) to wait for events. If `timeout` + * is `-1`, the call will block indefinitely. If `timeout` is `0`, + * the call will return immediately. + * @return On success, returns the number of file descriptors with events that are ready, + * or `0` if the timeout expired with no events. On error, returns `-1` and sets `errno`. + * + * @note The `pollfd` structure used for each file descriptor contains the following fields: + * - `fd`: The file descriptor to be monitored. + * - `events`: The events to monitor (e.g., `POLLIN`, `POLLOUT`). + * - `revents`: The events that actually occurred. + * + * @warning Ensure that `fds` contains valid file descriptors. Invalid descriptors or + * unsupported types (such as sockets) may result in errors. Also, be mindful + * of the timeout behavior - passing `0` will cause an immediate return, and passing + * `-1` will block indefinitely. + * + * @see sys_select(), poll(), select() + */ +sysret_t sys_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + int ret = -1; +#ifdef ARCH_MM_MMU + struct pollfd *kfds = RT_NULL; + + if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds)) + { + return -EFAULT; + } + + kfds = (struct pollfd *)kmem_get(nfds * sizeof *kfds); + if (!kfds) + { + return -ENOMEM; + } + + lwp_get_from_user(kfds, fds, nfds * sizeof *kfds); + + ret = poll(kfds, nfds, timeout); + if (ret > 0) + { + lwp_put_to_user(fds, kfds, nfds * sizeof *kfds); + } + + kmem_put(kfds); + return ret; +#else + if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds)) + { + return -EFAULT; + } + ret = poll(fds, nfds, timeout); + return ret; +#endif /* ARCH_MM_MMU */ +} + +/** + * @brief Monitors multiple file descriptors for readiness to perform I/O operations. + * + * This system call allows a program to monitor multiple file descriptors to see if + * they are ready for reading, writing, or have exceptional conditions. It waits + * for one or more of the file descriptors to become ready or for the specified + * timeout to expire. + * + * @param nfds The highest-numbered file descriptor in any of the sets, plus one. + * This is the number of file descriptors to monitor. + * @param readfds A pointer to an `fd_set` structure specifying the file descriptors + * to be checked for readability. If a file descriptor is ready to + * read, it will be set in the returned `fd_set`. + * @param writefds A pointer to an `fd_set` structure specifying the file descriptors + * to be checked for writability. If a file descriptor is ready to + * write, it will be set in the returned `fd_set`. + * @param exceptfds A pointer to an `fd_set` structure specifying the file descriptors + * to be checked for exceptional conditions (e.g., out-of-band data). + * If a file descriptor has exceptional conditions, it will be set in + * the returned `fd_set`. + * @param timeout A pointer to a `struct timeval` that specifies the maximum time + * to wait for an event. If `NULL`, the call blocks indefinitely. If + * `timeout` is `0`, the call will return immediately. + * @return On success, returns the number of file descriptors that are ready for + * the requested operations, or `0` if the timeout expired without any events. + * On error, returns `-1` and sets `errno`. + * + * @note The `fd_set` structures should be initialized using the `FD_ZERO` macro + * and populated using the `FD_SET` macro. After the call, the `fd_set` structures + * will contain the file descriptors that are ready for the requested operations. + * + * @warning Ensure that the `fd_set` structures are properly initialized and that + * `nfds` correctly reflects the number of file descriptors to monitor. + * Passing invalid file descriptors or incorrect `nfds` can lead to undefined behavior. + * + * @see sys_poll(), sys_read(), sys_write(), poll(), select() + */ +sysret_t sys_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ +#ifdef ARCH_MM_MMU + int ret = -1; + fd_set *kreadfds = RT_NULL, *kwritefds = RT_NULL, *kexceptfds = RT_NULL; + struct timeval ktimeout, *ptimeout = RT_NULL; + + if (readfds) + { + if (!lwp_user_accessable((void *)readfds, sizeof *readfds)) + { + SET_ERRNO(EFAULT); + goto quit; + } + kreadfds = (fd_set *)kmem_get(sizeof *kreadfds); + if (!kreadfds) + { + SET_ERRNO(ENOMEM); + goto quit; + } + lwp_get_from_user(kreadfds, readfds, sizeof *kreadfds); + } + if (writefds) + { + if (!lwp_user_accessable((void *)writefds, sizeof *writefds)) + { + SET_ERRNO(EFAULT); + goto quit; + } + kwritefds = (fd_set *)kmem_get(sizeof *kwritefds); + if (!kwritefds) + { + SET_ERRNO(ENOMEM); + goto quit; + } + lwp_get_from_user(kwritefds, writefds, sizeof *kwritefds); + } + if (exceptfds) + { + if (!lwp_user_accessable((void *)exceptfds, sizeof *exceptfds)) + { + SET_ERRNO(EFAULT); + goto quit; + } + kexceptfds = (fd_set *)kmem_get(sizeof *kexceptfds); + if (!kexceptfds) + { + SET_ERRNO(EINVAL); + goto quit; + } + lwp_get_from_user(kexceptfds, exceptfds, sizeof *kexceptfds); + } + if (timeout) + { + if (!lwp_user_accessable((void *)timeout, sizeof *timeout)) + { + SET_ERRNO(EFAULT); + goto quit; + } + lwp_get_from_user(&ktimeout, timeout, sizeof ktimeout); + ptimeout = &ktimeout; + } + + ret = select(nfds, kreadfds, kwritefds, kexceptfds, ptimeout); + if (kreadfds) + { + lwp_put_to_user(readfds, kreadfds, sizeof *kreadfds); + } + if (kwritefds) + { + lwp_put_to_user(writefds, kwritefds, sizeof *kwritefds); + } + if (kexceptfds) + { + lwp_put_to_user(exceptfds, kexceptfds, sizeof *kexceptfds); + } +quit: + if (ret < 0) + { + ret = GET_ERRNO(); + } + + if (kreadfds) + { + kmem_put(kreadfds); + } + if (kwritefds) + { + kmem_put(kwritefds); + } + if (kexceptfds) + { + kmem_put(kexceptfds); + } + return ret; +#else + int ret; + + if (!lwp_user_accessable((void *)readfds, sizeof *readfds)) + { + return -EFAULT; + } + if (!lwp_user_accessable((void *)writefds, sizeof *writefds)) + { + return -EFAULT; + } + if (!lwp_user_accessable((void *)exceptfds, sizeof *exceptfds)) + { + return -EFAULT; + } + ret = select(nfds, readfds, writefds, exceptfds, timeout); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Removes a file or symbolic link from the filesystem. + * + * This system call deletes the file or symbolic link specified by `pathname`. + * After the call, the file will be unlinked from the filesystem, meaning it will + * no longer be accessible via its pathname. If the file is still open, it will + * remain available to processes that have it open until all file descriptors are + * closed. If the file is a regular file and has no other hard links, it will be + * removed from the disk once all references to it are closed. + * + * @param pathname The path to the file or symbolic link to be removed. It can be + * an absolute or relative path. + * @return On success, returns `0`. On failure, returns `errno` to indicate + * the error. + * + * @note If the file is currently being used by a process (i.e., the file descriptor + * is still open), it will not be immediately removed from the disk. The actual + * removal occurs once the file is no longer in use. Symbolic links are removed + * as soon as the call is executed. + * + * @warning Ensure that the file or link to be removed exists and that the `pathname` + * is correctly specified. The operation will fail if the file is open and + * in use by other processes or if the pathname does not exist. + * + * @see sys_open(), sys_remove(), unlink() + */ +sysret_t sys_unlink(const char *pathname) +{ +#ifdef ARCH_MM_MMU + int ret = -1; + rt_size_t len = 0; + char *kname = RT_NULL; + + len = lwp_user_strlen(pathname); + if (!len) + { + return -EINVAL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kname, (void *)pathname, len + 1) != (len + 1)) + { + kmem_put(kname); + return -EINVAL; + } + ret = unlink(kname); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kname); + return ret; +#else + int ret = 0; + ret = unlink(pathname); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} diff --git a/components/lwp/syscall/syscall_file_path.c b/components/lwp/syscall/syscall_file_path.c new file mode 100644 index 000000000000..bf086098a339 --- /dev/null +++ b/components/lwp/syscall/syscall_file_path.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Gets the current working directory. + * + * This function retrieves the absolute pathname of the current working directory + * and stores it in the provided buffer. The buffer must be large enough to hold + * the directory path, including the null-terminator. If the buffer is too small, + * the function will return an error. + * + * @param[out] buf A pointer to a buffer where the current working directory + * path will be stored. The buffer should be large enough + * to hold the path, including the null-terminator. + * @param[in] size The size of the buffer in bytes. The buffer must be large enough + * to accommodate the full path. + * + * @return long On success, returns the number of bytes written to the buffer + * (not including the null-terminator). If the buffer is not large + * enough, returns a negative error code. + * + * @note The maximum path length depends on the system's configuration. + * Ensure the buffer size is sufficient to hold the entire path. + * + * @see sys_chdir(), sys_getcwd_r(), sys_realpath() + */ +long sys_getcwd(char *buf, size_t size) +{ + char *tmp = RT_NULL; + long ret = -1; + + if (!lwp_user_accessable((void *)buf, size)) + { + return ret; + } + + tmp = (char *)rt_malloc(size); + if (!tmp) + { + return ret; + } + + if (getcwd(tmp, size) != RT_NULL) + { + if (lwp_put_to_user(buf, tmp, size) > 0) + { + if (buf != RT_NULL) + { + ret = strlen(buf); + } + else + { + ret = -EFAULT; + } + } + } + + rt_free(tmp); + + return ret; +} + +/** + * @brief Changes the current working directory. + * + * This function changes the current working directory of the calling process + * to the directory specified by the given path. The path can be absolute or + * relative. If the specified path does not exist or the process does not have + * sufficient permissions, the function will return an error. + * + * @param[in] path The path to the new working directory. This can be either + * an absolute or a relative path. + * + * @return sysret_t Returns `0` on success, indicating that the working + * directory was successfully changed. On failure, returns a + * negative error code indicating the reason for failure. + * + * @note If the specified path is a relative path, it is interpreted relative + * to the current working directory. + * + * @see sys_getcwd(), sys_chdir() for changing directories, sys_opendir(), sys_stat() + */ +sysret_t sys_chdir(const char *path) +{ +#ifdef ARCH_MM_MMU + int err = 0; + int len = 0; + int errcode; + char *kpath = RT_NULL; + + len = lwp_user_strlen(path); + if (len <= 0) + { + return -EFAULT; + } + + kpath = (char *)kmem_get(len + 1); + if (!kpath) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1)) + { + kmem_put(kpath); + return -EINVAL; + } + + err = chdir(kpath); + errcode = err != 0 ? GET_ERRNO() : 0; + + kmem_put(kpath); + + return errcode; +#else + int ret = chdir(path); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Changes the current working directory to the one associated with the specified file descriptor. + * + * This function changes the current working directory of the calling process + * to the directory associated with the file descriptor `fd`. The file descriptor + * should refer to an open directory. If the file descriptor does not refer to a + * directory, or if the process lacks the necessary permissions, the function will + * return an error. + * + * @param[in] fd The file descriptor referring to the directory to which the current + * working directory should be changed. + * + * @return sysret_t Returns `0` on success, indicating the current working directory + * was successfully changed. On failure, returns a negative error code. + * + * @note The file descriptor must refer to a directory. If it refers to a file or other + * non-directory object, the operation will fail. + * + * @see sys_chdir(), sys_getcwd(), sys_opendir(), sys_open() + */ +sysret_t sys_fchdir(int fd) +{ + int errcode = -ENOSYS; +#ifdef ARCH_MM_MMU +#ifdef RT_USING_DFS_V2 + int err = -1; + struct dfs_file *d; + char *kpath; + + d = fd_get(fd); + if (!d || !d->vnode) + { + return -EBADF; + } + kpath = dfs_dentry_full_path(d->dentry); + if (!kpath) + { + return -EACCES; + } + + err = chdir(kpath); + errcode = err != 0 ? GET_ERRNO() : 0; + + kmem_put(kpath); +#endif +#endif + return errcode; +} + +/** + * @brief Creates a new directory with the specified path and mode. + * + * This function creates a new directory with the specified path and mode. + * The directory is created with the permissions specified by the `mode` parameter. + * If the directory already exists, the function will return an error. + * + * @param[in] path The path of the directory to be created. This can be an absolute + * or relative path. If the directory does not exist, it will be created. + * @param[in] mode The permissions to be set for the new directory. This parameter + * specifies the access permissions for the directory owner, group, and others. + * + * @return sysret_t Returns `0` on success, indicating that the directory was successfully + * created. On failure, returns a negative error code indicating the reason for failure. + * + * @note The `mode` parameter specifies the permissions for the new directory. The permissions + * are typically specified using the `S_IRWXU`, `S_IRWXG`, and `S_IRWXO` macros, which + * define the read, write, and execute permissions for the owner, group, and others. + * + * @see sys_rmdir(), sys_chdir(), sys_mkdirat() + */ +sysret_t sys_mkdir(const char *path, mode_t mode) +{ +#ifdef ARCH_MM_MMU + int err = 0; + int len = 0; + char *kpath = RT_NULL; + + len = lwp_user_strlen(path); + if (len <= 0) + { + return -EFAULT; + } + + kpath = (char *)kmem_get(len + 1); + if (!kpath) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1)) + { + kmem_put(kpath); + return -EINVAL; + } + + err = _SYS_WRAP(mkdir(kpath, mode)); + + kmem_put(kpath); + + return err; +#else + int ret = mkdir(path, mode); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Removes the specified directory. + * + * This function removes the directory specified by the given path. The directory + * must be empty for the operation to succeed. If the directory is not empty, the + * function will return an error. If the directory does not exist or the process + * lacks the necessary permissions, the function will also return an error. + * + * @param[in] path The path of the directory to be removed. This can be an absolute + * or relative path. The directory must be empty for the operation to succeed. + * + * @return sysret_t Returns `0` on success, indicating that the directory was successfully + * removed. On failure, returns a negative error code indicating the reason for failure. + * + * @note The directory must be empty for the operation to succeed. If the directory contains + * files or subdirectories, the operation will fail. To remove a non-empty directory, + * the contents must be deleted first. + * + * @see sys_mkdir(), sys_chdir(), sys_rmdirat() + */ +sysret_t sys_rmdir(const char *path) +{ + int err = 0; + int ret = 0; +#ifdef ARCH_MM_MMU + int len = 0; + char *kpath = RT_NULL; + + len = lwp_user_strlen(path); + if (len <= 0) + { + return -EFAULT; + } + + kpath = (char *)kmem_get(len + 1); + if (!kpath) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kpath, (void *)path, len + 1) != (len + 1)) + { + kmem_put(kpath); + return -EINVAL; + } + + ret = rmdir(kpath); + if (ret < 0) + { + err = GET_ERRNO(); + } + + kmem_put(kpath); + + return (err < 0 ? err : ret); +#else + ret = rmdir(path); + if (ret < 0) + { + err = GET_ERRNO(); + } + return (err < 0 ? err : ret); +#endif +} + +/** + * @brief Reads directory entries. + * + * This function reads the directory entries from the directory stream specified by the file descriptor `fd`. + * It stores the directory entries in the buffer pointed to by `dirp`, up to the specified number of bytes (`nbytes`). + * The entries are returned in a format compatible with the `struct libc_dirent` structure. + * + * @param[in] fd The file descriptor referring to an open directory. This descriptor must be obtained + * from a call to `sys_open()` with the appropriate flags for directory reading. + * @param[out] dirp A pointer to a buffer where the directory entries will be stored. + * The buffer must be large enough to hold at least `nbytes` bytes of directory entries. + * @param[in] nbytes The size of the buffer (`dirp`) in bytes. It limits the number of directory entries + * that can be read in a single call. The function will return as many entries as possible + * that fit within the buffer size. + * + * @return sysret_t Returns the number of bytes read on success, which may be less than `nbytes` if there + * are fewer entries in the directory or if the buffer is too small. + * On failure, returns a negative error code. + * + * @note If the function returns 0, it indicates the end of the directory stream. A negative return value + * indicates an error. The caller can use the returned number of bytes to process the entries in `dirp`. + * + * @see sys_open(), sys_close(), sys_readdir(), sys_stat() + */ +sysret_t sys_getdents(int fd, struct libc_dirent *dirp, size_t nbytes) +{ + int ret = -1; + struct dfs_file *file; + size_t cnt = (nbytes / sizeof(struct libc_dirent)); + size_t rtt_nbytes = 0; + struct dirent *rtt_dirp; + +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)dirp, sizeof(struct libc_dirent))) + { + return -EFAULT; + } +#endif + + if (cnt == 0) + { + return -EINVAL; + } + rtt_nbytes = cnt * sizeof(struct dirent); + rtt_dirp = (struct dirent *)rt_malloc(rtt_nbytes); + if (!rtt_dirp) + { + return -ENOMEM; + } + file = fd_get(fd); + ret = dfs_file_getdents(file, rtt_dirp, rtt_nbytes); + if (ret > 0) + { + size_t i = 0; + cnt = ret / sizeof(struct dirent); + for (i = 0; i < cnt; i++) + { + dirp[i].d_ino = 0; + dirp[i].d_off = i * sizeof(struct libc_dirent); + dirp[i].d_type = rtt_dirp[i].d_type; + dirp[i].d_reclen = sizeof(struct libc_dirent); + strcpy(dirp[i].d_name, rtt_dirp[i].d_name); + } + ret = cnt * sizeof(struct libc_dirent); + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + rt_free(rtt_dirp); + + return ret; +} + +/** + * @brief Retrieves the current value of the error code. + * + * This function returns the most recent error code set by a system call. The error code is typically set + * when a system call fails. This function allows users to retrieve the last error that occurred, helping to + * diagnose issues or handle errors in a more controlled manner. + * + * @return sysret_t The current error code. A value of `0` indicates no error, while a non-zero value + * represents the most recent error. The error codes are system-specific and can + * represent various failure conditions (e.g., `EINVAL`, `ENOMEM`, `EIO`). + * + * @note The error code returned is specific to the current thread or process and is typically updated + * each time a system call fails. The error code is persistent until it is overwritten by the next + * failed system call or explicitly reset. + * + * @see sys_set_errno(), sys_perror(), sys_strerror() + */ +sysret_t sys_access(const char *filename, int mode) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + rt_size_t len = 0; + char *kfilename = RT_NULL; + + len = lwp_user_strlen(filename); + if (len <= 0) + { + return -EINVAL; + } + + kfilename = (char *)kmem_get(len + 1); + if (!kfilename) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kfilename, (void *)filename, len + 1) != (len + 1)) + { + kmem_put(kfilename); + return -EFAULT; + } + + ret = access(kfilename, mode); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kfilename); + return ret; +#else + ret = access(filename, mode); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Creates a pipe, a unidirectional data channel. + * + * This function creates a pipe, which is a unidirectional data channel used for inter-process communication. + * The pipe consists of two file descriptors: one for reading from the pipe and one for writing to the pipe. + * The pipe is used for passing data between processes or threads, typically in a producer-consumer scenario. + * + * @param[out] fd An array of two integers where the file descriptors for the read and write ends of the pipe will be stored. + * - `fd[0]`: The file descriptor for reading from the pipe. + * - `fd[1]`: The file descriptor for writing to the pipe. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The pipe created by this function is typically used for simple communication between processes or threads. + * The data written to `fd[1]` can be read from `fd[0]`. After usage, both file descriptors should be closed. + * + * @see sys_read(), sys_write(), sys_close(), sys_fork(), sys_execve() + */ +sysret_t sys_pipe(int fd[2]) +{ + int ret; + int kfd[2] = { 0, 0 }; + + if (!lwp_user_accessable((void *)fd, sizeof(int[2]))) + { + return -EFAULT; + } + + ret = pipe(kfd); + + lwp_put_to_user((void *)fd, kfd, sizeof(int[2])); + + return (ret < 0 ? GET_ERRNO() : ret); +} diff --git a/components/lwp/syscall/syscall_file_stat.c b/components/lwp/syscall/syscall_file_stat.c new file mode 100644 index 000000000000..430517736a51 --- /dev/null +++ b/components/lwp/syscall/syscall_file_stat.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Retrieves information about a file or directory. + * + * This system call obtains metadata about the specified file or directory and stores it in + * the `buf` structure. The metadata includes attributes such as file size, permissions, + * ownership, and timestamps. + * + * @param[in] file A pointer to the path of the file or directory to be queried. + * The path should be a null-terminated string. + * @param[out] buf A pointer to a `struct stat` structure where the file's metadata + * will be stored. This structure must be allocated by the caller. + * + * @return sysret_t Returns a status code: + * - `0`: The operation completed successfully, and the metadata + * has been written to `buf`. + * - Other error codes may indicate issues with the file path. + * + * @note The `file` path must be valid and accessible by the calling process. Ensure that + * the `buf` pointer points to a properly allocated memory region. + * + * @warning Passing a `NULL` pointer for `file` or `buf` may result in undefined behavior. + * Check all inputs before invoking this function to avoid potential issues. + */ +sysret_t sys_stat(const char *file, struct stat *buf) +{ + int ret = 0; + size_t len; + size_t copy_len; + char *copy_path; + struct stat statbuff = { 0 }; + + if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) + { + return -EFAULT; + } + + len = lwp_user_strlen(file); + if (len <= 0) + { + return -EFAULT; + } + + copy_path = (char *)rt_malloc(len + 1); + if (!copy_path) + { + return -ENOMEM; + } + + copy_len = lwp_get_from_user(copy_path, (void *)file, len); + if (copy_len == 0) + { + rt_free(copy_path); + return -EFAULT; + } + copy_path[copy_len] = '\0'; + + ret = _SYS_WRAP(stat(copy_path, &statbuff)); + rt_free(copy_path); + + if (ret == 0) + { + lwp_put_to_user(buf, &statbuff, sizeof statbuff); + } + + return ret; +} + +/** + * @brief Retrieves metadata about a file or symbolic link. + * + * This system call obtains metadata for the specified file or symbolic link and stores + * it in the `buf` structure. Unlike `sys_stat`, if the specified path refers to a + * symbolic link, this function retrieves information about the link itself, not the + * target it points to. + * + * @param[in] file A pointer to the path of the file or symbolic link to be queried. + * The path must be a null-terminated string. + * @param[out] buf A pointer to a `struct stat` structure where the metadata will be + * stored. This structure must be allocated by the caller. + * + * @return sysret_t Returns a status code: + * - `0`: The operation completed successfully, and the metadata + * has been written to `buf`. + * - Other error codes may indicate issues with the file path. + * + * @note This function is particularly useful for handling symbolic links when you want + * to get information about the link itself rather than the target file or directory. + * The `file` path must be valid and accessible by the calling process. + * + * @warning Passing a `NULL` pointer for `file` or `buf` may result in undefined behavior. + * Always check inputs for validity before invoking this function. + */ +sysret_t sys_lstat(const char *file, struct stat *buf) +{ + int ret = 0; + size_t len; + size_t copy_len; + char *copy_path; + struct stat statbuff = { 0 }; + + if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) + { + return -EFAULT; + } + + len = lwp_user_strlen(file); + if (len <= 0) + { + return -EFAULT; + } + + copy_path = (char *)rt_malloc(len + 1); + if (!copy_path) + { + return -ENOMEM; + } + + copy_len = lwp_get_from_user(copy_path, (void *)file, len); + if (copy_len == 0) + { + rt_free(copy_path); + return -EFAULT; + } + copy_path[copy_len] = '\0'; +#ifdef RT_USING_DFS_V2 + ret = _SYS_WRAP(dfs_file_lstat(copy_path, &statbuff)); +#else + ret = _SYS_WRAP(stat(copy_path, &statbuff)); +#endif + rt_free(copy_path); + + if (ret == 0) + { + lwp_put_to_user(buf, &statbuff, sizeof statbuff); + } + + return ret; +} diff --git a/components/lwp/syscall/syscall_fs.c b/components/lwp/syscall/syscall_fs.c new file mode 100644 index 000000000000..08ea40250f05 --- /dev/null +++ b/components/lwp/syscall/syscall_fs.c @@ -0,0 +1,950 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Get filesystem statistics. + * + * This function retrieves statistics about the filesystem at the specified path, + * storing the results in the provided `statfs` structure. It can be used to obtain + * information such as the total number of blocks, free blocks, available inodes, etc. + * + * @param[in] path The path to the filesystem to query. If the path is the root directory + * (`"/"`), it returns statistics for the root filesystem. + * @param[out] buf A pointer to a `statfs` structure where the filesystem statistics will + * be stored. This structure includes information such as: + * - `f_type`: The type of the filesystem. + * - `f_bsize`: The optimal block size for I/O operations. + * - `f_blocks`: Total number of blocks in the filesystem. + * - `f_bfree`: Number of free blocks. + * - `f_bavail`: Number of free blocks available to non-superuser. + * - `f_files`: Total number of file nodes (inodes). + * - `f_ffree`: Number of free inodes. + * - `f_favail`: Number of inodes available to non-superuser. + * - `f_flag`: Flags describing the filesystem. + * - `f_namelen`: Maximum length of a filename. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function is useful for determining the available space and file system + * characteristics of a mounted filesystem. + * + * @see sys_fstatfs + */ +sysret_t sys_statfs(const char *path, struct statfs *buf) +{ + int ret = 0; + size_t len; + size_t copy_len; + char *copy_path; + struct statfs statfsbuff = { 0 }; + + if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) + { + return -EFAULT; + } + + len = lwp_user_strlen(path); + if (len <= 0) + { + return -EFAULT; + } + + copy_path = (char *)rt_malloc(len + 1); + if (!copy_path) + { + return -ENOMEM; + } + + copy_len = lwp_get_from_user(copy_path, (void *)path, len); + if (copy_len == 0) + { + rt_free(copy_path); + return -EFAULT; + } + copy_path[copy_len] = '\0'; + + ret = _SYS_WRAP(statfs(copy_path, &statfsbuff)); + rt_free(copy_path); + + if (ret == 0) + { + lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); + } + + return ret; +} + +/** + * @brief Get extended filesystem statistics (64-bit). + * + * This function retrieves extended statistics about the filesystem at the specified path, + * using 64-bit values for larger filesystems or filesystems with a large number of blocks or inodes. + * The information is stored in the provided `statfs` structure, which includes details such as total + * blocks, free blocks, available inodes, etc. + * + * @param[in] path The path to the filesystem to query. Typically, this would be the root directory + * (`"/"`) for the root filesystem, or any other directory on the filesystem. + * @param[in] sz The size of the `statfs` structure. This parameter allows for future extensions + * of the `statfs` structure without breaking compatibility with older applications. + * @param[out] buf A pointer to a `statfs` structure where the extended filesystem statistics + * will be stored. This structure includes information such as: + * - `f_bsize`: The optimal block size for I/O operations. + * - `f_blocks`: Total number of blocks in the filesystem. + * - `f_bfree`: Number of free blocks. + * - `f_bavail`: Number of free blocks available to non-superuser. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function is particularly useful for querying large filesystems or filesystems with + * 64-bit addresses or data values, and is an extended version of the standard `sys_statfs`. + * + * @see sys_statfs + */ +sysret_t sys_statfs64(const char *path, size_t sz, struct statfs *buf) +{ + int ret = 0; + size_t len; + size_t copy_len; + char *copy_path; + struct statfs statfsbuff = { 0 }; + + if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) + { + return -EFAULT; + } + + if (sz != sizeof(struct statfs)) + { + return -EINVAL; + } + + len = lwp_user_strlen(path); + if (len <= 0) + { + return -EFAULT; + } + + copy_path = (char *)rt_malloc(len + 1); + if (!copy_path) + { + return -ENOMEM; + } + + copy_len = lwp_get_from_user(copy_path, (void *)path, len); + if (copy_len == 0) + { + rt_free(copy_path); + return -EFAULT; + } + copy_path[copy_len] = '\0'; + + ret = _SYS_WRAP(statfs(copy_path, &statfsbuff)); + rt_free(copy_path); + + if (ret == 0) + { + lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); + } + + return ret; +} + +/** + * @brief Get filesystem statistics for a file descriptor. + * + * This function retrieves statistics about the filesystem containing the file referred to by the + * file descriptor `fd`. The information is stored in the provided `statfs` structure, which includes + * details such as total blocks, free blocks, available inodes, etc. + * + * @param[in] fd The file descriptor referring to an open file. The file descriptor must be + * valid and represent a file on a mounted filesystem. + * @param[out] buf A pointer to a `statfs` structure where the filesystem statistics will be + * stored. This structure includes information such as: + * - `f_bsize`: The optimal block size for I/O operations. + * - `f_blocks`: Total number of blocks in the filesystem. + * - `f_bfree`: Number of free blocks. + * - `f_bavail`: Number of free blocks available to non-superuser. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function is useful for obtaining filesystem information about a specific file + * represented by its file descriptor, rather than querying the filesystem associated with + * a specific path. + * + * @see sys_statfs + */ +sysret_t sys_fstatfs(int fd, struct statfs *buf) +{ + int ret = 0; + struct statfs statfsbuff = { 0 }; + + if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) + { + return -EFAULT; + } + + ret = _SYS_WRAP(fstatfs(fd, &statfsbuff)); + + if (ret == 0) + { + lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); + } + + return ret; +} + +/** + * @brief Get 64-bit filesystem statistics for a file descriptor. + * + * This function retrieves 64-bit statistics about the filesystem containing the file referred to by + * the file descriptor `fd`. The statistics are stored in the provided `statfs` structure, which + * includes details such as total blocks, free blocks, available inodes, etc. The function differs + * from `sys_fstatfs` in that it supports 64-bit values for filesystem sizes and counts. + * + * @param[in] fd The file descriptor referring to an open file. The file descriptor must be + * valid and represent a file on a mounted filesystem. + * @param[in] sz The size of the `statfs` structure (typically used to ensure compatibility with + * different versions of the structure). + * @param[out] buf A pointer to a `statfs` structure where the filesystem statistics will be + * stored. This structure includes information such as: + * - `f_bsize`: The optimal block size for I/O operations. + * - `f_blocks`: Total number of blocks in the filesystem (64-bit). + * - `f_bfree`: Number of free blocks (64-bit). + * - `f_bavail`: Number of free blocks available to non-superuser (64-bit). + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * @note This function is particularly useful for systems supporting 64-bit filesystem statistics. + * It provides extended accuracy for large filesystems, including those with very large + * numbers of blocks or inodes. + * + * @see sys_fstatfs + */ +sysret_t sys_fstatfs64(int fd, size_t sz, struct statfs *buf) +{ + int ret = 0; + struct statfs statfsbuff = { 0 }; + + if (!lwp_user_accessable((void *)buf, sizeof(struct statfs))) + { + return -EFAULT; + } + + if (sz != sizeof(struct statfs)) + { + return -EINVAL; + } + + ret = _SYS_WRAP(fstatfs(fd, &statfsbuff)); + + if (ret == 0) + { + lwp_put_to_user(buf, &statfsbuff, sizeof statfsbuff); + } + + return ret; +} + +static char *_cp_from_usr_string(char *dst, char *src, size_t length) +{ + char *rc; + size_t copied_bytes; + if (length) + { + copied_bytes = lwp_get_from_user(dst, src, length); + dst[copied_bytes] = '\0'; + rc = dst; + } + else + { + rc = RT_NULL; + } + return rc; +} + +/** + * @brief Mount a filesystem. + * + * This function mounts a filesystem onto a specified directory. It allows for mounting various + * types of filesystems, including but not limited to `ext4`, `nfs`, and `tmpfs`. The function + * allows the specification of the source device, the target mount point, and additional parameters + * such as filesystem type, mount flags, and extra data. + * + * @param[in] source The source of the filesystem, which can be a device (e.g., `/dev/sda1`) + * or a network resource (e.g., a NFS share). For certain filesystems like + * `tmpfs`, this can be `NULL`. + * @param[in] target The target directory where the filesystem will be mounted. + * This should be an existing empty directory. + * @param[in] filesystemtype The type of filesystem to mount, e.g., `ext4`, `tmpfs`, `nfs`, etc. + * @param[in] mountflags Flags that control the mount operation, such as `MS_RDONLY` for read-only + * mounts or `MS_NODEV` to prevent device files from being created. + * @param[in] data Optional data that is passed to the filesystem's mount handler. This + * may be used for setting up specific parameters for the filesystem type. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note Mounting a filesystem on an existing mount point can replace the existing filesystem at that + * location. Make sure the target directory is empty before mounting to avoid any conflicts. + */ +sysret_t sys_mount(char *source, char *target, char *filesystemtype, + unsigned long mountflags, void *data) +{ + char *kbuffer, *ksource, *ktarget, *kfs; + size_t len_source, len_target, len_fs; + char *tmp = NULL; + int ret = 0; + struct stat buf = { 0 }; + char *dev_fullpath = RT_NULL; + + len_source = source ? lwp_user_strlen(source) : 0; + if (len_source < 0) + { + return -EINVAL; + } + + len_target = target ? lwp_user_strlen(target) : 0; + if (len_target <= 0) + { + return -EINVAL; + } + + len_fs = filesystemtype ? lwp_user_strlen(filesystemtype) : 0; + if (len_fs < 0) + { + return -EINVAL; + } + + kbuffer = (char *)rt_malloc(len_source + 1 + len_target + 1 + len_fs + 1); + if (!kbuffer) + { + return -ENOMEM; + } + + /* get parameters from user space */ + ksource = kbuffer; + ktarget = ksource + len_source + 1; + kfs = ktarget + len_target + 1; + ksource = _cp_from_usr_string(ksource, source, len_source); + ktarget = _cp_from_usr_string(ktarget, target, len_target); + kfs = _cp_from_usr_string(kfs, filesystemtype, len_fs); + + if (mountflags & MS_REMOUNT) + { + ret = dfs_remount(ktarget, mountflags, data); + } + else + { + if (strcmp(kfs, "nfs") == 0) + { + tmp = ksource; + ksource = NULL; + } + if (strcmp(kfs, "tmp") == 0) + { + ksource = NULL; + } + + if (ksource && !dfs_file_stat(ksource, &buf) && S_ISBLK(buf.st_mode)) + { + dev_fullpath = dfs_normalize_path(RT_NULL, ksource); + RT_ASSERT(rt_strncmp(dev_fullpath, "/dev/", sizeof("/dev/") - 1) == 0); + ret = dfs_mount(dev_fullpath + sizeof("/dev/") - 1, ktarget, kfs, 0, tmp); + } + else + { + ret = dfs_mount(ksource, ktarget, kfs, 0, tmp); + } + + if (ret < 0) + { + ret = -rt_get_errno(); + } + } + + rt_free(kbuffer); + rt_free(dev_fullpath); + return ret; +} + +/** + * @brief Unmount a filesystem. + * + * This function unmounts a previously mounted filesystem from a specified target directory or file. + * It removes the filesystem from the system, making the resources associated with it available for + * other uses. It can also support additional flags to control the unmounting behavior. + * + * @param[in] __special_file The target directory or mount point from which the filesystem is to be + * unmounted. This should be a valid mount point that was previously mounted. + * @param[in] __flags Flags that control the unmounting behavior. Common flags include: + * - `MNT_FORCE`: Forces the unmount even if the filesystem is busy. + * - `MNT_DETACH`: Detaches the filesystem, allowing it to be unmounted + * asynchronously. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The `MNT_FORCE` flag should be used with caution, as it may result in data loss or corruption + * if there are pending writes or active processes using the filesystem. + * + * @see sys_mount + */ +sysret_t sys_umount2(char *__special_file, int __flags) +{ + char *copy_special_file; + size_t len_special_file, copy_len_special_file; + int ret = 0; + + len_special_file = lwp_user_strlen(__special_file); + if (len_special_file <= 0) + { + return -EFAULT; + } + + copy_special_file = (char *)rt_malloc(len_special_file + 1); + if (!copy_special_file) + { + return -ENOMEM; + } + + copy_len_special_file = lwp_get_from_user(copy_special_file, __special_file, len_special_file); + copy_special_file[copy_len_special_file] = '\0'; + + ret = dfs_unmount(copy_special_file); + rt_free(copy_special_file); + + return ret; +} + +/** + * @brief Create a new hard link to an existing file. + * + * This function creates a new hard link to an existing file, making the file accessible from + * multiple filenames. The new link points to the same inode as the existing file, meaning + * both names refer to the same underlying data. The link count for the file is incremented. + * + * @param[in] existing The path to the existing file. It must be an absolute or relative path + * to a file that already exists. + * @param[in] new The path to the new link to be created. This can be a new filename or + * an existing directory where the link will be placed. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note Hard links share the same inode number, meaning changes to the data of the file via one + * link will be reflected in all hard links. Deleting any of the links will not remove the + * file data until all links are deleted. + */ +sysret_t sys_link(const char *existing, const char *new) +{ + int ret = -1; + int err = 0; +#ifdef RT_USING_DFS_V2 +#ifdef ARCH_MM_MMU + int len = 0; + char *kexisting = RT_NULL; + char *knew = RT_NULL; + + len = lwp_user_strlen(existing); + if (len <= 0) + { + return -EFAULT; + } + + kexisting = (char *)kmem_get(len + 1); + if (!kexisting) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kexisting, (void *)existing, len + 1) != (len + 1)) + { + kmem_put(kexisting); + return -EINVAL; + } + + len = lwp_user_strlen(new); + if (len <= 0) + { + kmem_put(kexisting); + return -EFAULT; + } + + knew = (char *)kmem_get(len + 1); + if (!knew) + { + kmem_put(kexisting); + return -ENOMEM; + } + + if (lwp_get_from_user(knew, (void *)new, len + 1) != (len + 1)) + { + kmem_put(knew); + kmem_put(kexisting); + return -EINVAL; + } + + ret = dfs_file_link(kexisting, knew); + if (ret < 0) + { + err = GET_ERRNO(); + } + + kmem_put(knew); + kmem_put(kexisting); +#else + ret = dfs_file_link(existing, new); +#endif +#else + SET_ERRNO(EFAULT); + err = GET_ERRNO(); +#endif + + return (err < 0 ? err : ret); +} + +/** + * @brief Create a symbolic link to an existing file or directory. + * + * This function creates a new symbolic link, which is a special file that contains a reference + * to another file or directory. The symbolic link allows for easy redirection or aliasing of + * file paths. Unlike a hard link, a symbolic link can point to a file or directory across + * different file systems. + * + * @param[in] existing The path to the existing file or directory that the symbolic link + * will refer to. It must be an absolute or relative path. + * @param[in] new The path to the new symbolic link to be created. This must be a valid + * path where the link will be placed. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note A symbolic link is distinct from a hard link in that it points to the path of the target + * file or directory, rather than directly to the inode. If the target file or directory is + * removed or moved, the symbolic link will become "broken" and will not resolve to a valid + * location. + */ +sysret_t sys_symlink(const char *existing, const char *new) +{ + int ret = -1; + int err = 0; +#ifdef ARCH_MM_MMU + + ret = lwp_user_strlen(existing); + if (ret <= 0) + { + return -EFAULT; + } + + ret = lwp_user_strlen(new); + if (ret <= 0) + { + return -EFAULT; + } +#endif +#ifdef RT_USING_DFS_V2 + ret = dfs_file_symlink(existing, new); + if (ret < 0) + { + err = GET_ERRNO(); + } +#else + SET_ERRNO(EFAULT); +#endif + return (err < 0 ? err : ret); +} + +/** + * @brief Create an event file descriptor. + * + * This function creates an eventfd, which is a file descriptor used for event notification. + * Eventfd is a simple mechanism to notify threads or processes about certain events using + * an integer counter. It is typically used for inter-process communication (IPC) or + * synchronization purposes, where one thread or process increments the counter to signal + * another thread or process. + * + * @param[in] count Initial value for the eventfd counter. It can be set to a non-zero + * value to initialize the counter to that value. + * @param[in] flags Flags that control the behavior of the eventfd. Valid flags include: + * - `EFD_CLOEXEC`: Set the close-on-exec flag for the eventfd. + * - `EFD_NONBLOCK`: Set the non-blocking flag for the eventfd. + * + * @return sysret_t On success, returns a valid file descriptor referring to the eventfd. On + * failure, returns a negative error code. + * + * @note Eventfd can be used for both counting and signaling purposes. It provides + * efficient signaling between threads or processes. + */ +sysret_t sys_eventfd2(unsigned int count, int flags) +{ + int ret; + + ret = eventfd(count, flags); + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Create an epoll instance. + * + * This function creates an epoll instance, which is used for monitoring multiple file descriptors + * to see if I/O is possible on them. It allows a program to efficiently wait for events such as + * data being available for reading or space becoming available for writing. Epoll provides a scalable + * mechanism for managing large numbers of file descriptors. + * + * @param[in] flags Flags that control the behavior of the epoll instance. The valid flags include: + * - `EPOLL_CLOEXEC`: Set the close-on-exec flag for the epoll instance. + * - `EPOLL_NONBLOCK`: Set the non-blocking flag for the epoll instance. + * + * @return sysret_t On success, returns a file descriptor for the epoll instance. On failure, + * returns a negative error code. + * + * @note The `sys_epoll_create1` function is similar to `sys_epoll_create`, but it allows for + * additional control over the epoll instance creation via flags. + */ +sysret_t sys_epoll_create1(int flags) +{ + int ret; + + ret = epoll_create(flags); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Control an epoll instance. + * + * This function performs control operations on an epoll instance, such as adding, modifying, + * or removing file descriptors from the epoll instance's interest list. + * + * @param[in] fd The file descriptor of the epoll instance created using `sys_epoll_create` or `sys_epoll_create1`. + * @param[in] op The operation to perform on the epoll instance. It can be one of the following: + * - `EPOLL_CTL_ADD`: Add the specified file descriptor to the epoll interest list. + * - `EPOLL_CTL_MOD`: Modify the event mask of an existing file descriptor in the epoll interest list. + * - `EPOLL_CTL_DEL`: Remove the specified file descriptor from the epoll interest list. + * @param[in] fd2 The file descriptor to be added, modified, or removed from the epoll interest list. + * @param[in] ev A pointer to an `epoll_event` structure that describes the event to be associated with `fd2`. + * This parameter is required for `EPOLL_CTL_ADD` and `EPOLL_CTL_MOD`, but not for `EPOLL_CTL_DEL`. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note This function is typically used to manage the set of file descriptors monitored by an epoll instance. + */ +sysret_t sys_epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) +{ + int ret = 0; + struct epoll_event *kev = RT_NULL; + + if (ev) + { + if (!lwp_user_accessable((void *)ev, sizeof(struct epoll_event))) + { + return -EFAULT; + } + + kev = kmem_get(sizeof(struct epoll_event)); + if (kev == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kev, ev, sizeof(struct epoll_event)) != sizeof(struct epoll_event)) + { + kmem_put(kev); + return -EINVAL; + } + + ret = epoll_ctl(fd, op, fd2, kev); + + kmem_put(kev); + } + else + { + ret = epoll_ctl(fd, op, fd2, RT_NULL); + } + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Wait for events on an epoll file descriptor, with the ability to block for specific signals. + * + * This function waits for events on an epoll file descriptor. It blocks until one or more events + * are available or a timeout occurs. In addition to waiting for events, it can also block for specific signals + * as specified by the `sigs` argument. + * + * @param[in] fd The file descriptor of the epoll instance, created using `sys_epoll_create` or `sys_epoll_create1`. + * @param[out] ev A pointer to an array of `epoll_event` structures that will receive the events that have occurred. + * @param[in] cnt The maximum number of events to return. This specifies the size of the `ev` array. + * @param[in] to The timeout in milliseconds. A negative value means no timeout, and `0` means non-blocking. + * @param[in] sigs A pointer to a signal set (`sigset_t`) that specifies the signals to block during the wait. + * If `NULL`, no signals are blocked. + * @param[in] sigsetsize The size of the signal set (`sigs`) in bytes. This should be the size of `sigset_t` if `sigs` is not `NULL`. + * + * @return sysret_t On success, returns the number of events returned (may be `0` if the timeout expires with no events). + * On failure, returns a negative error code. + * + * @note This function is similar to `sys_epoll_wait`, but it also allows for signal handling, blocking the calling thread + * from receiving signals specified in the `sigs` set while waiting for events. + */ +sysret_t sys_epoll_pwait(int fd, + struct epoll_event *ev, + int cnt, + int to, + const sigset_t *sigs, + unsigned long sigsetsize) +{ + int ret = 0; + struct epoll_event *kev = RT_NULL; + sigset_t *ksigs = RT_NULL; + + if (!lwp_user_accessable((void *)ev, cnt * sizeof(struct epoll_event))) + { + return -EFAULT; + } + + kev = kmem_get(cnt * sizeof(struct epoll_event)); + if (kev == RT_NULL) + { + return -ENOMEM; + } + + if (sigs != RT_NULL) + { + if (!lwp_user_accessable((void *)sigs, sizeof(sigset_t))) + { + kmem_put(kev); + return -EFAULT; + } + + ksigs = kmem_get(sizeof(sigset_t)); + if (ksigs == RT_NULL) + { + kmem_put(kev); + return -ENOMEM; + } + + if (lwp_get_from_user(ksigs, (void *)sigs, sizeof(sigset_t)) != sizeof(sigset_t)) + { + kmem_put(kev); + kmem_put(ksigs); + return -EINVAL; + } + } + + ret = epoll_pwait(fd, kev, cnt, to, ksigs); + + if (ret > 0) + { + lwp_put_to_user((void *)ev, kev, ret * sizeof(struct epoll_event)); + } + + if (sigs != RT_NULL) + { + kmem_put(ksigs); + } + + kmem_put(kev); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Truncate a file to a specified length. + * + * This function resizes the file associated with the given file descriptor `fd` to the specified length. + * If the current size of the file is greater than the specified length, the file is truncated. If the file is smaller, + * it is extended, and the new space is initialized to zero. + * + * @param[in] fd The file descriptor referring to the file to truncate. This must be a valid open file descriptor. + * @param[in] length The new length of the file. The file will be truncated or extended to this size. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note The file descriptor `fd` must be opened with write permissions for this operation to succeed. + */ +sysret_t sys_ftruncate(int fd, size_t length) +{ + int ret; + + ret = ftruncate(fd, length); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Set file access and modification times. + * + * This function updates the access and modification times of a file or directory referenced by `__fd` or `__path`. + * If `__fd` is a valid file descriptor, the times will be applied to the file or directory it refers to. + * If `__fd` is `AT_FDCWD`, the times will be applied to the file or directory specified by `__path`. + * The `__times` argument consists of two `timespec` values: the first represents the access time, + * and the second represents the modification time. If `__times` is `NULL`, the current time is used. + * + * @param[in] __fd The file descriptor of the file or directory to modify, or `AT_FDCWD` to modify using `__path`. + * @param[in] __path The path to the file or directory to modify. Ignored if `__fd` is not `AT_FDCWD`. + * @param[in] __times An array of two `timespec` structures. The first element is the access time, and the second is the modification time. + * If `__times` is `NULL`, the current time is used for both access and modification times. + * @param[in] __flags Flags to modify behavior. Supported flags include: + * - `AT_SYMLINK_NOFOLLOW`: Do not follow symbolic links. + * - `AT_NO_AUTOMOUNT`: Do not trigger automounting. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note This function modifies both access and modification times of files, and may affect file system timestamps. + */ +sysret_t sys_utimensat(int __fd, const char *__path, const struct timespec __times[2], int __flags) +{ +#ifdef RT_USING_DFS_V2 +#ifdef ARCH_MM_MMU + int ret = -1; + rt_size_t len = 0; + char *kpath = RT_NULL; + + len = lwp_user_strlen(__path); + if (len <= 0) + { + return -EINVAL; + } + + kpath = (char *)kmem_get(len + 1); + if (!kpath) + { + return -ENOMEM; + } + + lwp_get_from_user(kpath, (void *)__path, len + 1); + ret = utimensat(__fd, kpath, __times, __flags); + + kmem_put(kpath); + + return ret; +#else + if (!lwp_user_accessable((void *)__path, 1)) + { + return -EFAULT; + } + int ret = utimensat(__fd, __path, __times, __flags); + return ret; +#endif +#else + return -1; +#endif +} + +/** + * @brief Change file permissions. + * + * This function changes the permissions of a file or directory specified by the `pathname`. + * The new permissions are specified using the `mode` argument, which is a bitwise OR of permission bits. + * The permissions apply to the file owner, group, and others, depending on the value of `mode`. + * + * @param[in] pathname The path to the file or directory whose permissions are to be changed. + * @param[in] mode The new permissions to set, represented as a bitwise OR of permission flags. + * For example, `S_IRUSR`, `S_IWUSR`, `S_IRGRP`, etc. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note If the file or directory is a symbolic link, this function will modify the permissions of the symbolic link itself, + * not the target file or directory. + */ +sysret_t sys_chmod(const char *pathname, mode_t mode) +{ + char *copy_file; + size_t len_file, copy_len_file; + struct dfs_attr attr = { 0 }; + int ret = 0; + + len_file = lwp_user_strlen(pathname); + if (len_file <= 0) + { + return -EFAULT; + } + + copy_file = (char *)rt_malloc(len_file + 1); + if (!copy_file) + { + return -ENOMEM; + } + + copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file); + + attr.st_mode = mode; + attr.ia_valid |= ATTR_MODE_SET; + + copy_file[copy_len_file] = '\0'; + ret = dfs_file_setattr(copy_file, &attr); + rt_free(copy_file); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Changes the permissions of an open file descriptor. + * + * @param[in] fd The open file descriptor. + * @param[in] mode The new file permission bits. + * @return sysret_t Zero on success, otherwise a negative error code. + */ +sysret_t sys_fchmod(int fd, mode_t mode) +{ + int ret; + + ret = fchmod(fd, mode); + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Change the ownership of a file or directory. + * + * This function changes the owner and/or group of the file or directory specified by `pathname`. + * The `owner` and `group` parameters represent the new owner and group IDs, respectively. If either + * parameter is set to `-1`, that aspect (owner or group) will remain unchanged. + * + * @param[in] pathname The path to the file or directory whose ownership is to be changed. + * @param[in] owner The new owner ID. If set to `-1`, the owner's ID is not changed. + * @param[in] group The new group ID. If set to `-1`, the group's ID is not changed. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note The caller must have the appropriate permissions (i.e., be the superuser or the file owner) + * to change the ownership of a file or directory. + */ +sysret_t sys_chown(const char *pathname, uid_t owner, gid_t group) +{ + char *copy_file; + size_t len_file, copy_len_file; + struct dfs_attr attr = { 0 }; + int ret = 0; + + len_file = lwp_user_strlen(pathname); + if (len_file <= 0) + { + return -EFAULT; + } + + copy_file = (char *)rt_malloc(len_file + 1); + if (!copy_file) + { + return -ENOMEM; + } + + copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file); + + if (owner >= 0) + { + attr.st_uid = owner; + attr.ia_valid |= ATTR_UID_SET; + } + + if (group >= 0) + { + attr.st_gid = group; + attr.ia_valid |= ATTR_GID_SET; + } + + copy_file[copy_len_file] = '\0'; + ret = dfs_file_setattr(copy_file, &attr); + rt_free(copy_file); + + return (ret < 0 ? GET_ERRNO() : ret); +} diff --git a/components/lwp/syscall/syscall_internal.h b/components/lwp/syscall/syscall_internal.h new file mode 100644 index 000000000000..2d0746ca745c --- /dev/null +++ b/components/lwp/syscall/syscall_internal.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __LWP_SYSCALL_INTERNAL_H__ +#define __LWP_SYSCALL_INTERNAL_H__ + +#define __RT_IPC_SOURCE__ +#define _GNU_SOURCE + +#include +#include +#include + +#include +#include + +#define DBG_TAG "lwp.syscall" +#define DBG_LVL DBG_INFO +#include + +#include "syscall_generic.h" +#include "libc_musl.h" +#include "lwp_internal.h" +#include "lwp_ipc_internal.h" +#include "mqueue.h" + +#ifdef ARCH_MM_MMU +#include +#include +#include +#endif + +#include +#include + +#ifdef RT_USING_DFS +#include +#include +#include +#include +#include +#ifdef RT_USING_DFS_V2 +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef RT_USING_MUSLLIBC +#include +#endif +#endif + +#ifdef RT_USING_SAL +#include +#include +#include +#include +#include +#include "lwp_sys_socket.h" +#endif + +#include +#include + +#ifndef GRND_NONBLOCK +#define GRND_NONBLOCK 0x0001 +#endif +#ifndef GRND_RANDOM +#define GRND_RANDOM 0x0002 +#endif + +#ifndef RT_USING_POSIX_TIMER +#error "No definition RT_USING_POSIX_TIMER" +#endif +#ifndef RT_USING_POSIX_CLOCK +#error "No definition RT_USING_POSIX_CLOCK" +#endif + +void lwp_cleanup(struct rt_thread *tid); +void _crt_thread_entry(void *parameter); + +#ifdef ARCH_MM_MMU +void *kmem_get(size_t size); +void kmem_put(void *kptr); +#define ALLOC_KERNEL_STACK_SIZE 5120 +#else +#define ALLOC_KERNEL_STACK_SIZE 1536 +#define ALLOC_KERNEL_STACK_SIZE_MIN 1024 +#define ALLOC_KERNEL_STACK_SIZE_MAX 4096 +void set_user_context(void *stack); +#endif + +sysret_t sys_notimpl(void); + +#ifdef RT_USING_SAL +void convert_sockopt(int *level, int *optname); +#endif + +#if defined(RT_USING_LWIP) || defined(SAL_USING_UNET) || defined(RT_USING_SAL) +struct musl_sockaddr; +void sockaddr_tolwip(const struct musl_sockaddr *std, struct sockaddr *lwip); +void sockaddr_tomusl(const struct sockaddr *lwip, struct musl_sockaddr *std); +#endif + +#endif /* __LWP_SYSCALL_INTERNAL_H__ */ diff --git a/components/lwp/syscall/syscall_memory.c b/components/lwp/syscall/syscall_memory.c new file mode 100644 index 000000000000..1386ce659ba1 --- /dev/null +++ b/components/lwp/syscall/syscall_memory.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +#ifdef ARCH_MM_MMU +/** + * @brief Adjusts the end of the data segment (heap) of the calling process. + * + * This system call changes the location of the program break, which defines the end of + * the data segment (heap) of the calling process. It provides a unified interface for adjusting + * the heap size. The `addr` parameter specifies the new location for the program break. + * + * @param addr The new program break location. If `addr` is `NULL`, the current program + * break is returned. If a valid address is provided, the program break will + * be adjusted to this address. The address must be page-aligned, and changes + * must stay within the process's allowed address space. + * + * @return On success, returns the new program break. + * + * @note This function is typically used to manage heap space in a process. The program + * break adjustment can impact memory allocations and deallocations in the heap. + * + * @warning The `addr` must be a valid address within the process's allocated memory + * space. Attempting to set an invalid program break address may result in + * undefined behavior or memory corruption. + * + * @see malloc(), free(), sys_sbrk() + */ +rt_base_t sys_brk(void *addr) +{ + return lwp_brk(addr); +} + +/** + * @brief Maps a file or device into memory. + * + * This system call implements the `mmap2` system call, which maps a file or device into memory. + * It provides a way for processes to access file contents directly in memory, bypassing + * the need for explicit read or write operations. This function supports advanced memory + * mapping options such as shared or private mappings, specific protection flags, and + * offset alignment. + * + * @param addr The starting address for the memory mapping. If `NULL`, the kernel chooses + * the address. If not `NULL`, the address must be page-aligned and meet + * platform-specific alignment constraints. + * @param length The length of the memory mapping in bytes. This value is rounded up to + * the nearest page size. + * @param prot The memory protection flags. Possible values include: + * - `PROT_READ`: Pages can be read. + * - `PROT_WRITE`: Pages can be written. + * - `PROT_EXEC`: Pages can be executed. + * - `PROT_NONE`: Pages cannot be accessed. + * These flags can be combined using bitwise OR (`|`). + * @param flags Flags that determine the type and behavior of the mapping. Possible values include: + * - `MAP_SHARED`: Updates are visible to other processes that map this file. + * - `MAP_PRIVATE`: Updates are not visible to other processes and are not written to the file. + * - `MAP_FIXED`: Use the exact address specified in `addr`. + * - `MAP_ANONYMOUS`: The mapping is not backed by a file and uses zero-initialized memory. + * @param fd The file descriptor of the file to map. If `MAP_ANONYMOUS` is set in `flags`, + * `fd` is ignored and should be set to `-1`. + * @param pgoffset The offset in the file where the mapping starts, specified in pages (not bytes). + * This allows finer-grained control over the starting point of the mapping. + * + * @return On success, returns a pointer to the mapped memory region. + * + * @warning Ensure that the combination of `addr`, `length`, `prot`, `flags`, and `fd` is valid. + * Improper use may lead to undefined behavior or memory access violations. + * + * @see mmap(), munmap(), msync() + */ +void *sys_mmap2(void *addr, size_t length, int prot, + int flags, int fd, size_t pgoffset) +{ + sysret_t rc = 0; + long offset = 0; + + /* aligned for user addr */ + if ((rt_base_t)addr & ARCH_PAGE_MASK) + { + if (flags & MAP_FIXED) + { + rc = -EINVAL; + } + else + { + offset = (char *)addr - (char *)RT_ALIGN_DOWN((rt_base_t)addr, ARCH_PAGE_SIZE); + length += offset; + addr = (void *)RT_ALIGN_DOWN((rt_base_t)addr, ARCH_PAGE_SIZE); + } + } + + if (rc == 0) + { + /* fix parameter passing (both along have same effect) */ + if (fd == -1 || flags & MAP_ANONYMOUS) + { + fd = -1; + /* MAP_SHARED has no effect and treated as nothing */ + flags &= ~MAP_SHARED; + flags |= MAP_PRIVATE | MAP_ANONYMOUS; + } + rc = (sysret_t)lwp_mmap2(lwp_self(), addr, length, prot, flags, fd, pgoffset); + } + + return rc < 0 ? (char *)rc : (char *)rc + offset; +} + +/** + * @brief Unmaps a memory region previously mapped with mmap or mmap2. + * + * This system call implements the `munmap` system call, which removes a mapping + * for a region of memory that was previously created using `mmap` or `mmap2`. + * + * @param addr The starting address of the memory region to unmap. This address + * must be page-aligned and refer to a region previously mapped. + * @param length The length of the memory region to unmap, in bytes. This value + * is rounded up to the nearest page size internally if needed. + * + * @return On success, returns `0`. On failure, returns error code. + * + * @warning + * - Ensure the specified memory region corresponds to a valid, active mapping. + * Providing invalid parameters may result in undefined behavior. + * - Unmapping a region that is still in use by another thread or process can + * cause concurrency issues or data corruption. + * + * @see mmap(), mmap2(), msync() + */ +sysret_t sys_munmap(void *addr, size_t length) +{ + return lwp_munmap(lwp_self(), addr, length); +} + +/** + * @brief Changes the size or location of an existing memory mapping. + * + * This system call implements the `mremap` system call, allowing the resizing + * or relocation of a previously created memory mapping. It is typically used + * to dynamically adjust memory allocation for mapped regions without unmapping + * and remapping them explicitly. + * + * @param old_address The starting address of the existing memory mapping to be resized + * or relocated. This must be the address returned by a previous + * `mmap` or `mremap` call. + * @param old_size The current size of the memory mapping, in bytes. It must match + * the size of the original mapping. + * @param new_size The new desired size of the memory mapping, in bytes. The size will + * be rounded up to the nearest page size if necessary. + * @param flags Options to control the behavior of the remapping. Possible values include: + * - `MREMAP_MAYMOVE`: Allows the kernel to move the mapping to a new + * address if the current region cannot be resized in place. + * - `MREMAP_FIXED`: Requires the mapping to be relocated to the + * specified `new_address`. This flag must be used with caution. + * @param new_address If `MREMAP_FIXED` is set in `flags`, this specifies the address + * for the new mapping. Otherwise, it is ignored. + * + * @return On success, returns a pointer to the resized or relocated memory mapping. + * + * @warning + * - Ensure that the `old_address` and `old_size` correspond to a valid, existing mapping. + * + * @see mmap(), munmap(), msync() + */ +void *sys_mremap(void *old_address, size_t old_size, + size_t new_size, int flags, void *new_address) +{ + return lwp_mremap(lwp_self(), old_address, old_size, new_size, flags, new_address); +} + +/** + * @brief Advises the kernel about expected memory access patterns. + * + * This implementation currently accepts no advice and returns `-ENOSYS`. + * + * @param[in] addr The start address of the memory region. + * @param[in] len The length of the memory region in bytes. + * @param[in] behav The requested access pattern. + * @return sysret_t Always returns `-ENOSYS`. + */ +sysret_t sys_madvise(void *addr, size_t len, int behav) +{ + return -ENOSYS; +} + +/** + * @brief Allocates or retrieves a shared memory segment. + * + * This system call allocates a new shared memory segment or retrieves an existing one + * based on the specified `key`. Shared memory allows processes to communicate by + * sharing a region of memory. + * + * @param[in] key A unique identifier for the shared memory segment. If `key` matches + * an existing segment, it will be retrieved. If `create` is set and the + * segment does not exist, a new one will be created. + * @param[in] size The size (in bytes) of the shared memory segment. If creating a new + * segment, this specifies its size. If retrieving an existing segment, + * `size` is ignored. + * @param[in] create A flag indicating whether to create the segment if it does not exist: + * - `1`: Create the shared memory segment if it does not exist. + * - `0`: Only retrieve an existing segment. + * + * @return sysret_t Returns a status code: + * - `0`: The shared memory segment was successfully created or + * retrieved. + * - Other error codes may indicate issues with the shared memory + * + * @note Shared memory segments identified by the same `key` are accessible across + * processes. Ensure proper synchronization mechanisms (e.g., semaphores) are in + * place to manage access to the shared memory. + * + * @warning Using a `NULL` or invalid `key` may result in undefined behavior. When creating + * a new segment, ensure that `size` is non-zero and meaningful. + */ +sysret_t sys_shmget(size_t key, size_t size, int create) +{ + return lwp_shmget(key, size, create); +} + +/** + * @brief Removes a shared memory segment. + * + * This system call removes the specified shared memory segment identified by its `id`. + * Once removed, the segment will no longer be accessible, and any memory associated + * with it will be freed. It is typically used to clean up shared memory resources + * when they are no longer needed. + * + * @param[in] id The identifier of the shared memory segment to be removed. This identifier + * was obtained when the segment was created or retrieved using `sys_shmget`. + * + * @return sysret_t Returns a status code: + * - `0`: The shared memory segment was successfully removed. + * - Other error codes may indicate issues with the shared memory removal. + * + * @note This function should be called only when all processes that were using the shared + * memory segment have finished accessing it. Removing the segment while it is in use + * by another process may result in undefined behavior or memory corruption. + * + * @warning Ensure that the shared memory segment is no longer needed by any process before + * calling this function to avoid premature removal and potential data loss. + */ +sysret_t sys_shmrm(int id) +{ + return lwp_shmrm(id); +} + +/** + * @brief Attaches a shared memory segment to the calling process's address space. + * + * This system call maps a shared memory segment identified by `id` into the calling + * process's address space. The segment can then be accessed using the returned virtual + * address. If the segment was previously detached or created, it will be made available + * for reading and writing. + * + * @param[in] id The identifier of the shared memory segment to be attached. + * This identifier was obtained when the segment was created + * or retrieved using `sys_shmget`. + * @param[in] shm_vaddr A pointer to the desired virtual address where the shared + * memory segment should be mapped. If `NULL`, the system will + * choose an appropriate address. + * + * @return void* Returns the virtual address where the shared memory segment + * is mapped. On success, this will be the address in the + * calling process's address space. If the attachment fails, + * `NULL` is returned. + * + * @note Once the shared memory segment is attached, it can be accessed like any + * regular memory, but it should be used with caution, especially in multi-process + * environments. Ensure that proper synchronization mechanisms (e.g., semaphores) + * are used to manage concurrent access. + * + * @warning Ensure that the shared memory segment is properly allocated and not in use + * by other processes before attaching it. Passing invalid `id` or an inaccessible + * segment may result in undefined behavior. + */ +void *sys_shmat(int id, void *shm_vaddr) +{ + return lwp_shmat(id, shm_vaddr); +} + +/** + * @brief Detaches a shared memory segment from the calling process's address space. + * + * This system call detaches the shared memory segment previously attached to the calling + * process's address space using `sys_shmat`. After calling this function, the shared + * memory will no longer be accessible via the returned address. + * + * @param[in] shm_vaddr A pointer to the virtual address where the shared memory + * segment was previously mapped. This address was returned + * by the `sys_shmat` function. + * + * @return sysret_t Returns a status code: + * - `0`: The shared memory segment was successfully detached. + * - Other error codes may indicate issues with the detachment process. + * + * @note It is important to ensure that no processes are using the shared memory segment + * before detaching it. Detaching the segment while it is still being accessed + * may lead to undefined behavior. + * + * @warning Ensure that `shm_vaddr` corresponds to a valid attached address returned + * by `sys_shmat`. Passing an incorrect or uninitialized address may result + * in undefined behavior. + */ +sysret_t sys_shmdt(void *shm_vaddr) +{ + return lwp_shmdt(shm_vaddr); +} +#elif defined RT_LWP_USING_SHM + +/** + * @brief Allocates a new shared memory segment. + * + * This system call allocates a new shared memory segment of the specified size and + * returns a pointer to the allocated memory. The allocated segment can be used for + * inter-process communication or shared memory access. The segment is typically + * managed by the system, and it can be accessed by other processes with appropriate + * synchronization. + * + * @param[in] size The size (in bytes) of the shared memory segment to be allocated. + * + * @return void* Returns a pointer to the allocated shared memory segment. + * If the allocation fails, `RT_NULL` is returned. + * + * @note The allocated shared memory segment can be used like regular memory, + * but it should be accessed with proper synchronization mechanisms (e.g., + * semaphores or mutexes) to avoid race conditions or data corruption in + * multi-process environments. + * + * @warning Ensure that the `size` parameter is a valid, non-zero value, and that + * there is enough system memory to allocate the requested size. + * Failure to do so may result in errors or resource exhaustion. + */ +void *sys_shm_alloc(int size) +{ + if (size < 0) + { + return RT_NULL; + } + return lwp_shm_alloc((rt_size_t)size); +} + +/** + * @brief Retains an existing shared memory segment. + * + * This system call retains an existing shared memory segment that was previously + * allocated or attached, ensuring it remains available for further use. It is + * typically used to increment the reference count or to ensure the segment is + * not removed or deallocated while it is still being used. + * + * @param[in] mem A pointer to the shared memory segment to retain. This should be + * the address previously returned by a memory allocation or + * attachment function such as `sys_shmat` or `sys_shm_alloc`. + * + * @return void* Returns the original pointer `mem` if the retention is successful. + * If the operation fails, `NULL` is returned. + * + * @note This function does not affect the actual content of the shared memory segment, + * but ensures the segment is not removed or deallocated while still in use. + * It is generally used in conjunction with `sys_shmdt` or `sys_shmrm` to ensure + * proper resource management. + * + * @warning Ensure that the provided pointer `mem` corresponds to a valid shared + * memory segment that is currently allocated or attached. Passing an + * invalid or uninitialized pointer may result in undefined behavior. + */ +void *sys_shm_retain(void *mem) +{ + if (!lwp_user_accessable(mem, sizeof(void *))) + { + return RT_NULL; + } + return lwp_shm_retain(mem); +} + +/** + * @brief Frees a previously allocated shared memory segment. + * + * This system call frees a shared memory segment that was previously allocated or + * retained using functions like `sys_shm_alloc` or `sys_shm_retain`. It ensures + * that the shared memory is properly deallocated, making it available for future + * allocations or for removal from the system. + * + * @param[in] mem A pointer to the shared memory segment to be freed. This should be + * the address returned by a memory allocation or retention function. + * + * @return sysret_t Returns a status code: + * - `0`: The shared memory segment was successfully freed. + * - Other error codes may indicate issues with the memory deallocation. + * + * @note After calling this function, the memory will be deallocated, and any subsequent + * access to the freed memory will result in undefined behavior. Ensure that no + * processes are using the shared memory before calling this function. + * + * @warning Ensure that `mem` corresponds to a valid shared memory segment that was + * previously allocated or retained. Passing an invalid or already freed + * pointer may result in undefined behavior. + */ +sysret_t sys_shm_free(void *mem) +{ + if (!lwp_user_accessable(mem, sizeof(void *))) + { + return -EFAULT; + } + lwp_shm_free(mem); + return 0; +} +#endif + +#define ICACHE (1 << 0) +#define DCACHE (1 << 1) +#define BCACHE (ICACHE | DCACHE) + +/** + * @brief Flush the cache for a specified memory region. + * + * This function flushes the cache for a specified memory region. It is commonly used + * to ensure that the contents of a memory region are written back to the main memory + * or that stale cache entries are invalidated. The cache operation can be controlled + * by the `cache` parameter to determine whether to clean or invalidate the cache. + * + * @param[in] addr The starting address of the memory region to flush. + * @param[in] size The size of the memory region to flush, in bytes. + * @param[in] cache A flag to specify the cache operation: + * - `CACHE_CLEAN`: Clean the cache (write back to memory). + * - `CACHE_INVALIDATE`: Invalidate the cache (discard cached data). + * - `CACHE_FLUSH`: Both clean and invalidate the cache. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function is typically used in low-level operations or in systems where + * cache coherence between memory and cache is critical. + */ +rt_weak sysret_t sys_cacheflush(void *addr, int size, int cache) +{ + if (!lwp_user_accessable(addr, size)) + { + return -EFAULT; + } + + if (((size_t)addr < (size_t)addr + size) && + ((size_t)addr >= USER_VADDR_START) && + ((size_t)addr + size < USER_VADDR_TOP)) + { + if ((cache & DCACHE)) + { + rt_hw_cpu_dcache_clean_and_invalidate(addr, size); + } + + if ((cache & ICACHE)) + { + rt_hw_cpu_icache_invalidate(addr, size); + } + + return 0; + } + return -EFAULT; +} diff --git a/components/lwp/syscall/syscall_mqueue.c b/components/lwp/syscall/syscall_mqueue.c new file mode 100644 index 000000000000..f6d4dde29b5f --- /dev/null +++ b/components/lwp/syscall/syscall_mqueue.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Open a message queue. + * + * This function opens a message queue for communication between processes. + * It can create a new message queue or open an existing one, depending on the specified + * flags. + * + * @param[in] name The name of the message queue. The name should be a null-terminated + * string and is subject to system-specific naming conventions. + * @param[in] flags Flags that control the behavior of the message queue. Common flags include: + * - `O_CREAT`: Create the message queue if it does not exist. + * - `O_EXCL`: Fail if the message queue already exists. + * - `O_RDONLY`: Open the queue for reading. + * - `O_WRONLY`: Open the queue for writing. + * - `O_RDWR`: Open the queue for both reading and writing. + * @param[in] mode The mode to be applied when creating the message queue, which defines + * the permissions for the message queue (e.g., read, write). + * @param[in] attr A pointer to a `struct mq_attr` that defines the attributes of the + * message queue, such as the maximum number of messages and the size of + * each message. If `NULL`, default values are used. + * + * @return mqd_t Returns a non-negative file descriptor for the message queue on success. + * On failure, returns `-1` and sets `errno` to indicate the error. + * + * @note If the message queue is created, its attributes (such as the maximum number of + * messages and message size) must be defined in the `mq_attr` structure. If the + * `O_CREAT` flag is not specified and the queue does not exist, the function will + * return `-1`. + */ +mqd_t sys_mq_open(const char *name, int flags, mode_t mode, struct mq_attr *attr) +{ + mqd_t mqdes; + sysret_t ret = 0; +#ifdef ARCH_MM_MMU + char *kname = RT_NULL; + rt_size_t len = 0; + struct mq_attr attr_k; + + len = lwp_user_strlen(name); + if (!len) + { + return (mqd_t)-EINVAL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return (mqd_t)-ENOMEM; + } + + if (attr == NULL) + { + attr_k.mq_maxmsg = 10; + attr_k.mq_msgsize = 8192; + attr_k.mq_flags = 0; + attr = &attr_k; + } + else + { + if (!lwp_get_from_user(&attr_k, (void *)attr, sizeof(struct mq_attr))) + { + return -EINVAL; + } + } + + lwp_get_from_user(kname, (void *)name, len + 1); + mqdes = mq_open(kname, flags, mode, &attr_k); + if (mqdes == -1) + { + ret = GET_ERRNO(); + } + lwp_put_to_user(attr, &attr_k, sizeof(struct mq_attr)); + kmem_put(kname); +#else + mqdes = mq_open(name, flags, mode, attr); +#endif + if (mqdes == -1) + { + return (mqd_t)ret; + } + else + { + return mqdes; + } +} + +/** + * @brief Remove a message queue. + * + * This function removes a message queue identified by its name. If the message queue + * is open by any process, it will be removed only when all the processes close their + * file descriptors associated with the message queue. + * + * @param[in] name The name of the message queue to be removed. It should be a null-terminated + * string that conforms to system-specific naming conventions. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative + * error code. + * + * @note After a successful call, the message queue is removed from the system. However, + * the removal will not take effect until all processes close their file descriptors + * associated with the queue. The function will fail if the message queue is still + * open by other processes. + */ +sysret_t sys_mq_unlink(const char *name) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + char *kname = RT_NULL; + rt_size_t len = 0; + + len = lwp_user_strlen(name); + if (!len) + { + return -EINVAL; + } + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return -ENOMEM; + } + + lwp_get_from_user(kname, (void *)name, len + 1); + ret = mq_unlink(kname); + if (ret < 0) + { + ret = GET_ERRNO(); + } + kmem_put(kname); + return ret; +#else + ret = mq_unlink(name); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Send a message to a message queue with a timeout. + * + * This function sends a message to the specified message queue, but it allows the sender + * to specify a timeout. If the message queue is full, the function will block until either + * space becomes available or the specified timeout expires. If the timeout expires without + * space being available, the function returns an error. + * + * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. + * @param[in] msg A pointer to the message to be sent. + * @param[in] len The length of the message to send. + * @param[in] prio The priority of the message (higher values indicate higher priority). + * @param[in] at A pointer to a `timespec` structure that specifies the absolute timeout. + * If the timeout expires before the message can be sent, the function returns + * an error. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error + * code. + * + * @note The function uses the `timespec` structure to specify the absolute timeout. The + * `at` parameter should indicate the time at which the operation should time out. + * If the timeout is `NULL`, the function will not apply any timeout (it will block + * indefinitely until the message is sent). + * + * @see sys_mq_send + */ +sysret_t sys_mq_timedsend(mqd_t mqd, const char *msg, size_t len, unsigned prio, const struct timespec *at) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + char *kmsg = RT_NULL; + struct timespec at_k; + + kmsg = (char *)kmem_get(len + 1); + if (!kmsg) + { + return -ENOMEM; + } + + lwp_get_from_user(&at_k, (void *)at, sizeof(struct timespec)); + lwp_get_from_user(kmsg, (void *)msg, len + 1); + ret = mq_timedsend(mqd, kmsg, len, prio, &at_k); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmsg); + + return ret; +#else + ret = mq_timedsend(mqd, msg, len, prio, at); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Receive a message from a message queue with a timeout. + * + * This function attempts to receive a message from the specified message queue, but it + * allows the receiver to specify a timeout. If the queue is empty, the function will block + * until either a message becomes available or the specified timeout expires. If the timeout + * expires without receiving a message, the function returns an error. + * + * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. + * @param[out] msg A pointer to the buffer where the received message will be stored. + * @param[in] len The maximum length of the buffer to store the received message. + * @param[out] prio A pointer to an unsigned integer that will be set to the priority + * of the received message. + * @param[in] at A pointer to a `timespec` structure that specifies the absolute timeout. + * If the timeout expires before a message is received, the function will + * return an error. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The function uses the `timespec` structure to specify the absolute timeout. The + * `at` parameter should indicate the time at which the operation should time out. + * If the timeout is `NULL`, the function will block indefinitely until a message is + * received. + * + * @see sys_mq_receive + */ +sysret_t sys_mq_timedreceive(mqd_t mqd, char * restrict msg, size_t len, unsigned * restrict prio, const struct timespec * restrict at) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + char * restrict kmsg = RT_NULL; + + struct timespec at_k; + + kmsg = (char * restrict)kmem_get(len + 1); + if (!kmsg) + { + return -ENOMEM; + } + + lwp_get_from_user(kmsg, (void *)msg, len + 1); + if (at == RT_NULL) + { + ret = mq_timedreceive(mqd, kmsg, len, prio, RT_NULL); + } + else + { + if (!lwp_get_from_user(&at_k, (void *)at, sizeof(struct timespec))) + { + return -EINVAL; + } + ret = mq_timedreceive(mqd, kmsg, len, prio, &at_k); + } + + if (ret > 0) + { + lwp_put_to_user(msg, kmsg, len + 1); + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmsg); + + return ret; +#else + ret = mq_timedreceive(mqd, msg, len, prio, at); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Set up asynchronous notification for a message queue. + * + * This function configures asynchronous notification for a message queue. When a message + * is available in the queue, the system can notify the calling process through a signal + * or another method specified in the `sigevent` structure. This is typically used to allow + * a process to be notified when a message arrives without having to block in the receive call. + * + * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. + * @param[in] sev A pointer to a `sigevent` structure that specifies the notification + * mechanism to be used when a message is received. It could include + * signals or other notification types such as event flags or message passing. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function enables asynchronous notification, but the specific behavior depends + * on the configuration specified in the `sev` parameter, which could involve signals + * or other forms of notification. + * + * @see sys_mq_send, sys_mq_timedreceive + */ +sysret_t sys_mq_notify(mqd_t mqd, const struct sigevent *sev) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + struct sigevent sev_k; + lwp_get_from_user(&sev_k, (void *)sev, sizeof(struct timespec)); + ret = mq_notify(mqd, &sev_k); +#else + ret = mq_notify(mqd, sev); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Get or set attributes of a message queue. + * + * This function allows you to get or set the attributes of an existing message queue. + * If the `new` attribute structure is non-NULL, it updates the message queue with the new + * attributes. Otherwise, If the `old` attribute structure is non-NULL, it will return the current + * attributes of the message queue. + * + * @param[in] mqd The message queue descriptor returned by `sys_mq_open`. + * @param[in] new A pointer to a `mq_attr` structure containing the new attributes to set. + * If `NULL`, the function will not modify the message queue attributes. + * @param[out] old A pointer to a `mq_attr` structure where the current message queue + * attributes will be returned. If `NULL`, the current attributes will not + * be returned. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The `mq_attr` structure contains parameters like the maximum number of messages, + * the maximum message size, and other attributes that control the behavior of the + * message queue. + * + * @see sys_mq_open, sys_mq_notify + */ +sysret_t sys_mq_getsetattr(mqd_t mqd, const struct mq_attr * restrict new, struct mq_attr * restrict old) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + size_t size = sizeof(struct mq_attr); + struct mq_attr * restrict knew = NULL; + struct mq_attr * restrict kold = NULL; + + if (new != RT_NULL) + { + if (!lwp_user_accessable((void *)new, size)) + { + return -EFAULT; + } + knew = kmem_get(size); + if (!knew) + { + return -ENOMEM; + } + lwp_get_from_user(knew, (void *)new, size); + } + + if (!lwp_user_accessable((void *)old, size)) + { + return -EFAULT; + } + kold = kmem_get(size); + if (!kold) + { + return -ENOMEM; + } + + lwp_get_from_user(kold, (void *)old, size); + ret = mq_setattr(mqd, knew, kold); + if (ret != -1) + { + lwp_put_to_user(old, kold, size); + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kold); + if (new != RT_NULL) + { + kmem_put(knew); + } + + return ret; +#else + ret = mq_setattr(mqd, new, old); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Close a message queue descriptor. + * + * This function closes a message queue descriptor. After calling this function, the + * descriptor can no longer be used to interact with the message queue. Any resources + * associated with the descriptor are released. If the message queue was opened with the + * `O_CLOEXEC` flag, it will be automatically closed when the calling process executes + * an `exec` system call. + * + * @param[in] mqd The message queue descriptor to be closed. It was previously returned + * by `sys_mq_open`. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @see sys_mq_open, sys_mq_unlink + */ +sysret_t sys_mq_close(mqd_t mqd) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + ret = mq_close(mqd); +#else + ret = mq_close(mqd); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} diff --git a/components/lwp/syscall/syscall_netdb.c b/components/lwp/syscall/syscall_netdb.c new file mode 100644 index 000000000000..6c1dfde6e727 --- /dev/null +++ b/components/lwp/syscall/syscall_netdb.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +#if defined(RT_USING_SAL) && defined(SAL_USING_POSIX) +struct musl_addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + + struct musl_sockaddr *ai_addr; + char *ai_canonname; + + struct musl_addrinfo *ai_next; +}; + +/** + * @brief Resolves network addresses and service names into a list of address structures. + * + * This function provides a mechanism for resolving a host name and service name (or port number) + * into a list of address structures suitable for use with socket-based communication. The function + * can handle both IPv4 and IPv6 addresses and provides a flexible way to specify different types + * of address and service resolution options. + * + * @param[in] nodename The host name to be resolved. This can be a DNS name or an IP address in string format. + * If `nodename` is `NULL`, the function will resolve the local host. + * @param[in] servname The service name (e.g., "http") or port number to be resolved. If `servname` is `NULL`, + * the function will resolve the port number or address without any service association. + * @param[in] hints A pointer to a `struct musl_addrinfo` that provides hints for the address resolution. + * It can be used to specify criteria such as the desired address family (IPv4 or IPv6), + * socket type, protocol, and flags for resolution. + * @param[out] res A pointer to a `struct musl_addrinfo` that will be filled with the resolved address + * information. The resulting linked list will contain one or more `struct musl_sockaddr` + * structures, each representing a different address that can be used for communication. + * + * @return sysret_t Returns `0` on success, indicating that the address resolution was successful. + * On failure, returns a negative error code. + * + * @note The `res` parameter points to a linked list of resolved address structures. Each node in the list + * contains a different resolved address and can be used for socket connection purposes. + * It is important to free the memory allocated for the list after it is no longer needed, using `freeaddrinfo()`. + * + * @warning The `hints` structure allows you to specify various options for the resolution. However, incorrect + * hints may lead to unexpected or incorrect results. For example, if you request IPv6 addresses but + * the host only supports IPv4, the function may not return the expected results. + * + * @see freeaddrinfo(), sys_socket(), sys_connect(), sys_gethostbyname(), sys_gethostbyaddr() + */ +sysret_t sys_getaddrinfo(const char *nodename, + const char *servname, + const struct musl_addrinfo *hints, + struct musl_addrinfo *res) +{ + int ret = -1; + struct addrinfo *k_res = NULL; + char *k_nodename = NULL; + char *k_servname = NULL; + struct addrinfo *k_hints = NULL; +#ifdef ARCH_MM_MMU + int len = 0; +#endif + +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)res, sizeof(*res))) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + if (nodename) + { +#ifdef ARCH_MM_MMU + len = lwp_user_strlen(nodename); + if (len <= 0) + { + SET_ERRNO(EFAULT); + goto exit; + } + + k_nodename = (char *)kmem_get(len + 1); + if (!k_nodename) + { + SET_ERRNO(ENOMEM); + goto exit; + } + + if (lwp_get_from_user(k_nodename, (void *)nodename, len + 1) != len + 1) + { + SET_ERRNO(EFAULT); + goto exit; + } +#else + k_nodename = rt_strdup(nodename); + if (!k_nodename) + { + SET_ERRNO(ENOMEM); + goto exit; + } +#endif + } + if (servname) + { +#ifdef ARCH_MM_MMU + len = lwp_user_strlen(servname); + if (len <= 0) + { + SET_ERRNO(EFAULT); + goto exit; + } + + k_servname = (char *)kmem_get(len + 1); + if (!k_servname) + { + SET_ERRNO(ENOMEM); + goto exit; + } + + if (lwp_get_from_user(k_servname, (void *)servname, len + 1) < 0) + { + SET_ERRNO(EFAULT); + goto exit; + } +#else + k_servname = rt_strdup(servname); + if (!k_servname) + { + SET_ERRNO(ENOMEM); + goto exit; + } +#endif + } + + if (hints) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)hints, sizeof(*hints))) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + k_hints = (struct addrinfo *)rt_malloc(sizeof *hints); + if (!k_hints) + { + SET_ERRNO(ENOMEM); + goto exit; + } + + rt_memset(k_hints, 0x0, sizeof(struct addrinfo)); + k_hints->ai_flags = hints->ai_flags; + k_hints->ai_family = hints->ai_family; + k_hints->ai_socktype = hints->ai_socktype; + k_hints->ai_protocol = hints->ai_protocol; + k_hints->ai_addrlen = hints->ai_addrlen; + } + + ret = sal_getaddrinfo(k_nodename, k_servname, k_hints, &k_res); + if (ret == 0) + { + /* set sockaddr */ + sockaddr_tomusl(k_res->ai_addr, res->ai_addr); + res->ai_addrlen = k_res->ai_addrlen; + + /* set up addrinfo */ + res->ai_family = k_res->ai_family; + res->ai_flags = k_res->ai_flags; + res->ai_next = NULL; + + if (hints != NULL) + { + /* copy socktype & protocol from hints if specified */ + res->ai_socktype = hints->ai_socktype; + res->ai_protocol = hints->ai_protocol; + } + + sal_freeaddrinfo(k_res); + k_res = NULL; + } + +exit: + if (ret < 0) + { + ret = GET_ERRNO(); + } +#ifdef ARCH_MM_MMU + if (k_nodename) + { + kmem_put(k_nodename); + } +#else + if (k_nodename) + { + rt_free(k_nodename); + } +#endif +#ifdef ARCH_MM_MMU + if (k_servname) + { + kmem_put(k_servname); + } +#else + if (k_servname) + { + rt_free(k_servname); + } +#endif + if (k_hints) + { + rt_free(k_hints); + } + + return ret; +} + +#define HOSTENT_BUFSZ 512 + +/** + * @brief Resolves a host name to an address, with support for specifying the address family. + * + * This function performs a lookup of the specified host name, and resolves it to an address, + * while allowing the caller to specify the desired address family (e.g., IPv4 or IPv6). It is + * a reentrant version of `gethostbyname2`, meaning it is safe for use in multi-threaded applications. + * The results are returned in a user-provided buffer to avoid memory allocation overhead. + * + * @param[in] name The host name to be resolved. This can be a DNS name or an IP address in string format. + * @param[in] af The address family to use for the resolution. Common values are: + * - `AF_INET` for IPv4 addresses. + * - `AF_INET6` for IPv6 addresses. + * @param[out] ret A pointer to a `struct hostent` where the resolved host information will be stored. + * This includes the host name, alias names, address type, and the address itself. + * @param[in] buf A buffer to store additional information required for the `struct hostent` structure. + * This is needed to ensure the reentrant behavior and avoid memory allocation. + * @param[in] buflen The size of the buffer provided. + * @param[out] result A pointer to a `struct hostent*` that will point to the resolved host entry. + * This will be set to the value of `ret` upon success. + * @param[out] err A pointer to an integer where error codes will be stored. If the function fails, + * `err` will contain a non-zero value corresponding to the error. + * + * @return sysret_t Returns `0` on success, indicating the resolution was successful. + * On failure, returns a negative error code that indicates the failure reason. + * + * @note This function is reentrant and thread-safe, meaning it does not use static memory or global state. + * It relies on the buffers provided by the caller to store the resolved data. + * + * @see gethostbyname2(), sys_gethostbyname(), sys_socket(), sys_connect(), sys_getaddrinfo() + */ +sysret_t sys_gethostbyname2_r(const char *name, int af, struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *err) +{ + int ret_val = -1; + int sal_ret = -1, sal_err = -1; + struct hostent sal_he, sal_tmp; + struct hostent *sal_result = NULL; + char *sal_buf = NULL; + char *k_name = NULL; + int len = 0; + +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)err, sizeof(*err))) + { + SET_ERRNO(EFAULT); + goto __exit; + } + + if (!lwp_user_accessable((void *)result, sizeof(*result)) || !lwp_user_accessable((void *)ret, sizeof(*ret)) || !lwp_user_accessable((void *)buf, buflen)) + { + /* not all arguments given */ + *err = EFAULT; + SET_ERRNO(EFAULT); + goto __exit; + } + + len = lwp_user_strlen(name); + if (len <= 0) + { + *err = EFAULT; + SET_ERRNO(EFAULT); + goto __exit; + } + + k_name = (char *)kmem_get(len + 1); + if (!k_name) + { + SET_ERRNO(ENOMEM); + goto __exit; + } + + if (lwp_get_from_user(k_name, (void *)name, len + 1) < 0) + { + SET_ERRNO(EFAULT); + goto __exit; + } +#else + k_name = rt_strdup(name); + if (k_name == NULL) + { + SET_ERRNO(ENOMEM); + goto __exit; + } +#endif + + *result = ret; + sal_buf = (char *)malloc(HOSTENT_BUFSZ); + if (sal_buf == NULL) + { + SET_ERRNO(ENOMEM); + goto __exit; + } + + /* get host by name in SAL */ + sal_ret = sal_gethostbyname_r(k_name, &sal_he, sal_buf, HOSTENT_BUFSZ, &sal_result, &sal_err); + if (sal_ret == 0) + { + int index = 0, cnt = 0; + char *ptr = buf; + + /* get counter */ + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + index++; + } + cnt = index + 1; + +#ifdef ARCH_MM_MMU + /* update user space hostent */ + lwp_put_to_user(buf, k_name, buflen - (ptr - buf)); + lwp_memcpy(&sal_tmp, &sal_he, sizeof(sal_he)); + sal_tmp.h_name = ptr; + ptr += rt_strlen(k_name); + + sal_tmp.h_addr_list = (char **)ptr; + ptr += cnt * sizeof(char *); + + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + sal_tmp.h_addr_list[index] = ptr; + lwp_memcpy(ptr, sal_he.h_addr_list[index], sal_he.h_length); + + ptr += sal_he.h_length; + index++; + } + sal_tmp.h_addr_list[index] = NULL; + lwp_put_to_user(ret, &sal_tmp, sizeof(sal_tmp)); +#else + /* update user space hostent */ + ret->h_addrtype = sal_he.h_addrtype; + ret->h_length = sal_he.h_length; + + rt_strncpy(ptr, k_name, buflen - (ptr - buf)); + ret->h_name = ptr; + ptr += strlen(k_name); + + ret->h_addr_list = (char **)ptr; + ptr += cnt * sizeof(char *); + + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + ret->h_addr_list[index] = ptr; + lwp_memcpy(ptr, sal_he.h_addr_list[index], sal_he.h_length); + + ptr += sal_he.h_length; + index++; + } + ret->h_addr_list[index] = NULL; +#endif + ret_val = 0; + } + else + { + SET_ERRNO(EINVAL); + } + +__exit: + if (ret_val < 0) + { + ret_val = GET_ERRNO(); + } + + /* release buffer */ + if (sal_buf) + { + free(sal_buf); + } +#ifdef ARCH_MM_MMU + if (k_name) + { + kmem_put(k_name); + } +#else + if (k_name) + { + free(k_name); + } +#endif + + return ret_val; +} +#endif diff --git a/components/lwp/syscall/syscall_process.c b/components/lwp/syscall/syscall_process.c new file mode 100644 index 000000000000..9da79959ac83 --- /dev/null +++ b/components/lwp/syscall/syscall_process.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Terminates all threads in the current thread group. + * + * This system call ends execution for all threads within the same thread group, + * releasing resources such as memory and file descriptors. It is typically used + * in multithreaded environments to ensure a clean exit for the entire process. + * + * @param value The exit code to be returned to the parent process. + * @return sysret_t: return value of the system call execution. + */ +sysret_t sys_exit_group(int value) +{ + sysret_t rc = 0; + lwp_status_t lwp_status; + struct rt_lwp *lwp = lwp_self(); + + if (lwp) + { + lwp_status = LWP_CREATE_STAT_EXIT(value); + lwp_exit(lwp, lwp_status); + } + else + { + LOG_E("Can't find matching process of current thread"); + rc = -EINVAL; + } + + return rc; +} + +/** + * @brief Terminates the calling thread and exits the process. + * + * This system call ends the execution of the calling thread and the process + * it belongs to. + * + * @param status The exit code to be returned to the parent process. + * A value of 0 typically indicates successful execution, while + * non-zero values indicate an error or specific exit condition. + * @return sysret_t: return value of the system call execution. + * + * @note Unlike `sys_exit_group`, which terminates all threads in a thread group, + * `sys_exit` only terminates the calling thread and the process. + */ +sysret_t sys_exit(int status) +{ + sysret_t rc = 0; + rt_thread_t tid; + + tid = rt_thread_self(); + if (tid && tid->lwp) + { + lwp_thread_exit(tid, status); + } + else + { + LOG_E("Can't find matching process of current thread"); + rc = -EINVAL; + } + + return rc; +} + +/** + * @brief Replaces the current process with a new process. + * + * This system call loads a new program into the current process's address space, + * replacing the current program with the one specified by `filename`. It passes the + * arguments `argv` and environment variables `envp` to the new program. If the execution + * is successful, the current process is completely replaced, and no code after the + * `sys_exec` call is executed. If an error occurs, the system call returns, and the + * current process continues executing. + * + * @param filename The path to the executable file to be run. This can be an absolute + * or relative path. + * @param argc The number of arguments passed to the new program. This includes the + * executable name itself as the first argument. + * @param argv An array of strings (character pointers), where each string is an argument + * passed to the new program. The first element (`argv[0]`) is conventionally + * the name of the executable. + * @param envp An array of strings representing the environment variables for the new + * process. The array is terminated by a `NULL` pointer. + * @return This function does not return on success, as the current process is replaced. + * On failure, it returns error code to indicate the error. + * + * @note The new process inherits most of the attributes of the current process, such + * as file descriptors, unless explicitly modified. It is important that `argv` + * and `envp` are properly formatted, and the `filename` points to a valid executable. + * + * @warning If `filename` is invalid or not an executable, or if the arguments or + * environment variables are incorrectly set, the system call will fail and + * return `-1`. Ensure that the executable file is accessible and that `argv` + * and `envp` are properly constructed. + * + * @see execve(), execvp(), execv(), execle() + */ +sysret_t sys_exec(char *filename, int argc, char **argv, char **envp) +{ + int ret = 0; + int len = 0; + char *kfilename = RT_NULL; + + len = lwp_user_strlen(filename); + if (len <= 0) + { + return -EFAULT; + } + + kfilename = (char *)kmem_get(len + 1); + if (!kfilename) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kfilename, (void *)filename, len + 1) != (len + 1)) + { + kmem_put(kfilename); + return -EFAULT; + } + + ret = lwp_execve(kfilename, 0, argc, argv, envp); + + kmem_put(kfilename); + + return ret; +} + +/** + * @brief Sends a signal to a process or a group of processes. + * + * This system call sends the signal specified by `signo` to the process or process group + * identified by `pid`. If `pid` is positive, the signal is sent to the process with the + * specified process ID. If `pid` is `0`, the signal is sent to all processes in the + * same process group as the caller. If `pid` is `-1`, the signal is sent to all processes + * except for the caller. If `pid` is less than `-1`, the signal is sent to all processes + * in the process group with the process group ID equal to `-pid`. + * + * @param pid The process ID or process group ID to which the signal is to be sent. + * - A positive value sends the signal to the process with that ID. + * - `0` sends the signal to all processes in the same process group as the caller. + * - `-1` sends the signal to all processes except the caller. + * - A negative value sends the signal to all processes in the process group + * with the process group ID equal to `-pid`. + * @param signo The signal to send. This is an integer value that specifies the signal + * type. Common signal values include: + * - `SIGTERM` (terminate process) + * - `SIGKILL` (force kill process) + * - `SIGSTOP` (suspend process) + * - `SIGCONT` (resume process) + * @return On success, returns `0`. On failure, it returns error code to indicate the error. + * + * @note Signals are a mechanism for inter-process communication, allowing processes + * to send notifications or requests to other processes. The behavior of signals + * depends on the signal type and how the receiving process handles them. + * + * @warning The `signo` value must be a valid signal number. Passing an invalid signal + * number or an invalid `pid` may result in an error. Additionally, some signals + * (e.g., `SIGKILL`) cannot be caught or ignored by processes. + * + * @see signal(), killpg(), raise() + */ +sysret_t sys_kill(int pid, int signo) +{ + rt_err_t kret = 0; + sysret_t sysret; + struct rt_lwp *lwp = RT_NULL; + + /* handling the semantics of sys_kill */ + if (pid > 0) + { + /** + * Brief: Match the pid and send signal to the lwp if found + * Note: Critical Section + * - pid tree (READ. since the lwp is fetch from the pid tree, it must stay there) + */ + lwp_pid_lock_take(); + lwp = lwp_from_pid_raw_locked(pid); + if (lwp) + { + lwp_ref_inc(lwp); + lwp_pid_lock_release(); + } + else + { + lwp_pid_lock_release(); + kret = -RT_ENOENT; + } + + if (lwp) + { + kret = lwp_signal_kill(lwp, signo, SI_USER, 0); + lwp_ref_dec(lwp); + } + } + else if (pid < -1 || pid == 0) + { + pid_t pgid = 0; + rt_processgroup_t group; + + if (pid == 0) + { + /** + * sig shall be sent to all processes (excluding an unspecified set + * of system processes) whose process group ID is equal to the process + * group ID of the sender, and for which the process has permission to + * send a signal. + */ + pgid = lwp_pgid_get_byprocess(lwp_self()); + } + else + { + /** + * sig shall be sent to all processes (excluding an unspecified set + * of system processes) whose process group ID is equal to the absolute + * value of pid, and for which the process has permission to send a signal. + */ + pgid = -pid; + } + + group = lwp_pgrp_find(pgid); + if (group != RT_NULL) + { + PGRP_LOCK(group); + kret = lwp_pgrp_signal_kill(group, signo, SI_USER, 0); + PGRP_UNLOCK(group); + } + else + { + kret = -ECHILD; + } + } + else if (pid == -1) + { + /** + * sig shall be sent to all processes (excluding an unspecified set + * of system processes) for which the process has permission to send + * that signal. + */ + kret = lwp_signal_kill_all(signo, SI_USER, 0); + } + + switch (kret) + { + case -RT_ENOENT: + case -ECHILD: + sysret = -ESRCH; + break; + case -RT_EINVAL: + sysret = -EINVAL; + break; + case -RT_ENOSYS: + sysret = -ENOSYS; + break; + + /** + * kill() never returns ENOMEM, so return normally to caller. + * IEEE Std 1003.1-2017 says the kill() function is successful + * if the process has permission to send sig to any of the + * processes specified by pid. + */ + case -RT_ENOMEM: + default: + sysret = 0; + } + return sysret; +} + +/** + * @brief Retrieves the process ID of the calling process. + * + * This system call returns the process ID (PID) of the calling process. The PID is a + * unique identifier assigned by the operating system to each running process. It can + * be used to refer to or manipulate the process in subsequent system calls. + * + * @return The PID of the calling process. On failure, returns `-1` and sets `errno` + * to indicate the error (although this function typically does not fail). + * + * @note The PID returned by this function is a positive integer, and it remains + * unique during the lifetime of the process. This value can be used to manage + * the process, including sending signals or querying process information. + * + * @warning This function does not take any arguments and is typically used when + * a process needs to obtain its own PID for management or logging purposes. + * + * @see getppid(), getuid(), getgid() + */ +sysret_t sys_getpid(void) +{ + return lwp_getpid(); +} + +/** + * @brief Retrieves the parent process ID of the calling process. + * + * This system call returns the process ID (PID) of the parent process of the calling process. + * The parent process is the process that created the calling process, typically through a + * system call like `fork()`. This information can be useful for process management and + * for determining the hierarchy of processes. + * + * @return The PID of the parent process. If the calling process has no parent (for example, + * the init process), it typically returns `1` as the PID of the system's "root" process. + * On failure, returns `-1` and sets `errno` to indicate the error (although this function + * typically does not fail). + * + * @note The `getppid()` function is commonly used when a process needs to know which + * process is responsible for it, or for managing relationships between parent and child processes. + * + * @warning This function does not take any arguments and is typically used when the + * process requires information about its parent. It should not be confused with `getpid()`, + * which retrieves the PID of the calling process itself. + * + * @see getpid(), getuid(), getgid() + */ +sysret_t sys_getppid(void) +{ + rt_lwp_t process; + + process = lwp_self(); + if (process->parent == RT_NULL) + { + LOG_E("%s: process %d has no parent process", __func__, lwp_to_pid(process)); + return 0; + } + else + { + return lwp_to_pid(process->parent); + } +} + +/** + * @brief Retrieves the priority of a process or process group. + * + * This system call returns the priority of a process or a process group, depending on + * the value of the `which` argument. + * + * @param which The entity for which the priority is being requested. Possible values include: + * - `PRIO_PROCESS`: Retrieves the priority of the process specified by `who`. + * - `PRIO_PGRP`: Retrieves the priority of the process group specified by `who`. (Currently not supported.) + * - `PRIO_USER`: Retrieves the priority of the user specified by `who` (the user ID). (Currently not supported.) + * @param who The ID of the process, process group, or user. The meaning of this parameter + * depends on the value of `which`: + * - When `which` is `PRIO_PROCESS`, `who` is the process ID (PID) of the process. + * - When `which` is `PRIO_PGRP`(Currently not supported.), `who` is the process group ID (PGID). + * - When `which` is `PRIO_USER`(Currently not supported.), `who` is the user ID (UID) of the user whose + * priority is being queried. + * @return On success, returns the priority value (a negative integer indicates a lower + * priority). + * + * @warning Ensure that `which` and `who` are correctly specified to avoid errors. + * Invalid values for `which` or `who` may result in an error, and the function + * will return `-1`. + * + * @see setpriority(), getpid(), getppid() + */ +sysret_t sys_getpriority(int which, id_t who) +{ + long prio = 0xff; + + if (which == PRIO_PROCESS) + { + struct rt_lwp *lwp = RT_NULL; + + lwp_pid_lock_take(); + lwp = lwp_from_pid_locked(who); + + if (lwp) + { + rt_thread_t thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + prio = RT_SCHED_PRIV(thread).current_priority; + } + + lwp_pid_lock_release(); + } + + return prio; +} + +/** + * @brief Sets the priority of a process, process group, or user. + * + * This system call sets the priority of a process, process group, or user, depending + * on the value of the `which` argument. The priority is used by the operating system + * for scheduling processes. Lower numerical values represent higher priority in many + * systems, and the priority range may be system-specific. + * + * @param which The entity for which the priority is being set. Possible values include: + * - `PRIO_PROCESS`: Sets the priority of the process specified by `who`. + * - `PRIO_PGRP`: Sets the priority of the process group specified by `who`.(Currently not supported.) + * - `PRIO_USER`: Sets the priority of the user specified by `who` (the user ID).(Currently not supported.) + * @param who The ID of the process, process group, or user for which the priority is being set. + * The meaning of this parameter depends on the value of `which`: + * - When `which` is `PRIO_PROCESS`, `who` is the process ID (PID) of the process. + * - When `which` is `PRIO_PGRP`(Currently not supported.) , `who` is the process group ID (PGID). + * - When `which` is `PRIO_USER`(Currently not supported.) , `who` is the user ID (UID) of the user whose priority + * is being set. + * @param prio The priority value to set. + * @return On success, returns `0`. On failure, returns `-1`. + * + * @note This function modifies the priority of the specified process, process group, or user. + * The priority value is system-specific and may vary based on the system's scheduling + * policies. Ensure that the specified priority is within the acceptable range. + * + * @warning Ensure that `which`, `who`, and `prio` are valid. Invalid values for these + * parameters can result in errors, and the system call will return `-1`. + * + * @see getpriority(), getpid(), getppid() + */ +sysret_t sys_setpriority(int which, id_t who, int prio) +{ + if (which == PRIO_PROCESS) + { + struct rt_lwp *lwp = RT_NULL; + + lwp_pid_lock_take(); + lwp = lwp_from_pid_locked(who); + + if (lwp && prio >= 0 && prio < RT_THREAD_PRIORITY_MAX) + { + rt_list_t *list; + rt_thread_t thread; + for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next) + { + thread = rt_list_entry(list, struct rt_thread, sibling); + rt_thread_control(thread, RT_THREAD_CTRL_RESET_PRIORITY, &prio); + } + lwp_pid_lock_release(); + return 0; + } + else + { + lwp_pid_lock_release(); + } + } + + return -1; +} + +/** + * @brief Waits for a process to change state. + * + * This function suspends the execution of the calling process until one of its child processes terminates + * or a specified process (identified by `pid`) changes its state. The function returns the process ID of the child + * process that terminated, and provides information about its exit status and resource usage. + * + * @param[in] pid The process ID of the child process to wait for. The behavior of this parameter can be one of the following: + * - `pid > 0`: Wait for the child process with the specified PID. + * - `pid == 0`: Wait for any child process in the same process group. + * - `pid == -1`: Wait for any child process (default behavior). + * - `pid < -1`: Wait for any child process in the specified process group. + * + * @param[out] status A pointer to an integer where the exit status of the terminated child process will be stored. + * This status can be interpreted using macros such as `WIFEXITED` or `WIFSIGNALED`. + * + * @param[in] options Options for the wait operation, which can include: + * - `WNOHANG`: Return immediately if no child has exited. + * - `WUNTRACED`: Return if a child has stopped, but not yet terminated. + * + * @param[out] ru A pointer to a `struct rusage` where resource usage information of the child process will be stored. + * This includes information such as CPU time consumed by the child process. + * + * @return sysret_t Returns the process ID of the terminated child on success. In case of failure, a negative error code is returned. + * + * @note This function is useful for monitoring and cleaning up child processes in parent-child relationships. + * The `status` value can be further analyzed to determine if the child process terminated normally or due to a signal. + * + * @see sys_waitpid(), sys_fork(), sys_exit() + */ +sysret_t sys_wait4(pid_t pid, int *status, int options, struct rusage *ru) +{ + return lwp_waitpid(pid, status, options, ru); +} diff --git a/components/lwp/syscall/syscall_rt_ipc.c b/components/lwp/syscall/syscall_rt_ipc.c new file mode 100644 index 000000000000..d103cb22cdea --- /dev/null +++ b/components/lwp/syscall/syscall_rt_ipc.c @@ -0,0 +1,1415 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Creates a semaphore. + * + * This system call creates a new semaphore with the specified `name` and initializes + * its value. The semaphore is used for synchronizing access to shared resources in + * concurrent programming. The semaphore's behavior is defined by the value (`value`) + * and any flags (`flag`) that specify additional properties or settings. + * + * @param name The name of the semaphore. This is a string used to identify the semaphore + * in the system. The name must be unique if the system requires it for reference. + * @param value The initial value of the semaphore. The value typically represents the + * number of available resources or tokens. The semaphore will be initialized + * with this value. + * @param flag The flag that specifies the attributes or properties of the semaphore. + * Flags can be used to define various characteristics of the semaphore, + * such as whether it is binary or counting, whether it is shared, etc. + * + * @return On success, returns a handle or reference to the created semaphore. On failure, + * returns `NULL`. + * + * @note Semaphores are commonly used in multithreading and multiprocessing environments + * to synchronize access to shared resources and ensure proper order of execution. + * The behavior of the semaphore depends on its type and the system's implementation. + * + * @warning Ensure that the `name` is unique and appropriate for your system's naming + * conventions. Invalid values for `value` or `flag` may lead to errors in semaphore + * creation. + * + * @see sem_wait(), sem_post(), sem_destroy() + */ +rt_sem_t sys_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) +{ + int len = 0; + char *kname = RT_NULL; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return RT_NULL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return RT_NULL; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return RT_NULL; + } + + rt_sem_t sem = rt_sem_create(kname, value, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)sem) != 0) + { + rt_sem_delete(sem); + sem = NULL; + } + + kmem_put(kname); + + return sem; +} + +/** + * @brief Deletes a semaphore and releases associated resources. + * + * This system call deletes an existing semaphore identified by the given handle `sem`. + * It releases any resources associated with the semaphore and ensures that it is no longer + * available for further synchronization operations. After deletion, the semaphore handle + * is invalid and should not be used. + * + * @param sem The semaphore handle to be deleted. This handle is obtained when the semaphore + * is created using `sys_sem_create()` or similar functions. + * + * @return On success, returns `0`. On failure, returns error code. + * + * @note Deleting a semaphore is a critical operation that should be performed only when + * the semaphore is no longer needed and no tasks are currently waiting or posting + * to it. Deleting a semaphore while it is still in use may lead to undefined behavior. + * + * @warning Ensure that no tasks are using or blocking on the semaphore before deleting it. + * Using a deleted semaphore will result in undefined behavior and potential system errors. + * + * @see sys_sem_create(), sem_wait(), sem_post() + */ +sysret_t sys_sem_delete(rt_sem_t sem) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)sem); +} + +/** + * @brief Attempts to take (acquire) a semaphore. + * + * This system call attempts to acquire a semaphore, blocking the calling task until the + * semaphore becomes available or the specified timeout expires. If the semaphore is already + * available, the calling task will immediately take it and proceed. If the semaphore is not + * available, the task will block until the semaphore becomes available or the timeout period + * has elapsed. The `time` parameter specifies the maximum amount of time the task will block + * waiting for the semaphore. + * + * @param sem The semaphore handle to be taken. This handle is obtained when the semaphore + * is created using `sys_sem_create()` or similar functions. + * @param time The maximum time to wait for the semaphore to become available. If the semaphore + * is not acquired within this time period, the function will return with an error. + * A value of `0` means no waiting (non-blocking), while a negative value may indicate + * an infinite wait, depending on the system's implementation. + * + * @return On success, returns `0` if the semaphore was successfully taken. On failure, returns error code. + * + * @warning Ensure that the semaphore handle is valid and has been properly created before + * calling this function. If the semaphore is deleted or invalid, the behavior of + * this function is undefined. + * + * @see sys_sem_create(), sys_sem_delete(), sem_post() + */ +sysret_t sys_sem_take(rt_sem_t sem, rt_int32_t time) +{ + return rt_sem_take_interruptible(sem, time); +} + +/** + * @brief Releases a semaphore and wakes up any waiting tasks. + * + * This system call releases a semaphore, incrementing its value and allowing any tasks + * that are blocked (waiting) on the semaphore to proceed. If there are tasks waiting for + * the semaphore, one of them will be unblocked and allowed to take the semaphore. + * The release operation does not block the calling task, and it will return immediately. + * + * @param sem The semaphore handle to be released. This handle is obtained when the semaphore + * is created using `sys_sem_create()` or similar functions. + * + * @return On success, returns `0`. On failure, returns error code. + * + * @warning Ensure that the semaphore handle is valid before calling this function. If the semaphore + * is deleted or invalid, the behavior of this function is undefined. + * + * @see sys_sem_create(), sys_sem_delete(), sys_sem_take() + */ +sysret_t sys_sem_release(rt_sem_t sem) +{ + return rt_sem_release(sem); +} + +/** + * @brief Creates a mutex. + * + * This system call creates a new mutex with the specified `name` and initializes it + * with the given `flag`. The mutex is used for synchronizing access to shared resources + * between tasks. Mutexes are typically used to ensure that only one task can access + * a critical section of code or a resource at a time. The `flag` parameter allows + * for setting certain properties of the mutex, such as whether it is recursive or whether + * it can be used by multiple tasks. + * + * @param name The name of the mutex. This is a string used to uniquely identify the mutex + * within the system. The name must be unique if the system requires it. + * @param flag The flag that specifies the attributes or properties of the mutex. Flags can + * define various mutex behaviors, such as whether the mutex is recursive (allows + * the same task to lock it multiple times) or whether it can be shared across + * different parts of the system. + * + * @return On success, returns a handle to the created mutex. On failure, returns `-RT_NULL`. + * + * @note Mutexes are typically used to prevent race conditions and ensure mutual exclusion + * when multiple tasks or threads are accessing shared resources. + * + * @warning Ensure that the `name` is unique and appropriate according to the system's + * naming conventions. Invalid values for `flag` or `name` may result in errors. + * + * @see sys_mutex_delete(), sys_mutex_lock(), sys_mutex_unlock() + */ +rt_mutex_t sys_mutex_create(const char *name, rt_uint8_t flag) +{ + int len = 0; + char *kname = RT_NULL; + rt_mutex_t mutex = RT_NULL; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return RT_NULL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return RT_NULL; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return RT_NULL; + } + + mutex = rt_mutex_create(kname, flag); + if (mutex == RT_NULL) + { + return RT_NULL; + } + + if (lwp_user_object_add(lwp_self(), (rt_object_t)mutex) != 0) + { + rt_mutex_delete(mutex); + mutex = RT_NULL; + } + + kmem_put(kname); + + return mutex; +} + +/** + * @brief Deletes a mutex and releases associated resources. + * + * This system call deletes an existing mutex identified by the given handle `mutex`. + * It releases any resources associated with the mutex and ensures that it is no longer + * available for further synchronization operations. After deletion, the mutex handle + * becomes invalid and should not be used. This operation also ensures that no tasks + * can block or attempt to lock the mutex after it has been deleted. + * + * @param mutex The mutex handle to be deleted. This handle is obtained when the mutex + * is created using `sys_mutex_create()` or similar functions. + * + * @return On success, returns `0`. On failure, returns error code. + * + * @warning Ensure that the mutex handle is valid before calling this function. Deleting + * an already deleted or invalid mutex will result in undefined behavior. + * + * @see sys_mutex_create(), sys_mutex_lock(), sys_mutex_unlock() + */ +sysret_t sys_mutex_delete(rt_mutex_t mutex) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)mutex); +} + +/** + * @brief Attempts to acquire (lock) a mutex. + * + * This system call attempts to acquire a mutex, blocking the calling task until the + * mutex becomes available or the specified timeout expires. If the mutex is available, + * the calling task will immediately acquire it and proceed. If the mutex is already locked + * by another task, the calling task will block until the mutex is released or the timeout + * period elapses. The `time` parameter specifies the maximum amount of time the task will + * block while waiting for the mutex. + * + * @param mutex The mutex handle to be locked. This handle is obtained when the mutex is + * created using `sys_mutex_create()` or similar functions. + * @param time The maximum time to wait for the mutex to become available. If the mutex + * is not acquired within this time period, the function will return an error. + * A value of `0` means no waiting (non-blocking), while a negative value may + * indicate an infinite wait. + * + * @return On success, returns `0` if the mutex was successfully acquired. On failure, returns error code. + * + * @warning Ensure that the mutex handle is valid and has been properly created before + * calling this function. If the mutex is deleted or invalid, the behavior of + * this function is undefined. + * + * @see sys_mutex_create(), sys_mutex_delete(), sys_mutex_unlock() + */ +sysret_t sys_mutex_take(rt_mutex_t mutex, rt_int32_t time) +{ + return rt_mutex_take_interruptible(mutex, time); +} + +/** + * @brief Releases (unlocks) a mutex. + * + * This system call releases a mutex that was previously acquired (locked) by the calling task. + * If any other task is waiting for the mutex, one of them will be unblocked and allowed to + * acquire the mutex. The release operation does not block the calling task, and it will return + * immediately after unlocking the mutex. + * + * @param mutex The mutex handle to be released. This handle is obtained when the mutex is + * created using `sys_mutex_create()` or similar functions. + * + * @return On success, returns `0`. On failure, returns error code. + * + * @warning Ensure that the mutex handle is valid before calling this function. Attempting + * to release a mutex that has not been locked or is already released can result + * in undefined behavior. + * + * @see sys_mutex_create(), sys_mutex_delete(), sys_mutex_take() + */ +sysret_t sys_mutex_release(rt_mutex_t mutex) +{ + return rt_mutex_release(mutex); +} + +/** + * @brief Creates an event object for inter-thread or inter-process communication. + * + * @param name A string representing the name of the event. If `NULL`, the event will + * be created without a name. Named events can be identified and accessed + * globally if supported by the system. + * @param flag Specifies the behavior of the event. Possible values include: + * - `RT_IPC_FLAG_FIFO`: Events are handled in a first-in, first-out order. + * - `RT_IPC_FLAG_PRIO`: Events are handled in priority order. + * + * @return On success, returns a handle (`rt_event_t`) to the created event. On failure, + * returns `-RT_NULL` to indicate that the event could not be created. + * + * @note + * - The event object must be explicitly deleted using `sys_event_delete` when it is + * no longer needed to free system resources. + * + * @warning + * - Ensure that sufficient system resources (e.g., memory) are available to create + * the event. If resources are insufficient, the function will fail. + * - Named events can potentially lead to naming conflicts if multiple events with + * the same name are created. Use unique names to avoid such issues. + * + * @see sys_event_delete(), sys_event_send(), sys_event_recv() + */ +rt_event_t sys_event_create(const char *name, rt_uint8_t flag) +{ + int len = 0; + rt_event_t event = RT_NULL; + char *kname = RT_NULL; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return RT_NULL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return RT_NULL; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return RT_NULL; + } + + event = rt_event_create(kname, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)event) != 0) + { + rt_event_delete(event); + event = NULL; + } + + kmem_put(kname); + + return event; +} + +/** + * @brief Deletes a system event object. + * + * This system call removes the specified system event object, releasing + * any resources associated with it. After deletion, the event object + * should not be used. + * + * @param[in] event The handle to the event object to be deleted. + * Must be a valid `rt_event_t` object. + * + * @return sysret_t Returns a status code indicating the result of the + * operation: + * - `0` if the operation was successful. + * - An appropriate error code otherwise. + * + * @note Ensure that the event is no longer being accessed by any + * threads or tasks before calling this function. + * + * @warning Deleting an event that is in use or invalid may lead to + * undefined behavior. + * @see sys_event_create(), sys_event_send(), sys_event_recv() + */ +sysret_t sys_event_delete(rt_event_t event) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)event); +} + +/** + * @brief Sends an event to the specified event object. + * + * This system call sends an event to the specified event object, setting + * the specified bits in the event object's set. The event can be used + * to signal other threads or tasks that a particular condition has + * occurred. + * + * @param[in] event The handle to the event object to which the event + * will be sent. Must be a valid `rt_event_t` object. + * @param[in] set The bits to set in the event object's set. The bits + * are specified as a bitmask, where each bit represents + * a different event condition. + * + * @return sysret_t Returns a status code indicating the result of the + * operation: + * - `0` if the operation was successful. + * - An appropriate error code otherwise. + * + * @note The event object must be created before sending events to it. + * + * @warning Ensure that the event object is valid and has been created + * before calling this function. + * @see sys_event_create(), sys_event_recv() + */ +sysret_t sys_event_send(rt_event_t event, rt_uint32_t set) +{ + return rt_event_send(event, set); +} + +/** + * @brief Receives an event from the specified event object. + * + * This system call waits for an event to be received from the specified + * event object. The function blocks until the specified event bits are + * set in the event object's set or the specified timeout period has + * elapsed. If the event is received, the function returns the set of + * bits that were set in the event object. + * + * @param[in] event The handle to the event object from which the event + * will be received. Must be a valid `rt_event_t` object. + * @param[in] set The bits to wait for in the event object's set. The + * bits are specified as a bitmask, where each bit + * represents a different event condition. + * @param[in] opt The options for receiving the event. Possible values + * include: + * - `EV_EVENT_ANY`: Wait for any of the specified bits + * to be set. + * - `EV_EVENT_ALL`: Wait for all of the specified bits + * to be set. + * @param[in] timeout The maximum time to wait for the event to be + * received, in milliseconds. A value of `0` means + * no waiting (non-blocking), while a negative value + * may indicate an infinite wait. + * @param[out] recved A pointer to a variable that will receive the set + * of bits that were set in the event object. If the + * event is received, this variable will be updated + * with the set of bits. + * + * @return sysret_t Returns a status code indicating the result of the + * operation: + * - `0` if the operation was successful. + * - An appropriate error code otherwise. + * + * @note The event object must be created before receiving events from it. + * + * @warning Ensure that the event object is valid and has been created + * before calling this function. + * @see sys_event_create(), sys_event_send() + */ +sysret_t sys_event_recv(rt_event_t event, + rt_uint32_t set, + rt_uint8_t opt, + rt_int32_t timeout, + rt_uint32_t *recved) +{ + int ret = 0; + rt_uint32_t krecved; + + if ((recved != NULL) && !lwp_user_accessable((void *)recved, sizeof(rt_uint32_t *))) + { + return -EFAULT; + } + + ret = rt_event_recv(event, set, opt, timeout, &krecved); + if ((ret == RT_EOK) && recved) + { + lwp_put_to_user((void *)recved, &krecved, sizeof(rt_uint32_t *)); + } + + return ret; +} + +/** + * @brief Creates a mailbox for inter-thread or inter-process communication. + * + * This system call creates a mailbox object with the specified name and size. + * The mailbox is used to exchange messages between threads or tasks in a + * synchronized manner. The mailbox can be used to send and receive messages + * of a specified size. + * + * @param[in] name A string representing the name of the mailbox. If `NULL`, + * the mailbox will be created without a name. Named mailboxes + * can be identified and accessed globally if supported by the system. + * @param[in] size The size of the mailbox, which determines the maximum number + * of messages that can be stored in the mailbox at any given time. + * @param[in] flag The behavior of the mailbox. Possible values include: + * - `RT_IPC_FLAG_FIFO`: Messages are handled in a first-in, first-out order. + * - `RT_IPC_FLAG_PRIO`: Messages are handled in priority order. + * + * @return rt_mailbox_t Returns a handle to the created mailbox object. + * On failure, returns `-RT_NULL`. + * + * @note + * - The mailbox object must be explicitly deleted using `sys_mb_delete` when it is + * no longer needed to free system resources. + * + * @warning + * - Ensure that sufficient system resources (e.g., memory) are available to create + * the mailbox. If resources are insufficient, the function will fail. + * - Named mailboxes can potentially lead to naming conflicts if multiple mailboxes + * with the same name are created. Use unique names to avoid such issues. + * + * @see sys_mb_delete(), sys_mb_send(), sys_mb_recv() + */ +rt_mailbox_t sys_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) +{ + int len = 0; + rt_mailbox_t mb = RT_NULL; + char *kname = RT_NULL; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return RT_NULL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return RT_NULL; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return RT_NULL; + } + + mb = rt_mb_create(kname, size, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)mb) != 0) + { + rt_mb_delete(mb); + mb = NULL; + } + + kmem_put(kname); + + return mb; +} + +/** + * @brief Deletes a mailbox object. + * + * This system call deletes the specified mailbox object, releasing any resources + * associated with it. After deletion, the mailbox object should not be used. + * + * @param[in] mb The handle to the mailbox object to be deleted. + * Must be a valid `rt_mailbox_t` object. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0` if the operation was successful. + * - An appropriate error code otherwise. + * + * @note Ensure that the mailbox is no longer being accessed by any threads or tasks + * before calling this function. + * + * @warning Deleting a mailbox that is in use or invalid may lead to undefined behavior. + * + * @see sys_mb_create(), sys_mb_send(), sys_mb_recv() + */ +sysret_t sys_mb_delete(rt_mailbox_t mb) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)mb); +} + +/** + * @brief Sends a message to a mailbox object. + * + * This system call posts a message (a single value) to the specified mailbox. + * If the mailbox is full, the function will return an error code immediately without waiting time. + * + * @param[in] mb The handle to the mailbox object where the message + * will be sent. Must be a valid `rt_mailbox_t` object. + * @param[in] value The value to be sent to the mailbox. Typically, this + * is a pointer or an integral value that represents + * the message content. + * + * @return sysret_t Returns a status code indicating the result of the + * operation: + * - `0` if the message was successfully sent. + * - `-RT_EFULL` if the mailbox is full. + * - An appropriate error code for other failures. + * + * @note Ensure the mailbox has been properly initialized before calling + * this function. Sending messages to an uninitialized or invalid + * mailbox may result in undefined behavior. + * + * @warning If the mailbox is full and the function blocks, ensure + * proper handling to avoid potential deadlocks. + * + * @see sys_mb_create(), sys_mb_recv(), sys_mb_send_wait() + */ +sysret_t sys_mb_send(rt_mailbox_t mb, rt_ubase_t value) +{ + return rt_mb_send(mb, value); +} + +/** + * @brief Sends a message to a mailbox object with a timeout. + * + * This system call attempts to post a message (a single value) to the specified mailbox. + * If the mailbox is full, the function will wait for a specified timeout period + * for space to become available. If the timeout expires before the message is sent, + * the function returns an error. + * + * @param[in] mb The handle to the mailbox object where the message + * will be sent. Must be a valid `rt_mailbox_t` object. + * @param[in] value The value to be sent to the mailbox. Typically, this + * is a pointer or an integral value representing the + * message content. + * @param[in] timeout The maximum time to wait for space to become available + * in the mailbox, in milliseconds. + * - a negative value can be used to wait indefinitely. + * - `0` can be used for non-blocking behavior. + * + * @return sysret_t Returns a status code indicating the result of the + * operation: + * - `0` if the message was successfully sent. + * - `-RT_ETIMEOUT` if the operation timed out. + * - `-RT_EFULL` if the mailbox is full and `timeout` is `0`. + * - An appropriate error code for other failures. + * + * @note Ensure the mailbox has been properly initialized before calling this function. + * Passing an uninitialized or invalid mailbox handle may result in undefined behavior. + * + * @warning Using a negative value without appropriate logic may lead to indefinite blocking, + * potentially causing deadlocks. + * @see sys_mb_send() + */ +sysret_t sys_mb_send_wait(rt_mailbox_t mb, + rt_ubase_t value, + rt_int32_t timeout) +{ + return rt_mb_send_wait(mb, value, timeout); +} + +/** + * @brief Receives a message from a mailbox with a timeout. + * + * This system call attempts to receive a message from the specified mailbox. + * If the mailbox is empty, the function will wait for a specified timeout + * period for a message to arrive. If no message is received within the timeout, + * an error is returned. + * + * @param[in] mb The handle to the mailbox object from which the message + * is to be received. Must be a valid `rt_mailbox_t` object. + * @param[out] value Pointer to a variable where the received message will + * be stored. Must not be NULL. + * @param[in] timeout The maximum time to wait for a message, in milliseconds. + * - a negative value: Wait indefinitely until a message is available. + * - `0`: Non-blocking mode. If no message is available, return immediately. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The message was successfully received. + * - `-RT_ETIMEOUT`: The operation timed out before a message was received. + * - Other error codes may indicate failures. + * + * @note Ensure the mailbox is properly initialized and the `value` pointer + * is valid before calling this function. Passing an invalid mailbox + * or NULL `value` pointer may lead to undefined behavior. + * + * @warning Using a negative value without proper safeguards may cause + * indefinite blocking, potentially resulting in deadlocks. + */ +sysret_t sys_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) +{ + int ret = 0; + rt_ubase_t *kvalue; + + if (!lwp_user_accessable((void *)value, sizeof(rt_ubase_t *))) + { + return -EFAULT; + } + + kvalue = kmem_get(sizeof(rt_ubase_t *)); + if (kvalue == RT_NULL) + { + return -ENOMEM; + } + + ret = rt_mb_recv(mb, (rt_ubase_t *)kvalue, timeout); + if (ret == RT_EOK) + { + lwp_put_to_user(value, kvalue, sizeof(rt_ubase_t *)); + } + + kmem_put(kvalue); + + return ret; +} + +rt_weak int syslog_ctrl(int type, char *buf, int len) +{ + return -EINVAL; +} + +/** + * @brief Sends a log message to the system log. + * + * This system call sends a message to the system log for recording or debugging purposes. + * + * @param[in] type The type of the log message, typically representing the severity + * or category (e.g., debug, info, warning, error). This should be + * a valid predefined log type. + * @param[in] buf A pointer to a buffer containing the log message. Must be a + * null-terminated string and a valid pointer. + * @param[in] len The length of the log message to be sent, excluding the null + * terminator. If the length exceeds the system log's limit, + * the message may be truncated. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The log message was successfully recorded. + * - Other error codes may indicate additional failures. + * + * @note Ensure the log type is a valid predefined value supported by the system. + * The buffer must remain valid and accessible during the execution of this function. + * + * @warning Sending excessively large or frequent log messages may impact system performance. + */ +sysret_t sys_syslog(int type, char *buf, int len) +{ + char *tmp; + int ret = -1; + + if (!lwp_user_accessable((void *)buf, len)) + { + return -EFAULT; + } + + tmp = (char *)rt_malloc(len); + if (!tmp) + { + return -ENOMEM; + } + + ret = syslog_ctrl(type, tmp, len); + lwp_put_to_user(buf, tmp, len); + rt_free(tmp); + + return ret; +} + +/** + * @brief Creates a message queue. + * + * This system call creates a new message queue with a specified name, message size, + * maximum number of messages, and associated flags. The message queue allows + * messages of a given size to be sent and received between tasks or threads. + * + * @param[in] name The name of the message queue. It should be a unique + * identifier and a null-terminated string. + * @param[in] msg_size The size of each message in the queue. This defines + * the maximum size for individual messages that can + * be sent or received. + * @param[in] max_msgs The maximum number of messages the queue can hold. + * If the queue is full, further send operations may + * block or return an error depending on the flags. + * @param[in] flag Flags that control the behavior of the message queue. + * This can specify whether the queue is blocking, + * non-blocking, or has other specific attributes. + * + * @return rt_mq_t Returns the handle to the created message queue, or `NULL` + * if the creation failed (e.g., due to invalid parameters + * or insufficient resources). + * + * @note Ensure that the message queue name is unique. The size of the messages + * and the number of messages should be chosen based on the application + * requirements. + * + * @warning Creating too many message queues or setting an overly large + * `max_msgs` may lead to resource exhaustion. + */ +rt_mq_t sys_mq_create(const char *name, + rt_size_t msg_size, + rt_size_t max_msgs, + rt_uint8_t flag) +{ + rt_mq_t mq = RT_NULL; + + int len = 0; + char *kname = RT_NULL; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return RT_NULL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return RT_NULL; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return RT_NULL; + } + + mq = rt_mq_create(kname, msg_size, max_msgs, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)mq) != 0) + { + rt_mq_delete(mq); + mq = NULL; + } + + kmem_put(kname); + + return mq; +} + +/** + * @brief Deletes a message queue. + * + * This system call deletes the specified message queue and releases any resources + * associated with it. After calling this function, the message queue handle + * becomes invalid and should not be used. + * + * @param[in] mq The handle to the message queue to be deleted. + * Must be a valid `rt_mq_t` object. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The message queue was successfully deleted. + * - An appropriate error code for other failures. + * + * @note Ensure that no tasks or threads are using the message queue before + * deleting it to avoid undefined behavior or data loss. + * + * @warning Deleting an active message queue that is being used by tasks or + * threads may lead to resource leaks or corruption. Ensure proper + * synchronization before deletion. + */ +sysret_t sys_mq_delete(rt_mq_t mq) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)mq); +} + +/** + * @brief Sends a message to a message queue. + * + * This system call sends a message to the specified message queue. The message + * is copied into the queue's buffer. If the queue is full, the behavior will + * depend on the flags set during queue creation (e.g., whether it is blocking + * or non-blocking). + * + * @param[in] mq The handle to the message queue where the message will be sent. + * Must be a valid `rt_mq_t` object. + * @param[in] buffer A pointer to the message data to be sent. This must not be NULL. + * @param[in] size The size of the message to be sent, in bytes. This should be + * less than or equal to the maximum message size defined when + * the queue was created. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The message was successfully sent to the queue. + * - An appropriate error code for other failures. + * + * @note Ensure that the message size does not exceed the maximum allowed message + * size when the queue was created. Passing an invalid queue handle or buffer + * may result in undefined behavior. + * + * @warning Sending messages to a full queue in blocking mode may cause the calling + * task or thread to block indefinitely if not properly handled. + */ +sysret_t sys_mq_send(rt_mq_t mq, void *buffer, rt_size_t size) +{ + int ret = 0; + void *kbuffer = RT_NULL; + + if (!lwp_user_accessable((void *)buffer, size)) + { + return -EFAULT; + } + + kbuffer = kmem_get(size); + if (kbuffer == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kbuffer, buffer, size) != size) + { + kmem_put(kbuffer); + return -EINVAL; + } + + ret = rt_mq_send(mq, kbuffer, size); + + kmem_put(kbuffer); + + return ret; +} + +/** + * @brief Sends an urgent message to a message queue. + * + * This system call sends a message to the specified message queue with higher priority, + * meaning it will be placed at the front of the queue, bypassing normal message + * order. The message is copied into the queue's buffer. If the queue is full, + * the behavior will depend on the flags set during queue creation (e.g., whether + * it is blocking or non-blocking). + * + * @param[in] mq The handle to the message queue where the message will be sent. + * Must be a valid `rt_mq_t` object. + * @param[in] buffer A pointer to the message data to be sent. This must not be NULL. + * @param[in] size The size of the message to be sent, in bytes. This should be + * less than or equal to the maximum message size defined when + * the queue was created. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The urgent message was successfully sent to the queue. + * - `-RT_EFULL`: The queue is full and the message could not be sent. + * - An appropriate error code for other failures. + * + * @note Ensure that the message size does not exceed the maximum allowed message + * size when the queue was created. The urgent message will be processed before + * other normal messages in the queue. + * + * @warning Sending urgent messages to a full queue in blocking mode may cause the + * calling task or thread to block indefinitely if not properly handled. + */ +sysret_t sys_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size) +{ + int ret = 0; + void *kbuffer = RT_NULL; + + if (!lwp_user_accessable((void *)buffer, size)) + { + return -EFAULT; + } + + kbuffer = kmem_get(size); + if (kbuffer == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kbuffer, buffer, size) != size) + { + kmem_put(kbuffer); + return -EINVAL; + } + + ret = rt_mq_urgent(mq, kbuffer, size); + + kmem_put(kbuffer); + + return ret; +} + +/** + * @brief Receives a message from a message queue. + * + * This system call attempts to receive a message from the specified message queue. + * The message is copied into the provided buffer. If no message is available + * in the queue, the function will block for a specified timeout period before + * returning. If the timeout expires without receiving a message, an error is returned. + * + * @param[in] mq The handle to the message queue from which the message + * will be received. Must be a valid `rt_mq_t` object. + * @param[out] buffer A pointer to the buffer where the received message will + * be stored. Must not be NULL and large enough to hold the + * message. + * @param[in] size The size of the buffer, in bytes. This should be at least + * the size of a message in the queue. + * @param[in] timeout The maximum time to wait for a message, in milliseconds. + * - a negative value: Wait indefinitely until a message is available. + * - `0`: Non-blocking mode. If no message is available, return immediately. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The message was successfully received. + * - `-RT_ETIMEOUT`: The operation timed out before a message was received. + * - Other error codes may indicate additional failures. + * + * @note Ensure the buffer is large enough to store the message received from the queue. + * If the buffer is too small, the function may fail or behave unexpectedly. + * + * @warning Using a negative value without proper safeguards may cause indefinite + * blocking, potentially resulting in deadlocks if no message is received. + */ +sysret_t sys_mq_recv(rt_mq_t mq, + void *buffer, + rt_size_t size, + rt_int32_t timeout) +{ + int ret = 0; + void *kbuffer = RT_NULL; + + if (!lwp_user_accessable((void *)buffer, size)) + { + return -EFAULT; + } + + kbuffer = kmem_get(size); + if (kbuffer == RT_NULL) + { + return -ENOMEM; + } + + ret = rt_mq_recv(mq, kbuffer, size, timeout); + if (ret > 0) + { + lwp_put_to_user((void *)buffer, (void *)kbuffer, ret); + } + + kmem_put(kbuffer); + + return ret; +} + +static void timer_timeout_callback(void *parameter) +{ + rt_sem_t sem = (rt_sem_t)parameter; + rt_sem_release(sem); +} + +/** + * @brief Creates a timer. + * + * This system call creates a timer that can be used to trigger events or + * actions after a specified timeout or interval. The timer will operate in a + * real-time manner and can be configured to trigger once or repeatedly. The + * created timer can be started, stopped, or deleted as required. + * + * @param[in] name The name of the timer. It should be a unique string identifier. + * The name is used for debugging and logging purposes. + * @param[in] data A pointer to user-defined data that will be passed to the timer's + * callback function when it is triggered. This may be used to carry + * context or other necessary information for the callback. + * @param[in] period The timer period in milliseconds. The timer will trigger every + * `period` milliseconds, or after the specified timeout. + * @param[in] flag Flags that control the behavior of the timer. These can specify + * whether the timer is one-shot (triggers once) or periodic + * (triggers repeatedly). + * + * @return rt_timer_t Returns the handle to the created timer, or `NULL` if the creation failed. + * + * @note The timer callback function must be implemented to handle the event triggered + * by the timer. Ensure that the `data` parameter, if used, is properly handled + * in the callback. + * + * @warning Ensure the timer's period and flags are configured correctly to avoid + * undesired behavior, especially if the timer is periodic. + */ +rt_timer_t sys_rt_timer_create(const char *name, + void *data, + rt_tick_t time, + rt_uint8_t flag) +{ + int len = 0; + char *kname = RT_NULL; + rt_timer_t timer = RT_NULL; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return RT_NULL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return RT_NULL; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return RT_NULL; + } + + timer = rt_timer_create(kname, timer_timeout_callback, (void *)data, time, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)timer) != 0) + { + rt_timer_delete(timer); + timer = NULL; + } + + kmem_put(kname); + + return timer; +} + +/** + * @brief Deletes a timer. + * + * This system call deletes the specified timer and releases any resources + * associated with it. Once the timer is deleted, its handle becomes invalid, + * and any further operations on the timer should be avoided. + * + * @param[in] timer The handle to the timer to be deleted. Must be a valid `rt_timer_t` object. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The timer was successfully deleted. + * - Other error codes may indicate additional failures. + * + * @note Ensure that the timer is not active or being used before deleting it to avoid + * any unexpected behavior or resource leaks. + * + * @warning Deleting an active timer may lead to undefined behavior, especially + * if the timer is in the middle of triggering or executing its callback. + */ +sysret_t sys_rt_timer_delete(rt_timer_t timer) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)timer); +} + +/** + * @brief Starts a timer. + * + * This system call starts the specified timer, causing it to begin counting down + * based on its configured period. Once the timer reaches the set period, it triggers the + * associated callback function. The behavior depends on whether the timer is configured + * as one-shot or periodic. + * + * @param[in] timer The handle to the timer to be started. Must be a valid `rt_timer_t` object. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The timer was successfully started. + * - Other error codes may indicate additional failures. + * + * @note Ensure that the timer has been created and is in a valid state before attempting to start it. + * + * @warning Starting a timer that is already running may lead to undefined behavior. Ensure that the + * timer is stopped or not in use before starting it. + */ +sysret_t sys_rt_timer_start(rt_timer_t timer) +{ + return rt_timer_start(timer); +} + +/** + * @brief Stops a timer. + * + * This system call stops the specified timer, halting its countdown and + * preventing it from triggering further callbacks. If the timer is periodic, + * stopping it will prevent further periodic triggers until it is started again. + * + * @param[in] timer The handle to the timer to be stopped. Must be a valid `rt_timer_t` object. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The timer was successfully stopped. + * - Other error codes may indicate additional failures. + * + * @note Ensure that the timer is in a valid state before attempting to stop it. + * Stopping an inactive or already stopped timer may not have any effect. + * + * @warning Stopping a timer that is actively triggering or in use may disrupt its expected + * behavior. Ensure proper synchronization or state management when stopping + * the timer during active use. + */ +sysret_t sys_rt_timer_stop(rt_timer_t timer) +{ + return rt_timer_stop(timer); +} + +/** + * @brief Controls various properties of a timer. + * + * This system call provides control over various aspects of a timer, such as + * modifying its configuration, querying its status, or changing its behavior. + * The specific behavior is determined by the command (`cmd`) and any associated arguments (`arg`). + * + * @param[in] timer The handle to the timer to be controlled. Must be a valid `rt_timer_t` object. + * @param[in] cmd The command to execute. The meaning of this parameter depends on the command value. + * Common commands might include modifying the timer period, changing its callback, + * or querying its current state. + * @param[in] arg A pointer to any additional arguments needed for the command. The type and content + * of this argument depend on the specific command being executed. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The timer control operation was successful. + * - Other error codes may indicate additional failures. + * + * @note Ensure that the provided command (`cmd`) is valid for the specific timer implementation. + * Incorrect commands or arguments may lead to unexpected behavior or errors. + * + * @warning Using invalid or unsupported commands may cause undefined behavior or crashes. + */ +sysret_t sys_rt_timer_control(rt_timer_t timer, int cmd, void *arg) +{ + return rt_timer_control(timer, cmd, arg); +} + +/* MUSL compatible */ +struct ksigevent +{ + union sigval sigev_value; + int sigev_signo; + int sigev_notify; + int sigev_tid; +}; + +/* to protect unsafe implementation in current rt-smart toolchain */ +RT_STATIC_ASSERT(sigevent_compatible, offsetof(struct ksigevent, sigev_tid) == offsetof(struct sigevent, sigev_notify_function)); + +/** + * @brief Creates a per-process timer. + * + * This system call creates a new timer associated with the specified clock, and + * initializes the timer with the provided event notification attributes. + * Once created, the timer can be started, stopped, or controlled as needed. + * The timer will trigger when the specified expiration time or interval is reached. + * + * @param[in] clockid The clock to be used for the timer. Common clock values include: + * - `CLOCK_REALTIME`: System real-time clock. + * - `CLOCK_MONOTONIC`: Monotonic clock that cannot be set and is not affected by system time changes. + * - Other clock IDs can be used depending on the platform and requirements. + * @param[in] sevp A pointer to a `sigevent` structure that specifies how the process + * should be notified when the timer expires. This can include notification + * types such as signal delivery, thread notification, or posting to a queue. + * @param[out] timerid A pointer to a `timer_t` variable where the created timer's ID will be stored. + * The timer ID will be used for subsequent timer operations (e.g., starting, stopping). + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The timer was successfully created. + * - Other error codes may indicate additional failures. + * + * @warning Ensure that the provided `sigevent` structure is properly configured, as invalid or + * unsupported notification types may result in unexpected behavior. + */ +sysret_t sys_timer_create(clockid_t clockid, struct sigevent * restrict sevp, timer_t * restrict timerid) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + struct sigevent sevp_k; + timer_t timerid_k; + int utimer; + + if (sevp == NULL) + { + sevp_k.sigev_notify = SIGEV_SIGNAL; + sevp_k.sigev_signo = SIGALRM; + sevp = &sevp_k; + } + else + { + /* clear extra bytes if any */ + if (sizeof(struct ksigevent) < sizeof(struct sigevent)) + { + memset(&sevp_k, 0, sizeof(sevp_k)); + } + + /* musl passes `struct ksigevent` to kernel, we shoule only get size of that bytes */ + if (!lwp_get_from_user(&sevp_k, (void *)sevp, sizeof(struct ksigevent))) + { + return -EINVAL; + } + } + + ret = _SYS_WRAP(timer_create(clockid, &sevp_k, &timerid_k)); + + if (ret != -RT_ERROR) + { + utimer = (rt_ubase_t)timerid_k; + if (!lwp_put_to_user(sevp, (void *)&sevp_k, sizeof(struct ksigevent)) || + !lwp_put_to_user(timerid, (void *)&utimer, sizeof(utimer))) + { + ret = -EINVAL; + } + } +#else + ret = _SYS_WRAP(timer_create(clockid, sevp, timerid)); +#endif + return ret; +} + +/** + * @brief Deletes a timer. + * + * This system call deletes the specified timer and releases any resources associated + * with it. Once the timer is deleted, it can no longer be used, and any further + * operations on the timer (such as starting or stopping) will result in an error. + * + * @param[in] timerid The ID of the timer to be deleted. This is the timer handle + * returned by `sys_timer_create`. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The timer was successfully deleted. + * - Other error codes may indicate additional failures. + * + * @note After calling this function, the timer ID becomes invalid, and it should + * not be used in any further operations. + * + * @warning Make sure the timer is not active or in use when attempting to delete it, + * as deleting an active timer may cause unexpected behavior or resource leaks. + */ +sysret_t sys_timer_delete(timer_t timerid) +{ + int ret = timer_delete(timerid); + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Changes the time or interval of an existing timer. + * + * This system call modifies the expiration time or interval of a previously created + * timer. It can either set a new expiration time for the timer or update the + * interval for periodic timers. The timer can be started or modified based on + * the flags provided. The current (old) timer settings can be retrieved if + * requested. + * + * @param[in] timerid The ID of the timer to modify. This is the timer handle + * returned by `sys_timer_create`. + * @param[in] flags Flags that control the behavior of the operation. Common + * values include: + * - `TIMER_ABSTIME`: Specifies that `new_value` contains + * an absolute time. Otherwise, it is treated as relative. + * @param[in] new_value A pointer to the `itimerspec` structure specifying + * the new time settings for the timer. The structure includes: + * - `it_value`: The initial expiration time (relative or absolute). + * - `it_interval`: The period for periodic timers. + * @param[out] old_value A pointer to an `itimerspec` structure where the previous + * timer settings will be stored. If NULL, the old value is ignored. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The timer time/interval was successfully updated. + * - Other error codes may indicate additional failures. + * + * @warning Modifying a timer that is currently active can cause timing-related issues + * if not handled correctly. Make sure the timer is in an appropriate state + * before making changes. + */ +sysret_t sys_timer_settime(timer_t timerid, int flags, + const struct itimerspec * restrict new_value, + struct itimerspec * restrict old_value) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + struct itimerspec new_value_k; + struct itimerspec old_value_k; + + if (!lwp_get_from_user(&new_value_k, (void *)new_value, sizeof(*new_value)) || + (old_value && !lwp_get_from_user(&old_value_k, (void *)old_value, sizeof(*old_value)))) + { + return -EFAULT; + } + + ret = timer_settime(timerid, flags, &new_value_k, &old_value_k); + lwp_put_to_user(old_value, (void *)&old_value_k, sizeof old_value_k); + +#else + ret = timer_settime(timerid, flags, new_value, old_value); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Retrieves the current time and interval of a timer. + * + * This system call fetches the current expiration time (`it_value`) and interval (`it_interval`) + * of a previously created timer. It allows the caller to determine the current state of the timer, + * whether it is one-shot or periodic, and the remaining time before expiration. + * + * @param[in] timerid The ID of the timer to query. This is the timer handle + * returned by `sys_timer_create`. + * @param[out] curr_value A pointer to an `itimerspec` structure where the current timer values + * will be stored. This structure includes: + * - `it_value`: The remaining time until the timer expires. + * - `it_interval`: The interval between subsequent expirations (for periodic timers). + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The current time/interval was successfully retrieved. + * - Other error codes may indicate additional failures. + * + * @warning Ensure that the timer ID is valid before calling this function, as invalid timer IDs + * will result in errors. + */ +sysret_t sys_timer_gettime(timer_t timerid, struct itimerspec *curr_value) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + + struct itimerspec curr_value_k; + lwp_get_from_user(&curr_value_k, (void *)curr_value, sizeof curr_value_k); + ret = timer_gettime(timerid, &curr_value_k); + lwp_put_to_user(curr_value, (void *)&curr_value_k, sizeof curr_value_k); +#else + ret = timer_gettime(timerid, curr_value); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Retrieves the overrun count for a periodic timer. + * + * This system call retrieves the number of times a periodic timer has "overrun." An overrun occurs + * when a timer expires before the previous expiration has been acknowledged or handled. For periodic + * timers, this indicates how many times the timer's expiration has been missed due to delayed processing + * or handling. + * + * @param[in] timerid The ID of the timer to query. This is the timer handle + * returned by `sys_timer_create`. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - `0`: The overrun count was successfully retrieved. + * - Other error codes may indicate additional failures. + * + * @warning Ensure that the timer ID is valid before calling this function, as invalid timer IDs + * will result in errors. + */ +sysret_t sys_timer_getoverrun(timer_t timerid) +{ + int ret = 0; + ret = timer_getoverrun(timerid); + return (ret < 0 ? GET_ERRNO() : ret); +} diff --git a/components/lwp/syscall/syscall_signal.c b/components/lwp/syscall/syscall_signal.c new file mode 100644 index 000000000000..e0fb7459c149 --- /dev/null +++ b/components/lwp/syscall/syscall_signal.c @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +struct k_sigaction +{ + void (*handler)(int); + unsigned long flags; + void (*restorer)(void); + unsigned mask[2]; +}; + +/** + * @brief Changes the action taken by the system on receiving a signal. + * + * This system call allows a process to specify how signals should be handled. It allows you to set a + * new action for a specific signal, retrieve the old action, and define the signal mask that should + * be applied during the execution of the signal handler. + * + * @param[in] sig The signal number for which the action is to be set or retrieved. + * Signal numbers are typically defined as constants (e.g., `SIGINT`, `SIGTERM`). + * @param[in] act A pointer to a `k_sigaction` structure that specifies the new action for the signal. + * If `act` is `NULL`, the signal's action is not changed. + * @param[out] oact A pointer to a `k_sigaction` structure where the old action will be stored. + * If `oact` is `NULL`, the old action is not retrieved. + * @param[in] sigsetsize The size of the `sigset_t` structure used in `k_sigaction`. This is to ensure the + * compatibility with the signal mask size. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note The `k_sigaction` structure allows you to specify the signal handler, signal mask, and flags for + * the signal action. It is important to correctly configure the handler to prevent unexpected + * behavior in the signal handling process. + * + * @warning Be cautious when modifying signal handling behavior, as incorrect configuration may result + * in unhandled signals or undefined behavior. Signal handlers should be designed to perform + * minimal, safe operations. + * + * @see sys_signal(), sys_sigprocmask() + */ +sysret_t sys_sigaction(int sig, const struct k_sigaction *act, + struct k_sigaction *oact, size_t sigsetsize) +{ + int ret = -RT_EINVAL; + struct rt_lwp *lwp; + struct lwp_sigaction kact, *pkact = RT_NULL; + struct lwp_sigaction koact, *pkoact = RT_NULL; + + if (!sigsetsize) + { + SET_ERRNO(EINVAL); + goto out; + } + if (sigsetsize > sizeof(lwp_sigset_t)) + { + sigsetsize = sizeof(lwp_sigset_t); + } + if (!act && !oact) + { + SET_ERRNO(EINVAL); + goto out; + } + if (oact) + { + if (!lwp_user_accessable((void *)oact, sizeof(*oact))) + { + SET_ERRNO(EFAULT); + goto out; + } + pkoact = &koact; + } + if (act) + { + if (!lwp_user_accessable((void *)act, sizeof(*act))) + { + SET_ERRNO(EFAULT); + goto out; + } + kact.sa_flags = act->flags; + kact.__sa_handler._sa_handler = act->handler; + lwp_memcpy(&kact.sa_mask, &act->mask, sigsetsize); + kact.sa_restorer = act->restorer; + pkact = &kact; + } + + lwp = lwp_self(); + RT_ASSERT(lwp); + ret = lwp_signal_action(lwp, sig, pkact, pkoact); +#ifdef ARCH_MM_MMU + if (ret == 0 && oact) + { + lwp_put_to_user(&oact->handler, &pkoact->__sa_handler._sa_handler, sizeof(void (*)(int))); + lwp_put_to_user(&oact->mask, &pkoact->sa_mask, sigsetsize); + lwp_put_to_user(&oact->flags, &pkoact->sa_flags, sizeof(int)); + lwp_put_to_user(&oact->restorer, &pkoact->sa_restorer, sizeof(void (*)(void))); + } +#endif /* ARCH_MM_MMU */ +out: + return (ret < 0 ? GET_ERRNO() : ret); +} + +static int mask_command_u2k[] = { + [SIG_BLOCK] = LWP_SIG_MASK_CMD_BLOCK, + [SIG_UNBLOCK] = LWP_SIG_MASK_CMD_UNBLOCK, + [SIG_SETMASK] = LWP_SIG_MASK_CMD_SET_MASK, +}; + +/** + * @brief Sets or retrieves the signal mask for the calling process. + * + * This system call allows the caller to block or unblock signals by modifying the signal mask. The signal + * mask determines which signals are blocked and which signals can be delivered to the process. The + * function can also be used to retrieve the current signal mask. + * + * @param[in] how The action to be taken on the signal mask. It can be one of the following values: + * - `SIG_BLOCK`: Add the signals in `sigset` to the current mask. + * - `SIG_UNBLOCK`: Remove the signals in `sigset` from the current mask. + * - `SIG_SETMASK`: Set the signal mask to the value in `sigset`, replacing the current mask. + * @param[in] sigset A pointer to a `sigset_t` structure that specifies the signals to be blocked or unblocked. + * This parameter is ignored when `how` is `SIG_SETMASK`, in which case `sigset` is used + * as the new signal mask. + * @param[out] oset A pointer to a `sigset_t` structure where the previous signal mask will be stored. + * If `oset` is `NULL`, the previous signal mask will not be returned. + * @param[in] size The size of the `sigset_t` structure, ensuring compatibility with the signal mask. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note Modifying the signal mask prevents signals from being delivered to the process while they are blocked. + * Once the signals are unblocked, they will be delivered to the process if they are pending. + * + * @warning Be careful when blocking signals, especially critical ones like `SIGKILL`, as it may interfere + * with the normal operation of the system. Ensure that signal masks are managed carefully to avoid + * missing important signals. + * + * @see sys_sigaction(), sys_sigpending() + */ +sysret_t sys_sigprocmask(int how, const sigset_t *sigset, sigset_t *oset, size_t size) +{ + int ret = -1; + lwp_sigset_t *pnewset = RT_NULL, *poldset = RT_NULL; +#ifdef ARCH_MM_MMU + lwp_sigset_t newset, oldset; +#endif /* ARCH_MM_MMU*/ + + if (!size) + { + return -EINVAL; + } + if (!oset && !sigset) + { + return -EINVAL; + } + if (size > sizeof(lwp_sigset_t)) + { + size = sizeof(lwp_sigset_t); + } + if (oset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = &oldset; +#else + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = (lwp_sigset_t *)oset; +#endif + } + if (sigset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + lwp_get_from_user(&newset, (void *)sigset, size); + pnewset = &newset; +#else + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + pnewset = (lwp_sigset_t *)sigset; +#endif /* ARCH_MM_MMU */ + } + ret = lwp_thread_signal_mask(rt_thread_self(), mask_command_u2k[how], pnewset, poldset); +#ifdef ARCH_MM_MMU + if (ret < 0) + { + return ret; + } + if (oset) + { + lwp_put_to_user(oset, poldset, size); + } +#endif /* ARCH_MM_MMU */ + return (ret < 0 ? -EFAULT : ret); +} + +/** + * @brief Retrieves the set of signals that are pending for delivery to the calling process. + * + * This system call allows a process to query the set of signals that are pending, i.e., signals that have + * been sent to the process but have not yet been delivered because the process is blocking those signals + * or has not yet handled them. The function returns the set of signals that are waiting to be delivered. + * + * @param[out] sigset A pointer to a `sigset_t` structure where the set of pending signals will be stored. + * The `sigset_t` structure will contain the signals that are pending for delivery. + * @param[in] sigsize The size of the `sigset_t` structure, used to ensure compatibility with the signal set. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note The returned signal set will contain the signals that have been sent to the process but are blocked, + * or that are waiting to be handled. These signals will be delivered once they are unblocked or the + * process handles them. + * + * @warning Be cautious when querying pending signals, as unblocking too many signals at once may lead + * to unexpected behavior or a flood of signal deliveries. It's recommended to carefully manage + * the signals the process can accept at any given time. + * + * @see sys_sigaction(), sys_sigprocmask() + */ +sysret_t sys_sigpending(sigset_t *sigset, size_t sigsize) +{ + sysret_t ret = 0; + lwp_sigset_t lwpset; + + /* Verify and Get sigset, timeout */ + if (!sigset || !lwp_user_accessable((void *)sigset, sigsize)) + { + ret = -EFAULT; + } + else + { + /* Fit sigset size to lwp set */ + if (sizeof(lwpset) < sigsize) + { + LOG_I("%s: sigsize (%lx) extends lwp sigset chunk\n", __func__, sigsize); + sigsize = sizeof(lwpset); + } + + lwp_thread_signal_pending(rt_thread_self(), &lwpset); + + if (!lwp_put_to_user(sigset, &lwpset, sigsize)) + { + RT_ASSERT(0); /* should never happened */ + } + } + + return ret; +} + +/** + * @brief Waits for a signal to be delivered, with a timeout. + * + * This system call allows a process to wait for one of the signals specified in the `sigset` to be delivered. + * The process will block until a signal in the set is received or the specified timeout period expires. + * If the signal is received before the timeout, information about the signal will be returned in the `siginfo_t` structure. + * If the timeout expires without any signal being delivered, the function will return with an appropriate error code. + * + * @param[in] sigset A pointer to a `sigset_t` structure that specifies the set of signals to wait for. + * The function will block until a signal in this set is received, or the timeout expires. + * @param[out] info A pointer to a `siginfo_t` structure where information about the delivered signal will be stored. + * If no signal is received before the timeout, this structure will not be filled. + * @param[in] timeout A pointer to a `timespec` structure that specifies the maximum amount of time to wait + * for a signal to be delivered. If this value is `NULL`, the function will wait indefinitely. + * @param[in] sigsize The size of the `sigset_t` structure, ensuring compatibility with the signal set. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note If the `timeout` is `NULL`, the function will block indefinitely until a signal is delivered. + * If a signal is received, the corresponding information is returned in the `siginfo_t` structure. + * The `sigset` should contain only the signals you are interested in. + * + * @warning If the timeout expires, no signal will be delivered, and the function will return a timeout error. + * Make sure to handle the timeout case correctly to prevent any unexpected behavior. + * + * @see sys_sigaction(), sys_sigtimedwait() + */ +sysret_t sys_sigtimedwait(const sigset_t *sigset, siginfo_t *info, const struct timespec *timeout, size_t sigsize) +{ + int sig; + size_t ret; + lwp_sigset_t lwpset; + siginfo_t kinfo; + struct timespec ktimeout; + struct timespec *ptimeout; + + /* for RT_ASSERT */ + RT_UNUSED(ret); + + /* Fit sigset size to lwp set */ + if (sizeof(lwpset) < sigsize) + { + LOG_I("%s: sigsize (%lx) extends lwp sigset chunk\n", __func__, sigsize); + sigsize = sizeof(lwpset); + } + else + { + /* if sigset of user is smaller, clear extra space */ + memset(&lwpset, 0, sizeof(lwpset)); + } + + /* Verify and Get sigset, timeout */ + if (!sigset || !lwp_user_accessable((void *)sigset, sigsize)) + { + return -EFAULT; + } + else + { + ret = lwp_get_from_user(&lwpset, (void *)sigset, sigsize); + RT_ASSERT(ret == sigsize); + } + + if (timeout) + { + if (!lwp_user_accessable((void *)timeout, sizeof(*timeout))) + { + return -EFAULT; + } + else + { + ret = lwp_get_from_user(&ktimeout, (void *)timeout, sizeof(*timeout)); + ptimeout = &ktimeout; + RT_ASSERT(ret == sizeof(*timeout)); + } + } + else + { + ptimeout = RT_NULL; + } + + sig = lwp_thread_signal_timedwait(rt_thread_self(), &lwpset, &kinfo, ptimeout); + + if (sig > 0 && info) + { + if (!lwp_user_accessable((void *)info, sizeof(*info))) + { + return -EFAULT; + } + else + { + ret = lwp_put_to_user(info, &kinfo, sizeof(*info)); + RT_ASSERT(ret == sizeof(*info)); + } + } + + return sig; +} + +/** + * @brief Sends a signal to a specific thread. + * + * This system call allows a process to send a signal to a specific thread within the same process. + * The signal specified by the `sig` parameter will be delivered to the thread with the ID `tid`. + * This function is similar to `kill()`, but it targets a specific thread rather than a process. + * + * @param[in] tid The thread ID to which the signal will be sent. This ID identifies the target thread within the same process. + * @param[in] sig The signal number to be sent to the specified thread. The signal can be any valid signal number. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note The `sig` parameter must be a valid signal number. It can range from `1` to `31`, or be one of the predefined signal constants like `SIGKILL`, `SIGTERM`, etc. + * If the target thread does not exist or is not eligible to receive the signal, the function will fail. + * + * @warning If an invalid signal number is provided or the target thread does not exist, an error will be returned. + * Be careful when sending signals, as some signals (e.g., `SIGKILL`) can immediately terminate the target thread. + * + * @see sys_kill(), sys_sigaction() + */ +sysret_t sys_tkill(int tid, int sig) +{ +#ifdef ARCH_MM_MMU + rt_thread_t thread; + sysret_t ret; + + /** + * Brief: Match a tid and do the kill + * + * Note: Critical Section + * - the thread (READ. may be released at the meantime; protected by locked) + */ + thread = lwp_tid_get_thread_and_inc_ref(tid); + ret = lwp_thread_signal_kill(thread, sig, SI_USER, 0); + lwp_tid_dec_ref(thread); + + return ret; +#else + return lwp_thread_kill((rt_thread_t)tid, sig); +#endif +} + +/** + * @brief Manipulates the signal mask for the current thread. + * + * This function allows a thread to modify its signal mask, which controls which signals are blocked (prevented from delivery). + * The signal mask can be modified by adding, removing, or setting the signal set as a whole, depending on the `how` parameter. + * The `sigset` specifies the signals to be manipulated, and the current signal mask before modification can be retrieved in `oset`. + * + * @param[in] how The operation to perform on the signal mask. It can be one of the following values: + * - `SIG_BLOCK`: Add the signals in `sigset` to the current mask (block those signals). + * - `SIG_UNBLOCK`: Remove the signals in `sigset` from the current mask (unblock those signals). + * - `SIG_SETMASK`: Set the signal mask to the signals specified in `sigset` (replace the current mask). + * @param[in] sigset A pointer to an `lwp_sigset_t` structure that specifies the set of signals to be manipulated. + * Signals in this set will be added, removed, or set in the current thread's signal mask based on the `how` parameter. + * @param[out] oset A pointer to an `lwp_sigset_t` structure where the previous signal mask will be stored. + * This allows the caller to restore the previous mask if needed. + * @param[in] size The size of the `lwp_sigset_t` structure. This ensures that the correct structure size is used during signal mask manipulation. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note The `sigset` structure should contain only valid signal numbers. If `how` is `SIG_SETMASK`, the entire signal mask will be replaced by the contents of `sigset`. + * If `how` is `SIG_BLOCK` or `SIG_UNBLOCK`, the signals in `sigset` will be added or removed from the current mask, respectively. + * + * @warning Be cautious when modifying the signal mask, as blocking signals can cause the thread to miss important signals. + * If a signal is blocked, it will not be delivered to the thread until it is unblocked, or the thread is explicitly made to handle it. + * + * @see sys_sigaction(), sys_thread_sigpending() + */ +sysret_t sys_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset, size_t size) +{ + int ret = -1; + lwp_sigset_t *pnewset = RT_NULL, *poldset = RT_NULL; +#ifdef ARCH_MM_MMU + lwp_sigset_t newset, oldset; +#endif /* ARCH_MM_MMU */ + + if (!size) + { + return -EINVAL; + } + if (!oset && !sigset) + { + return -EINVAL; + } + if (size != sizeof(lwp_sigset_t)) + { + return -EINVAL; + } + if (oset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = &oldset; +#else + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = oset; +#endif + } + if (sigset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + lwp_get_from_user(&newset, (void *)sigset, sizeof(lwp_sigset_t)); + pnewset = &newset; +#else + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + pnewset = (lwp_sigset_t *)sigset; +#endif + } + ret = lwp_thread_signal_mask(rt_thread_self(), mask_command_u2k[how], pnewset, poldset); + if (ret < 0) + { + return ret; + } +#ifdef ARCH_MM_MMU + if (oset) + { + lwp_put_to_user(oset, poldset, sizeof(lwp_sigset_t)); + } +#endif + return (ret < 0 ? -EFAULT : ret); +} + +#ifndef ARCH_MM_MMU + +/** + * @brief Sets the signal handler for a specific signal in the current thread. + * + * This function allows a thread to set a custom signal handler for a specific signal. + * When the specified signal is received, the corresponding handler function (`func`) will be invoked. + * The handler will be executed in the context of the current thread, allowing it to handle signals as needed. + * + * @param[in] sig The signal number for which the handler is being set. This should be a valid signal number. + * @param[in] func The signal handler function to be set. This function will be called when the specified signal is received. + * The signature of `func` should match the expected format for signal handlers. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note The `func` parameter must be a valid function pointer. It will be called when the signal specified by `sig` is received by the thread. + * The handler function typically has the signature: `void handler(int sig)`, where `sig` is the signal number. + * + * @warning Setting a custom signal handler may override any default actions for that signal. + * It is important to ensure that the handler function is properly designed to handle the signal appropriately. + * For example, some signals may require special handling (e.g., `SIGKILL` cannot be caught). + * + * @see sys_signal(), sys_lwp_sigaction() + */ +sysret_t sys_lwp_sighandler_set(int sig, lwp_sighandler_t func) +{ + if (!lwp_user_accessable((void *)func, sizeof(lwp_sighandler_t))) + { + return -EFAULT; + } + + lwp_sighandler_set(sig, func); + return 0; +} + +/** + * @brief Sets the signal handler for a specific signal in the target thread. + * + * This function allows setting a custom signal handler for a specific signal within a particular thread. + * When the specified signal is delivered to the thread, the corresponding handler function (`func`) will be invoked. + * The handler is executed in the context of the thread that receives the signal. + * + * @param[in] sig The signal number for which the handler is being set. This should be a valid signal number. + * @param[in] func The signal handler function to be set. This function will be called when the specified signal is delivered to the target thread. + * The handler function should have the signature `void func(int sig)`. + * + * @return sysret_t Returns `0` on success or a negative error code on failure. + * + * @note The `func` parameter must be a valid function pointer, and it will be invoked when the signal specified by `sig` is delivered to the target thread. + * The handler function typically has the signature: `void handler(int sig)`, where `sig` is the signal number. + * + * @warning Setting a custom signal handler may override any default actions for that signal. + * It is important to ensure that the handler function is properly designed to handle the signal appropriately. + * For example, some signals may require special handling (e.g., `SIGKILL` cannot be caught). + * + * @see sys_thread_sigprocmask(), sys_thread_sigaction(), sys_signal() + */ +sysret_t sys_thread_sighandler_set(int sig, lwp_sighandler_t func) +{ + if (!lwp_user_accessable((void *)func, sizeof(lwp_sighandler_t))) + { + return -EFAULT; + } + + lwp_thread_sighandler_set(sig, func); + return 0; +} +#endif /* not defined ARCH_MM_MMU */ + +/** + * @brief Waits for the termination of a child process. + * + * This function makes the calling process wait until one of its child processes terminates or until a + * specified condition is met. The function retrieves information about the terminated child process, + * including its exit status, and can also handle other process-related events, such as stopping or + * continuing execution. + * + * @param[in] pid The process ID of the child process to wait for. If `pid` is: + * - `-1`: Wait for any child process. + * - `0`: Wait for any child process in the same process group. + * - > 0: Wait for the child process with the specified process ID. + * @param[out] status A pointer to an integer where the exit status of the terminated child process will be stored. + * This value provides information about the child's termination status, such as normal exit, + * signal termination, etc. + * @param[in] options A bitmask of options that control the behavior of the function. It can include: + * - `WNOHANG`: Return immediately if no child has exited. + * - `WUNTRACED`: Report status of stopped child processes. + * - Other flags can be defined depending on the implementation. + * + * @return sysret_t Returns the process ID of the child process that terminated on success, or a negative error code on failure. + * + * @note The `status` argument provides detailed information about the termination of the child process. To interpret this + * status, macros such as `WIFEXITED()`, `WIFSIGNALED()`, and `WEXITSTATUS()` are commonly used. + * + * @warning This function should be used carefully when managing child processes, as not properly handling child processes + * may lead to zombie processes. + * + * @see sys_wait(), sys_waitid() + */ +sysret_t sys_waitpid(int32_t pid, int *status, int options) +{ + int ret = -1; +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)status, sizeof(int))) + { + return -EFAULT; + } + else + { + ret = lwp_waitpid(pid, status, options, RT_NULL); + } +#else + if (!lwp_user_accessable((void *)status, sizeof(int))) + { + return -EFAULT; + } + ret = waitpid(pid, status, options); +#endif + return ret; +} diff --git a/components/lwp/syscall/syscall_socket.c b/components/lwp/syscall/syscall_socket.c new file mode 100644 index 000000000000..ff4ec5b71733 --- /dev/null +++ b/components/lwp/syscall/syscall_socket.c @@ -0,0 +1,1544 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +#ifdef RT_USING_SAL +/* network interfaces */ + +union lwp_sockaddr_buffer +{ + struct sockaddr address; + struct sockaddr_un unix_address; +}; + +static int lwp_sockaddr_from_user(union lwp_sockaddr_buffer *kernel_address, + const struct musl_sockaddr *user_address, + socklen_t length) +{ + uint16_t family; + struct musl_sockaddr musl_address; + + if (user_address == RT_NULL || length < sizeof(family) || + length > sizeof(*kernel_address)) + { + return -EINVAL; + } + if (!lwp_user_accessable((void *)user_address, length)) + { + return -EFAULT; + } + + rt_memset(kernel_address, 0, sizeof(*kernel_address)); + lwp_get_from_user(&family, (void *)user_address, sizeof(family)); + if (family == AF_UNIX || family == AF_NETLINK) + { + lwp_get_from_user(kernel_address, (void *)user_address, length); + } + else + { + if (length > sizeof(musl_address)) + { + return -EINVAL; + } + rt_memset(&musl_address, 0, sizeof(musl_address)); + lwp_get_from_user(&musl_address, (void *)user_address, length); + sockaddr_tolwip(&musl_address, &kernel_address->address); + } + return 0; +} + +static socklen_t lwp_sockaddr_to_user( + struct musl_sockaddr *user_address, socklen_t user_length, + const union lwp_sockaddr_buffer *kernel_address, + socklen_t kernel_length) +{ + socklen_t actual_length; + socklen_t copy_length; + struct musl_sockaddr musl_address; + + if (kernel_address->unix_address.sa_family == AF_UNIX) + { + actual_length = kernel_length; + copy_length = user_length < actual_length ? user_length : actual_length; + lwp_put_to_user(user_address, (void *)kernel_address, copy_length); + } + else + { + rt_memset(&musl_address, 0, sizeof(musl_address)); + sockaddr_tomusl(&kernel_address->address, &musl_address); + actual_length = sizeof(musl_address); + copy_length = user_length < actual_length ? user_length : actual_length; + lwp_put_to_user(user_address, &musl_address, copy_length); + } + return actual_length; +} + +/** + * @brief Accepts a connection on a socket. + * + * This system call is used to accept a pending connection request on a socket. It extracts the + * first connection request from the incoming queue, creates a new socket for the connection, + * and stores the address information of the remote peer in the provided address structure. + * The original socket must be a listening socket that has been previously bound and set to listen + * for incoming connections. + * + * @param[in] socket The listening socket file descriptor that is waiting for incoming connections. + * This socket must be in a listening state, created using `sys_socket` and + * bound to an address using `sys_bind`. + * @param[out] addr A pointer to a `musl_sockaddr` structure where the address of the connecting + * client will be stored. The structure is populated with information such as + * the client's IP address and port number. + * @param[in,out] addrlen A pointer to a socklen_t variable that specifies the size of the `addr` + * structure on input. On output, it will contain the actual size of the + * address returned in `addr`. + * + * @return sysret_t Returns a socket descriptor for the new connection on success, or + * `SYSRET_ERROR` on failure: + * - On success: A new socket descriptor that can be used for further + * communication with the connected peer. + * - On failure: A negative error code indicating the reason for the failure. + * + * @note The socket passed as `socket` must be in a listening state and ready to accept incoming + * connections. The `addr` structure is populated with the client's address details, which can + * be used for further operations, such as identifying the client. + * + * @warning Ensure that the `addr` structure is sufficiently large to hold the address information. + * The `addrlen` parameter must be set to the size of the `musl_sockaddr` structure before + * calling this function. Calling the function with an incorrectly sized `addrlen` may lead + * to buffer overflows or undefined behavior. + */ +sysret_t sys_accept(int socket, struct musl_sockaddr *addr, socklen_t *addrlen) +{ + int ret = -1; + union lwp_sockaddr_buffer kernel_address; + socklen_t uaddrlen; + socklen_t kaddrlen; + + if (addr) + { + if (!lwp_user_accessable(addrlen, sizeof(socklen_t))) + { + return -EFAULT; + } + lwp_get_from_user(&uaddrlen, addrlen, sizeof(socklen_t)); + if (!uaddrlen) + { + return -EINVAL; + } + + if (!lwp_user_accessable(addr, uaddrlen)) + { + return -EFAULT; + } + } + + rt_memset(&kernel_address, 0, sizeof(kernel_address)); + kaddrlen = sizeof(kernel_address); + ret = accept(socket, &kernel_address.address, &kaddrlen); + if (ret >= 0) + { + if (addr) + { + kaddrlen = lwp_sockaddr_to_user(addr, uaddrlen, + &kernel_address, kaddrlen); + lwp_put_to_user(addrlen, &kaddrlen, sizeof(socklen_t)); + } + } + return ret; +} + +/** + * @brief Binds a socket to a local address. + * + * This system call binds a socket to a specific local address and port. The socket must be created + * using `sys_socket` before it can be bound. The `bind` operation allows the application to associate + * a socket with a specific address, such as an IP address and port number, which can then be used + * for sending or receiving data. + * + * @param[in] socket The socket descriptor to which the address will be bound. This socket must + * be created using `sys_socket` and should not already be bound to another address. + * @param[in] name A pointer to a `musl_sockaddr` structure that contains the address to which + * the socket will be bound. This can represent an IP address and port number. + * @param[in] namelen The size of the `musl_sockaddr` structure in bytes. This value should be set + * to the actual size of the `name` structure before calling this function. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note The `socket` must be created before calling this function, and the address provided in `name` + * should be a valid local address for the socket. This operation is typically used for server-side + * sockets that listen for incoming connections or for any socket that needs to specify its local + * address before communication. + * + * @warning If the specified address is already in use by another socket or if the socket type is incompatible + * with the address, the function will return an error. Ensure that the address is not in use and is + * valid for the socket type. + */ +sysret_t sys_bind(int socket, const struct musl_sockaddr *name, socklen_t namelen) +{ + int ret; + union lwp_sockaddr_buffer kernel_address; + + ret = lwp_sockaddr_from_user(&kernel_address, name, namelen); + if (ret < 0) + { + return ret; + } + ret = bind(socket, &kernel_address.address, namelen); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Shuts down part of a full-duplex connection. + * + * This system call disables further send or receive operations on a socket. The `how` parameter + * specifies which part of the connection should be shut down. This operation is typically used + * when the application no longer needs to send or receive data, but wants to keep the connection open + * for other purposes (e.g., receiving shutdown acknowledgment). + * + * @param[in] socket The socket descriptor to be shut down. This socket must be an open socket that + * has been created using `sys_socket`. + * @param[in] how The shutdown behavior: + * - `SHUT_RD`: Disables further receives on the socket. The application can no longer + * receive data from this socket. + * - `SHUT_WR`: Disables further sends on the socket. The application can no longer + * send data through this socket. + * - `SHUT_RDWR`: Disables both sends and receives on the socket. The socket is + * fully shut down for communication. + * + * @return sysret_t Returns `0` on success, or a negative error code (e.g., `SYSRET_ERROR`) on failure. + * + * @note The `socket` should be open and in a connected state when calling this function. The `how` + * parameter determines which part of the connection is to be shut down, allowing for finer control + * over communication termination. + * + * @warning Once a socket is shut down with `SHUT_RD`, no further data can be received on it. Similarly, + * after `SHUT_WR`, no more data can be sent. Calling `sys_shutdown` with incompatible `how` values + * or on an invalid socket may result in errors. + */ +sysret_t sys_shutdown(int socket, int how) +{ + return shutdown(socket, how); +} + +/** + * @brief Retrieves the address of the peer connected to a socket. + * + * This system call retrieves the address of the peer (remote endpoint) that is connected to the specified + * socket. The socket must be connected (i.e., for stream-oriented protocols such as TCP). The address + * of the peer is stored in the `name` structure, and the size of the structure is updated in `namelen`. + * + * @param[in] socket The socket descriptor of the connected socket. This socket must be in a connected + * state (e.g., after a successful `sys_connect` or `sys_accept` call). + * @param[out] name A pointer to a `musl_sockaddr` structure where the address of the peer will be + * stored. This structure will be populated with the peer's IP address and port number. + * @param[in,out] namelen A pointer to a `socklen_t` variable that specifies the size of the `name` structure + * on input. On output, it will contain the actual size of the address returned in `name`. + * + * @return sysret_t Returns `SYSRET_OK` on success, or a negative error code on failure. + * + * @note The `socket` must be in a connected state when calling this function. The `name` structure will + * contain the peer's address information, which can be used for logging, debugging, or further + * communication. + * + * @warning Ensure that the `name` structure is sufficiently large to hold the peer's address information. + * The `namelen` parameter must be set to the size of the `name` structure before calling this function. + * Failing to provide a correctly sized `namelen` could result in buffer overflows or undefined behavior. + */ +sysret_t sys_getpeername(int socket, struct musl_sockaddr *name, socklen_t *namelen) +{ + int ret = -1; + union lwp_sockaddr_buffer kernel_address; + socklen_t unamelen; + socklen_t knamelen; + + if (!lwp_user_accessable(namelen, sizeof(socklen_t))) + { + return -EFAULT; + } + lwp_get_from_user(&unamelen, namelen, sizeof(socklen_t)); + if (!unamelen) + { + return -EINVAL; + } + + if (!lwp_user_accessable(name, unamelen)) + { + return -EFAULT; + } + + rt_memset(&kernel_address, 0, sizeof(kernel_address)); + knamelen = sizeof(kernel_address); + ret = getpeername(socket, &kernel_address.address, &knamelen); + + if (ret == 0) + { + knamelen = lwp_sockaddr_to_user(name, unamelen, + &kernel_address, knamelen); + lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t)); + } + else + { + ret = GET_ERRNO(); + } + + return ret; +} + +/** + * @brief Retrieves the local address of the socket. + * + * This system call retrieves the local address (local endpoint) that is bound to the specified socket. + * The socket must be created and, if necessary, bound to a local address using `sys_bind`. The address + * of the local endpoint is stored in the `name` structure, and the size of the structure is updated in `namelen`. + * + * @param[in] socket The socket descriptor of the socket. This socket must be created and, if required, + * bound to a local address. + * @param[out] name A pointer to a `musl_sockaddr` structure where the local address of the socket will + * be stored. This structure will be populated with the local IP address and port number. + * @param[in,out] namelen A pointer to a `socklen_t` variable that specifies the size of the `name` structure + * on input. On output, it will contain the actual size of the address returned in `name`. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note The `socket` must be created and, if needed, bound to a local address using `sys_bind`. + * The `name` structure will contain the local address of the socket, which can be used for logging, + * debugging, or further communication. + * + * @warning Ensure that the `name` structure is sufficiently large to hold the local address information. + * The `namelen` parameter must be set to the size of the `name` structure before calling this function. + * Failing to provide a correctly sized `namelen` could result in buffer overflows or undefined behavior. + */ +sysret_t sys_getsockname(int socket, struct musl_sockaddr *name, socklen_t *namelen) +{ + int ret = -1; + union lwp_sockaddr_buffer kernel_address; + socklen_t unamelen; + socklen_t knamelen; + + if (!lwp_user_accessable(namelen, sizeof(socklen_t))) + { + return -EFAULT; + } + lwp_get_from_user(&unamelen, namelen, sizeof(socklen_t)); + if (!unamelen) + { + return -EINVAL; + } + + if (!lwp_user_accessable(name, unamelen)) + { + return -EFAULT; + } + + rt_memset(&kernel_address, 0, sizeof(kernel_address)); + knamelen = sizeof(kernel_address); + ret = getsockname(socket, &kernel_address.address, &knamelen); + if (ret == 0) + { + knamelen = lwp_sockaddr_to_user(name, unamelen, + &kernel_address, knamelen); + lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t)); + } + else + { + ret = GET_ERRNO(); + } + return ret; +} + +/** + * @brief Retrieves the value of a socket option. + * + * This system call retrieves the current value of a socket option for the specified socket. The socket + * options allow fine-grained control over various aspects of socket behavior, such as timeouts, buffering, + * and connection settings. The option value is stored in the `optval` buffer, and the size of the buffer + * is specified by the `optlen` parameter. + * + * @param[in] socket The socket descriptor for which the option value is being retrieved. The socket + * must be valid and open. + * @param[in] level The level at which the option is defined. Typically, this is `SOL_SOCKET` for general + * socket options, or a protocol-specific level (e.g., `IPPROTO_TCP` for TCP options). + * @param[in] optname The option name. This specifies which socket option to retrieve (e.g., `SO_RCVBUF` + * for receive buffer size or `SO_RCVBUF` for send buffer size). + * @param[out] optval A pointer to a buffer where the option value will be stored. The buffer's type depends + * on the option being retrieved. + * @param[in,out] optlen A pointer to a `socklen_t` variable that specifies the size of the `optval` buffer on + * input. On output, it will contain the actual size of the option value retrieved. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note The `socket` must be valid and open when calling this function. The `level` and `optname` parameters + * define the specific option to be retrieved. The `optval` buffer will contain the option value after + * the function call, and `optlen` will be updated to reflect the size of the retrieved value. + * + * @warning Ensure that the `optval` buffer is large enough to hold the value for the specified option. + * The `optlen` parameter must be set to the correct size of the buffer before calling this function. + * Failing to provide a correctly sized buffer could result in undefined behavior or buffer overflows. + */ +sysret_t sys_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen) +{ + int ret = 0; + socklen_t koptlen = 0; + void *koptval = RT_NULL; + + if (!lwp_user_accessable((void *)optlen, sizeof(uint32_t))) + { + return -EFAULT; + } + + if (lwp_get_from_user(&koptlen, optlen, sizeof(uint32_t)) != sizeof(uint32_t)) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)optval, koptlen)) + { + return -EFAULT; + } + + koptval = kmem_get(koptlen); + if (koptval == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(koptval, optval, koptlen) != koptlen) + { + kmem_put(koptval); + return -EINVAL; + } + + convert_sockopt(&level, &optname); + ret = getsockopt(socket, level, optname, koptval, &koptlen); + + lwp_put_to_user((void *)optval, koptval, koptlen); + lwp_put_to_user((void *)optlen, &koptlen, sizeof(uint32_t)); + + kmem_put(koptval); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Sets the value of a socket option. + * + * This system call sets a socket option for the specified socket. Socket options control various aspects + * of socket behavior, such as timeouts, buffer sizes, or connection parameters. The option value is + * provided in the `optval` buffer, and its length is specified by the `optlen` parameter. + * + * @param[in] socket The socket descriptor for which the option is being set. The socket must be valid and open. + * @param[in] level The level at which the option is defined. This can be `SOL_SOCKET` for general socket options, + * or a protocol-specific level (e.g., `IPPROTO_TCP` for TCP options). + * @param[in] optname The option name. This specifies which socket option to set (e.g., `SO_RCVBUF` for receive buffer size). + * @param[in] optval A pointer to the buffer that contains the option value to set. The format of this buffer depends + * on the option being set. + * @param[in] optlen The size of the `optval` buffer. This specifies the length of the data to be used when setting + * the option value. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure: + * + * @note The `socket` must be valid and open. The `level` and `optname` parameters define the specific option to be set, + * and the `optval` buffer should contain the appropriate value for that option. The `optlen` parameter must + * match the size of the `optval` buffer. + * + * @warning Ensure that the `optval` buffer contains valid data for the specified option. The `optlen` parameter must be + * set to the correct size of the `optval` buffer before calling this function. Failing to provide a correctly + * sized buffer could result in undefined behavior or errors. + */ +sysret_t sys_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen) +{ + int ret; + void *koptval = RT_NULL; + + if (!lwp_user_accessable((void *)optval, optlen)) + { + return -EFAULT; + } + + koptval = kmem_get(optlen); + if (koptval == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(koptval, (void *)optval, optlen) != optlen) + { + kmem_put(koptval); + return -EINVAL; + } + + convert_sockopt(&level, &optname); + ret = setsockopt(socket, level, optname, koptval, optlen); + + kmem_put(koptval); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Establishes a connection to a remote socket. + * + * This system call attempts to establish a connection to a remote socket specified by the `name` parameter. + * It is typically used for client-side socket operations to connect to a server. The connection is established + * by using the specified socket descriptor and the address of the remote server. + * + * @param[in] socket The socket descriptor for the connection attempt. The socket must be valid and in a + * `SOCK_STREAM` or `SOCK_DGRAM` state, depending on the protocol. + * @param[in] name A pointer to a `sockaddr` structure containing the address of the remote host. This address + * must include the correct IP address and port number for the connection. + * @param[in] namelen The size of the `sockaddr` structure pointed to by `name`. This should match the size + * of the address structure (e.g., `sizeof(struct sockaddr_in)` for IPv4). + * + * @return sysret_t Returns `0` on success, or a negative error code on failure: + * + * @note This function may block if the connection is being established, depending on the socket's configuration. + * For non-blocking sockets, it will return immediately, even if the connection has not been established. + * + * @warning The `socket` must be of a valid type (e.g., `SOCK_STREAM` for TCP or `SOCK_DGRAM` for UDP). + * The `name` parameter must point to a valid `sockaddr` structure that contains a correctly formatted address. + */ +sysret_t sys_connect(int socket, const struct musl_sockaddr *name, socklen_t namelen) +{ + int ret; + union lwp_sockaddr_buffer kernel_address; + + ret = lwp_sockaddr_from_user(&kernel_address, name, namelen); + if (ret < 0) + { + return ret; + } + ret = connect(socket, &kernel_address.address, namelen); + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Prepares a socket to accept incoming connection requests. + * + * This system call marks a socket as a passive socket, indicating that it will be used to accept + * incoming connection requests. It is typically used on server-side sockets after binding a + * local address and port using `sys_bind()`. + * + * @param[in] socket The socket descriptor to be set up for listening. The socket must be valid + * and of type `SOCK_STREAM` (for TCP connections). + * @param[in] backlog The maximum number of pending connections that can be queued. This value + * is system-dependent and may be limited to a lower maximum. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note Before calling this function, the socket must be bound to a local address and port using + * `sys_bind()`. Only sockets of type `SOCK_STREAM` can be used for listening. The actual + * maximum queue length may be less than the specified `backlog` value, depending on system limits. + * + * @warning Ensure that the socket is properly initialized, bound, and of the correct type before calling this function. + * Failure to do so will result in undefined behavior or errors. + */ +sysret_t sys_listen(int socket, int backlog) +{ + return listen(socket, backlog); +} + +#define MUSLC_MSG_OOB 0x0001 +#define MUSLC_MSG_PEEK 0x0002 +#define MUSLC_MSG_CTRUNC 0x0008 +#define MUSLC_MSG_DONTWAIT 0x0040 +#define MUSLC_MSG_WAITALL 0x0100 +#define MUSLC_MSG_MORE 0x8000 + +static int netflags_muslc_2_lwip(int flags) +{ + int flgs = 0; + + if (flags & MUSLC_MSG_PEEK) + { + flgs |= MSG_PEEK; + } + if (flags & MUSLC_MSG_WAITALL) + { + flgs |= MSG_WAITALL; + } + if (flags & MUSLC_MSG_OOB) + { + flgs |= MSG_OOB; + } + if (flags & MUSLC_MSG_DONTWAIT) + { + flgs |= MSG_DONTWAIT; + } + if (flags & MUSLC_MSG_MORE) + { + flgs |= MSG_MORE; + } + if (flags & MSG_ERRQUEUE) + { + flgs |= MSG_ERRQUEUE; + } + + return flgs; +} + +static int netflags_lwip_2_muslc(int flags) +{ + int flgs = 0; + + if (flags & MSG_CTRUNC) + { + flgs |= MUSLC_MSG_CTRUNC; + } + return flgs; +} + +static void cmsg_level_muslc_2_lwip(struct msghdr *message) +{ + struct cmsghdr *cmsg; + + for_each_cmsghdr(cmsg, message) + { + if (!CMSG_OK(message, cmsg)) + { + break; + } + if (cmsg->cmsg_level == INTF_SOL_SOCKET) + { + cmsg->cmsg_level = IMPL_SOL_SOCKET; + } + } +} + +static void cmsg_level_lwip_2_muslc(struct msghdr *message) +{ + struct cmsghdr *cmsg; + + for_each_cmsghdr(cmsg, message) + { + if (!CMSG_OK(message, cmsg)) + { + break; + } + if (cmsg->cmsg_level == IMPL_SOL_SOCKET) + { + cmsg->cmsg_level = INTF_SOL_SOCKET; + } + } +} + +#ifdef ARCH_MM_MMU +static int copy_msghdr_from_user(struct msghdr *kmsg, + struct musl_msghdr *umsg, + struct iovec **out_iov, void **out_msg_control, + void **out_msg_name, void **out_buffer, + socklen_t *out_user_name_length, + rt_bool_t receive) +{ + int index; + size_t iovs_size; + struct musl_msghdr user_message; + struct iovec *uiov, *kiov; + size_t iovs_buffer_size = 0; + char *iovs_buffer; + char *buffer_cursor; + + if (!lwp_user_accessable(umsg, sizeof(user_message))) + { + return -EFAULT; + } + + lwp_get_from_user(&user_message, umsg, sizeof(user_message)); + kmsg->msg_name = user_message.msg_name; + kmsg->msg_namelen = user_message.msg_namelen; + kmsg->msg_iov = user_message.msg_iov; + kmsg->msg_iovlen = user_message.msg_iovlen; + kmsg->msg_control = user_message.msg_control; + kmsg->msg_controllen = user_message.msg_controllen; + kmsg->msg_flags = user_message.msg_flags; + + if (out_user_name_length != RT_NULL) + { + *out_user_name_length = kmsg->msg_namelen; + } + + if (kmsg->msg_iovlen < 0 || + (size_t)kmsg->msg_iovlen > SIZE_MAX / sizeof(*kmsg->msg_iov) || + (!receive && + kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer))) + { + return -EINVAL; + } + if (receive) + { + if (kmsg->msg_name == RT_NULL) + { + kmsg->msg_namelen = 0; + } + else if (kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer)) + { + kmsg->msg_namelen = sizeof(union lwp_sockaddr_buffer); + } + if (kmsg->msg_control == RT_NULL) + { + kmsg->msg_controllen = 0; + } + } + if (kmsg->msg_name != RT_NULL && + !lwp_user_accessable(kmsg->msg_name, kmsg->msg_namelen)) + { + return -EFAULT; + } + if (kmsg->msg_control != RT_NULL && + !lwp_user_accessable(kmsg->msg_control, kmsg->msg_controllen)) + { + return -EFAULT; + } + if (kmsg->msg_control == RT_NULL) + { + if (kmsg->msg_controllen != 0) + { + return -EFAULT; + } + } + if (kmsg->msg_name == RT_NULL) + { + kmsg->msg_namelen = 0; + } + + iovs_size = sizeof(*kmsg->msg_iov) * kmsg->msg_iovlen; + if (!lwp_user_accessable(kmsg->msg_iov, iovs_size)) + { + return -EFAULT; + } + + /* user and kernel */ + kiov = kmem_get(iovs_size * 2); + if (!kiov) + { + return -ENOMEM; + } + + uiov = (void *)kiov + iovs_size; + lwp_get_from_user(uiov, kmsg->msg_iov, iovs_size); + + if (out_iov) + { + *out_iov = uiov; + } + kmsg->msg_iov = kiov; + + for (index = 0; index < kmsg->msg_iovlen; ++index) + { + /* + * We MUST check we can copy data to user after socket done in uiov + * otherwise we will be lost the messages from the network! + */ + if (!lwp_user_accessable(uiov->iov_base, uiov->iov_len)) + { + kmem_put(kmsg->msg_iov); + + return -EPERM; + } + + if (SIZE_MAX - iovs_buffer_size < uiov->iov_len) + { + kmem_put(kmsg->msg_iov); + return -EINVAL; + } + iovs_buffer_size += uiov->iov_len; + kiov->iov_len = uiov->iov_len; + + ++kiov; + ++uiov; + } + + /* msg_iov and msg_control */ + if (SIZE_MAX - iovs_buffer_size < kmsg->msg_controllen || + SIZE_MAX - iovs_buffer_size - kmsg->msg_controllen < + kmsg->msg_namelen) + { + kmem_put(kmsg->msg_iov); + return -EINVAL; + } + iovs_buffer = kmem_get(iovs_buffer_size + kmsg->msg_controllen + + kmsg->msg_namelen); + + if (!iovs_buffer) + { + kmem_put(kmsg->msg_iov); + + return -ENOMEM; + } + + *out_buffer = iovs_buffer; + buffer_cursor = iovs_buffer; + kiov = kmsg->msg_iov; + + for (index = 0; index < kmsg->msg_iovlen; ++index) + { + kiov->iov_base = buffer_cursor; + buffer_cursor += kiov->iov_len; + ++kiov; + } + + *out_msg_control = kmsg->msg_control; + if (kmsg->msg_control != RT_NULL) + { + kmsg->msg_control = buffer_cursor; + buffer_cursor += kmsg->msg_controllen; + } + *out_msg_name = kmsg->msg_name; + if (kmsg->msg_name != RT_NULL && kmsg->msg_namelen != 0) + { + kmsg->msg_name = buffer_cursor; + } + else + { + kmsg->msg_name = RT_NULL; + kmsg->msg_namelen = 0; + } + + return 0; +} +#endif /* ARCH_MM_MMU */ + +/** + * @brief Receives a message from a socket using a message header structure. + * + * This system call is used to receive data and associated metadata from a socket. + * It supports both stream-oriented and message-oriented sockets and allows control + * over the behavior of the receiving operation via the `flags` parameter. + * + * @param[in] socket The socket descriptor from which the message will be received. + * The socket must be in a valid and connected state for stream-oriented + * sockets or bound for message-oriented sockets. + * @param[out] msg A pointer to an `msghdr` structure that specifies message buffers and + * will receive the incoming data. The structure also stores ancillary + * data for advanced socket operations. + * @param[in] flags Modifiers that control the behavior of the receive operation. Common + * flags include: + * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue. + * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning. + * - `MSG_DONTWAIT`: Perform a non-blocking receive operation. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note The `msghdr` structure should be initialized properly, including setting up buffers for + * receiving the data. The function can return fewer bytes than expected depending on socket + * type and flags. For stream-oriented sockets, partial data may be received. + * + * @warning The `socket` must be valid and in an appropriate state. If the socket is non-blocking + * and no data is available, the function will return immediately. + * + * @see sys_sendmsg() + */ +sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags) +{ + int flgs, ret = -1; + int error = 0; + struct msghdr kmsg; +#ifdef ARCH_MM_MMU + int index; + size_t remaining; + socklen_t user_name_length; + void *buffer; + void *msg_control; + void *msg_name; + struct iovec *uiov, *kiov; +#endif + + if (!msg) + { + return -EPERM; + } + + flgs = netflags_muslc_2_lwip(flags); + +#ifdef ARCH_MM_MMU + ret = copy_msghdr_from_user(&kmsg, msg, &uiov, &msg_control, + &msg_name, &buffer, &user_name_length, + RT_TRUE); + + if (ret < 0) + { + return ret; + } + ret = recvmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); + } + + if (ret < 0) + { + goto _free_res; + } + + kiov = kmsg.msg_iov; + remaining = (size_t)ret; + + for (index = 0; index < kmsg.msg_iovlen && remaining != 0; ++index) + { + size_t copy_length = kiov->iov_len; + + if (copy_length > remaining) + { + copy_length = remaining; + } + lwp_put_to_user(uiov->iov_base, kiov->iov_base, copy_length); + remaining -= copy_length; + + ++kiov; + ++uiov; + } + + if (msg_control != RT_NULL && kmsg.msg_controllen != 0) + { + cmsg_level_lwip_2_muslc(&kmsg); + lwp_put_to_user(msg_control, kmsg.msg_control, + kmsg.msg_controllen); + } + if (msg_name != RT_NULL && kmsg.msg_name != RT_NULL) + { + socklen_t name_length; + + name_length = lwp_sockaddr_to_user( + (struct musl_sockaddr *)msg_name, user_name_length, + (const union lwp_sockaddr_buffer *)kmsg.msg_name, + kmsg.msg_namelen); + lwp_put_to_user(&msg->msg_namelen, &name_length, + sizeof(name_length)); + } + kmsg.msg_flags = netflags_lwip_2_muslc(kmsg.msg_flags); + lwp_put_to_user(&msg->msg_flags, &kmsg.msg_flags, sizeof(kmsg.msg_flags)); + lwp_put_to_user(&msg->msg_controllen, &kmsg.msg_controllen, + sizeof(kmsg.msg_controllen)); + +_free_res: + kmem_put(buffer); + kmem_put(kmsg.msg_iov); +#else + kmsg.msg_name = msg->msg_name; + kmsg.msg_namelen = msg->msg_namelen; + kmsg.msg_iov = msg->msg_iov; + kmsg.msg_iovlen = msg->msg_iovlen; + kmsg.msg_control = msg->msg_control; + kmsg.msg_controllen = msg->msg_controllen; + kmsg.msg_flags = msg->msg_flags; + + ret = recvmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); + } + + if (!ret) + { + msg->msg_flags = kmsg.msg_flags; + } +#endif /* ARCH_MM_MMU */ + + if (ret >= 0) + { + return ret; + } + return error != 0 ? error : ret; +} + +/** + * @brief Receives data from a socket, optionally capturing the source address. + * + * This system call is used to receive data from a socket. For connectionless sockets, it also + * retrieves the address of the sender. It supports both blocking and non-blocking operations + * depending on the socket configuration and the specified flags. + * + * @param[in] socket The socket descriptor from which the data will be received. The socket + * must be valid and appropriately configured (e.g., connected or bound). + * @param[out] mem A pointer to the buffer where the received data will be stored. + * @param[in] len The maximum number of bytes to receive. This defines the size of the `mem` buffer. + * @param[in] flags Modifiers that control the behavior of the receive operation. Common + * flags include: + * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue. + * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning. + * - `MSG_DONTWAIT`: Perform a non-blocking receive operation. + * @param[out] from A pointer to a `musl_sockaddr` structure that will hold the address of + * the sender. This parameter can be `NULL` if the sender's address is not needed. + * @param[in,out] fromlen A pointer to a `socklen_t` variable indicating the size of the `from` buffer. + * On return, it will be updated with the actual size of the sender's address. + * This parameter is ignored if `from` is `NULL`. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure: + * + * @note For stream-oriented sockets, this function behaves like `sys_recv()`, and the `from` and + * `fromlen` parameters are ignored. For datagram-oriented sockets, the function fills `from` + * with the address of the sender. + * + * @warning The `socket` must be valid and configured for receiving data. If the `mem` buffer is + * smaller than the received data, excess data may be discarded. In non-blocking mode, + * if no data is available, the function returns immediately. + * + * @see sys_sendto(), sys_recv() + */ +sysret_t sys_recvfrom(int socket, void *mem, size_t len, int flags, + struct musl_sockaddr *from, socklen_t *fromlen) +{ + int flgs = 0; + int ret = -1; + socklen_t user_from_length = 0; + socklen_t kernel_from_length = 0; + union lwp_sockaddr_buffer kernel_address; +#ifdef ARCH_MM_MMU + void *kmem = RT_NULL; +#endif + + flgs = netflags_muslc_2_lwip(flags); + if (from != RT_NULL) + { + if (fromlen == RT_NULL || + !lwp_user_accessable(fromlen, sizeof(*fromlen))) + { + return -EFAULT; + } + lwp_get_from_user(&user_from_length, fromlen, + sizeof(user_from_length)); + if (user_from_length == 0 || + !lwp_user_accessable(from, user_from_length)) + { + return -EFAULT; + } + rt_memset(&kernel_address, 0, sizeof(kernel_address)); + kernel_from_length = sizeof(kernel_address); + } +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)mem, len)) + { + return -EFAULT; + } + + kmem = kmem_get(len); + if (!kmem) + { + return -ENOMEM; + } + + if (from) + { + ret = recvfrom(socket, kmem, len, flgs, + &kernel_address.address, &kernel_from_length); + } + else + { + ret = recvfrom(socket, kmem, len, flgs, NULL, NULL); + } + + if (ret >= 0) + { + if (ret > 0) + { + lwp_put_to_user(mem, kmem, ret); + } + if (from != RT_NULL) + { + kernel_from_length = lwp_sockaddr_to_user( + from, user_from_length, &kernel_address, + kernel_from_length); + lwp_put_to_user(fromlen, &kernel_from_length, + sizeof(kernel_from_length)); + } + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmem); + + return ret; +#else + if (from) + { + ret = recvfrom(socket, mem, len, flgs, + &kernel_address.address, &kernel_from_length); + if (ret >= 0) + { + kernel_from_length = lwp_sockaddr_to_user( + from, user_from_length, &kernel_address, + kernel_from_length); + lwp_put_to_user(fromlen, &kernel_from_length, + sizeof(kernel_from_length)); + } + } + else + { + ret = recvfrom(socket, mem, len, flgs, NULL, NULL); + } + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Receives data from a connected socket. + * + * This system call is used to receive data from a socket that is in a connected state. + * It supports various flags to modify the behavior of the receive operation. + * + * @param[in] socket The socket descriptor from which the data will be received. + * The socket must be in a valid and connected state. + * @param[out] mem A pointer to the buffer where the received data will be stored. + * @param[in] len The maximum number of bytes to receive. This defines the size of the `mem` buffer. + * @param[in] flags Modifiers that control the behavior of the receive operation. Common flags include: + * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue. + * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning. + * - `MSG_DONTWAIT`: Perform a non-blocking receive operation. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note The function is designed for connected sockets, such as stream-oriented sockets (e.g., TCP). + * For datagram-oriented sockets (e.g., UDP), use `sys_recvfrom()` to capture the sender's address if needed. + * + * @warning The `socket` must be valid and in a connected state. If the `mem` buffer is smaller than the + * received data, excess data may be discarded. For non-blocking sockets, the function returns + * immediately if no data is available. + * + * @see sys_send(), sys_recvfrom() + */ +sysret_t sys_recv(int socket, void *mem, size_t len, int flags) +{ + int flgs = 0; + int ret; + void *kmem = RT_NULL; + + if (!lwp_user_accessable((void *)mem, len)) + { + return -EFAULT; + } + + kmem = kmem_get(len); + if (kmem == RT_NULL) + { + return -ENOMEM; + } + + flgs = netflags_muslc_2_lwip(flags); + ret = recvfrom(socket, kmem, len, flgs, NULL, NULL); + + if (ret > 0) + { + lwp_put_to_user((void *)mem, kmem, ret); + } + kmem_put(kmem); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Sends a message from a socket using scatter-gather I/O. + * + * This system call sends data from multiple buffers specified in the `msghdr` structure. It is particularly + * useful for advanced socket operations requiring ancillary data or scatter-gather I/O. + * + * @param[in] socket The socket descriptor through which the data will be sent. The socket must be + * valid and, for connection-oriented sockets, in a connected state. + * @param[in] msg A pointer to a `msghdr` structure that specifies the message to be sent. This structure includes: + * - `msg_name`: Optional address of the target (used for connectionless sockets like UDP). + * - `msg_namelen`: Length of the address in `msg_name`. + * - `msg_iov`: An array of `iovec` structures pointing to the data buffers. + * - `msg_iovlen`: The number of elements in the `msg_iov` array. + * - `msg_control`: Optional ancillary data (e.g., file descriptors). + * - `msg_controllen`: Length of the ancillary data. + * - `msg_flags`: Flags for the message (e.g., end-of-record markers). + * @param[in] flags Flags that modify the behavior of the send operation. Common flags include: + * - `MSG_DONTWAIT`: Perform a non-blocking send operation. + * - `MSG_EOR`: Indicates the end of a record. + * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note This function is versatile and supports both connection-oriented (e.g., TCP) and + * connectionless (e.g., UDP) sockets. For simpler use cases, consider using `sys_send()` or `sys_sendto()`. + * + * @warning The `socket` must be configured correctly for the intended communication. For non-blocking sockets, + * the function may return immediately if the send buffer is full. Ancillary data in `msg_control` + * must be formatted correctly to avoid undefined behavior. + * + * @see sys_send(), sys_sendto(), sys_recvmsg() + */ +sysret_t sys_sendmsg(int socket, const struct musl_msghdr *msg, int flags) +{ + int flgs, ret = -1; + int error = 0; + struct msghdr kmsg; +#ifdef ARCH_MM_MMU + int index; + void *buffer; + void *msg_control; + void *msg_name; + struct iovec *uiov, *kiov; +#endif + if (!msg) + { + return -EPERM; + } + + flgs = netflags_muslc_2_lwip(flags); + +#ifdef ARCH_MM_MMU + ret = copy_msghdr_from_user(&kmsg, (struct musl_msghdr *)msg, &uiov, + &msg_control, &msg_name, &buffer, RT_NULL, + RT_FALSE); + + if (ret < 0) + { + return ret; + } + kiov = kmsg.msg_iov; + + for (index = 0; index < kmsg.msg_iovlen; ++index) + { + lwp_get_from_user(kiov->iov_base, uiov->iov_base, kiov->iov_len); + + ++kiov; + ++uiov; + } + + if (msg_control != RT_NULL && kmsg.msg_controllen != 0) + { + lwp_get_from_user(kmsg.msg_control, msg_control, + kmsg.msg_controllen); + cmsg_level_muslc_2_lwip(&kmsg); + } + if (msg_name != RT_NULL && kmsg.msg_namelen != 0) + { + union lwp_sockaddr_buffer kernel_address; + + ret = lwp_sockaddr_from_user(&kernel_address, + (struct musl_sockaddr *)msg_name, + kmsg.msg_namelen); + if (ret < 0) + { + kmem_put(buffer); + kmem_put(kmsg.msg_iov); + return ret; + } + rt_memcpy(kmsg.msg_name, &kernel_address, kmsg.msg_namelen); + } + + ret = sendmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); + } + + kmem_put(buffer); + kmem_put(kmsg.msg_iov); +#else + kmsg.msg_name = msg->msg_name; + kmsg.msg_namelen = msg->msg_namelen; + kmsg.msg_iov = msg->msg_iov; + kmsg.msg_iovlen = msg->msg_iovlen; + kmsg.msg_control = msg->msg_control; + kmsg.msg_controllen = msg->msg_controllen; + kmsg.msg_flags = msg->msg_flags; + + ret = sendmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); + } + +#endif /* ARCH_MM_MMU */ + + if (ret >= 0) + { + return ret; + } + return error != 0 ? error : ret; +} + +/** + * @brief Sends data to a specific address using a socket. + * + * This system call is used to send data from a socket to a specified address. It is commonly + * used with connectionless sockets (e.g., UDP) but can also be used with connection-oriented + * sockets if the destination address needs to be overridden. + * + * @param[in] socket The socket descriptor used for sending data. It must be valid and + * properly configured for communication. + * @param[in] dataptr A pointer to the buffer containing the data to be sent. + * @param[in] size The size, in bytes, of the data to be sent. + * @param[in] flags Flags that modify the behavior of the send operation. Common flags include: + * - `MSG_DONTWAIT`: Perform a non-blocking send operation. + * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors. + * @param[in] to A pointer to a `musl_sockaddr` structure that specifies the destination address. + * This parameter can be `NULL` if the socket is connection-oriented. + * @param[in] tolen The length of the address structure pointed to by `to`. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note For connectionless sockets (e.g., UDP), `to` and `tolen` must specify a valid destination address. + * For connection-oriented sockets (e.g., TCP), these parameters can be ignored if the connection + * is already established. + * + * @warning Ensure that the buffer size (`size`) matches the expected size for the data protocol in use. + * For non-blocking sockets, this function may return immediately if the send buffer is full. + * + * @see sys_send(), sys_sendmsg(), sys_recvfrom() + */ +sysret_t sys_sendto(int socket, const void *dataptr, size_t size, int flags, + const struct musl_sockaddr *to, socklen_t tolen) +{ + int flgs = 0; + int ret = -1; + union lwp_sockaddr_buffer kernel_address; +#ifdef ARCH_MM_MMU + void *kmem = RT_NULL; +#endif + + flgs = netflags_muslc_2_lwip(flags); + if (to != RT_NULL) + { + ret = lwp_sockaddr_from_user(&kernel_address, to, tolen); + if (ret < 0) + { + return ret; + } + } +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)dataptr, size)) + { + return -EFAULT; + } + + kmem = kmem_get(size); + if (!kmem) + { + return -ENOMEM; + } + + lwp_get_from_user(kmem, (void *)dataptr, size); + + if (to) + { + ret = sendto(socket, kmem, size, flgs, + &kernel_address.address, tolen); + } + else + { + ret = sendto(socket, kmem, size, flgs, NULL, tolen); + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmem); + + return ret; +#else + if (to) + { + ret = sendto(socket, dataptr, size, flgs, + &kernel_address.address, tolen); + } + else + { + ret = sendto(socket, dataptr, size, flgs, NULL, tolen); + } + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Sends data over a connected socket. + * + * This system call sends data from the specified buffer through a connected socket. It is typically + * used with connection-oriented sockets (e.g., TCP) but can also work with connectionless sockets + * (e.g., UDP) if a connection is established using `sys_connect()`. + * + * @param[in] socket The socket descriptor used for sending data. It must be a valid socket + * and, for connection-oriented sockets, in a connected state. + * @param[in] dataptr A pointer to the buffer containing the data to be sent. + * @param[in] size The size, in bytes, of the data to be sent. + * @param[in] flags Flags that modify the behavior of the send operation. Common flags include: + * - `MSG_DONTWAIT`: Perform a non-blocking send operation. + * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note For connection-oriented sockets, the socket must already be connected using `sys_connect()` or + * `sys_accept()`. For connectionless sockets, use `sys_sendto()` to specify the destination address. + * + * @warning If the socket is in non-blocking mode and the send buffer is full, this function may return + * immediately with an error. Ensure that the size of the data (`size`) matches the protocol's + * expectations to avoid truncation or overflow issues. + * + * @see sys_sendto(), sys_sendmsg(), sys_recv() + */ +sysret_t sys_send(int socket, const void *dataptr, size_t size, int flags) +{ + int flgs = 0; + int ret = 0; + void *kdataptr = RT_NULL; + + if (!lwp_user_accessable((void *)dataptr, size)) + { + return -EFAULT; + } + + kdataptr = kmem_get(size); + if (kdataptr == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kdataptr, (void *)dataptr, size) != size) + { + kmem_put(kdataptr); + return -EINVAL; + } + + flgs = netflags_muslc_2_lwip(flags); + ret = sendto(socket, kdataptr, size, flgs, NULL, 0); + + kmem_put(kdataptr); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Creates a new socket for communication. + * + * This system call creates a new socket endpoint for communication and returns a file descriptor + * that can be used for subsequent socket operations, such as binding, connecting, or data transfer. + * + * @param[in] domain Specifies the protocol family to be used for the socket. Common values include: + * - `AF_INET`: IPv4 Internet protocols. + * - `AF_INET6`: IPv6 Internet protocols. + * - `AF_UNIX`: Local communication using UNIX domain sockets. + * @param[in] type Specifies the type of socket to be created. Common values include: + * - `SOCK_STREAM`: Provides sequenced, reliable, two-way, connection-based byte streams. + * - `SOCK_DGRAM`: Supports datagrams (connectionless, unreliable messages of fixed maximum length). + * - `SOCK_RAW`: Provides raw network protocol access. + * @param[in] protocol Specifies the specific protocol to be used. Typically set to `0` to use the default + * protocol for the specified domain and type. + * + * @return sysret_t Returns the socket file descriptor on success, or a negative error code on failure. + * + * @note The returned socket descriptor must be closed using `sys_close()` to release system resources. + * + * @warning Ensure the combination of `domain`, `type`, and `protocol` is valid. Invalid combinations + * may result in errors. For example, specifying `AF_INET` with `SOCK_DGRAM` is valid for UDP, + * but with `SOCK_STREAM` it is used for TCP. + * + * @see sys_bind(), sys_connect(), sys_accept(), sys_close() + */ +sysret_t sys_socket(int domain, int type, int protocol) +{ + int fd = -1; + int nonblock = 0; + /* not support SOCK_CLOEXEC type */ + if (type & SOCK_CLOEXEC) + { + type &= ~SOCK_CLOEXEC; + } + if (type & SOCK_NONBLOCK) + { + nonblock = 1; + type &= ~SOCK_NONBLOCK; + } + + fd = socket(domain, type, protocol); + if (fd < 0) + { + goto out; + } + if (nonblock) + { + fcntl(fd, F_SETFL, O_NONBLOCK); + } + +out: + return (fd < 0 ? GET_ERRNO() : fd); +} + +/** + * @brief Creates a pair of connected sockets. + * + * This system call creates two connected sockets that can be used for bidirectional communication + * between processes or threads. The sockets are returned as file descriptors in the `fd` array. + * + * @param[in] domain Specifies the protocol family to be used for the sockets. Common values include: + * - `AF_UNIX`: Local communication using UNIX domain sockets. + * @param[in] type Specifies the type of socket to be created. Common values include: + * - `SOCK_STREAM`: Provides sequenced, reliable, two-way, connection-based byte streams. + * - `SOCK_DGRAM`: Supports datagrams (connectionless, unreliable messages of fixed maximum length). + * @param[in] protocol Specifies the specific protocol to be used. Typically set to `0` to use the default + * protocol for the specified domain and type. + * @param[out] fd An array of two integers where the connected socket descriptors will be stored. + * After a successful call: + * - `fd[0]`: The first socket descriptor. + * - `fd[1]`: The second socket descriptor. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note The sockets in the pair are connected and can be used for inter-process communication + * (IPC) or between threads in the same process. + * + * @warning Ensure the `domain`, `type`, and `protocol` combination is valid. This function is typically + * supported only for `AF_UNIX` domain. + * + * @see sys_socket(), sys_close() + */ +sysret_t sys_socketpair(int domain, int type, int protocol, int fd[2]) +{ +#ifdef RT_USING_SAL + int ret = 0; + int k_fd[2]; + + if (!lwp_user_accessable((void *)fd, sizeof(int[2]))) + { + return -EFAULT; + } + + ret = socketpair(domain, type, protocol, k_fd); + + if (ret == 0) + { + lwp_put_to_user(fd, k_fd, sizeof(int[2])); + } + + return ret; +#else + return -ELIBACC; +#endif +} + +/** + * @brief Closes an open socket. + * + * This system call is used to close a previously opened socket. Once the socket is closed, it is no longer + * valid for any further operations, such as sending, receiving, or other socket-related functions. + * + * @param[in] socket The socket descriptor to be closed. This descriptor must be a valid socket that was + * previously created with `sys_socket()` or related functions. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note Once a socket is closed, any attempts to use the socket for communication will result in an error. + * The system will release any resources associated with the socket. + * + * @warning Make sure that no data is being transferred or pending on the socket before closing it. + * Closing an active socket might lead to data loss. + * + * @see sys_socket(), sys_shutdown() + */ +sysret_t sys_closesocket(int socket) +{ + sysret_t ret; + ret = closesocket(socket); + return ret; +} + +#endif diff --git a/components/lwp/syscall/syscall_system.c b/components/lwp/syscall/syscall_system.c new file mode 100644 index 000000000000..6296ea40c6fd --- /dev/null +++ b/components/lwp/syscall/syscall_system.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Get system information. + * + * This function retrieves information about the current system, such as the system name, + * version, release, architecture, and other details. The information is stored in the + * `utsname` structure pointed to by the `uts` parameter. + * + * @param[out] uts A pointer to a `utsname` structure where the system information will + * be stored. The structure includes fields such as: + * - `sysname`: Operating system name. + * - `nodename`: Network node hostname. + * - `release`: Operating system release version. + * - `version`: Operating system version. + * - `machine`: Hardware identifier (architecture). + * + * @return sysret_t Returns `SYSRET_OK` (0) on success. On failure, returns a negative error code. + * + * @note This function provides details about the system environment, which may be useful + * for applications needing to adapt to different system configurations. + * + * @see sys_gethostname, sys_uname + */ +sysret_t sys_uname(struct utsname *uts) +{ + struct utsname utsbuff = { 0 }; + int ret = 0; + const char *machine; + + if (!lwp_user_accessable((void *)uts, sizeof(struct utsname))) + { + return -EFAULT; + } + rt_strncpy(utsbuff.sysname, "RT-Thread", sizeof(utsbuff.sysname)); + utsbuff.nodename[0] = '\0'; + ret = rt_snprintf(utsbuff.release, sizeof(utsbuff.release), "%u.%u.%u", + RT_VERSION_MAJOR, RT_VERSION_MINOR, RT_VERSION_PATCH); + if (ret < 0) + { + return -EIO; + } + ret = rt_snprintf(utsbuff.version, sizeof(utsbuff.version), "RT-Thread %u.%u.%u %s %s", + RT_VERSION_MAJOR, RT_VERSION_MINOR, RT_VERSION_PATCH, __DATE__, __TIME__); + if (ret < 0) + { + return -EIO; + } + + machine = rt_hw_cpu_arch(); + rt_strncpy(utsbuff.machine, machine, sizeof(utsbuff.machine)); + + utsbuff.domainname[0] = '\0'; + lwp_put_to_user(uts, &utsbuff, sizeof utsbuff); + return 0; +} + +#ifndef LWP_USING_RUNTIME +sysret_t lwp_teardown(struct rt_lwp *lwp, void (*cb)(void)) +{ + /* if no LWP_USING_RUNTIME configured */ + return -ENOSYS; +} +#endif + +/** + * @brief Reboot the system. + * + * This function initiates a system reboot with a specific reboot type and optional arguments. + * The reboot process is performed by passing `magic` and `magic2` as security keys to validate the + * reboot request, along with the desired reboot type (`type`) and optional argument (`arg`) that + * may contain additional parameters depending on the reboot type. + * + * @param[in] magic A magic number for security validation, typically used to verify that the caller + * has sufficient privileges (e.g., root). + * @param[in] magic2 A second magic number for additional security verification, used in combination + * with `magic` to validate the reboot request. + * @param[in] type The type of reboot to perform, such as `RB_AUTOBOOT`, `RB_HALT`, `RB_RESTART`, + * etc. The exact values may depend on the platform. + * @param[in] arg An optional argument for the reboot process. This can provide additional data, + * such as a specific shutdown procedure, depending on the type of reboot. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note This operation is typically available to the system administrator (root) and should be used + * with caution as it can cause the system to restart or shut down. + */ +sysret_t sys_reboot(int magic, int magic2, int type, void *arg) +{ + sysret_t rc; + switch (type) + { + /* Hardware reset */ + case RB_AUTOBOOT: + rc = lwp_teardown(lwp_self(), rt_hw_cpu_reset); + break; + + /* Stop system and switch power off */ + case RB_POWER_OFF: + rc = lwp_teardown(lwp_self(), rt_hw_cpu_shutdown); + break; + default: + rc = -ENOSYS; + } + + return rc; +} + +/** + * @brief Read data from a file descriptor at a specific offset. + * + * This function reads data from a file descriptor `fd` into the buffer `buf`, starting from the + * specified byte `offset`. Unlike a regular `read` call, `sys_pread64` allows the file pointer to + * remain unchanged after the read, as the operation directly accesses the file at the provided + * offset. + * + * @param[in] fd The file descriptor from which to read. It must be a valid open file descriptor. + * @param[out] buf A pointer to the buffer where the read data will be stored. + * @param[in] size The number of bytes to read from the file. + * @param[in] offset The offset in bytes from the beginning of the file to start reading from. + * + * @return ssize_t On success, returns the number of bytes read (which may be less than `size` if + * the end of the file is reached). On error, returns a negative error code. + * + * @note This function is particularly useful for random access to files, allowing for efficient + * reads from arbitrary positions without affecting the current file pointer. + */ +ssize_t sys_pread64(int fd, void *buf, int size, size_t offset) +#ifdef RT_USING_DFS_V2 +{ + ssize_t pread(int fd, void *buf, size_t len, size_t offset); +#ifdef ARCH_MM_MMU + ssize_t ret = -1; + void *kmem = RT_NULL; + + if (!size) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)buf, size)) + { + return -EFAULT; + } + kmem = kmem_get(size); + if (!kmem) + { + return -ENOMEM; + } + + ret = pread(fd, kmem, size, offset); + if (ret > 0) + { + lwp_put_to_user(buf, kmem, ret); + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmem); + + return ret; +#else + if (!lwp_user_accessable((void *)buf, size)) + { + return -EFAULT; + } + + ssize_t ret = pread(fd, kmem, size, offset); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} +#else +{ + ssize_t ret = -ENOSYS; + return (ret < 0 ? GET_ERRNO() : ret); +} +#endif + +/** + * @brief Write data to a file descriptor at a specific offset. + * + * This function writes data from the buffer `buf` to the file descriptor `fd`, starting at the + * specified byte `offset`. Unlike a regular `write` call, `sys_pwrite64` allows the file pointer to + * remain unchanged after the write, as the operation directly writes the data to the file at the + * provided offset. + * + * @param[in] fd The file descriptor to which the data will be written. It must be a valid open + * file descriptor. + * @param[in] buf A pointer to the buffer containing the data to be written. + * @param[in] size The number of bytes to write to the file. + * @param[in] offset The offset in bytes from the beginning of the file to start writing to. + * + * @return ssize_t On success, returns the number of bytes written (which may be less than `size` if + * the write operation is partial). On error, returns a negative error code. + * + * @note This function is particularly useful for random access to files, allowing for efficient + * writes to arbitrary positions without affecting the current file pointer. + */ +ssize_t sys_pwrite64(int fd, void *buf, int size, size_t offset) +#ifdef RT_USING_DFS_V2 +{ + ssize_t pwrite(int fd, const void *buf, size_t len, size_t offset); +#ifdef ARCH_MM_MMU + ssize_t ret = -1; + void *kmem = RT_NULL; + + if (!size) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)buf, size)) + { + return -EFAULT; + } + kmem = kmem_get(size); + if (!kmem) + { + return -ENOMEM; + } + + lwp_get_from_user(kmem, (void *)buf, size); + + ret = pwrite(fd, kmem, size, offset); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kmem); + + return ret; +#else + if (!lwp_user_accessable((void *)buf, size)) + { + return -EFAULT; + } + + ssize_t ret = pwrite(fd, kmem, size, offset); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} +#else +{ + ssize_t ret = -ENOSYS; + return (ret < 0 ? GET_ERRNO() : ret); +} +#endif + +/** + * @brief Create a timer file descriptor. + * + * This function creates a timer that can be used to notify a process after a specified + * amount of time has passed. The timer is represented by a file descriptor, which can be + * used with functions such as `read` and `poll` to monitor and retrieve notifications + * about timer expirations. + * + * @param[in] clockid The clock to use for the timer. Possible values include: + * - `CLOCK_REALTIME`: The system's real-time clock. + * - `CLOCK_MONOTONIC`: A clock that cannot be set and is not affected by + * changes in the system time. + * @param[in] flags Flags that modify the behavior of the timer. The commonly used values are: + * - `TFD_CLOEXEC`: Set the close-on-exec flag for the file descriptor. + * - `TFD_NONBLOCK`: Set the non-blocking flag for the file descriptor. + * + * @return sysret_t On success, returns a non-negative file descriptor that refers to the timer. + * On failure, returns a negative error code. + * + * @note The timer created by this function can be used to create periodic or one-shot timers. + * The timer can be configured using `timerfd_settime` and notifications can be retrieved + * by reading from the returned file descriptor. + */ +sysret_t sys_timerfd_create(int clockid, int flags) +{ + int ret; + + ret = timerfd_create(clockid, flags); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Set or modify the expiration time for a timer. + * + * This function is used to arm or re-arm a timer associated with a file descriptor created + * by `sys_timerfd_create`. It allows setting a new expiration time and can also return + * the previous timer configuration. + * + * @param[in] fd The file descriptor referring to the timer, which was created using + * `sys_timerfd_create`. + * @param[in] flags Flags that modify the behavior of the timer. The commonly used values are: + * - `TFD_TIMER_ABSTIME`: If set, the `new` expiration time is an absolute time. + * - `TFD_TIMER_RELATIVE`: The default behavior, where `new` expiration time is + * a relative time from now. + * @param[in] new A pointer to a `struct itimerspec` specifying the new expiration time. + * It contains two fields: + * - `it_value`: The initial expiration time of the timer. + * - `it_interval`: The period of periodic timers (if zero, the timer is one-shot). + * @param[out] old A pointer to a `struct itimerspec` where the previous timer configuration + * will be stored. This can be `NULL` if the previous configuration is not needed. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note This function allows for both one-shot and periodic timers. If the timer is periodic, + * it will continue triggering at intervals specified in `it_interval` until canceled or modified. + * When a timer expires, the associated file descriptor becomes readable, and the application + * can retrieve the expiration event by reading from the file descriptor. + */ +sysret_t sys_timerfd_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old) +{ + int ret = -1; + struct itimerspec *knew = RT_NULL; + struct itimerspec *kold = RT_NULL; + + if (new == RT_NULL) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)new, sizeof(struct itimerspec))) + { + return -EFAULT; + } + + knew = kmem_get(sizeof(struct itimerspec)); + + if (knew) + { + lwp_get_from_user(knew, (void *)new, sizeof(struct itimerspec)); + + if (old) + { + if (!lwp_user_accessable((void *)old, sizeof(struct itimerspec))) + { + kmem_put(knew); + return -EFAULT; + } + + kold = kmem_get(sizeof(struct itimerspec)); + if (kold == RT_NULL) + { + kmem_put(knew); + return -ENOMEM; + } + } + + ret = timerfd_settime(fd, flags, knew, kold); + + if (old) + { + lwp_put_to_user(old, kold, sizeof(*kold)); + kmem_put(kold); + } + + kmem_put(knew); + } + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Retrieve the current configuration of a timer. + * + * This function is used to obtain the current expiration time and interval of a timer associated + * with a file descriptor created by `sys_timerfd_create`. It allows querying the current state + * of the timer, including the time remaining until the next expiration and the period (for periodic timers). + * + * @param[in] fd The file descriptor referring to the timer, which was created using + * `sys_timerfd_create`. + * @param[out] cur A pointer to a `struct itimerspec` where the current timer configuration will + * be stored. This structure contains: + * - `it_value`: The time remaining until the timer expires. + * - `it_interval`: The period for periodic timers (zero for one-shot timers). + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note The timer's `it_value` field will contain the time remaining until the next expiration. + * If the timer is periodic, the `it_interval` field will contain the period for the next expiration. + * If the timer has expired and there is no further interval (for one-shot timers), `it_value` will + * contain a value of `0`. + */ +sysret_t sys_timerfd_gettime(int fd, struct itimerspec *cur) +{ + int ret = -1; + struct itimerspec *kcur; + + if (cur == RT_NULL) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)cur, sizeof(struct itimerspec))) + { + return -EFAULT; + } + + kcur = kmem_get(sizeof(struct itimerspec)); + + if (kcur) + { + lwp_get_from_user(kcur, cur, sizeof(struct itimerspec)); + ret = timerfd_gettime(fd, kcur); + lwp_put_to_user(cur, kcur, sizeof(struct itimerspec)); + kmem_put(kcur); + } + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Create a file descriptor for receiving signals. + * + * This function creates a file descriptor that can be used to receive signals, similar to how a + * regular file descriptor is used. The signals specified in the provided signal mask are blocked + * for the calling thread, and any matching signals will be reported through the file descriptor. + * This allows signals to be handled in a more controlled, non-interrupt-driven way by using + * standard I/O operations (e.g., `read()` or `select()`) on the file descriptor. + * + * @param[in] fd A file descriptor associated with the process, typically obtained from + * `sys_signalfd()` or a similar mechanism. If `fd` is `-1`, a new file descriptor + * will be created. + * @param[in] mask A pointer to the signal mask (`sigset_t`) that specifies the signals that + * should be received by the file descriptor. Only signals in the mask will be + * reported. + * @param[in] flags Additional flags to control the behavior of the signalfd. Common flags + * include `O_NONBLOCK` for non-blocking operation. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note The caller must call `read()` on the resulting file descriptor to actually receive signals. + * Each read returns a `struct signalfd_siginfo` containing the signal number, and additional + * information, such as the signal source and sender. + * + * @see sys_read(), sigprocmask(), sigaction(), sigsuspend(). + */ +sysret_t sys_signalfd(int fd, const sigset_t *mask, int flags) +{ + int ret = 0; + sigset_t *kmask = RT_NULL; + +#ifdef RT_USING_MUSLLIBC + if (mask == RT_NULL) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)mask, sizeof(struct itimerspec))) + { + return -EFAULT; + } + + kmask = kmem_get(sizeof(struct itimerspec)); + + if (kmask) + { + if (lwp_get_from_user(kmask, (void *)mask, sizeof(struct itimerspec)) != sizeof(struct itimerspec)) + { + kmem_put(kmask); + return -EFAULT; + } + + ret = signalfd(fd, mask, flags); + kmem_put(kmask); + } +#endif + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Creates an anonymous memory-backed file descriptor. + * + * This implementation currently returns success without creating a descriptor + * for compatibility with callers that only probe this interface. + * + * @return sysret_t Zero on success. + */ +sysret_t sys_memfd_create() +{ + return 0; +} + +/** + * @brief Set the value of an interval timer. + * + * This function is used to set the value of a specified timer that generates periodic signals + * after the specified intervals. The timer can be configured to generate a signal when it expires, + * and it can be used for tasks like scheduling periodic events. + * + * @param[in] which Specifies which timer to set. Possible values include: + * - `ITIMER_REAL`: Timer that decrements in real time and sends `SIGALRM` when expired. + * - `ITIMER_VIRTUAL`: Timer that decrements only while the process is executing. + * - `ITIMER_PROF`: Timer that decrements while the process is executing or when it is + * executing in the kernel. + * @param[in] new A pointer to a `struct itimerspec` containing the new value for the timer. + * The `itimerspec` structure contains two `timespec` fields: `it_value` for the initial + * expiration time and `it_interval` for the interval between successive expirations. + * @param[out] old A pointer to a `struct itimerspec` where the previous timer values (before the + * timer is set) will be stored. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note If the specified timer is already active, it will be updated with the new values. The timer + * will start at the `it_value` time and will repeat at the interval specified in `it_interval`. + * If `it_interval` is set to `0`, the timer will not repeat and will only expire once. + * + * @see sys_getitimer(), sigaction(), alarm(), setitimer(). + */ +sysret_t sys_setitimer(int which, const struct itimerspec * restrict new, struct itimerspec * restrict old) +{ + sysret_t rc = 0; + rt_lwp_t lwp = lwp_self(); + struct itimerspec new_value_k; + struct itimerspec old_value_k; + + if (lwp_get_from_user(&new_value_k, (void *)new, sizeof(*new)) != sizeof(*new)) + { + return -EFAULT; + } + + rc = lwp_signal_setitimer(lwp, which, &new_value_k, &old_value_k); + if (old && lwp_put_to_user(old, (void *)&old_value_k, sizeof old_value_k) != sizeof old_value_k) + { + return -EFAULT; + } + + return rc; +} + +/** + * @brief Set the robust list for the current thread. + * + * The `sys_set_robust_list` function sets the robust list for the calling thread. The robust list is + * a list of `struct robust_list_head` elements that contain information about mutexes or other + * resources that need to be cleaned up if the thread dies unexpectedly. + * + * This function is typically used for thread safety in multi-threaded programs, where robust mutexes + * can be used to avoid leaving resources in an inconsistent state when a thread is terminated + * without properly unlocking locks or releasing resources. + * + * @param[in] head A pointer to the robust list to be set. This list contains `struct robust_list_head` + * entries which are used by the kernel to maintain information about robust mutexes. + * @param[in] len The length of the robust list (the number of entries in the list). This should correspond + * to the size of the list in memory, generally expressed in bytes. + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note The robust list should be set during the initialization of a thread or task, and it may be + * used by the kernel to track which mutexes need to be cleaned up in case of failure. + * The list may be manipulated using related functions such as `sys_get_robust_list`. + * + * @see sys_get_robust_list(), robust_mutex, pthread_mutex, set_robust_list. + */ +sysret_t sys_set_robust_list(struct robust_list_head *head, size_t len) +{ + if (len != sizeof(*head)) + { + return -EINVAL; + } + + rt_thread_self()->robust_list = head; + return 0; +} + +/** + * @brief Get the robust list for the specified thread. + * + * The `sys_get_robust_list` function retrieves the robust list associated with the given thread. + * The robust list contains `struct robust_list_head` entries used by the kernel to manage + * robust mutexes and other resources that need to be cleaned up if the thread terminates unexpectedly. + * + * This function is primarily used to retrieve the robust list for a specific thread, so that + * the caller can inspect or manipulate the robust list to handle clean-up operations in case + * the thread exits while holding resources. + * + * @param[in] tid The thread ID of the thread whose robust list is to be retrieved. + * + * @param[out] head_ptr A pointer to a pointer to `struct robust_list_head`. On success, + * this will point to the robust list for the specified thread. + * + * @param[out] len_ptr A pointer to a variable that will hold the length of the robust list + * (i.e., the number of entries in the list). + * + * @return sysret_t On success, returns `0`. On failure, returns a negative error code. + * + * @note The robust list is used to track mutexes and other resources that need to be cleaned up + * if a thread terminates without releasing the resources properly. It is typically used by + * the kernel for thread safety and resource management in multi-threaded programs. + * + * @see sys_set_robust_list(), robust_mutex, pthread_mutex, get_robust_list. + */ +sysret_t sys_get_robust_list(int tid, struct robust_list_head **head_ptr, size_t *len_ptr) +{ + rt_thread_t thread; + size_t len; + struct robust_list_head *head; + + if (!lwp_user_accessable((void *)head_ptr, sizeof(struct robust_list_head *))) + { + return -EFAULT; + } + if (!lwp_user_accessable((void *)len_ptr, sizeof(size_t))) + { + return -EFAULT; + } + + if (tid == 0) + { + thread = rt_thread_self(); + head = thread->robust_list; + } + else + { + thread = lwp_tid_get_thread_and_inc_ref(tid); + if (thread) + { + head = thread->robust_list; + lwp_tid_dec_ref(thread); + } + else + { + return -ESRCH; + } + } + + len = sizeof(*(head)); + + if (!lwp_put_to_user(len_ptr, &len, sizeof(size_t))) + { + return -EFAULT; + } + if (!lwp_put_to_user(head_ptr, &head, sizeof(struct robust_list_head *))) + { + return -EFAULT; + } + + return 0; +} diff --git a/components/lwp/syscall/syscall_thread.c b/components/lwp/syscall/syscall_thread.c new file mode 100644 index 000000000000..6ec936f4682d --- /dev/null +++ b/components/lwp/syscall/syscall_thread.c @@ -0,0 +1,1075 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Creates a new thread. + * + * This system call creates a new thread within the calling process. The newly created + * thread starts executing the function specified by the `arg` argument, which typically + * contains the necessary arguments or function pointer for the thread's execution. + * + * @param[in] arg An array of arguments that will be passed to the function executed + * by the new thread. This can include function pointers, structures, + * or any necessary data the thread will need to execute its work. + * + * @return rt_thread_t Returns a handle to the newly created thread. If the thread + * creation fails, `NULL` is returned. + * + * @warning Ensure that the system has sufficient resources to create a new thread. + * Thread creation failures can occur if system limits are exceeded or resources + * are unavailable. + */ +rt_thread_t sys_thread_create(void *arg[]) +{ + int tid = 0; + void *user_stack = 0; + struct rt_lwp *lwp = 0; + rt_thread_t thread = RT_NULL; + + lwp = rt_thread_self()->lwp; + lwp_ref_inc(lwp); + +#ifdef ARCH_MM_MMU + user_stack = lwp_map_user(lwp, 0, (size_t)arg[3], 0); + if (!user_stack) + { + goto fail; + } + if ((tid = lwp_tid_get()) == 0) + { + goto fail; + } + thread = rt_thread_create((const char *)arg[0], + _crt_thread_entry, + (void *)arg[2], + ALLOC_KERNEL_STACK_SIZE, + (rt_uint8_t)(size_t)arg[4], + (rt_uint32_t)(rt_size_t)arg[5]); + if (!thread) + { + goto fail; + } + +#ifdef RT_USING_SMP + RT_SCHED_CTX(thread).bind_cpu = lwp->bind_cpu; +#endif + thread->cleanup = lwp_cleanup; + thread->user_entry = (void (*)(void *))arg[1]; + thread->user_stack = (void *)user_stack; + thread->user_stack_size = (rt_size_t)arg[3]; +#else + rt_uint32_t kstack_size = (rt_uint32_t)arg[7]; + if (kstack_size < ALLOC_KERNEL_STACK_SIZE_MIN) + { + /* When kstack size is 0, the default size of the kernel stack is used */ + kstack_size = kstack_size ? ALLOC_KERNEL_STACK_SIZE_MIN : ALLOC_KERNEL_STACK_SIZE; + } + else if (kstack_size > ALLOC_KERNEL_STACK_SIZE_MAX) + { + kstack_size = ALLOC_KERNEL_STACK_SIZE_MAX; + } + + user_stack = (void *)arg[3]; + if ((!user_stack) || ((rt_uint32_t)arg[6] == RT_NULL)) + { + goto fail; + } + + if ((tid = lwp_tid_get()) == 0) + { + goto fail; + } + + thread = rt_thread_create((const char *)arg[0], _crt_thread_entry, (void *)arg[2], kstack_size, (rt_uint8_t)(size_t)arg[5], (rt_uint32_t)arg[6]); + if (!thread) + { + goto fail; + } + thread->cleanup = lwp_cleanup; + thread->user_entry = (void (*)(void *))arg[1]; + thread->user_stack = (void *)user_stack; + thread->user_stack_size = (uint32_t)arg[4]; + rt_memset(thread->user_stack, '#', thread->user_stack_size); +#endif /* ARCH_MM_MMU */ + + thread->lwp = (void *)lwp; + thread->tid = tid; + lwp_tid_set_thread(tid, thread); + + if (lwp->debug) + { + rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void *)0); + } + + LWP_LOCK(lwp); + rt_list_insert_after(&lwp->t_grp, &thread->sibling); + LWP_UNLOCK(lwp); + + return thread; + +fail: + lwp_tid_put(tid); + if (lwp) + { + lwp_ref_dec(lwp); + } + return RT_NULL; +} + +#ifdef ARCH_MM_MMU + +long _sys_clone(void *arg[]) +{ + struct rt_lwp *lwp = 0; + rt_thread_t thread = RT_NULL; + rt_thread_t self = RT_NULL; + int tid = 0; + rt_err_t err; + + unsigned long flags = 0; + void *user_stack = RT_NULL; + int *new_tid = RT_NULL; + void *tls = RT_NULL; + /* + musl call flags (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND + | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS + | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED); + */ + + /* check args */ + if (!lwp_user_accessable(arg, sizeof(void *[SYS_CLONE_ARGS_NR]))) + { + return -EFAULT; + } + + flags = (unsigned long)(size_t)arg[0]; + if ((flags & (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD | CLONE_SYSVSEM)) != (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD | CLONE_SYSVSEM)) + { + return -EINVAL; + } + + user_stack = arg[1]; + new_tid = (int *)arg[2]; + tls = (void *)arg[3]; + + if ((flags & CLONE_PARENT_SETTID) == CLONE_PARENT_SETTID) + { + if (!lwp_user_accessable(new_tid, sizeof(int))) + { + return -EFAULT; + } + } + + self = rt_thread_self(); + lwp = self->lwp; + lwp_ref_inc(lwp); + if (!user_stack) + { + SET_ERRNO(EINVAL); + goto fail; + } + if ((tid = lwp_tid_get()) == 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + thread = rt_thread_create(self->parent.name, + RT_NULL, + RT_NULL, + self->stack_size, + RT_SCHED_PRIV(self).init_priority, + RT_SCHED_PRIV(self).init_tick); + if (!thread) + { + goto fail; + } + +#ifdef RT_USING_SMP + RT_SCHED_CTX(self).bind_cpu = lwp->bind_cpu; +#endif + thread->cleanup = lwp_cleanup; + thread->user_entry = RT_NULL; + thread->user_stack = RT_NULL; + thread->user_stack_size = 0; + thread->lwp = (void *)lwp; + thread->tid = tid; + + if ((flags & CLONE_SETTLS) == CLONE_SETTLS) + { + thread->thread_idr = tls; + } + if ((flags & CLONE_PARENT_SETTID) == CLONE_PARENT_SETTID) + { + *new_tid = (int)(tid); + } + if ((flags & CLONE_CHILD_CLEARTID) == CLONE_CHILD_CLEARTID) + { + thread->clear_child_tid = (int *)arg[4]; + } + + if (lwp->debug) + { + rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void *)0); + } + + LWP_LOCK(lwp); + rt_list_insert_after(&lwp->t_grp, &thread->sibling); + LWP_UNLOCK(lwp); + + /* copy origin stack */ + lwp_memcpy(thread->stack_addr, self->stack_addr, thread->stack_size); + lwp_tid_set_thread(tid, thread); + arch_set_thread_context(arch_clone_exit, + (void *)((char *)thread->stack_addr + thread->stack_size), + user_stack, &thread->sp); + /* new thread never reach there */ + rt_thread_startup(thread); + return (long)tid; +fail: + err = GET_ERRNO(); + RT_ASSERT(err < 0); + + lwp_tid_put(tid); + if (thread) + { + rt_thread_delete(thread); + } + if (lwp) + { + lwp_ref_dec(lwp); + } + return (long)err; +} + +/** + * @brief Creates a new process or thread (clone). + * + * This system call creates a new process or thread by duplicating the calling process. + * The new process/thread begins execution by calling the function specified in the + * `arg[]` array, which typically contains the necessary arguments or function pointer. + * It is used to implement process/thread creation in the system and is often a lower-level + * operation in process management. + * + * @param[in] arg An array of arguments passed to the new process or thread. This could + * include function pointers, structures, or any necessary data the + * new process/thread will need to execute its work. + * + * @return long Returns a status code or the process/thread ID of the newly created + * process/thread. On success, it may return a positive value (such as a + * thread ID). On failure, a negative value indicating the error is returned. + * + * @warning Be cautious when using this function as improper management of process/thread + * creation can lead to resource exhaustion, deadlocks, or other synchronization issues. + */ +rt_weak long sys_clone(void *arg[]) +{ + return _sys_clone(arg); +} + +static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src) +{ +#ifdef ARCH_MM_MMU + dst->end_heap = src->end_heap; +#endif + dst->lwp_type = src->lwp_type; + dst->text_entry = src->text_entry; + dst->text_size = src->text_size; + dst->data_entry = src->data_entry; + dst->data_size = src->data_size; + dst->args = src->args; + dst->background = src->background; + dst->tty = src->tty; + + /* terminal API */ + dst->term_ctrlterm = src->term_ctrlterm; + + rt_memcpy(dst->cmd, src->cmd, RT_NAME_MAX); + if (src->exe_file) + { + if (dst->exe_file) + { + rt_free(dst->exe_file); + } + dst->exe_file = strndup(src->exe_file, DFS_PATH_MAX); + } + + rt_memcpy(&dst->signal.sig_action, &src->signal.sig_action, sizeof(dst->signal.sig_action)); + rt_memcpy(&dst->signal.sig_action_mask, &src->signal.sig_action_mask, sizeof(dst->signal.sig_action_mask)); + rt_memcpy(&dst->signal.sig_action_nodefer, &src->signal.sig_action_nodefer, sizeof(dst->signal.sig_action_nodefer)); + rt_memcpy(&dst->signal.sig_action_onstack, &src->signal.sig_action_onstack, sizeof(dst->signal.sig_action_onstack)); + rt_memcpy(&dst->signal.sig_action_restart, &dst->signal.sig_action_restart, sizeof(dst->signal.sig_action_restart)); + rt_memcpy(&dst->signal.sig_action_siginfo, &dst->signal.sig_action_siginfo, sizeof(dst->signal.sig_action_siginfo)); + rt_memcpy(&dst->signal.sig_action_nocldstop, &dst->signal.sig_action_nocldstop, sizeof(dst->signal.sig_action_nocldstop)); + rt_memcpy(&dst->signal.sig_action_nocldwait, &dst->signal.sig_action_nocldwait, sizeof(dst->signal.sig_action_nocldwait)); + rt_strcpy(dst->working_directory, src->working_directory); +} + +static int lwp_copy_files(struct rt_lwp *dst, struct rt_lwp *src) +{ + struct dfs_fdtable *dst_fdt; + struct dfs_fdtable *src_fdt; + + src_fdt = &src->fdt; + dst_fdt = &dst->fdt; + /* init fds */ + dst_fdt->fds = rt_calloc(src_fdt->maxfd, sizeof(void *)); + if (dst_fdt->fds) + { + struct dfs_file *d_s; + int i; + + dst_fdt->maxfd = src_fdt->maxfd; + + dfs_file_lock(); + /* dup files */ + for (i = 0; i < src_fdt->maxfd; i++) + { + d_s = fdt_get_file(src_fdt, i); + if (d_s) + { + dst_fdt->fds[i] = d_s; + d_s->ref_count++; + } + } + dfs_file_unlock(); + return 0; + } + return -RT_ERROR; +} + +sysret_t _sys_fork(void) +{ + int tid = 0; + sysret_t falival = 0; + struct rt_lwp *lwp = RT_NULL; + struct rt_lwp *self_lwp = RT_NULL; + rt_thread_t thread = RT_NULL; + rt_thread_t self_thread = RT_NULL; + void *user_stack = RT_NULL; + rt_processgroup_t group; + + /* new lwp */ + lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID); + if (!lwp) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* new tid */ + if ((tid = lwp_tid_get()) == 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* user space init */ + if (lwp_user_space_init(lwp, 1) != 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + self_lwp = lwp_self(); + + /* copy address space of process from this proc to forked one */ + if (lwp_fork_aspace(lwp, self_lwp) != 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* copy lwp struct data */ + lwp_struct_copy(lwp, self_lwp); + + /* copy files */ + if (lwp_copy_files(lwp, self_lwp) != 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* create thread */ + self_thread = rt_thread_self(); + + thread = rt_thread_create(self_thread->parent.name, + RT_NULL, + RT_NULL, + self_thread->stack_size, + RT_SCHED_PRIV(self_thread).init_priority, + RT_SCHED_PRIV(self_thread).init_tick); + if (!thread) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + thread->cleanup = self_thread->cleanup; + thread->user_entry = self_thread->user_entry; + thread->user_stack = self_thread->user_stack; + thread->user_stack_size = self_thread->user_stack_size; + thread->signal.sigset_mask = self_thread->signal.sigset_mask; + thread->thread_idr = self_thread->thread_idr; + thread->clear_child_tid = self_thread->clear_child_tid; + thread->lwp = (void *)lwp; + thread->tid = tid; + + LWP_LOCK(self_lwp); + /* add thread to lwp process */ + rt_list_insert_after(&lwp->t_grp, &thread->sibling); + LWP_UNLOCK(self_lwp); + + lwp_children_register(self_lwp, lwp); + + /* set pgid and sid */ + group = lwp_pgrp_find(lwp_pgid_get_byprocess(self_lwp)); + if (group) + { + lwp_pgrp_insert(group, lwp); + } + else + { + LOG_W("the process group of pid: %d cannot be found", lwp_pgid_get_byprocess(self_lwp)); + } + + /* copy kernel stack context from self thread */ + lwp_memcpy(thread->stack_addr, self_thread->stack_addr, self_thread->stack_size); + lwp_tid_set_thread(tid, thread); + + /* duplicate user objects */ + lwp_user_object_dup(lwp, self_lwp); + + user_stack = arch_get_user_sp(); + arch_set_thread_context(arch_fork_exit, + (void *)((char *)thread->stack_addr + thread->stack_size), + user_stack, &thread->sp); + + rt_thread_startup(thread); + return lwp_to_pid(lwp); +fail: + falival = GET_ERRNO(); + + if (tid != 0) + { + lwp_tid_put(tid); + } + if (thread) + { + rt_thread_delete(thread); + } + if (lwp) + { + lwp_ref_dec(lwp); + } + return falival; +} + +/* arm needs to wrap fork/clone call to preserved lr & caller saved regs */ + +/** + * @brief Creates a child process by duplicating the calling process. + * + * This system call creates a new child process by duplicating the calling process. The + * new child process is a copy of the parent process, except for the returned value. + * The child process starts executing from the point of the fork, but the return value + * differs between the parent and child: the parent receives the process ID (PID) of the + * child, and the child receives a return value of 0. + * + * @return sysret_t Returns a status code indicating the result of the operation: + * - A positive value (the child's PID) is returned to the parent. + * - A value of 0 is returned to the child process. + * - A negative value indicates an error (e.g., resource limits exceeded). + * + * @note This function is commonly used in operating systems to create new processes. + * + * @warning Be aware that improper management of child processes (such as failing to handle + * process termination or excessive forking) can lead to resource exhaustion or + * other system issues. Ensure proper process handling in the parent and child + * processes to avoid leaks and inconsistencies. + */ +rt_weak sysret_t sys_fork(void) +{ + return _sys_fork(); +} + +/** + * @brief Creates a child process using vfork-compatible semantics. + * + * This implementation delegates to `sys_fork`. + * + * @return sysret_t The child process ID in the parent or a negative error code. + */ +rt_weak sysret_t sys_vfork(void) +{ + return sys_fork(); +} + +#define _swap_lwp_data(lwp_used, lwp_new, type, member) \ + do \ + { \ + type tmp; \ + tmp = lwp_used->member; \ + lwp_used->member = lwp_new->member; \ + lwp_new->member = tmp; \ + } while (0) + +/** + * @brief Executes a program in the current process. + * + * This system call replaces the current process image with a new program specified by the + * `path` argument. It loads the program located at the given `path` and passes the arguments + * (`argv`) and environment variables (`envp`) to it. This effectively replaces the calling + * process with a new one, and if successful, it never returns. If there is an error, the current + * process continues to execute, and an error code is returned. + * + * @param[in] path The path to the executable file to be executed. This should be an + * absolute or relative file path to the program. + * @param[in] argv An array of arguments to pass to the new program. The first element + * (`argv[0]`) should typically be the name of the program, and the array + * should be terminated with a `NULL` pointer. + * @param[in] envp An array of environment variables to pass to the new program. This is + * typically in the form of `key=value` strings, with the array terminated + * by a `NULL` pointer. + * + * @return sysret_t Returns a status code: + * - `0`: The program was successfully executed (this value is + * never returned since the process is replaced). + * - Other error codes may indicate issues with the program execution + * + * @note If `execve` is successful, it does not return to the calling function. The process + * image is replaced by the new program. + */ +sysret_t sys_execve(const char *path, char * const argv[], char * const envp[]) +{ + rt_err_t error = -1; + size_t len; + struct rt_lwp *new_lwp = NULL; + struct rt_lwp *lwp; + int uni_thread; + rt_thread_t thread; + struct process_aux *aux; + struct lwp_args_info args_info; + char *kpath = RT_NULL; + + lwp = lwp_self(); + thread = rt_thread_self(); + uni_thread = 1; + + LWP_LOCK(lwp); + if (lwp->t_grp.prev != &thread->sibling) + { + uni_thread = 0; + } + if (lwp->t_grp.next != &thread->sibling) + { + uni_thread = 0; + } + LWP_UNLOCK(lwp); + + if (!uni_thread) + { + return -EINVAL; + } + + len = lwp_user_strlen(path); + if (len <= 0) + { + return -EFAULT; + } + + kpath = rt_malloc(len + 1); + if (!kpath) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kpath, (void *)path, len) != len) + { + rt_free(kpath); + return -EFAULT; + } + kpath[len] = '\0'; + + if (access(kpath, X_OK) != 0) + { + error = rt_get_errno(); + rt_free(kpath); + return (sysret_t)error; + } + + /* setup args */ + error = lwp_args_init(&args_info); + if (error) + { + rt_free(kpath); + return -ENOMEM; + } + + if (argv) + { + error = lwp_args_put_argv(&args_info, (void *)argv); + if (error) + { + error = -EFAULT; + goto quit; + } + } + + if (envp) + { + error = lwp_args_put_envp(&args_info, (void *)envp); + if (error) + { + error = -EFAULT; + goto quit; + } + } + + /* alloc new lwp to operation */ + new_lwp = lwp_create(LWP_CREATE_FLAG_NONE); + if (!new_lwp) + { + error = -ENOMEM; + goto quit; + } + + error = lwp_user_space_init(new_lwp, 0); + if (error != 0) + { + error = -ENOMEM; + goto quit; + } + + /* file is a script ? */ + path = kpath; + while (1) + { + error = lwp_args_load_script(&args_info, path); + if (error != 0) + { + break; + } + path = lwp_args_get_argv_0(&args_info); + } + + /* now load elf */ + if ((aux = lwp_argscopy(new_lwp, &args_info)) == NULL) + { + error = -ENOMEM; + goto quit; + } + error = lwp_load(path, new_lwp, RT_NULL, 0, aux); + if (error == RT_EOK) + { + int off = 0; + int last_backslash = 0; + + /* clear all user objects */ + lwp_user_object_clear(lwp); + + /* find last \ or / to get base name */ + while (1) + { + char c = path[off++]; + + if (c == '\0') + { + break; + } + if (c == '\\' || c == '/') + { + last_backslash = off; + } + } + + /** + * Set thread name and swap the data of lwp and new_lwp. + * Since no other threads can access the lwp field, it't uneccessary to + * take a lock here + */ + RT_ASSERT(rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling) == thread); + + strncpy(thread->parent.name, path + last_backslash, RT_NAME_MAX - 1); + strncpy(lwp->cmd, new_lwp->cmd, RT_NAME_MAX); + rt_free(lwp->exe_file); + lwp->exe_file = strndup(new_lwp->exe_file, DFS_PATH_MAX); + +#ifdef ARCH_MM_MMU + _swap_lwp_data(lwp, new_lwp, struct rt_aspace *, aspace); + _swap_lwp_data(lwp, new_lwp, size_t, end_heap); +#endif + + _swap_lwp_data(lwp, new_lwp, uint8_t, lwp_type); + _swap_lwp_data(lwp, new_lwp, void *, text_entry); + _swap_lwp_data(lwp, new_lwp, uint32_t, text_size); + _swap_lwp_data(lwp, new_lwp, void *, data_entry); + _swap_lwp_data(lwp, new_lwp, uint32_t, data_size); + + _swap_lwp_data(lwp, new_lwp, void *, args); + + lwp_thread_signal_detach(&thread->signal); + rt_memset(&thread->signal.sigset_mask, 0, sizeof(thread->signal.sigset_mask)); + + lwp_signal_detach(&lwp->signal); + lwp_signal_init(&lwp->signal); + + /* to do: clsoe files with flag CLOEXEC, recy sub-thread */ + + lwp_aspace_switch(thread); + + lwp_ref_dec(new_lwp); + arch_start_umode(lwp->args, + lwp->text_entry, + (void *)USER_STACK_VEND, + (char *)thread->stack_addr + thread->stack_size); + /* never reach here */ + } + error = -EINVAL; +quit: + if (kpath) + { + rt_free(kpath); + } + lwp_args_detach(&args_info); + if (new_lwp) + { + lwp_ref_dec(new_lwp); + } + return error; +} +#endif /* ARCH_MM_MMU */ + +/** + * @brief Deletes a thread. + * + * This system call is used to delete an existing thread. The specified thread is terminated, + * and its resources are released. If the thread is currently running, it will be forcefully + * terminated. The thread identifier (`thread`) refers to the thread that is to be deleted. + * + * @param[in] thread The identifier of the thread to be deleted. + * + * @return sysret_t Returns a status code: + * - `SYSRET_OK`: The thread was successfully deleted. + * - Other error codes may indicate additional failures. + * + * @note This function should be used carefully, as forcefully terminating a thread may + * lead to resource leaks or inconsistent state if the thread is performing critical + * operations at the time of termination. + * + * @warning Ensure that the thread being deleted has completed its necessary operations + * and that there are no outstanding resources or critical tasks before deleting + * it. Otherwise, it might lead to undefined behavior or resource leaks. + */ +sysret_t sys_thread_delete(rt_thread_t thread) +{ +#ifdef ARCH_MM_MMU + return rt_thread_delete(thread); +#else + sysret_t ret = 0; + + if (thread->parent.type != RT_Object_Class_Thread) + { + ret = -EINVAL; + goto __exit; + } + + ret = rt_thread_delete(thread); + + if (rt_thread_self() == thread) + { + rt_schedule(); + } + +__exit: + return ret; +#endif +} + +/** + * @brief Starts a previously created thread. + * + * This system call is used to start a thread that was created but has not yet started running. + * It initiates the thread's execution, allowing it to begin performing its assigned task. + * The `thread` parameter refers to the thread that is to be started. + * + * @param[in] thread The identifier of the thread to be started. + * + * @return sysret_t Returns a status code: + * - `0`: The thread was successfully started. + * - Other error codes may indicate additional failures. + * + * @warning Ensure that the thread has been correctly initialized and is in a valid state + * before calling this function to avoid undefined behavior. Improper initialization + * could lead to issues such as the thread not running as expected. + */ +sysret_t sys_thread_startup(rt_thread_t thread) +{ + return rt_thread_startup(thread); +} + +/** + * @brief Retrieves the identifier of the current thread. + * + * This system call returns the thread identifier of the currently executing thread. It allows + * a thread to obtain its own identifier, which can be useful for various thread management + * tasks such as self-identification, logging, or checking the thread's status. + * + * @return rt_thread_t The identifier of the current thread. + * If no thread is currently executing, a null or invalid thread ID + * might be returned depending on the system's implementation. + * + * @note This function is typically used when a thread needs to identify itself, especially + * in cases where thread management is performed dynamically or the thread identifier + * is required for synchronization or debugging purposes. + * + * @warning Be aware that in environments where there is no concept of threads or if the + * current context is not a thread (e.g., during interrupt handling or early system + * initialization), the return value might be invalid. + */ +rt_thread_t sys_thread_self(void) +{ + return rt_thread_self(); +} + +/** + * @brief Finds a thread by its name. + * + * This system call is used to search for a thread based on its name. It returns a reference to the + * thread if found, otherwise it returns `RT_NULL`. The name comparison is case-sensitive. + * + * @param[in] name The name of the thread to search for. This should be a valid string that + * uniquely identifies the thread within the system. + * + * @return rt_thread_t The thread object corresponding to the given name, or `RT_NULL` if no + * matching thread was found. + * + * @note The thread name is typically assigned when the thread is created, and it can be used to + * identify threads in the system. + * + * @warning Ensure that the `name` string is correctly set and unique within the system to avoid + * unexpected behavior. + * + * @see sys_thread_create(), sys_thread_delete() + */ +rt_thread_t sys_thread_find(char *name) +{ + int len = 0; + char *kname = RT_NULL; + rt_thread_t thread; + + len = lwp_user_strlen(name); + if (len <= 0) + { + return RT_NULL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return RT_NULL; + } + + if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) + { + kmem_put(kname); + return RT_NULL; + } + + thread = rt_thread_find(name); + + kmem_put(kname); + + return thread; +} + +/** + * @brief Gets the current system tick count. + * + * This system call returns the current value of the system tick counter, which is typically incremented + * at a fixed interval (e.g., every millisecond or microsecond). The tick count can be used for timing + * purposes, such as measuring the elapsed time or triggering time-based events. + * + * @return rt_tick_t The current value of the system tick counter. + * + * @note The system tick counter typically wraps around after reaching its maximum value, so the + * returned tick value may reset after a certain period of time, depending on the configuration + * of the system tick timer. + * + * @warning Be cautious when using the tick value for time-based calculations, as the counter may + * overflow and wrap around. Ensure that the code handling the tick count properly accounts + * for potential overflow. + * + * @see sys_tick_init(), sys_tick_delay() + */ +rt_tick_t sys_tick_get(void) +{ + return rt_tick_get(); +} + +/** + * @brief Delays the current thread for a specified number of milliseconds. + * + * This system call puts the calling thread to sleep for a given number of milliseconds. It is a blocking + * call, meaning the thread will not execute any further instructions until the specified delay has + * passed. The delay is achieved by the system's timer mechanism. + * + * @param[in] ms The number of milliseconds to delay. The value must be a positive integer. + * + * @return sysret_t Returns `0` on success, or a negative error code on failure. + * + * @note This function is useful for introducing a fixed delay in time-sensitive applications or when + * the thread needs to be paused before continuing execution. + * + * @warning Be cautious when using this function in real-time or time-critical applications, as + * excessive delays may affect overall system performance or responsiveness. + * + * @see sys_thread_delay(), sys_thread_sleep() + */ +sysret_t sys_thread_mdelay(rt_int32_t ms) +{ + return rt_thread_mdelay(ms); +} + +/** + * @brief Retrieves the current value of the error code. + * + * This function returns the most recent error code set by a system call. The error code is typically set + * when a system call fails. This function allows users to retrieve the last error that occurred, helping to + * diagnose issues or handle errors in a more controlled manner. + * + * @return sysret_t The current error code. A value of `0` indicates no error, while a non-zero value + * represents the most recent error. The error codes are system-specific and can + * represent various failure conditions (e.g., `EINVAL`, `ENOMEM`, `EIO`). + * + * @note The error code returned is specific to the current thread or process and is typically updated + * each time a system call fails. The error code is persistent until it is overwritten by the next + * failed system call or explicitly reset. + * + * @see sys_set_errno(), sys_perror(), sys_strerror() + */ +/** + * @brief Retrieves the current thread's error code. + * + * @return sysret_t The current error code. + */ +sysret_t sys_get_errno(void) +{ + return rt_get_errno(); +} + +#ifdef ARCH_MM_MMU + +/** + * @brief Sets the thread-specific data area. + * + * This function is used to associate a specific area of memory with the current thread. The area pointed to + * by `p` is set as the thread's local storage. Thread-specific data is used to store data that is unique to + * each thread, allowing different threads to maintain independent state information. + * + * @param[in] p A pointer to the memory area that is to be set as the thread's local storage. This area + * will be used by the thread to store its specific data. The structure and size of the data + * area depend on the implementation and use case. + * + * @return sysret_t Returns `0` on success. On failure, it returns a negative error code. + * + * @note This function is typically used to set up thread-specific storage for managing data that should + * not be shared between threads. The data area is accessible only by the thread that set it, ensuring + * thread safety for the stored information. + * + * @see sys_get_thread_area(), sys_thread_create(), sys_thread_self() + */ +sysret_t sys_set_thread_area(void *p) +{ + rt_thread_t thread; + + thread = rt_thread_self(); + thread->thread_idr = p; + arch_set_thread_area(p); + + return 0; +} + +/** + * @brief Sets the address at which the thread ID is stored. + * + * This function sets the address of a variable that will store the thread ID for the calling thread. + * The specified address `tidptr` will hold the thread's unique identifier. This can be useful for + * managing thread-specific state or for synchronization mechanisms where the thread's ID needs to be + * shared or checked by other parts of the system. + * + * @param[in] tidptr A pointer to an integer where the thread ID will be stored. This value will + * hold the calling thread's ID, and it can be accessed to identify the thread + * later. The value of the thread ID can be used in various thread management + * operations. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function is typically used in systems that require associating a specific address with the + * thread ID, often in real-time or embedded systems where managing and accessing thread IDs is + * crucial for scheduling or resource allocation. + * + * @see sys_get_tid(), sys_thread_self(), sys_thread_create() + */ +sysret_t sys_set_tid_address(int *tidptr) +{ + rt_thread_t thread; + +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)tidptr, sizeof(int))) + { + return -EFAULT; + } +#endif + thread = rt_thread_self(); + thread->clear_child_tid = tidptr; + return thread->tid; +} +#endif /* ARCH_MM_MMU */ + +/** + * @brief Gets the thread ID of the calling thread. + * + * This function returns the unique thread identifier (thread ID) for the calling thread. The thread ID + * can be used to uniquely identify the thread within the system and is typically used for debugging, + * thread management, or scheduling purposes. + * + * @return sysret_t The thread ID of the calling thread. The value is typically a positive integer + * representing the unique ID assigned to the thread. In case of failure, an error code + * may be returned. + * + * @note The thread ID returned by this function is unique within the system, and it may be used to + * reference or manipulate the specific thread associated with the ID. + * + * @see sys_set_tid_address(), sys_thread_self(), sys_thread_create() + */ +sysret_t sys_gettid(void) +{ + return rt_thread_self()->tid; +} + +/** + * @brief Checks the accessibility of a file or directory. + * + * This function checks whether the calling process has the specified access rights for the given file or directory. + * The check is performed based on the provided `mode`, which can indicate whether read, write, or execute permissions + * are required. + * + * @param[in] filename The path to the file or directory whose accessibility is being checked. + * @param[in] mode The access mode to check for. This can be a combination of the following: + * - `R_OK`: Check for read permission. + * - `W_OK`: Check for write permission. + * - `X_OK`: Check for execute permission. + * - `F_OK`: Check if the file exists. + * + * @return sysret_t Returns `0` if the specified access is allowed. On failure, returns a negative error code. + * + * @note This function does not modify the file or directory, it only checks if the specified access rights are granted. + * + * @see sys_open(), sys_stat(), sys_fstat(), sys_chmod(), sys_chown() + */ diff --git a/components/lwp/syscall/syscall_time_sched.c b/components/lwp/syscall/syscall_time_sched.c new file mode 100644 index 000000000000..fd8db540587a --- /dev/null +++ b/components/lwp/syscall/syscall_time_sched.c @@ -0,0 +1,1245 @@ +/* + * Copyright (c) 2006-2025 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscall_internal.h" + +/** + * @brief Suspends execution for a specified amount of time. + * + * This system call suspends the execution of the calling thread for the duration + * specified by the `rqtp` argument. The `rqtp` argument is a pointer to a `struct timespec` + * that defines the sleep time in seconds and nanoseconds. If the sleep is interrupted by a signal, + * the function may return early with the remaining time in `rmtp`, which is an optional argument. + * + * @param rqtp A pointer to a `struct timespec` that specifies the requested sleep time. + * The structure contains two fields: `tv_sec` (seconds) and `tv_nsec` (nanoseconds). + * @param rmtp A pointer to a `struct timespec` where the remaining time will be stored if the + * sleep is interrupted by a signal. This can be `NULL` if the remaining time is not needed. + * @return On success, returns `0`. On failure, returns `errno` to indicate the error. + * + * @note The `timespec` structure has two fields: + * - `tv_sec`: The number of whole seconds to sleep. + * - `tv_nsec`: The number of nanoseconds to sleep after the seconds. This value should be + * in the range [0, 1,000,000,000) nanoseconds. + * + * @warning Ensure that the values in `rqtp` are within valid ranges. A `tv_nsec` value greater than + * or equal to 1,000,000,000 will result in an error. If `rmtp` is provided, the caller + * must ensure the buffer is large enough to store the remaining time. + * + * @see sys_sleep(), clock_nanosleep(), nanosleep() + */ +sysret_t sys_nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +{ + int ret = 0; + LOG_D("sys_nanosleep\n"); + if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) + { + return -EFAULT; + } + +#ifdef ARCH_MM_MMU + struct timespec rqtp_k; + struct timespec rmtp_k; + + lwp_get_from_user(&rqtp_k, (void *)rqtp, sizeof rqtp_k); + ret = nanosleep(&rqtp_k, &rmtp_k); + if ((ret != -1 || rt_get_errno() == EINTR) && rmtp && lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + { + lwp_put_to_user(rmtp, (void *)&rmtp_k, sizeof rmtp_k); + if (ret != 0) + { + return -EINTR; + } + } +#else + if (rmtp) + { + if (!lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + { + return -EFAULT; + } + ret = nanosleep(rqtp, rmtp); + } +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Retrieves the current time and timezone information. + * + * This system call retrieves the current time in seconds and microseconds since + * the Unix epoch (1970-01-01 00:00:00 UTC) and stores it in the `tp` argument. + * It also retrieves the timezone information, if requested, and stores it in the + * `tzp` argument. The time returned is the local time of the system unless + * UTC is specified. + * + * @param tp A pointer to a `struct timeval` where the current time will be stored. + * The `struct timeval` contains two fields: + * - `tv_sec`: The number of seconds since the Unix epoch. + * - `tv_usec`: The number of microseconds past `tv_sec`. + * @param tzp A pointer to a `struct timezone` where the timezone information will + * be stored. This structure contains two fields: + * - `tz_minuteswest`: The number of minutes west of UTC. + * - `tz_dsttime`: A flag indicating the type of daylight saving time (DST) adjustment. + * If `tzp` is `NULL`, timezone information is not provided. + * @return On success, returns `0`. On failure, returns error code. + * + * @note The `struct timeval` represents the current time in seconds and microseconds, + * while `struct timezone` provides information on the timezone relative to UTC + * and any daylight saving adjustments. + * + * @warning Ensure that `tp` and `tzp` are valid pointers before calling the function. + * If you don't need timezone information, you can pass `NULL` for `tzp`. + * + * @see sys_time(), time(), gettimeofday() + */ +sysret_t sys_gettimeofday(struct timeval *tp, struct timezone *tzp) +{ +#ifdef ARCH_MM_MMU + struct timeval t_k; + + if (tp) + { + if (!lwp_user_accessable((void *)tp, sizeof *tp)) + { + return -EFAULT; + } + + t_k.tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; + t_k.tv_usec = (rt_tick_get() % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND); + + lwp_put_to_user(tp, (void *)&t_k, sizeof t_k); + } +#else + if (tp) + { + if (!lwp_user_accessable((void *)tp, sizeof *tp)) + { + return -EFAULT; + } + tp->tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; + tp->tv_usec = (rt_tick_get() % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND); + } +#endif + + return 0; +} + +/** + * @brief Sets the system's current time and timezone information. + * + * @param tv A pointer to a `struct timeval` that contains the new system time to set. + * The `struct timeval` consists of: + * - `tv_sec`: The number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * - `tv_usec`: The number of microseconds past `tv_sec`. + * @param tzp A pointer to a `struct timezone` that contains the new timezone settings. + * The `struct timezone` consists of: + * - `tz_minuteswest`: The number of minutes west of UTC. + * - `tz_dsttime`: The type of daylight saving time adjustment. + * If `tzp` is `NULL`, the timezone information is not changed. + * @return On success, returns `0`. On failure, returns error code. + * + * @note haven't supported now. + * + * @see sys_gettimeofday(), settimeofday(), time() + */ +sysret_t sys_settimeofday(const struct timeval *tv, const struct timezone *tzp) +{ + return 0; +} + +/** + * @brief Sets the time of a specified clock. + * + * This function sets the time of the specified clock (identified by `clk`) to the given value. The time is provided + * as a `struct timespec` containing seconds and nanoseconds. This function can be used to set the system clock or + * other specific clocks, such as monotonic or real-time clocks. + * + * @param[in] clk The clock ID for which to set the time. The clock can be one of the following: + * - `CLOCK_REALTIME`: Set the system's real-time clock. + * - `CLOCK_MONOTONIC`: Set the monotonic clock, which measures time since some unspecified starting point. + * - `CLOCK_PROCESS_CPUTIME_ID`: Set the CPU time used by the process. + * - `CLOCK_THREAD_CPUTIME_ID`: Set the CPU time used by the current thread. + * + * @param[in] ts A pointer to a `struct timespec` containing the new time to set for the specified clock. + * - `ts->tv_sec`: Seconds since the epoch (for `CLOCK_REALTIME`) or since some unspecified start point (for other clocks). + * - `ts->tv_nsec`: Nanoseconds within the current second. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function requires appropriate permissions for setting the system's clock. In some systems, only privileged users + * may change the `CLOCK_REALTIME` clock. + * + * @see sys_clock_gettime(), sys_clock_getres() + */ +sysret_t sys_clock_settime(clockid_t clk, const struct timespec *ts) +{ + int ret = 0; + +#ifdef ARCH_MM_MMU + size_t size = sizeof(struct timespec); + struct timespec *kts = NULL; + + if (!lwp_user_accessable((void *)ts, size)) + { + return -EFAULT; + } + + kts = kmem_get(size); + if (!kts) + { + return -ENOMEM; + } + + lwp_get_from_user(kts, (void *)ts, size); + ret = clock_settime(clk, kts); + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kts); + + return ret; +#else + if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) + { + return -EFAULT; + } + ret = clock_settime(clk, ts); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Retrieves the current time of a specified clock. + * + * This function retrieves the current time of the specified clock (identified by `clk`) and stores it in the + * `struct timespec` pointed to by `ts`. The time is expressed in seconds and nanoseconds. The clock can be + * one of several types, such as real-time, monotonic, or process-specific clocks. + * + * @param[in] clk The clock ID for which to get the time. The clock can be one of the following: + * - `CLOCK_REALTIME`: Get the system's real-time clock. + * - `CLOCK_MONOTONIC`: Get the monotonic clock, which measures time since some unspecified starting point. + * - `CLOCK_PROCESS_CPUTIME_ID`: Get the CPU time used by the process. + * - `CLOCK_THREAD_CPUTIME_ID`: Get the CPU time used by the current thread. + * + * @param[out] ts A pointer to a `struct timespec` where the current time for the specified clock will be stored. + * - `ts->tv_sec`: Seconds since the epoch (for `CLOCK_REALTIME`) or since some unspecified start point (for other clocks). + * - `ts->tv_nsec`: Nanoseconds within the current second. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function requires appropriate permissions for retrieving certain clocks (e.g., `CLOCK_REALTIME`). + * + * @see sys_clock_settime(), sys_clock_getres() + */ +sysret_t sys_clock_gettime(clockid_t clk, struct timespec *ts) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + size_t size = sizeof(struct timespec); + struct timespec *kts = NULL; + + if (!lwp_user_accessable((void *)ts, size)) + { + return -EFAULT; + } + + kts = kmem_get(size); + if (!kts) + { + return -ENOMEM; + } + + ret = clock_gettime(clk, kts); + if (ret != -1) + { + lwp_put_to_user(ts, kts, size); + } + + if (ret < 0) + { + ret = GET_ERRNO(); + } + + kmem_put(kts); + + return ret; +#else + if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) + { + return -EFAULT; + } + ret = clock_gettime(clk, ts); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/** + * @brief Suspends the execution of the calling thread for the specified time duration. + * + * This function causes the calling thread to sleep for the specified time duration, which is provided as a + * `struct timespec` containing seconds and nanoseconds. The sleep is done based on the specified clock (identified by `clk`). + * If the `flags` parameter is set to `TIMER_ABSTIME`, the specified time represents an absolute time, otherwise, + * it represents a relative time interval. + * + * @param[in] clk The clock ID for which to perform the sleep. The clock can be one of the following: + * - `CLOCK_REALTIME`: Use the system's real-time clock. + * - `CLOCK_MONOTONIC`: Use the monotonic clock, which measures time since some unspecified starting point. + * - `CLOCK_PROCESS_CPUTIME_ID`: Use the CPU time used by the process. + * - `CLOCK_THREAD_CPUTIME_ID`: Use the CPU time used by the current thread. + * + * @param[in] flags The sleep behavior flags. The possible flags are: + * - `0`: The sleep duration is relative to the current time. + * - `TIMER_ABSTIME`: The sleep duration is absolute (measured from the specified clock). + * + * @param[in] rqtp A pointer to a `struct timespec` containing the requested sleep time. + * - `rqtp->tv_sec`: Seconds to sleep. + * - `rqtp->tv_nsec`: Nanoseconds to sleep (0 ≤ `rqtp->tv_nsec` < 1 billion). + * + * @param[out] rmtp A pointer to a `struct timespec` where the remaining time will be stored if the sleep is interrupted. + * If the sleep completes successfully, `rmtp` will not be modified. + * - `rmtp->tv_sec`: Remaining seconds. + * - `rmtp->tv_nsec`: Remaining nanoseconds. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function can be interrupted by signals. In that case, the remaining time is returned in `rmtp`. + * If the sleep is not interrupted, the function returns `SYSRET_OK` when the requested time has passed. + * + * @see sys_clock_gettime(), sys_clock_settime(), sys_nanosleep() + */ +sysret_t sys_clock_nanosleep(clockid_t clk, int flags, const struct timespec *rqtp, struct timespec *rmtp) +{ + int ret = 0; + LOG_D("sys_nanosleep\n"); + if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) + { + return -EFAULT; + } + +#ifdef ARCH_MM_MMU + struct timespec rqtp_k; + struct timespec rmtp_k; + + lwp_get_from_user(&rqtp_k, (void *)rqtp, sizeof rqtp_k); + ret = clock_nanosleep(clk, flags, &rqtp_k, &rmtp_k); + if ((ret != -1 || rt_get_errno() == EINTR) && rmtp && lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + { + lwp_put_to_user(rmtp, (void *)&rmtp_k, sizeof rmtp_k); + if (ret != 0) + { + return -EINTR; + } + } +#else + if (rmtp) + { + if (!lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + { + return -EFAULT; + } + ret = clock_nanosleep(clk, flags, rqtp, rmtp); + } +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Get the resolution of the specified clock. + * + * This function retrieves the resolution of the specified clock, which is the smallest time interval that the clock can measure. + * The resolution is returned in a `struct timespec` which contains seconds and nanoseconds. + * + * @param[in] clk The clock ID for which to get the resolution. The clock can be one of the following: + * - `CLOCK_REALTIME`: System's real-time clock. + * - `CLOCK_MONOTONIC`: Monotonic clock (measures time since an unspecified point). + * - `CLOCK_PROCESS_CPUTIME_ID`: CPU time consumed by the current process. + * - `CLOCK_THREAD_CPUTIME_ID`: CPU time consumed by the current thread. + * + * @param[out] ts A pointer to a `struct timespec` where the clock's resolution will be stored. + * - `ts->tv_sec`: The number of seconds in the resolution. + * - `ts->tv_nsec`: The number of nanoseconds in the resolution (0 ≤ `ts->tv_nsec` < 1 billion). + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The resolution may be zero for some clocks, depending on the system's configuration. + * A clock's resolution determines the smallest unit of time the clock can measure. + * + * @see sys_clock_gettime(), sys_clock_settime(), sys_clock_nanosleep() + */ +sysret_t sys_clock_getres(clockid_t clk, struct timespec *ts) +{ + int ret = 0; +#ifdef ARCH_MM_MMU + struct timespec kts; + size_t size = sizeof(struct timespec); + + if (!lwp_user_accessable((void *)ts, size)) + { + return -EFAULT; + } + + ret = clock_getres(clk, &kts); + + if (ret != -1) + { + lwp_put_to_user(ts, &kts, size); + } +#else + if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) + { + return -EFAULT; + } + ret = clock_getres(clk, ts); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Rename or move a file or directory. + * + * This function renames or moves a file or directory from `oldpath` to `newpath`. + * If the `newpath` exists, it may be overwritten depending on the system's file system behavior and + * the permissions of the files involved. + * + * @param[in] oldpath The current path of the file or directory to rename or move. + * @param[in] newpath The new path or name to which the file or directory should be renamed or moved. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note If `oldpath` and `newpath` refer to different file systems, the behavior may vary, + * as the operation might involve copying and removing files instead of simply renaming them. + * The success of the operation also depends on the permissions of the source and destination. + * + * @see sys_unlink(), sys_mkdir(), sys_access() + */ +sysret_t sys_rename(const char *oldpath, const char *newpath) +{ + int ret = -1; +#ifdef ARCH_MM_MMU + int err; + + err = lwp_user_strlen(oldpath); + if (err <= 0) + { + return -EFAULT; + } + + err = lwp_user_strlen(newpath); + if (err <= 0) + { + return -EFAULT; + } +#endif + ret = rename(oldpath, newpath); + return (ret < 0 ? GET_ERRNO() : ret); +} + +typedef unsigned long long rlim_t; + +struct rlimit +{ + rlim_t rlim_cur; + rlim_t rlim_max; +}; + +#define RLIMIT_CPU 0 +#define RLIMIT_FSIZE 1 +#define RLIMIT_DATA 2 +#define RLIMIT_STACK 3 +#define RLIMIT_CORE 4 +#define RLIMIT_RSS 5 +#define RLIMIT_NPROC 6 +#define RLIMIT_NOFILE 7 +#define RLIMIT_MEMLOCK 8 +#define RLIMIT_AS 9 + +/** + * @brief Sets or gets process resource limits. + * + * This implementation is currently unsupported and returns `-ENOSYS`. + * + * @param[in] pid The target process ID. + * @param[in] resource The resource limit identifier. + * @param[in] new_rlim The new limit, or `NULL`. + * @param[out] old_rlim Receives the current limit, or `NULL`. + * @return sysret_t Always returns `-ENOSYS`. + */ +sysret_t sys_prlimit64(pid_t pid, + unsigned int resource, + const struct rlimit *new_rlim, + struct rlimit *old_rlim) +{ + return -ENOSYS; +} + +/** + * @brief Get resource limits. + * + * This function retrieves the current resource limits for the specified resource type. + * The resource limit specifies the maximum value for a particular resource that a process or thread can use. + * The limits are returned in an array `rlim` where: + * - `rlim[0]` represents the soft limit (the current value). + * - `rlim[1]` represents the hard limit (the maximum allowable value). + * + * @param[in] resource The resource for which to get the limits. It can be one of the following: + * - `RLIMIT_NOFILE`: Maximum number of file descriptors. + * + * @param[out] rlim An array to store the resource limits. The array should have at least 2 elements: + * - `rlim[0]`: The soft limit for the resource. + * - `rlim[1]`: The hard limit for the resource. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The limits returned by `sys_getrlimit` are subject to system constraints and may vary across different systems. + * + * @see sys_setrlimit(), sys_getrlimit64(), sys_getrusage() + */ +sysret_t sys_getrlimit(unsigned int resource, unsigned long rlim[2]) +{ + int ret = -1; + unsigned long krlim[2] = { 0, 0 }; + + if (!lwp_user_accessable((void *)rlim, sizeof(unsigned long[2]))) + { + return -EFAULT; + } + + if (lwp_get_from_user(krlim, rlim, sizeof(unsigned long[2])) != sizeof(unsigned long[2])) + { + return -EINVAL; + } + + switch (resource) + { + case RLIMIT_NOFILE: + { + struct dfs_fdtable *fdt = dfs_fdtable_get(); + + dfs_file_lock(); + krlim[0] = fdt->maxfd; + dfs_file_unlock(); + krlim[1] = DFS_FD_MAX; + ret = 0; + } + break; + default: + return -EINVAL; + break; + } + + lwp_put_to_user((void *)rlim, krlim, sizeof(unsigned long[2])); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +/** + * @brief Sets a process resource limit. + * + * This implementation is currently unsupported and returns `-ENOSYS`. + * + * @param[in] resource The resource limit identifier. + * @param[in] rlim The new resource limit. + * @return sysret_t Always returns `-ENOSYS`. + */ +sysret_t sys_setrlimit(unsigned int resource, struct rlimit *rlim) +{ + return -ENOSYS; +} + +/** + * @brief Get random data from the kernel's random number generator. + * + * This function retrieves cryptographically secure random data from the kernel's random number generator + * and stores it in the buffer provided by the user. The data can be used for cryptographic operations or + * other applications requiring randomization. + * + * @param[out] buf A pointer to the buffer where the random data will be stored. The buffer must be large + * enough to hold the requested amount of random data. + * @param[in] buflen The number of bytes of random data to retrieve. This value must be a positive integer. + * @param[in] flags Optional flags to modify the behavior of the random data retrieval. Possible values may include: + * - `GRND_RANDOM`: Requests random data from the system's non-blocking random source. + * - `GRND_NONBLOCK`: Instructs the function to return immediately even if insufficient entropy is available. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note + * - If the `GRND_NONBLOCK` flag is not set and there is insufficient entropy in the system's random pool, + * this function may block until enough entropy is available. + * - The function returns cryptographically secure random data, suitable for use in secure applications. + * + * @see sys_random(), sys_getentropy() + */ +sysret_t sys_getrandom(void *buf, size_t buflen, unsigned int flags) +{ + int ret = -1; + int count = 0; + void *kmem = RT_NULL; + rt_device_t rd_dev = RT_NULL; + + if (flags & GRND_RANDOM) + { + rd_dev = rt_device_find("random"); + } + else + { + rd_dev = rt_device_find("urandom"); + } + + if (rd_dev == RT_NULL) + { + return -EFAULT; + } + + if (rt_device_open(rd_dev, RT_DEVICE_OFLAG_RDONLY) != RT_EOK) + { + return -EFAULT; + } + + if (!lwp_user_accessable(buf, buflen)) + { + rt_device_close(rd_dev); + return -EFAULT; + } + +#ifdef ARCH_MM_MMU + kmem = kmem_get(buflen); + if (!kmem) + { + rt_device_close(rd_dev); + return -ENOMEM; + } + + while (count < buflen) + { + ret = rt_device_read(rd_dev, count, (char *)kmem + count, buflen - count); + if (ret <= 0) + { + break; + } + count += ret; + } + rt_device_close(rd_dev); + + ret = count; + if (count > 0) + { + ret = lwp_put_to_user(buf, kmem, count); + } + kmem_put(kmem); +#else + while (count < buflen) + { + ret = rt_device_read(rd_dev, count, (char *)kmem + count, buflen - count); + if (ret <= 0) + { + break; + } + count += ret; + } + rt_device_close(rd_dev); + + ret = count; +#endif + return ret; +} + +/** + * @brief Read the value of a symbolic link. + * + * This function reads the value of a symbolic link and stores it in the provided buffer. The value is the + * path to which the symbolic link points. If the symbolic link is too long to fit in the provided buffer, + * the function returns the number of bytes needed to store the entire path (not including the terminating null byte). + * + * @param[in] path The path of the symbolic link to read. + * @param[out] buf A buffer where the symbolic link's target will be stored. The buffer must be large enough + * to hold the path of the symbolic link. + * @param[in] bufsz The size of the buffer `buf`. It specifies the maximum number of bytes to read. + * + * @return ssize_t The number of bytes written to `buf` (excluding the terminating null byte) on success. + * On failure, it returns a negative error code: + * - `-EINVAL`: Invalid path. + * - `-ENOMEM`: Insufficient memory to read the link. + * - `-EFAULT`: Invalid address for the `buf`. + * + * @note It will (silently) truncate the contents(to a length of bufsiz characters), + * in case the buffer is too small to hold all of the contents. + * + * @see sys_symlink(), sys_lstat() + */ +ssize_t sys_readlink(char *path, char *buf, size_t bufsz) +{ + size_t len, copy_len; + int err, rtn; + char *copy_path; + + len = lwp_user_strlen(path); + if (len <= 0) + { + return -EFAULT; + } + + if (!lwp_user_accessable(buf, bufsz)) + { + return -EINVAL; + } + + copy_path = (char *)rt_malloc(len + 1); + if (!copy_path) + { + return -ENOMEM; + } + + copy_len = lwp_get_from_user(copy_path, path, len); + copy_path[copy_len] = '\0'; + + char *link_fn = (char *)rt_malloc(DFS_PATH_MAX); + if (link_fn) + { + err = dfs_file_readlink(copy_path, link_fn, DFS_PATH_MAX); + if (err > 0) + { + buf[bufsz > err ? err : bufsz] = '\0'; + rtn = lwp_put_to_user(buf, link_fn, bufsz > err ? err : bufsz); + } + else + { + rtn = -EIO; + } + rt_free(link_fn); + } + else + { + rtn = -ENOMEM; + } + + rt_free(copy_path); + return rtn; +} + +/** + * @brief Set the CPU affinity mask of a process. + * + * This function sets the CPU affinity mask for a specified process. The affinity mask determines which CPUs the + * process is allowed to execute on. The process will be restricted to the CPUs specified in the mask. + * + * @param[in] pid The process ID of the process whose CPU affinity is to be set. If the `pid` is `0`, the + * affinity of the calling process will be modified. + * @param[in] size The size (in bytes) of the CPU set, typically `sizeof(cpu_set_t)`. + * @param[in] set A pointer to the CPU set. The CPU set is a bitmask representing which CPUs the process + * is allowed to run on. The bitmask must have enough bits to cover the number of CPUs on the system. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The CPU set is represented as a bitmask where each bit corresponds to a CPU. If the bit is set, the process + * can execute on that CPU. The number of CPUs is platform-dependent, and the size of `set` must be large enough + * to hold a bit for each CPU. + * + * @see sys_sched_getaffinity(), sys_setcpu() + */ +sysret_t sys_sched_setaffinity(pid_t pid, size_t size, void *set) +{ + void *kset = RT_NULL; + + if (size <= 0 || size > sizeof(cpu_set_t)) + { + return -EINVAL; + } + if (!lwp_user_accessable((void *)set, size)) + { + return -EFAULT; + } + + kset = kmem_get(size); + if (kset == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(kset, set, size) != size) + { + kmem_put(kset); + return -EINVAL; + } + + for (int i = 0; i < size * 8; i++) + { + if (CPU_ISSET_S(i, size, kset)) + { + kmem_put(kset); + + /** + * yes it's tricky. + * But when we talk about 'pid' from GNU libc, it's the 'task-id' + * aka 'thread->tid' known in kernel. + */ + return lwp_setaffinity(pid, i); + } + } + + kmem_put(kset); + + return -1; +} + +/** + * @brief Get the CPU affinity mask of a process. + * + * This function retrieves the CPU affinity mask for a specified process. The affinity mask indicates which CPUs the + * process is allowed to execute on. The process can run on any of the CPUs represented by the bits set in the mask. + * + * @param[in] pid The process ID of the process whose CPU affinity is to be retrieved. If `pid` is `0`, the + * affinity mask of the calling process will be retrieved. + * @param[in] size The size (in bytes) of the CPU set, typically `sizeof(cpu_set_t)`. + * @param[out] set A pointer to a buffer where the CPU affinity mask will be stored. The mask is represented + * as a bitmask, where each bit corresponds to a CPU. The bit is set if the process can run on that CPU. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The CPU set is represented as a bitmask where each bit corresponds to a CPU. If the bit is set, the process + * can execute on that CPU. The number of CPUs is platform-dependent, and the size of `set` must be large enough + * to hold a bit for each CPU. + * + * @see sys_sched_setaffinity(), sys_getcpu() + */ +sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set) +{ +#ifdef ARCH_MM_MMU + LWP_DEF_RETURN_CODE(rc); + void *mask; + struct rt_lwp *lwp; + + if (size <= 0 || size > sizeof(cpu_set_t)) + { + return -EINVAL; + } + if (!lwp_user_accessable(set, size)) + { + return -EFAULT; + } + mask = kmem_get(size); + if (!mask) + { + return -ENOMEM; + } + + CPU_ZERO_S(size, mask); + + lwp_pid_lock_take(); + lwp = lwp_from_pid_locked(pid); + + if (!lwp) + { + rc = -ESRCH; + } + else + { +#ifdef RT_USING_SMP + if (lwp->bind_cpu == RT_CPUS_NR) /* not bind */ + { + for (int i = 0; i < RT_CPUS_NR; i++) + { + CPU_SET_S(i, size, mask); + } + } + else /* set bind cpu */ + { + /* TODO: only single-core bindings are now supported of rt-smart */ + CPU_SET_S(lwp->bind_cpu, size, mask); + } +#else + CPU_SET_S(0, size, mask); +#endif + + if (lwp_put_to_user(set, mask, size) != size) + { + rc = -EFAULT; + } + else + { + rc = size; + } + } + + lwp_pid_lock_release(); + + kmem_put(mask); + + LWP_RETURN(rc); +#else + return -1; +#endif +} + +/** + * @brief Retrieve system information. + * + * This function provides details about the current state of the system, such as uptime, memory usage, + * load average, and other key statistics. The information is stored in a structure pointed to by `info`. + * + * @param[out] info A pointer to a buffer where system information will be stored. The structure should + * be compatible with the format expected by the system, typically `struct sysinfo`. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The structure of `info` must be predefined and consistent with the system's expectations. + * This function does not allocate memory for `info`; the caller must provide sufficient + * memory for the structure. + */ +sysret_t sys_sysinfo(void *info) +{ +#ifdef ARCH_MM_MMU + struct sysinfo kinfo = { 0 }; + rt_size_t total_pages = 0, free_pages = 0; + + if (!lwp_user_accessable(info, sizeof(struct sysinfo))) + { + return -EFAULT; + } + + kinfo.uptime = rt_tick_get_millisecond() / 1000; + /* TODO: 1, 5, and 15 minute load averages */ + kinfo.loads[0] = kinfo.loads[1] = kinfo.loads[2] = rt_object_get_length(RT_Object_Class_Thread); + rt_page_get_info(&total_pages, &free_pages); + kinfo.totalram = total_pages; + kinfo.freeram = free_pages; + + /* TODO: implementation procfs, here is counter the lwp number */ + struct lwp_avl_struct *pids = lwp_get_pid_ary(); + for (int index = 0; index < RT_LWP_MAX_NR; index++) + { + struct rt_lwp *lwp = (struct rt_lwp *)pids[index].data; + + if (lwp) + { + kinfo.procs++; + } + } + + rt_page_high_get_info(&total_pages, &free_pages); + kinfo.totalhigh = total_pages; + kinfo.freehigh = free_pages; + kinfo.mem_unit = ARCH_PAGE_SIZE; + + if (lwp_put_to_user(info, &kinfo, sizeof(struct sysinfo)) != sizeof(struct sysinfo)) + { + return -EFAULT; + } + + return 0; +#else + return -1; +#endif +} + +static rt_bool_t _sys_sched_priority_is_valid(int priority); + +/** + * @brief Set scheduling parameters for a specific thread. + * + * This function allows setting the scheduling parameters for a thread identified by its thread ID (`tid`). + * The parameters are provided via the `param` argument, which should be a structure compatible with the + * system's scheduling policies. + * + * @param[in] tid The thread ID of the target thread for which the scheduling parameters are to be set. + * @param[in] param A pointer to a structure containing the new scheduling parameters. The structure + * typically includes fields like priority and policy. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The caller must have appropriate permissions to change the scheduling parameters of the specified thread. + * The exact structure and fields of `param` depend on the system's implementation and scheduling policies. + */ +sysret_t sys_sched_setparam(pid_t tid, void *param) +{ + struct sched_param *sched_param = RT_NULL; + rt_thread_t thread; + int ret = -1; + + if (!lwp_user_accessable(param, sizeof(struct sched_param))) + { + return -EFAULT; + } + + sched_param = kmem_get(sizeof(struct sched_param)); + if (sched_param == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(sched_param, param, sizeof(struct sched_param)) != sizeof(struct sched_param)) + { + kmem_put(sched_param); + return -EINVAL; + } + + if (!_sys_sched_priority_is_valid(sched_param->sched_priority)) + { + kmem_put(sched_param); + return -EINVAL; + } + + thread = lwp_tid_get_thread_and_inc_ref(tid); + + if (thread) + { + ret = rt_thread_control(thread, RT_THREAD_CTRL_RESET_PRIORITY, (void *)&sched_param->sched_priority); + } + + lwp_tid_dec_ref(thread); + + kmem_put(sched_param); + + return ret; +} + +/** + * @brief Relinquish the processor voluntarily. + * + * This function causes the calling thread to yield the processor, allowing other threads + * that are ready to run to execute. The thread will be placed back into the scheduler's + * ready queue and may be rescheduled according to its priority and the system's scheduling policy. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note This function is typically used in cooperative multitasking scenarios or when a thread + * explicitly determines it no longer needs the processor at the moment. + */ +sysret_t sys_sched_yield(void) +{ + rt_thread_yield(); + return 0; +} + +/** + * @brief Retrieve the scheduling parameters of a specific thread. + * + * This function retrieves the scheduling parameters of the thread identified by the thread ID (`tid`). + * The retrieved parameters are stored in the structure pointed to by the `param` argument. + * + * @param[in] tid The thread ID of the target thread whose scheduling parameters are to be retrieved. + * @param[out] param A pointer to a structure where the scheduling parameters will be stored. The structure + * typically includes fields like priority and policy. + * + * @return sysret_t Returns `0` on success. On failure, returns a negative error code. + * + * @note The caller must have appropriate permissions to query the scheduling parameters of the specified thread. + * The exact structure and fields of `param` depend on the system's implementation and scheduling policies. + */ +sysret_t sys_sched_getparam(const pid_t tid, void *param) +{ + struct sched_param *sched_param = RT_NULL; + rt_thread_t thread; + int ret = -1; + + if (!lwp_user_accessable(param, sizeof(struct sched_param))) + { + return -EFAULT; + } + + sched_param = kmem_get(sizeof(struct sched_param)); + if (sched_param == RT_NULL) + { + return -ENOMEM; + } + + thread = lwp_tid_get_thread_and_inc_ref(tid); + + if (thread) + { + sched_param->sched_priority = RT_SCHED_PRIV(thread).current_priority; + ret = 0; + } + + lwp_tid_dec_ref(thread); + + lwp_put_to_user((void *)param, sched_param, sizeof(struct sched_param)); + kmem_put(sched_param); + + return ret; +} + +/** + * @brief Get the maximum priority for a given scheduling policy. + * + * This function retrieves the maximum priority value that can be used with the specified + * scheduling policy. + * + * @param[in] policy The scheduling policy for which to retrieve the maximum priority. + * + * @return sysret_t Returns the maximum priority value on success. On failure, returns a + * negative error code. + * + * @note The valid priority range depends on the system's configuration and the selected + * scheduling policy. The returned value represents the highest priority that can + * be assigned to a thread using the given policy. + */ +sysret_t sys_sched_get_priority_max(int policy) +{ + if (policy < 0) + { + SET_ERRNO(EINVAL); + return -rt_get_errno(); + } + return RT_THREAD_PRIORITY_MAX; +} + +/** + * @brief Get the minimum priority for a given scheduling policy. + * + * This function retrieves the minimum priority value that can be used with the specified + * scheduling policy. + * + * @param[in] policy The scheduling policy for which to retrieve the minimum priority. + * + * @return sysret_t Returns the minimum priority value on success. On failure, returns a + * negative error code. + * + * @note The valid priority range depends on the system's configuration and the selected + * scheduling policy. The returned value represents the lowest priority that can + * be assigned to a thread using the given policy. + */ +sysret_t sys_sched_get_priority_min(int policy) +{ + if (policy < 0) + { + SET_ERRNO(EINVAL); + return -rt_get_errno(); + } + return 0; +} + +static rt_bool_t _sys_sched_priority_is_valid(int priority) +{ + return (priority >= 0) && (priority < RT_THREAD_PRIORITY_MAX); +} + +#if defined(RT_USING_UTESTCASES) && defined(RT_USING_SMART) +rt_bool_t rt_utest_sys_sched_priority_is_valid(int priority) +{ + return _sys_sched_priority_is_valid(priority); +} +#endif + +/** + * @brief Set the scheduling policy and parameters for a thread. + * + * This function sets the scheduling policy and associated parameters for the specified thread. + * It allows controlling the scheduling behavior of threads. + * + * @param[in] tid The thread ID of the target thread. + * @param[in] policy The scheduling policy to be applied. Common values include: + * - `SCHED_FIFO`: First-in, first-out scheduling. + * - `SCHED_RR`: Round-robin scheduling. + * - `SCHED_OTHER`: Default or standard scheduling. + * @param[in] param Pointer to a structure containing scheduling parameters, such as priority. + * The structure type depends on the system implementation. + * + * @return sysret_t Returns 0 on success. On failure, returns a negative error code. + * + * @note This function requires appropriate permissions to modify the scheduling settings + * of another thread. For most systems, elevated privileges may be required. + * Ensure the `param` structure is properly initialized for the given `policy`. + */ +sysret_t sys_sched_setscheduler(int tid, int policy, void *param) +{ + sysret_t ret; + struct sched_param *sched_param = RT_NULL; + rt_thread_t thread = RT_NULL; + + if (!lwp_user_accessable(param, sizeof(struct sched_param))) + { + return -EFAULT; + } + + sched_param = kmem_get(sizeof(struct sched_param)); + if (sched_param == RT_NULL) + { + return -ENOMEM; + } + + if (lwp_get_from_user(sched_param, param, sizeof(struct sched_param)) != sizeof(struct sched_param)) + { + kmem_put(sched_param); + return -EINVAL; + } + + if (!_sys_sched_priority_is_valid(sched_param->sched_priority)) + { + kmem_put(sched_param); + return -EINVAL; + } + + thread = lwp_tid_get_thread_and_inc_ref(tid); + ret = rt_thread_control(thread, RT_THREAD_CTRL_RESET_PRIORITY, (void *)&sched_param->sched_priority); + lwp_tid_dec_ref(thread); + + kmem_put(sched_param); + + return ret; +} + +/** + * @brief Get the scheduling policy of a thread. + * + * This function retrieves the current scheduling policy of the specified thread. + * + * @param[in] tid The thread ID of the target thread. + * + * @return sysret_t Returns the scheduling policy of the thread on success. On failure, + * returns a negative error code. + * + * @note The caller must have appropriate permissions to query the scheduling policy + * of the specified thread. + */ +sysret_t sys_sched_getscheduler(int tid) +{ + rt_thread_t thread = RT_NULL; + int rtn; + + thread = lwp_tid_get_thread_and_inc_ref(tid); + lwp_tid_dec_ref(thread); + + if (thread) + { + rtn = SCHED_RR; + } + else + { + rtn = -ESRCH; + } + + return rtn; +} + +/** + * @brief Flush the file descriptors' data to disk. + * + * This function flushes all modified data of the file associated with the specified + * file descriptor to disk, ensuring that any changes made to the file are committed + * to permanent storage. + * + * @param[in] fd The file descriptor associated with the file to be flushed. It should + * refer to an open file. + * + * @return sysret_t Returns `0` (0) on success. On failure, returns a negative + * error code. + * + * @note The `fsync` function ensures that all data written to the file is physically + * stored on disk, but it does not guarantee that all file metadata is flushed. + * To flush both data and metadata, `fdatasync` can be used. + */ +sysret_t sys_fsync(int fd) +{ + int res = fsync(fd); + if (res < 0) + { + res = rt_get_errno(); + } + return res; +}