summaryrefslogtreecommitdiff
path: root/components/lwp/syscall/syscall_system.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/lwp/syscall/syscall_system.c')
-rw-r--r--components/lwp/syscall/syscall_system.c647
1 files changed, 647 insertions, 0 deletions
diff --git a/components/lwp/syscall/syscall_system.c b/components/lwp/syscall/syscall_system.c
new file mode 100644
index 0000000000..6296ea40c6
--- /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;
+}