diff options
Diffstat (limited to 'components/lwp/syscall/syscall_time_sched.c')
| -rw-r--r-- | components/lwp/syscall/syscall_time_sched.c | 1245 |
1 files changed, 1245 insertions, 0 deletions
diff --git a/components/lwp/syscall/syscall_time_sched.c b/components/lwp/syscall/syscall_time_sched.c new file mode 100644 index 0000000000..fd8db54058 --- /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; +} |
