diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000..a7f6be4 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,62 @@ +name: Nightly Build + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +permissions: + contents: write + +jobs: + build-and-release: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + make \ + gcc-x86-64-linux-gnu \ + binutils-x86-64-linux-gnu \ + nasm \ + xorriso + sudo ln -sf /usr/bin/x86_64-linux-gnu-gcc /usr/local/bin/x86_64-elf-gcc + sudo ln -sf /usr/bin/x86_64-linux-gnu-ld /usr/local/bin/x86_64-elf-ld + + - name: Build ISO + run: make -j4 + + - name: Update nightly tag + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -fa nightly -m "Nightly build ${GITHUB_SHA}" "${GITHUB_SHA}" + git push origin refs/tags/nightly --force + + - name: Prepare release metadata + id: metadata + run: | + echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + + - name: Publish nightly release asset + uses: softprops/action-gh-release@v2 + with: + tag_name: nightly + name: Nightly Build (${{ steps.metadata.outputs.short_sha }}) + body: | + This is an automated nightly build of BoredOS, this is not a final release and may be unstable. + + Built from commit: + - Full hash: `${{ github.sha }}` + - Short hash: `${{ steps.metadata.outputs.short_sha }}` + prerelease: true + make_latest: false + files: | + boredos.iso + overwrite_files: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/Makefile b/Makefile index c2dec17..64adf71 100644 --- a/Makefile +++ b/Makefile @@ -209,7 +209,7 @@ clean: run-windows: $(ISO_IMAGE) qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \ -smp 4 \ - -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \ + -audiodev dsound,id=audio0 -machine pcspk-audiodev=audio0 \ -vga std -global VGA.xres=1920 -global VGA.yres=1080 \ -drive file=disk.img,format=raw,file.locking=off run-mac: $(ISO_IMAGE) diff --git a/docs/README.md b/docs/README.md index a44df9d..6f15c6e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,7 +25,9 @@ Instructions for compiling the OS from source. ### 3. 🚀 [Application Development](appdev/) The SDK and toolchain guides for creating your own `.elf` userland binaries. -- [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls. +- [`SDK Reference`](appdev/sdk_reference.md): Overview hub for SDK layout, includes, and links to detailed libc/syscall docs. +- [`Syscalls`](appdev/syscalls.md): Current syscall numbers, FS/SYSTEM command IDs, and wrapper guidance. +- [`libc Reference`](appdev/libc_reference.md): Current libc headers, implemented APIs, and behavior notes. - [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`. - [`Widget API`](appdev/widget_api.md): High-level UI components like buttons, textboxes, and scrollbars using `libwidget.h`. - [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO. diff --git a/docs/appdev/libc_reference.md b/docs/appdev/libc_reference.md new file mode 100644 index 0000000..1f715e7 --- /dev/null +++ b/docs/appdev/libc_reference.md @@ -0,0 +1,184 @@ +# libc Reference + +This page documents the current BoredOS userland libc surface from `src/userland/libc/`. + +BoredOS libc is a compact implementation focused on the APIs used by in-tree apps. It is not a full glibc replacement. + +## Header Overview + +| Header | Focus | +|---|---| +| `stdlib.h` | allocation, conversion, process helpers | +| `string.h` | memory/string primitives | +| `stdio.h` | `FILE*` and formatted I/O | +| `unistd.h` | POSIX-like fd/process calls | +| `fcntl.h` | open/fcntl flags, `dup`, `pipe` | +| `input.h` | keyboard keycode constants | +| `signal.h` | signal handlers and masks | +| `sys/stat.h` | `stat`/`fstat` and file mode bits | +| `sys/types.h` | core typedefs (`pid_t`, `ssize_t`, ...) | +| `sys/wait.h` | `waitpid` and wait macros | +| `errno.h` | errno values | +| `time.h` | time/date utilities | +| `math.h` | floating-point math helpers | +| `libui.h` | GUI/window drawing API | + +## stdlib.h + +Implemented core functions: +- Memory: `malloc`, `free`, `calloc`, `realloc` +- Memory aliases: `memset`, `memcpy` +- Conversions: `atoi`, `itoa`, `strtod`, `abs` +- Output: `puts`, `printf` +- Process/environment: `exit`, `_exit`, `sleep`, `chdir`, `getcwd`, `access`, `system`, `getenv`, `abort` + +Notes: +- `sleep` is millisecond-based and maps to kernel sleep command. +- `system` is a stub-style helper in this libc, not a full shell launcher equivalent. + +## string.h + +Implemented C string/memory set includes: +- Memory: `memmove`, `memcmp`, `memcpy`, `memset`, `memchr` +- Search: `strchr`, `strrchr`, `strpbrk`, `strstr` +- Span: `strspn`, `strcspn` +- Compare: `strcmp`, `strncmp`, `strcasecmp`, `strncasecmp`, `strcoll` +- Build/copy: `strlen`, `strcpy`, `strcat`, `strdup` +- Errors: `strerror` + +## stdio.h + +Provided API includes: +- Stream open/close: `fopen`, `freopen`, `fclose` +- Read/write: `fread`, `fwrite`, `fgets`, `fputs`, `getc`, `fputc`, `putchar` +- Positioning: `fseek`, `ftell`, `filelength` +- Formatting: `fprintf`, `vfprintf`, `snprintf`, `vsnprintf`, `sprintf`, `sscanf` +- Stream state: `feof`, `ferror`, `clearerr`, `fflush`, `ungetc` +- Temp/filesystem helpers: `remove`, `rename`, `tmpfile`, `tmpnam` + +## unistd.h + +Provided POSIX-like interfaces: +- FD I/O: `read`, `write`, `close`, `lseek`, `isatty` +- Filesystem: `unlink` +- Exec family: `execv`, `execve`, `execvp`, `execl`, `execlp`, `execle` +- Process wait: `waitpid` + +Also defines: +- `SEEK_SET`, `SEEK_CUR`, `SEEK_END` +- `F_OK`, `X_OK`, `W_OK`, `R_OK` + +## fcntl.h + +Flags and fd control: +- Open flags: `O_RDONLY`, `O_WRONLY`, `O_RDWR`, `O_CREAT`, `O_EXCL`, `O_TRUNC`, `O_APPEND`, `O_NONBLOCK`, `O_ACCMODE` +- fcntl ops: `F_GETFL`, `F_SETFL` +- FD flag: `FD_CLOEXEC` (declared) + +Functions: +- `open` +- `fcntl` +- `dup` +- `dup2` +- `pipe` + +## input.h + +Defines keyboard/control keycode constants used by apps that process + +Current constants include: +- Arrow keys: `KEY_UP`, `KEY_DOWN`, `KEY_LEFT`, `KEY_RIGHT` +- Controls: `KEY_ENTER`, `KEY_BACKSPACE`, `KEY_ESCAPE`, `KEY_SPACE`, `KEY_ALT`, `KEY_CTRL_L`, `KEY_TAB` + +## signal.h + +Current signal surface: +- Basic handler API: `signal`, `raise`, `kill` +- POSIX-style API: `sigaction`, `sigprocmask`, `sigpending` +- Types: `sighandler_t`, `sigset_t`, `struct sigaction` +- Constants: `SIGINT`, `SIGTERM`, `SIGKILL`, `SIG_DFL`, `SIG_IGN`, `SIG_ERR` +- Mask ops: `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` +- Action flags: `SA_RESTART`, `SA_NODEFER`, `SA_RESETHAND` + +## ctype.h + +Character classification and case conversion: +- `isdigit`, `isalpha`, `isalnum`, `isspace` +- `isupper`, `islower`, `isxdigit` +- `iscntrl`, `ispunct`, `isprint`, `isgraph` +- `tolower`, `toupper` + +## locale.h + +Locale stubs and conventions: +- `struct lconv` +- `setlocale` +- `localeconv` +- `LC_ALL` + +## limits.h + +Integer and floating-point limit macros: +- `CHAR_BIT`, `INT_MIN`, `INT_MAX`, `UINT_MAX` +- `LONG_MIN`, `LONG_MAX`, `ULONG_MAX` +- `LLONG_MIN`, `LLONG_MAX`, `ULLONG_MAX` +- `DBL_MAX` + +## setjmp.h + +Non-local jump support: +- `jmp_buf` +- `setjmp` +- `longjmp` + +## time.h + +Time/date APIs and types: +- Types: `time_t`, `clock_t`, `struct tm` +- Constants: `CLOCKS_PER_SEC` +- Functions: `time`, `clock`, `localtime`, `gmtime`, `strftime`, `mktime` + +## libui.h + +Windowing and drawing API used by GUI apps: +- Window/event: `ui_window_create`, `ui_get_event`, `ui_mark_dirty`, `ui_window_set_title`, `ui_window_set_resizable` +- Drawing: `ui_draw_rect`, `ui_draw_rounded_rect_filled`, `ui_draw_string`, `ui_draw_string_bitmap`, `ui_draw_image` +- Text metrics/scaled text: `ui_get_string_width`, `ui_get_font_height`, `ui_draw_string_scaled`, `ui_draw_string_scaled_sloped`, `ui_get_string_width_scaled`, `ui_get_font_height_scaled` +- System UI helpers: `ui_get_screen_size`, `ui_set_font` + +## sys/stat.h and sys/types.h + +`sys/stat.h` provides: +- `struct stat` +- `stat`, `fstat`, `mkdir` +- mode/type macros (`S_IFREG`, `S_IFDIR`, `S_ISREG`, `S_ISDIR`, permission bits) + +Note: +- `access` is declared in `stdlib.h` in this libc. + +`sys/types.h` provides: +- `ssize_t`, `off_t`, `mode_t`, `pid_t`, `uid_t`, `gid_t` + +## sys/wait.h + +- `waitpid` +- `WNOHANG` +- status macros: `WEXITSTATUS`, `WIFEXITED`, `WTERMSIG`, `WIFSIGNALED` + +## errno.h + +Defined errno values include: +- Generic/input: `EINVAL`, `EDOM`, `ERANGE`, `E2BIG` +- File/path: `ENOENT`, `EEXIST`, `EISDIR`, `ENOTDIR`, `EBADF` +- Runtime/state: `ENOMEM`, `EACCES`, `EIO`, `EAGAIN`, `EINTR`, `ECHILD`, `EBUSY`, `EPIPE`, `ESPIPE`, `ENOSYS`, `ENOTSUP` + +## Relationship to raw syscalls + +- libc high-level I/O and process APIs are backed by wrappers in `src/userland/libc/syscall.c`. +- Full syscall command IDs and multiplexer details are documented in `docs/appdev/syscalls.md`. + +## Practical Guidance + +- Prefer libc APIs (`open`, `read`, `write`, `waitpid`, `sigaction`) for portability inside BoredOS userland. +- Use raw wrapper calls from `syscall.h` only for capabilities that do not yet have higher-level libc wrappers. +- Avoid numeric `sys_system(...)` command literals in app code; use `SYSTEM_CMD_*` macros. diff --git a/docs/appdev/sdk_reference.md b/docs/appdev/sdk_reference.md index 2fed154..92fd65a 100644 --- a/docs/appdev/sdk_reference.md +++ b/docs/appdev/sdk_reference.md @@ -1,214 +1,52 @@

Userland SDK Reference

-

Comprehensive manual for custom libc and system calls in BoredOS.

+

Overview and entry point for BoredOS userland development.

--- -BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features. +BoredOS provides a compact userland SDK for building `.elf` applications. +This page is the high-level map; detailed API references now live in dedicated pages. -All headers are located in `src/userland/libc/` (standard functions) and `src/wm/` (UI and widgets). -- `stdlib.h`: Memory, strings, and basic I/O. -- `math.h`: Freestanding floating-point math library. -- `libui.h`: Core window and drawing API. -- `libwidget.h`: High-level UI components. +## SDK Structure -## Standard Library (`stdlib.h` & `string.h`) +Primary headers are in `src/userland/libc/` and UI helpers are in `src/wm/`. -The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls. +- `stdlib.h`, `string.h`, `stdio.h`, `unistd.h`: core libc surface +- `syscall.h`: raw syscall wrappers and command constants +- `libui.h`: window creation, drawing, and event polling +- `libwidget.h`: higher-level reusable widgets +- `math.h`: freestanding math helpers -### Memory Allocation -* `void* malloc(size_t size);` - Allocate a block of memory on the heap. -* `void free(void* ptr);` - Free a previously allocated memory block. -* `void* calloc(size_t nmemb, size_t size);` - Allocate and zero-out a block of memory for an array. -* `void* realloc(void* ptr, size_t size);` - Resize an existing memory block. +## Detailed References -### Memory Manipulation (`string.h`) -* `void* memset(void *s, int c, size_t n);` - Fill a block of memory with a specific byte. -* `void* memcpy(void *dest, const void *src, size_t n);` - Copy memory from source to destination. -* `void* memmove(void *dest, const void *src, size_t n);` - Safely copy overlapping memory blocks. -* `int memcmp(const void *s1, const void *s2, size_t n);` - Compare two memory blocks. +- [`libc Reference`](libc_reference.md): current libc headers and implemented APIs +- [`Syscalls`](syscalls.md): syscall numbers, FS/SYSTEM command IDs, and wrappers +- [`UI API`](ui_api.md): drawing and event APIs +- [`Widget API`](widget_api.md): common widgets and interaction helpers -### String Utilities -* `size_t strlen(const char *s);` - Get the length of a string. -* `int strcmp(const char *s1, const char *s2);` - Compare two strings lexicographically. -* `char* strcpy(char *dest, const char *src);` - Copy a string to a destination buffer. -* `char* strcat(char *dest, const char *src);` - Concatenate two strings. - -### Conversion and Formatting -* `int atoi(const char *nptr);` - String to integer conversion. -* `void itoa(int n, char *buf);` - Integer to string conversion. - -### Input / Output -* `void puts(const char *s);` - Print a string followed by a newline to the standard output. -* `void printf(const char *fmt, ...);` - Formatted print to standard output (supports `%d`, `%s`, `%x`, etc.). - -### Process Control -* `void exit(int status);` - Terminate the current process. -* `void sleep(int ms);` - Pause execution for a specified number of milliseconds. - ---- - -## Math Library (`math.h`) - -BoredOS ships a freestanding floating-point math library in `libc/math.h`. It uses pure arithmetic — Taylor series, Newton-Raphson, and range-reduction — with no dependency on a host `libm` or hardware math intrinsics. It is automatically linked into every userland ELF. +## Typical Include Set ```c -#include "math.h" +#include +#include +#include +#include +#include ``` -### Constants +For GUI apps: -| Constant | Value | Description | -|---|---|---| -| `M_PI` | 3.14159… | π | -| `M_E` | 2.71828… | Euler's number | -| `M_LN2` | 0.69315… | Natural log of 2 | -| `M_SQRT2` | 1.41421… | √2 | -| `HUGE_VAL` | ~+∞ | Overflow sentinel | - -### Functions - -#### Absolute value & remainder -* `double fabs(double x);` — Absolute value. -* `double fmod(double x, double y);` — Floating-point remainder. Returns `0` when `y == 0`. - -#### Rounding -* `double floor(double x);` — Largest integer ≤ x. -* `double ceil(double x);` — Smallest integer ≥ x. - -#### Trigonometry *(arguments in radians)* -* `double sin(double x);` — Sine. Range-reduced to `[-π, π]` then computed via 8-term Taylor series. -* `double cos(double x);` — Cosine. Computed via `sin(x + π/2)`. -* `double tan(double x);` — Tangent. Returns sentinel `1e15` near poles. - -#### Exponential & logarithm -* `double sqrt(double x);` — Square root via Newton-Raphson (25 iterations). Returns `0` for `x ≤ 0`. -* `double log(double x);` — Natural logarithm (ln). Returns `-1e30` for `x ≤ 0`. -* `double log2(double x);` — Base-2 logarithm. -* `double log10(double x);` — Base-10 logarithm. -* `double exp(double x);` — e^x. Saturates to `1e300` for `x > 700`, `0` for `x < -700`. -* `double pow(double base, double exponent);` — General power. Integer exponents use fast binary exponentiation; fractional exponents use `exp(e * log(b))`. - -#### Hyperbolic -* `double sinh(double x);` — Hyperbolic sine. -* `double cosh(double x);` — Hyperbolic cosine. -* `double tanh(double x);` — Hyperbolic tangent. - -#### Utility -* `double hypot(double x, double y);` — `sqrt(x² + y²)` without intermediate overflow. -* `double fmin(double a, double b);` — Minimum of two values. -* `double fmax(double a, double b);` — Maximum of two values. -* `double fclamp(double x, double lo, double hi);` — Clamps `x` into `[lo, hi]`. - -> [!NOTE] -> The implementation file is named `libc/libmath.c` (not `libc/math.c`) to avoid a name collision with the `math` CLI calculator app in userland. The public header is still included as `#include "math.h"`. - -For advanced operations, `syscall.h` provides direct wrappers into the kernel. - -### Process & System Info -* `void sys_exit(int status);` - Raw exit syscall. -* `int sys_write(int fd, const char *buf, int len);` - Write to a file descriptor (usually fd 1 for stdout). -* `void* sys_sbrk(int incr);` - Expand or shrink the process data segment (used internally by `malloc`). -* `void sys_kill(int pid);` - Send a kill signal to a process. -* `void sys_yield(void);` - Yield the remainder of the process's timeslice to the scheduler. -* `int sys_system(int cmd, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);` - Execute a privileged system command (e.g., reboot, shutdown, beep). -* `int sys_get_os_info(os_info_t *info);` - Populate an `os_info_t` struct with version and environment details. -* `uint64_t sys_get_shell_config(const char *key);` - Retrieve internal shell configuration values. -* `void sys_set_text_color(uint32_t color);` - Set the raw CLI text output color. - -### File System API (VFS) -Interacting with files and directories using the Virtual File System. -* `int sys_open(const char *path, const char *mode);` - Open a file, returning a file descriptor. -* `int sys_read(int fd, void *buf, uint32_t len);` - Read from an open file. -* `int sys_write_fs(int fd, const void *buf, uint32_t len);` - Write to an open file. -* `void sys_close(int fd);` - Close an open file descriptor. -* `int sys_seek(int fd, int offset, int whence);` - Reposition the file offset. -* `uint32_t sys_tell(int fd);` - Get the current file offset. -* `uint32_t sys_size(int fd);` - Get the total file size. -* `int sys_delete(const char *path);` - Delete a file. -* `int sys_mkdir(const char *path);` - Create a new directory. -* `int sys_exists(const char *path);` - Check if a path exists on the filesystem. -* `int sys_getcwd(char *buf, int size);` - Get the current working directory string. -* `int sys_chdir(const char *path);` - Change the current working directory. -* `int sys_list(const char *path, FAT32_FileInfo *entries, int max_entries);` - List directory contents into an array of `FAT32_FileInfo` structs. -* `int sys_get_file_info(const char *path, FAT32_FileInfo *info);` - Retrieve metadata for a specific file. - -### Networking Stack API -BoredOS includes lwIP for hardware TCP/UDP networking. - -#### Configuration and Status -* `int sys_network_init(void);` - Initialize the network stack. -* `int sys_network_is_initialized(void);` - Check stack status. -* `int sys_network_dhcp_acquire(void);` - Request an IP configuration from a DHCP server. -* `int sys_network_has_ip(void);` - Check if the system has a valid IP address. -* `int sys_network_get_mac(net_mac_address_t *mac);` - Get the physical MAC address of the NIC. -* `int sys_network_get_nic_name(char *name_out);` - Get the name of the active network interface card. -* `int sys_network_get_ip(net_ipv4_address_t *ip);` - Get current local IPv4 address. -* `int sys_network_set_ip(const net_ipv4_address_t *ip);` - Manually assign a static IP. -* `int sys_network_get_gateway(net_ipv4_address_t *ip);` - Get the default gateway IP. -* `int sys_network_get_dns(net_ipv4_address_t *ip);` - Get the primary DNS server IP. -* `int sys_set_dns_server(const net_ipv4_address_t *ip);` - Set the primary DNS server. -* `int sys_get_dns_server(net_ipv4_address_t *ip);` - Retrieve configured DNS server. -* `int sys_network_get_stat(int stat_type);` - Get network statistics (packets in/out, drops, etc.). -* `void sys_network_force_unlock(void);` - Force release of network stack locks (use with caution). - -#### Communication -* `int sys_icmp_ping(const net_ipv4_address_t *dest_ip);` - Send an ICMP echo request. -* `int sys_udp_send(const net_ipv4_address_t *dest_ip, uint16_t dest_port, uint16_t src_port, const void *data, size_t data_len);` - Send a raw UDP datagram. -* `int sys_dns_lookup(const char *name, net_ipv4_address_t *out_ip);` - Resolve a hostname to an IPv4 address. -* `int sys_tcp_connect(const net_ipv4_address_t *ip, uint16_t port);` - Establish a TCP connection to a remote host. -* `int sys_tcp_send(const void *data, size_t len);` - Send data over an active TCP socket. -* `int sys_tcp_recv(void *buf, size_t max_len);` - Receive data from a TCP socket (blocking). -* `int sys_tcp_recv_nb(void *buf, size_t max_len);` - Receive data from a TCP socket (non-blocking). -* `int sys_tcp_close(void);` - Close the active TCP socket. - ---- - -## Core Data Structures - -### `os_info_t` -Contains detailed build and version information about the OS. ```c -typedef struct { - char os_name[64]; - char os_version[64]; - char os_codename[64]; - char kernel_name[64]; - char kernel_version[64]; - char build_date[64]; - char build_time[64]; - char build_arch[64]; -} os_info_t; +#include +#include ``` -### `FAT32_FileInfo` -Represents a filesystem entry. -```c -typedef struct { - char name[256]; - uint32_t size; - uint8_t is_directory; - uint32_t start_cluster; - uint16_t write_date; - uint16_t write_time; -} FAT32_FileInfo; -``` +## Build and Packaging + +- Add app source under `src/userland/` (CLI, GUI, or games subfolder). +- Ensure it is included in the userland build rules/targets. +- Build from repo root with `make`. +- Built binaries are copied into initrd under `/bin` by the top-level `Makefile`. -### `ProcessInfo` -Provides status information for an active process. -```c -typedef struct { - uint32_t pid; - char name[64]; - uint64_t ticks; - size_t used_memory; -} ProcessInfo; -``` -### IP / MAC Addresses -Wrappers for raw byte arrays. -```c -typedef struct { uint8_t bytes[6]; } net_mac_address_t; -typedef struct { uint8_t bytes[4]; } net_ipv4_address_t; -``` diff --git a/docs/appdev/syscalls.md b/docs/appdev/syscalls.md new file mode 100644 index 0000000..9b7e924 --- /dev/null +++ b/docs/appdev/syscalls.md @@ -0,0 +1,146 @@ +# Syscall Reference + +This page documents the current syscall surface in BoredOS as implemented in: +- `src/sys/syscall.h` (kernel command IDs) +- `src/userland/libc/syscall.h` (userland wrappers) + +Use libc wrappers when possible instead of calling raw syscall numbers directly. + +## Top-Level Syscall Numbers + +| Number | Name | Purpose | +|---|---|---| +| 0 | `SYS_EXIT` (userland header) | Terminate current process | +| 1 | `SYS_WRITE` | Write to stdout/tty path | +| 3 | `SYS_GUI` | Window manager and drawing commands | +| 4 | `SYS_FS` | Filesystem and fd commands | +| 5 | `SYS_SYSTEM` | System-wide command multiplexer | +| 9 | `SYS_SBRK` (userland header) | Heap break management | +| 10 | `SYS_KILL` (userland header) | Kill process by PID | +| 60 | `SYS_EXIT` (kernel header) | Internal kernel syscall number map | + +Notes: +- Some numbers differ between kernel and userland headers for historical reasons. For app code, rely on wrapper functions in `src/userland/libc/syscall.c`. +- `SYS_GUI`, `SYS_FS`, and `SYS_SYSTEM` are command multiplexers. + +## FS Command IDs (`SYS_FS`) + +| ID | Macro | Meaning | +|---|---|---| +| 1 | `FS_CMD_OPEN` | Open file | +| 2 | `FS_CMD_READ` | Read from fd | +| 3 | `FS_CMD_WRITE` | Write to fd | +| 4 | `FS_CMD_CLOSE` | Close fd | +| 5 | `FS_CMD_SEEK` | Seek in file | +| 6 | `FS_CMD_TELL` | Current offset | +| 7 | `FS_CMD_LIST` | Directory listing | +| 8 | `FS_CMD_DELETE` | Delete file | +| 9 | `FS_CMD_SIZE` | File size | +| 10 | `FS_CMD_MKDIR` | Create directory | +| 11 | `FS_CMD_EXISTS` | Path exists check | +| 12 | `FS_CMD_GETCWD` | Get cwd | +| 13 | `FS_CMD_CHDIR` | Change cwd | +| 14 | `FS_CMD_GET_INFO` | File metadata | +| 15 | `FS_CMD_DUP` | `dup` fd | +| 16 | `FS_CMD_DUP2` | `dup2` fd | +| 17 | `FS_CMD_PIPE` | Create pipe | +| 18 | `FS_CMD_FCNTL` | `fcntl` flags ops | + +## SYSTEM Command IDs (`SYS_SYSTEM`) + +### Desktop and display + +| ID | Macro | Meaning | +|---|---|---| +| 1 | `SYSTEM_CMD_SET_BG_COLOR` | Set desktop background color | +| 2 | `SYSTEM_CMD_SET_BG_PATTERN` | Set desktop background pattern | +| 3 | `SYSTEM_CMD_SET_WALLPAPER` | Legacy wallpaper command slot | +| 4 | `SYSTEM_CMD_SET_DESKTOP_PROP` | Set desktop behavior property | +| 5 | `SYSTEM_CMD_SET_MOUSE_SPEED` | Set mouse speed | +| 7 | `SYSTEM_CMD_GET_DESKTOP_PROP` | Get desktop property | +| 8 | `SYSTEM_CMD_GET_MOUSE_SPEED` | Get mouse speed | +| 9 | `SYSTEM_CMD_GET_WALLPAPER_THUMB` | Legacy wallpaper thumb slot | +| 10 | `SYSTEM_CMD_CLEAR_SCREEN` | Clear text console | +| 29 | `SYSTEM_CMD_SET_TEXT_COLOR` | Set console text color | +| 31 | `SYSTEM_CMD_SET_WALLPAPER_PATH` | Set wallpaper from path | +| 40 | `SYSTEM_CMD_SET_FONT` | Set active font | +| 47 | `SYSTEM_CMD_SET_RESOLUTION` | Set display mode | + +### Time, power, and system state + +| ID | Macro | Meaning | +|---|---|---| +| 11 | `SYSTEM_CMD_RTC_GET` | Read RTC datetime | +| 12 | `SYSTEM_CMD_REBOOT` | Reboot machine | +| 13 | `SYSTEM_CMD_SHUTDOWN` | Power off machine | +| 14 | `SYSTEM_CMD_BEEP` | PC speaker beep | +| 15 | `SYSTEM_CMD_GET_MEM_INFO` | Return total/used memory | +| 16 | `SYSTEM_CMD_GET_TICKS` | Return scheduler/WM tick count | +| 28 | `SYSTEM_CMD_GET_SHELL_CONFIG` | Read shell config value | +| 32 | `SYSTEM_CMD_RTC_SET` | Set RTC datetime | +| 41 | `SYSTEM_CMD_SET_RAW_MODE` | Terminal raw-mode control | +| 43 | `SYSTEM_CMD_YIELD` | Yield scheduler timeslice | +| 46 | `SYSTEM_CMD_SLEEP` | Sleep current process | + +### Network + +| ID | Macro | Meaning | +|---|---|---| +| 6 | `SYSTEM_CMD_NETWORK_INIT` | Init networking | +| 17 | `SYSTEM_CMD_PCI_LIST` | PCI device list access | +| 18 | `SYSTEM_CMD_NETWORK_DHCP` | DHCP acquire | +| 19 | `SYSTEM_CMD_NETWORK_GET_MAC` | Read NIC MAC | +| 20 | `SYSTEM_CMD_NETWORK_GET_IP` | Read IPv4 | +| 21 | `SYSTEM_CMD_NETWORK_SET_IP` | Set static IPv4 | +| 22 | `SYSTEM_CMD_UDP_SEND` | Send UDP packet | +| 23 | `SYSTEM_CMD_NETWORK_GET_STATS` | Network stats | +| 24 | `SYSTEM_CMD_NETWORK_GET_GATEWAY` | Read gateway | +| 25 | `SYSTEM_CMD_NETWORK_GET_DNS` | Read DNS server | +| 26 | `SYSTEM_CMD_ICMP_PING` | ICMP ping | +| 27 | `SYSTEM_CMD_NETWORK_IS_INIT` | Network initialized flag | +| 30 | `SYSTEM_CMD_NETWORK_HAS_IP` | Has IPv4 address flag | +| 33 | `SYSTEM_CMD_TCP_CONNECT` | TCP connect | +| 34 | `SYSTEM_CMD_TCP_SEND` | TCP send | +| 35 | `SYSTEM_CMD_TCP_RECV` | TCP recv (blocking) | +| 36 | `SYSTEM_CMD_TCP_CLOSE` | TCP close | +| 37 | `SYSTEM_CMD_DNS_LOOKUP` | DNS lookup | +| 38 | `SYSTEM_CMD_SET_DNS` | Set DNS server | +| 39 | `SYSTEM_CMD_NET_UNLOCK` | Force net lock release | +| 42 | `SYSTEM_CMD_TCP_RECV_NB` | TCP recv (non-blocking) | +| 48 | `SYSTEM_CMD_NETWORK_GET_NIC_NAME` | NIC name | + +### Process, tty, signals + +| ID | Macro | Meaning | +|---|---|---| +| 50 | `SYSTEM_CMD_PARALLEL_RUN` | Dispatch parallel job | +| 60 | `SYSTEM_CMD_TTY_CREATE` | Create tty | +| 61 | `SYSTEM_CMD_TTY_READ_OUT` | Read tty output buffer | +| 62 | `SYSTEM_CMD_TTY_WRITE_IN` | Write tty input buffer | +| 63 | `SYSTEM_CMD_TTY_READ_IN` | Read input for current tty | +| 64 | `SYSTEM_CMD_SPAWN` | Spawn process | +| 65 | `SYSTEM_CMD_TTY_SET_FG` | Set tty foreground PID | +| 66 | `SYSTEM_CMD_TTY_GET_FG` | Get tty foreground PID | +| 67 | `SYSTEM_CMD_TTY_KILL_FG` | Kill tty foreground PID | +| 68 | `SYSTEM_CMD_TTY_KILL_ALL` | Kill tty process group | +| 69 | `SYSTEM_CMD_TTY_DESTROY` | Destroy tty | +| 70 | `SYSTEM_CMD_EXEC` | Exec replace current process | +| 71 | `SYSTEM_CMD_WAITPID` | Wait/reap child | +| 72 | `SYSTEM_CMD_KILL_SIGNAL` | Send signal | +| 73 | `SYSTEM_CMD_SIGACTION` | Set/get handler | +| 74 | `SYSTEM_CMD_SIGPROCMASK` | Signal mask ops | +| 75 | `SYSTEM_CMD_SIGPENDING` | Get pending signals | + +## Common Wrapper API (`src/userland/libc/syscall.h`) + +Typical wrappers used by apps: +- Process/system: `sys_exit`, `sys_yield`, `sys_spawn`, `sys_exec`, `sys_waitpid`, `sys_kill_signal` +- Filesystem: `sys_open`, `sys_read`, `sys_write_fs`, `sys_close`, `sys_seek`, `sys_tell`, `sys_size`, `sys_list` +- Network: `sys_network_init`, `sys_network_dhcp_acquire`, `sys_udp_send`, `sys_tcp_connect`, `sys_tcp_recv_nb`, `sys_dns_lookup` +- TTY: `sys_tty_create`, `sys_tty_read_out`, `sys_tty_write_in`, `sys_tty_set_fg` + +## Best Practices + +- Do not hardcode numeric command IDs in app code. +- Prefer high-level libc calls (`open`, `read`, `waitpid`, `sigaction`) where available. +- Use `syscall.h` macros when a raw `sys_system` call is still needed. diff --git a/src/mem/memory_manager.c b/src/mem/memory_manager.c index 41c9698..8e24831 100644 --- a/src/mem/memory_manager.c +++ b/src/mem/memory_manager.c @@ -216,7 +216,7 @@ void* kmalloc_aligned(size_t size, size_t alignment) { // 4. [Allocated] // We'll modify block_list[i] and insert others as needed. - // To keep things simple and maintain sorted order, we update from right to left or carefully. + // To keep things simple and maintain sorted order, we update from right to left. if (padding > 0 && remaining_size > 0) { // Case 1: Split into 3 diff --git a/src/sys/process.c b/src/sys/process.c index 3261c32..9dbb2bb 100644 --- a/src/sys/process.c +++ b/src/sys/process.c @@ -10,6 +10,7 @@ #include "memory_manager.h" #include "elf.h" #include "wm.h" +#include "vfs.h" #include "spinlock.h" #include "smp.h" #include "lapic.h" @@ -29,6 +30,47 @@ static uint32_t next_cpu_assign = 1; static void process_cleanup_inner(process_t *proc); +static void process_close_fd_inner(process_t *proc, int fd) { + if (!proc || fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) { + return; + } + + if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) { + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd]; + if (ref) { + ref->refs--; + if (ref->refs <= 0) { + if (ref->file) vfs_close((vfs_file_t *)ref->file); + kfree(ref); + } + } + } else if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd]; + if (pipe) { + if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ) pipe->readers--; + else pipe->writers--; + if (pipe->readers <= 0 && pipe->writers <= 0) { + kfree(pipe); + } + } + } + + proc->fds[fd] = NULL; + proc->fd_kind[fd] = PROC_FD_KIND_NONE; + proc->fd_flags[fd] = 0; +} + +static void process_init_signal_state(process_t *proc) { + if (!proc) return; + proc->signal_mask = 0; + proc->signal_pending = 0; + for (int i = 0; i < MAX_SIGNALS; i++) { + proc->signal_handlers[i] = 0; + proc->signal_action_mask[i] = 0; + proc->signal_action_flags[i] = 0; + } +} + void process_init(void) { for (int i = 0; i < MAX_PROCESSES; i++) { processes[i].pid = 0xFFFFFFFF; @@ -47,7 +89,16 @@ void process_init(void) { kernel_proc->fpu_initialized = true; - for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL; + for (int i = 0; i < MAX_PROCESS_FDS; i++) { + kernel_proc->fds[i] = NULL; + kernel_proc->fd_kind[i] = 0; + kernel_proc->fd_flags[i] = 0; + } + kernel_proc->parent_pid = 0; + kernel_proc->pgid = 0; + kernel_proc->exited = false; + kernel_proc->exit_status = 0; + process_init_signal_state(kernel_proc); extern void mem_memcpy(void *dest, const void *src, size_t len); mem_memcpy(kernel_proc->name, "kernel", 7); @@ -77,12 +128,18 @@ process_t* process_create(void (*entry_point)(void), bool is_user) { return NULL; } + process_t *parent = process_get_current(); + new_proc->pid = next_pid++; new_proc->is_user = is_user; new_proc->tty_id = -1; new_proc->kill_pending = false; + new_proc->parent_pid = parent ? parent->pid : 0; + new_proc->pgid = parent ? parent->pgid : new_proc->pid; + new_proc->exited = false; + new_proc->exit_status = 0; + process_init_signal_state(new_proc); - process_t *parent = process_get_current(); if (parent) { extern void mem_memcpy(void *dest, const void *src, size_t len); mem_memcpy(new_proc->cwd, parent->cwd, 1024); @@ -204,7 +261,11 @@ process_t* process_create_elf(const char* filepath, const char* args_str, bool t new_proc->pml4_phys = paging_create_user_pml4_phys(); if (!new_proc->pml4_phys) return NULL; - for (int i = 0; i < MAX_PROCESS_FDS; i++) new_proc->fds[i] = NULL; + for (int i = 0; i < MAX_PROCESS_FDS; i++) { + new_proc->fds[i] = NULL; + new_proc->fd_kind[i] = 0; + new_proc->fd_flags[i] = 0; + } new_proc->gui_event_head = 0; new_proc->gui_event_tail = 0; new_proc->ui_window = NULL; @@ -213,15 +274,22 @@ process_t* process_create_elf(const char* filepath, const char* args_str, bool t new_proc->is_terminal_proc = terminal_proc; new_proc->tty_id = tty_id; new_proc->kill_pending = false; + new_proc->exited = false; + new_proc->exit_status = 0; + process_init_signal_state(new_proc); process_t *parent = process_get_current(); if (parent) { extern void mem_memcpy(void *dest, const void *src, size_t len); mem_memcpy(new_proc->cwd, parent->cwd, 1024); + new_proc->parent_pid = parent->pid; + new_proc->pgid = parent->pgid; } else { extern void mem_memset(void *dest, int val, size_t len); mem_memset(new_proc->cwd, 0, 1024); new_proc->cwd[0] = '/'; + new_proc->parent_pid = 0; + new_proc->pgid = new_proc->pid; } // 2. Load ELF executable @@ -469,7 +537,7 @@ uint64_t process_schedule(uint64_t current_rsp) { current_process[my_cpu] = next_proc; - cur->pid = 0xFFFFFFFF; + cur->exited = true; cur->cpu_affinity = 0xFFFFFFFF; cur->ui_window = NULL; cur->is_terminal_proc = false; @@ -570,7 +638,7 @@ uint64_t process_schedule(uint64_t current_rsp) { process_t* process_get_by_pid(uint32_t pid) { for (int i = 0; i < MAX_PROCESSES; i++) { - if (processes[i].pid == pid) return &processes[i]; + if (processes[i].pid == pid && !processes[i].exited) return &processes[i]; } return NULL; } @@ -593,12 +661,8 @@ static void process_cleanup_inner(process_t *proc) { proc->ui_window = NULL; } - extern void fat32_close(struct FAT32_FileHandle *fh); for (int i = 0; i < MAX_PROCESS_FDS; i++) { - if (proc->fds[i]) { - fat32_close(proc->fds[i]); - proc->fds[i] = NULL; - } + process_close_fd_inner(proc, i); } extern void cmd_process_finished(void); @@ -613,12 +677,18 @@ static void process_cleanup_inner(process_t *proc) { } void process_terminate(process_t *to_delete) { + process_terminate_with_status(to_delete, 0); +} + +void process_terminate_with_status(process_t *to_delete, int status) { if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return; uint32_t cpu_count = smp_cpu_count(); for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) { if (current_process[c] == to_delete) { to_delete->kill_pending = true; + to_delete->exit_status = status; + to_delete->exited = true; return; } } @@ -662,13 +732,13 @@ void process_terminate(process_t *to_delete) { } // Mark slot as free - to_delete->pid = 0xFFFFFFFF; to_delete->cpu_affinity = 0xFFFFFFFF; to_delete->kill_pending = false; + to_delete->exited = true; + to_delete->exit_status = status; if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc); - // Defer kernel stack until we switch away from it - to_delete->kernel_stack_alloc = NULL; + if (to_delete->kernel_stack_alloc) kfree(to_delete->kernel_stack_alloc); extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys); if (to_delete->pml4_phys && to_delete->is_user) { @@ -694,6 +764,8 @@ uint64_t process_terminate_current(void) { } process_cleanup_inner(cur); + cur->exited = true; + cur->exit_status = 0; // 2. Find previous process in circular list process_t *prev = cur; @@ -729,7 +801,6 @@ uint64_t process_terminate_current(void) { current_process[my_cpu] = next_proc; // Mark slot as free - to_delete->pid = 0xFFFFFFFF; to_delete->cpu_affinity = 0xFFFFFFFF; to_delete->ui_window = NULL; to_delete->is_terminal_proc = false; @@ -756,6 +827,222 @@ uint64_t process_terminate_current(void) { return next_rsp; } +int process_reap(uint32_t caller_pid, uint32_t pid, int *status_out) { + process_t *p = NULL; + for (int i = 0; i < MAX_PROCESSES; i++) { + if (processes[i].pid == pid) { + p = &processes[i]; + break; + } + } + if (!p) return -1; + if (!p->exited) return -2; + if (p->parent_pid != caller_pid && caller_pid != 0) return -1; + + if (status_out) { + *status_out = p->exit_status; + } + + p->pid = 0xFFFFFFFF; + p->parent_pid = 0; + p->pgid = 0; + p->cpu_affinity = 0xFFFFFFFF; + p->exited = false; + p->exit_status = 0; + p->sleep_until = 0; + p->ui_window = NULL; + p->is_terminal_proc = false; + p->tty_id = -1; + p->kill_pending = false; + p->used_memory = 0; + p->ticks = 0; + p->next = NULL; + for (int i = 0; i < MAX_PROCESS_FDS; i++) { + p->fds[i] = NULL; + p->fd_kind[i] = PROC_FD_KIND_NONE; + p->fd_flags[i] = 0; + } + process_init_signal_state(p); + return 0; +} + +int process_waitpid(uint32_t caller_pid, int target_pid, int options, int *status_out) { + process_t *caller = process_get_by_pid(caller_pid); + int found_child = 0; + int found_waitable = 0; + + for (int i = 0; i < MAX_PROCESSES; i++) { + process_t *p = &processes[i]; + int match = 0; + + if (p->pid == 0xFFFFFFFF || p->pid == 0 || p->parent_pid != caller_pid) { + continue; + } + + found_child = 1; + if (target_pid > 0) { + match = ((int)p->pid == target_pid); + } else if (target_pid == -1) { + match = 1; + } else if (target_pid == 0) { + match = (caller && p->pgid == caller->pgid); + } else { + match = (p->pgid == (uint32_t)(-target_pid)); + } + + if (!match) { + continue; + } + + found_waitable = 1; + if (!p->exited) { + continue; + } + + if (process_reap(caller_pid, p->pid, status_out) != 0) { + return -1; + } + return (int)p->pid; + } + + if (!found_child || !found_waitable) { + return -1; + } + if (options & 1) { + return 0; + } + return -2; +} + +int process_exec_replace_current(registers_t *regs, const char* filepath, const char* args_str) { + process_t *proc = process_get_current(); + if (!proc || !proc->is_user || !regs || !filepath) return -1; + + uint64_t new_pml4 = paging_create_user_pml4_phys(); + if (!new_pml4) return -1; + + size_t elf_load_size = 0; + uint64_t entry_point = elf_load(filepath, new_pml4, &elf_load_size); + if (entry_point == 0) { + extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys); + paging_destroy_user_pml4_phys(new_pml4); + return -1; + } + + size_t user_stack_size = 262144; + void* stack = kmalloc_aligned(user_stack_size, 4096); + if (!stack) { + extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys); + paging_destroy_user_pml4_phys(new_pml4); + return -1; + } + + for (uint64_t i = 0; i < (user_stack_size / 4096); i++) { + paging_map_page(new_pml4, 0x800000 - user_stack_size + (i * 4096), v2p((uint64_t)stack + (i * 4096)), PT_PRESENT | PT_RW | PT_USER); + } + + int argc = 1; + char *args_buf = (char *)stack + user_stack_size; + uint64_t user_args_buf = 0x800000; + + int path_len = 0; + while (filepath[path_len]) path_len++; + args_buf -= (path_len + 1); + user_args_buf -= (path_len + 1); + for (int i = 0; i <= path_len; i++) args_buf[i] = filepath[i]; + + uint64_t argv_ptrs[32]; + argv_ptrs[0] = user_args_buf; + + if (args_str) { + int i = 0; + while (args_str[i] && argc < 31) { + while (args_str[i] == ' ') i++; + if (!args_str[i]) break; + + int arg_start = i; + bool in_quotes = false; + if (args_str[i] == '"') { + in_quotes = true; + i++; + arg_start = i; + while (args_str[i] && args_str[i] != '"') i++; + } else { + while (args_str[i] && args_str[i] != ' ') i++; + } + + int arg_len = i - arg_start; + args_buf -= (arg_len + 1); + user_args_buf -= (arg_len + 1); + for (int k = 0; k < arg_len; k++) args_buf[k] = args_str[arg_start + k]; + args_buf[arg_len] = '\0'; + argv_ptrs[argc++] = user_args_buf; + if (in_quotes && args_str[i] == '"') i++; + } + } + argv_ptrs[argc] = 0; + + uint64_t current_user_sp = user_args_buf; + current_user_sp &= ~7ULL; + args_buf = (char *)((uint64_t)stack + (current_user_sp - (0x800000 - user_stack_size))); + + int argv_size = (argc + 1) * (int)sizeof(uint64_t); + args_buf -= argv_size; + current_user_sp -= argv_size; + uint64_t actual_argv_ptr = current_user_sp; + uint64_t *user_argv_array = (uint64_t *)args_buf; + for (int i = 0; i <= argc; i++) user_argv_array[i] = argv_ptrs[i]; + + current_user_sp &= ~15ULL; + + if (proc->user_stack_alloc) { + kfree(proc->user_stack_alloc); + } + if (proc->pml4_phys) { + extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys); + paging_destroy_user_pml4_phys(proc->pml4_phys); + } + + proc->pml4_phys = new_pml4; + proc->user_stack_alloc = stack; + proc->used_memory = elf_load_size + user_stack_size + 65536; + proc->heap_start = 0x20000000; + proc->heap_end = 0x20000000; + proc->sleep_until = 0; + process_init_signal_state(proc); + + int last_slash = -1; + for (int i = 0; filepath[i]; i++) if (filepath[i] == '/') last_slash = i; + const char *filename = (last_slash == -1) ? filepath : (filepath + last_slash + 1); + int ni = 0; + while (filename[ni] && ni < 63) { + proc->name[ni] = filename[ni]; + ni++; + } + proc->name[ni] = 0; + + regs->rip = entry_point; + regs->rdi = argc; + regs->rsi = actual_argv_ptr; + regs->rsp = current_user_sp; + regs->rax = 0; + regs->rbx = 0; + regs->rcx = 0; + regs->rdx = 0; + regs->r8 = 0; + regs->r9 = 0; + regs->r10 = 0; + regs->r11 = 0; + regs->r12 = 0; + regs->r13 = 0; + regs->r14 = 0; + regs->r15 = 0; + regs->rbp = 0; + + paging_switch_directory(proc->pml4_phys); + return 0; +} + // SMP: IPI handler called on AP cores when BSP broadcasts scheduling IPI uint64_t sched_ipi_handler(registers_t *regs) { lapic_eoi(); // Acknowledge the IPI diff --git a/src/sys/process.h b/src/sys/process.h index 632afc8..820734d 100644 --- a/src/sys/process.h +++ b/src/sys/process.h @@ -11,6 +11,26 @@ #define MAX_GUI_EVENTS 32 #define MAX_PROCESS_FDS 16 +#define MAX_SIGNALS 32 + +#define PROC_FD_KIND_NONE 0 +#define PROC_FD_KIND_FILE 1 +#define PROC_FD_KIND_PIPE_READ 2 +#define PROC_FD_KIND_PIPE_WRITE 3 + +typedef struct { + void *file; + int refs; +} process_fd_file_ref_t; + +typedef struct { + uint8_t data[4096]; + uint32_t read_pos; + uint32_t write_pos; + uint32_t count; + int readers; + int writers; +} process_fd_pipe_t; struct FAT32_FileHandle; @@ -38,6 +58,8 @@ typedef struct process { uint64_t heap_end; void *fds[MAX_PROCESS_FDS]; + uint8_t fd_kind[MAX_PROCESS_FDS]; + int fd_flags[MAX_PROCESS_FDS]; void *kernel_stack_alloc; void *user_stack_alloc; @@ -57,6 +79,17 @@ typedef struct process { uint32_t cpu_affinity; bool is_idle; char cwd[1024]; + + uint32_t parent_pid; + uint32_t pgid; + bool exited; + int exit_status; + + uint64_t signal_mask; + uint64_t signal_pending; + uint64_t signal_handlers[MAX_SIGNALS]; + uint64_t signal_action_mask[MAX_SIGNALS]; + int signal_action_flags[MAX_SIGNALS]; } __attribute__((aligned(16))) process_t; typedef struct { @@ -70,13 +103,17 @@ typedef struct { void process_init(void); process_t* process_create(void (*entry_point)(void), bool is_user); process_t* process_create_elf(const char* filepath, const char* args_str, bool terminal_proc, int tty_id); +int process_exec_replace_current(registers_t *regs, const char* filepath, const char* args_str); process_t* process_get_current(void); void process_set_current_for_cpu(uint32_t cpu_id, process_t* p); process_t* process_get_current_for_cpu(uint32_t cpu_id); uint64_t process_schedule(uint64_t current_rsp); uint64_t process_terminate_current(void); void process_terminate(process_t *proc); +void process_terminate_with_status(process_t *proc, int status); process_t* process_get_by_pid(uint32_t pid); +int process_waitpid(uint32_t caller_pid, int target_pid, int options, int *status_out); +int process_reap(uint32_t caller_pid, uint32_t pid, int *status_out); void process_kill_by_tty(int tty_id); // SMP: IPI handler for AP scheduling diff --git a/src/sys/syscall.c b/src/sys/syscall.c index 37dccff..e31fb8e 100644 --- a/src/sys/syscall.c +++ b/src/sys/syscall.c @@ -967,6 +967,35 @@ static const syscall_handler_fn gui_cmd_table[GUI_CMD_TABLE_SIZE] = { [GUI_CMD_GET_DATETIME] = gui_cmd_get_datetime, }; +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +#define O_APPEND 0x0400 +#define O_NONBLOCK 0x0800 +#define F_GETFL 3 +#define F_SETFL 4 + +static int fs_alloc_fd_slot(process_t *proc, int start) { + for (int i = start; i < MAX_PROCESS_FDS; i++) { + if (!proc->fds[i]) return i; + } + return -1; +} + +static int fs_mode_to_flags(const char *mode) { + if (!mode || !mode[0]) return O_RDONLY; + if (mode[0] == 'r') { + return (mode[1] == '+') ? O_RDWR : O_RDONLY; + } + if (mode[0] == 'a') { + return (mode[1] == '+') ? (O_RDWR | O_APPEND) : (O_WRONLY | O_APPEND); + } + if (mode[0] == 'w') { + return (mode[1] == '+') ? O_RDWR : O_WRONLY; + } + return O_RDONLY; +} + static uint64_t fs_cmd_open(const syscall_args_t *args) { process_t *proc = process_get_current(); const char *path = (const char *)args->arg2; @@ -977,13 +1006,25 @@ static uint64_t fs_cmd_open(const syscall_args_t *args) { // but let's be explicit if we can. vfs_file_t *vf = vfs_open(path, mode); if (!vf) return -1; - + + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)kmalloc(sizeof(process_fd_file_ref_t)); + if (!ref) { + vfs_close(vf); + return -1; + } + ref->file = vf; + ref->refs = 1; + for (int i = 0; i < MAX_PROCESS_FDS; i++) { if (proc->fds[i] == NULL) { - proc->fds[i] = vf; + proc->fds[i] = ref; + proc->fd_kind[i] = PROC_FD_KIND_FILE; + proc->fd_flags[i] = fs_mode_to_flags(mode); return (uint64_t)i; } } + + kfree(ref); vfs_close(vf); return -1; } @@ -994,7 +1035,35 @@ static uint64_t fs_cmd_read(const syscall_args_t *args) { void *buf = (void *)args->arg3; uint32_t len = (uint32_t)args->arg4; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)vfs_read((vfs_file_t*)proc->fds[fd], buf, (int)len); + + if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) { + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd]; + if (!ref || !ref->file) return -1; + return (uint64_t)vfs_read(ref->file, buf, (int)len); + } + + if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd]; + if (!pipe || !buf) return -1; + uint8_t *out = (uint8_t *)buf; + uint32_t n = 0; + while (n < len) { + if (pipe->count == 0) { + if (pipe->writers == 0) break; + if (proc->fd_flags[fd] & O_NONBLOCK) { + if (n == 0) return (uint64_t)-1; + break; + } + break; + } + out[n++] = pipe->data[pipe->read_pos]; + pipe->read_pos = (pipe->read_pos + 1) % sizeof(pipe->data); + pipe->count--; + } + return n; + } + + return -1; } static uint64_t fs_cmd_write(const syscall_args_t *args) { @@ -1003,15 +1072,65 @@ static uint64_t fs_cmd_write(const syscall_args_t *args) { const void *buf = (const void *)args->arg3; uint32_t len = (uint32_t)args->arg4; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)vfs_write((vfs_file_t*)proc->fds[fd], buf, (int)len); + + if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) { + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd]; + if (!ref || !ref->file) return -1; + return (uint64_t)vfs_write(ref->file, buf, (int)len); + } + + if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd]; + if (!pipe || !buf) return -1; + if (pipe->readers <= 0) return (uint64_t)-1; + const uint8_t *in = (const uint8_t *)buf; + uint32_t n = 0; + while (n < len) { + if (pipe->count == sizeof(pipe->data)) { + if (proc->fd_flags[fd] & O_NONBLOCK) { + if (n == 0) return (uint64_t)-1; + break; + } + break; + } + pipe->data[pipe->write_pos] = in[n++]; + pipe->write_pos = (pipe->write_pos + 1) % sizeof(pipe->data); + pipe->count++; + } + return n; + } + + return -1; } static uint64_t fs_cmd_close(const syscall_args_t *args) { process_t *proc = process_get_current(); int fd = (int)args->arg2; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - vfs_close((vfs_file_t*)proc->fds[fd]); + + if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) { + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd]; + if (ref) { + ref->refs--; + if (ref->refs <= 0) { + if (ref->file) vfs_close(ref->file); + kfree(ref); + } + } + } else if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd]; + if (pipe) { + if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ) pipe->readers--; + else pipe->writers--; + if (pipe->readers <= 0 && pipe->writers <= 0) { + kfree(pipe); + } + } + } + proc->fds[fd] = NULL; + proc->fd_kind[fd] = PROC_FD_KIND_NONE; + proc->fd_flags[fd] = 0; return 0; } @@ -1021,21 +1140,142 @@ static uint64_t fs_cmd_seek(const syscall_args_t *args) { int offset = (int)args->arg3; int whence = (int)args->arg4; // 0=SET, 1=CUR, 2=END if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)vfs_seek((vfs_file_t*)proc->fds[fd], offset, whence); + if (proc->fd_kind[fd] != PROC_FD_KIND_FILE) return -1; + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd]; + if (!ref || !ref->file) return -1; + return (uint64_t)vfs_seek(ref->file, offset, whence); } static uint64_t fs_cmd_tell(const syscall_args_t *args) { process_t *proc = process_get_current(); int fd = (int)args->arg2; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)vfs_file_position((vfs_file_t*)proc->fds[fd]); + if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd]; + return pipe ? pipe->count : 0; + } + if (proc->fd_kind[fd] != PROC_FD_KIND_FILE) return -1; + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd]; + if (!ref || !ref->file) return -1; + return (uint64_t)vfs_file_position(ref->file); } static uint64_t fs_cmd_size(const syscall_args_t *args) { process_t *proc = process_get_current(); int fd = (int)args->arg2; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]); + if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd]; + return pipe ? pipe->count : 0; + } + if (proc->fd_kind[fd] != PROC_FD_KIND_FILE) return -1; + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd]; + if (!ref || !ref->file) return -1; + return (uint64_t)vfs_file_size(ref->file); +} + +static uint64_t fs_cmd_dup(const syscall_args_t *args) { + process_t *proc = process_get_current(); + int oldfd = (int)args->arg2; + if (oldfd < 0 || oldfd >= MAX_PROCESS_FDS || !proc->fds[oldfd]) return -1; + + int newfd = fs_alloc_fd_slot(proc, 0); + if (newfd < 0) return -1; + + proc->fds[newfd] = proc->fds[oldfd]; + proc->fd_kind[newfd] = proc->fd_kind[oldfd]; + proc->fd_flags[newfd] = proc->fd_flags[oldfd]; + + if (proc->fd_kind[oldfd] == PROC_FD_KIND_FILE) { + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[oldfd]; + if (ref) ref->refs++; + } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_READ) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd]; + if (pipe) pipe->readers++; + } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_WRITE) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd]; + if (pipe) pipe->writers++; + } + + return (uint64_t)newfd; +} + +static uint64_t fs_cmd_dup2(const syscall_args_t *args) { + process_t *proc = process_get_current(); + int oldfd = (int)args->arg2; + int newfd = (int)args->arg3; + if (oldfd < 0 || oldfd >= MAX_PROCESS_FDS || !proc->fds[oldfd]) return -1; + if (newfd < 0 || newfd >= MAX_PROCESS_FDS) return -1; + if (oldfd == newfd) return (uint64_t)newfd; + + if (proc->fds[newfd]) { + syscall_args_t close_args = *args; + close_args.arg2 = (uint64_t)newfd; + if (fs_cmd_close(&close_args) != 0) return -1; + } + + proc->fds[newfd] = proc->fds[oldfd]; + proc->fd_kind[newfd] = proc->fd_kind[oldfd]; + proc->fd_flags[newfd] = proc->fd_flags[oldfd]; + + if (proc->fd_kind[oldfd] == PROC_FD_KIND_FILE) { + process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[oldfd]; + if (ref) ref->refs++; + } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_READ) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd]; + if (pipe) pipe->readers++; + } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_WRITE) { + process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd]; + if (pipe) pipe->writers++; + } + + return (uint64_t)newfd; +} + +static uint64_t fs_cmd_pipe(const syscall_args_t *args) { + process_t *proc = process_get_current(); + int *pipefd = (int *)args->arg2; + if (!pipefd) return -1; + + int rfd = fs_alloc_fd_slot(proc, 0); + if (rfd < 0) return -1; + int wfd = fs_alloc_fd_slot(proc, rfd + 1); + if (wfd < 0) return -1; + + process_fd_pipe_t *pipe = (process_fd_pipe_t *)kmalloc(sizeof(process_fd_pipe_t)); + if (!pipe) return -1; + mem_memset(pipe, 0, sizeof(*pipe)); + pipe->readers = 1; + pipe->writers = 1; + + proc->fds[rfd] = pipe; + proc->fd_kind[rfd] = PROC_FD_KIND_PIPE_READ; + proc->fd_flags[rfd] = O_RDONLY; + + proc->fds[wfd] = pipe; + proc->fd_kind[wfd] = PROC_FD_KIND_PIPE_WRITE; + proc->fd_flags[wfd] = O_WRONLY; + + pipefd[0] = rfd; + pipefd[1] = wfd; + return 0; +} + +static uint64_t fs_cmd_fcntl(const syscall_args_t *args) { + process_t *proc = process_get_current(); + int fd = (int)args->arg2; + int cmd = (int)args->arg3; + int val = (int)args->arg4; + if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; + + if (cmd == F_GETFL) { + return (uint64_t)proc->fd_flags[fd]; + } + if (cmd == F_SETFL) { + proc->fd_flags[fd] = (proc->fd_flags[fd] & ~(O_APPEND | O_NONBLOCK)) | (val & (O_APPEND | O_NONBLOCK)); + return 0; + } + return -1; } static uint64_t fs_cmd_list(const syscall_args_t *args) { @@ -1137,7 +1377,7 @@ static uint64_t fs_cmd_chdir(const syscall_args_t *args) { } return -1; } -#define FS_CMD_TABLE_SIZE 15 +#define FS_CMD_TABLE_SIZE 19 static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = { [FS_CMD_OPEN] = fs_cmd_open, // 1 [FS_CMD_READ] = fs_cmd_read, // 2 @@ -1153,6 +1393,10 @@ static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = { [FS_CMD_GETCWD] = fs_cmd_getcwd, // 12 [FS_CMD_CHDIR] = fs_cmd_chdir, // 13 [FS_CMD_GET_INFO] = fs_cmd_get_info, // 14 + [FS_CMD_DUP] = fs_cmd_dup, // 15 + [FS_CMD_DUP2] = fs_cmd_dup2, // 16 + [FS_CMD_PIPE] = fs_cmd_pipe, // 17 + [FS_CMD_FCNTL] = fs_cmd_fcntl, // 18 }; static uint64_t sys_cmd_set_bg_color(const syscall_args_t *args) { @@ -1178,7 +1422,7 @@ static uint64_t sys_cmd_set_bg_pattern(const syscall_args_t *args) { return 0; } -static uint64_t sys_cmd_set_wallpaper_obsolete(const syscall_args_t *args) { +static uint64_t sys_cmd_set_wallpaper(const syscall_args_t *args) { (void)args; return -1; } @@ -1230,7 +1474,7 @@ static uint64_t sys_cmd_get_mouse_speed(const syscall_args_t *args) { return mouse_speed; } -static uint64_t sys_cmd_get_wallpaper_thumb_obsolete(const syscall_args_t *args) { +static uint64_t sys_cmd_get_wallpaper_thumb(const syscall_args_t *args) { (void)args; return -1; } @@ -1270,6 +1514,21 @@ static uint64_t sys_cmd_beep(const syscall_args_t *args) { return 0; } +static uint64_t sys_cmd_get_mem_info(const syscall_args_t *args) { + uint64_t *out = (uint64_t *)args->arg2; + if (!out) return -1; + MemStats stats = memory_get_stats(); + out[0] = (uint64_t)stats.total_memory; + out[1] = (uint64_t)stats.used_memory; + return 0; +} + +static uint64_t sys_cmd_get_ticks(const syscall_args_t *args) { + (void)args; + extern uint32_t wm_get_ticks(void); + return (uint64_t)wm_get_ticks(); +} + static uint64_t sys_cmd_pci_list(const syscall_args_t *args) { typedef struct { uint16_t vendor; @@ -1661,6 +1920,140 @@ static uint64_t sys_cmd_spawn_process(const syscall_args_t *args) { return (uint64_t)child->pid; } +typedef struct { + uint64_t sa_handler; + uint64_t sa_mask; + int sa_flags; +} k_sigaction_t; + +#define SA_RESETHAND 0x80000000 +#define SIGKILL_NUM 9 + +static uint64_t sys_cmd_exec_process(const syscall_args_t *args) { + const char *user_path = (const char *)args->arg2; + const char *user_args = (const char *)args->arg3; + if (!user_path) return -1; + + char path_buf[256]; + int pi = 0; + while (pi < 255 && user_path[pi]) { + path_buf[pi] = user_path[pi]; + pi++; + } + path_buf[pi] = 0; + + char args_buf[512]; + const char *args_ptr = NULL; + if (user_args) { + int ai = 0; + while (ai < 511 && user_args[ai]) { + args_buf[ai] = user_args[ai]; + ai++; + } + args_buf[ai] = 0; + args_ptr = args_buf; + } + + return process_exec_replace_current(args->regs, path_buf, args_ptr); +} + +static uint64_t sys_cmd_waitpid(const syscall_args_t *args) { + process_t *proc = process_get_current(); + int pid = (int)args->arg2; + int *status = (int *)args->arg3; + int options = (int)args->arg4; + if (!proc) return -1; + + int st = 0; + int res = process_waitpid(proc->pid, pid, options, &st); + if (res == -2) { + if (options & 1) return 0; // WNOHANG + return (uint64_t)-2; + } + if (res < 0) return (uint64_t)-1; + if (status) *status = st; + return (uint64_t)res; +} + +static uint64_t sys_cmd_kill_signal(const syscall_args_t *args) { + int pid = (int)args->arg2; + int sig = (int)args->arg3; + process_t *target; + if (pid == -1) { + target = process_get_current(); + } else { + target = process_get_by_pid((uint32_t)pid); + } + if (!target) return -1; + if (sig == 0) return 0; + if (sig <= 0 || sig >= MAX_SIGNALS) return -1; + + if (sig == 9) { + process_terminate_with_status(target, 128 + sig); + return 0; + } + + target->signal_pending |= (1ULL << (uint32_t)sig); + return 0; +} + +static uint64_t sys_cmd_sigaction(const syscall_args_t *args) { + process_t *proc = process_get_current(); + int sig = (int)args->arg2; + const k_sigaction_t *act = (const k_sigaction_t *)args->arg3; + k_sigaction_t *oldact = (k_sigaction_t *)args->arg4; + if (!proc || sig <= 0 || sig >= MAX_SIGNALS) return -1; + + if (oldact) { + oldact->sa_handler = proc->signal_handlers[sig]; + oldact->sa_mask = proc->signal_action_mask[sig]; + oldact->sa_flags = proc->signal_action_flags[sig]; + } + if (act) { + if (sig == SIGKILL_NUM && act->sa_handler != 0) { + return -1; + } + proc->signal_handlers[sig] = act->sa_handler; + proc->signal_action_mask[sig] = act->sa_mask; + proc->signal_action_flags[sig] = act->sa_flags; + } + return 0; +} + +static uint64_t sys_cmd_sigprocmask(const syscall_args_t *args) { + process_t *proc = process_get_current(); + int how = (int)args->arg2; + const uint64_t *set = (const uint64_t *)args->arg3; + uint64_t *oldset = (uint64_t *)args->arg4; + if (!proc) return -1; + + if (oldset) { + *oldset = proc->signal_mask; + } + if (!set) return 0; + + if (how == 0) { + proc->signal_mask |= *set; + } else if (how == 1) { + proc->signal_mask &= ~(*set); + } else if (how == 2) { + proc->signal_mask = *set; + } else { + return -1; + } + proc->signal_mask &= ~(1ULL << SIGKILL_NUM); + + return 0; +} + +static uint64_t sys_cmd_sigpending(const syscall_args_t *args) { + process_t *proc = process_get_current(); + uint64_t *set = (uint64_t *)args->arg2; + if (!proc || !set) return -1; + *set = proc->signal_pending; + return 0; +} + static uint64_t sys_cmd_tty_set_fg(const syscall_args_t *args) { int tty_id = (int)args->arg2; int pid = (int)args->arg3; @@ -1694,61 +2087,69 @@ static uint64_t sys_cmd_tty_destroy(const syscall_args_t *args) { return tty_destroy(tty_id); } -#define SYS_CMD_TABLE_SIZE 70 +#define SYS_CMD_TABLE_SIZE 76 static const syscall_handler_fn sys_cmd_table[SYS_CMD_TABLE_SIZE] = { - [1] = sys_cmd_set_bg_color, - [2] = sys_cmd_set_bg_pattern, - [3] = sys_cmd_set_wallpaper_obsolete, - [4] = sys_cmd_set_desktop_prop, - [5] = sys_cmd_set_mouse_speed, - [6] = sys_cmd_network_init, - [7] = sys_cmd_get_desktop_prop, - [8] = sys_cmd_get_mouse_speed, - [9] = sys_cmd_get_wallpaper_thumb_obsolete, - [10] = sys_cmd_clear_screen, - [11] = sys_cmd_rtc_get, - [12] = sys_cmd_reboot, - [13] = sys_cmd_shutdown, - [14] = sys_cmd_beep, - [17] = sys_cmd_pci_list, - [18] = sys_cmd_network_dhcp, - [19] = sys_cmd_network_get_mac, - [20] = sys_cmd_network_get_ip, - [21] = sys_cmd_network_set_ip, - [22] = sys_cmd_udp_send, - [23] = sys_cmd_network_get_stats, - [24] = sys_cmd_network_get_gateway, - [25] = sys_cmd_network_get_dns, - [26] = sys_cmd_icmp_ping, - [27] = sys_cmd_network_is_init, - [28] = sys_cmd_get_shell_config, - [29] = sys_cmd_set_text_color, - [30] = sys_cmd_network_has_ip, - [31] = sys_cmd_set_wallpaper_path, - [32] = sys_cmd_rtc_set, - [33] = sys_cmd_tcp_connect, - [34] = sys_cmd_tcp_send, - [35] = sys_cmd_tcp_recv, - [36] = sys_cmd_tcp_close, - [37] = sys_cmd_dns_lookup, - [38] = sys_cmd_set_dns, - [39] = sys_cmd_net_unlock, - [40] = sys_cmd_set_font, - [41] = sys_cmd_set_raw_mode, - [42] = sys_cmd_tcp_recv_nb, - [47] = sys_cmd_set_resolution, - [48] = sys_cmd_network_get_nic_name, - [50] = sys_cmd_parallel_run, - [60] = sys_cmd_tty_create, - [61] = sys_cmd_tty_read_out, - [62] = sys_cmd_tty_write_in, - [63] = sys_cmd_tty_read_in, - [64] = sys_cmd_spawn_process, - [65] = sys_cmd_tty_set_fg, - [66] = sys_cmd_tty_get_fg, - [67] = sys_cmd_tty_kill_fg, - [68] = sys_cmd_tty_kill_all, - [69] = sys_cmd_tty_destroy, + [SYSTEM_CMD_SET_BG_COLOR] = sys_cmd_set_bg_color, + [SYSTEM_CMD_SET_BG_PATTERN] = sys_cmd_set_bg_pattern, + [SYSTEM_CMD_SET_WALLPAPER] = sys_cmd_set_wallpaper, + [SYSTEM_CMD_SET_DESKTOP_PROP] = sys_cmd_set_desktop_prop, + [SYSTEM_CMD_SET_MOUSE_SPEED] = sys_cmd_set_mouse_speed, + [SYSTEM_CMD_NETWORK_INIT] = sys_cmd_network_init, + [SYSTEM_CMD_GET_DESKTOP_PROP] = sys_cmd_get_desktop_prop, + [SYSTEM_CMD_GET_MOUSE_SPEED] = sys_cmd_get_mouse_speed, + [SYSTEM_CMD_GET_WALLPAPER_THUMB] = sys_cmd_get_wallpaper_thumb, + [SYSTEM_CMD_CLEAR_SCREEN] = sys_cmd_clear_screen, + [SYSTEM_CMD_RTC_GET] = sys_cmd_rtc_get, + [SYSTEM_CMD_REBOOT] = sys_cmd_reboot, + [SYSTEM_CMD_SHUTDOWN] = sys_cmd_shutdown, + [SYSTEM_CMD_BEEP] = sys_cmd_beep, + [SYSTEM_CMD_GET_MEM_INFO] = sys_cmd_get_mem_info, + [SYSTEM_CMD_GET_TICKS] = sys_cmd_get_ticks, + [SYSTEM_CMD_PCI_LIST] = sys_cmd_pci_list, + [SYSTEM_CMD_NETWORK_DHCP] = sys_cmd_network_dhcp, + [SYSTEM_CMD_NETWORK_GET_MAC] = sys_cmd_network_get_mac, + [SYSTEM_CMD_NETWORK_GET_IP] = sys_cmd_network_get_ip, + [SYSTEM_CMD_NETWORK_SET_IP] = sys_cmd_network_set_ip, + [SYSTEM_CMD_UDP_SEND] = sys_cmd_udp_send, + [SYSTEM_CMD_NETWORK_GET_STATS] = sys_cmd_network_get_stats, + [SYSTEM_CMD_NETWORK_GET_GATEWAY] = sys_cmd_network_get_gateway, + [SYSTEM_CMD_NETWORK_GET_DNS] = sys_cmd_network_get_dns, + [SYSTEM_CMD_ICMP_PING] = sys_cmd_icmp_ping, + [SYSTEM_CMD_NETWORK_IS_INIT] = sys_cmd_network_is_init, + [SYSTEM_CMD_GET_SHELL_CONFIG] = sys_cmd_get_shell_config, + [SYSTEM_CMD_SET_TEXT_COLOR] = sys_cmd_set_text_color, + [SYSTEM_CMD_NETWORK_HAS_IP] = sys_cmd_network_has_ip, + [SYSTEM_CMD_SET_WALLPAPER_PATH] = sys_cmd_set_wallpaper_path, + [SYSTEM_CMD_RTC_SET] = sys_cmd_rtc_set, + [SYSTEM_CMD_TCP_CONNECT] = sys_cmd_tcp_connect, + [SYSTEM_CMD_TCP_SEND] = sys_cmd_tcp_send, + [SYSTEM_CMD_TCP_RECV] = sys_cmd_tcp_recv, + [SYSTEM_CMD_TCP_CLOSE] = sys_cmd_tcp_close, + [SYSTEM_CMD_DNS_LOOKUP] = sys_cmd_dns_lookup, + [SYSTEM_CMD_SET_DNS] = sys_cmd_set_dns, + [SYSTEM_CMD_NET_UNLOCK] = sys_cmd_net_unlock, + [SYSTEM_CMD_SET_FONT] = sys_cmd_set_font, + [SYSTEM_CMD_SET_RAW_MODE] = sys_cmd_set_raw_mode, + [SYSTEM_CMD_TCP_RECV_NB] = sys_cmd_tcp_recv_nb, + [SYSTEM_CMD_SET_RESOLUTION] = sys_cmd_set_resolution, + [SYSTEM_CMD_NETWORK_GET_NIC_NAME] = sys_cmd_network_get_nic_name, + [SYSTEM_CMD_PARALLEL_RUN] = sys_cmd_parallel_run, + [SYSTEM_CMD_TTY_CREATE] = sys_cmd_tty_create, + [SYSTEM_CMD_TTY_READ_OUT] = sys_cmd_tty_read_out, + [SYSTEM_CMD_TTY_WRITE_IN] = sys_cmd_tty_write_in, + [SYSTEM_CMD_TTY_READ_IN] = sys_cmd_tty_read_in, + [SYSTEM_CMD_SPAWN] = sys_cmd_spawn_process, + [SYSTEM_CMD_TTY_SET_FG] = sys_cmd_tty_set_fg, + [SYSTEM_CMD_TTY_GET_FG] = sys_cmd_tty_get_fg, + [SYSTEM_CMD_TTY_KILL_FG] = sys_cmd_tty_kill_fg, + [SYSTEM_CMD_TTY_KILL_ALL] = sys_cmd_tty_kill_all, + [SYSTEM_CMD_TTY_DESTROY] = sys_cmd_tty_destroy, + [SYSTEM_CMD_EXEC] = sys_cmd_exec_process, + [SYSTEM_CMD_WAITPID] = sys_cmd_waitpid, + [SYSTEM_CMD_KILL_SIGNAL] = sys_cmd_kill_signal, + [SYSTEM_CMD_SIGACTION] = sys_cmd_sigaction, + [SYSTEM_CMD_SIGPROCMASK] = sys_cmd_sigprocmask, + [SYSTEM_CMD_SIGPENDING] = sys_cmd_sigpending, }; static uint64_t handle_sys_write(const syscall_args_t *args) { @@ -1871,6 +2272,49 @@ static uint64_t syscall_handler_inner(registers_t *regs) { return 0; } +static uint64_t syscall_maybe_deliver_signal(registers_t *regs) { + process_t *proc = process_get_current(); + if (!proc || !proc->is_user) return (uint64_t)regs; + + uint64_t pending = proc->signal_pending & ~proc->signal_mask; + if (!pending) return (uint64_t)regs; + + int sig = -1; + for (int i = 1; i < MAX_SIGNALS; i++) { + if (pending & (1ULL << (uint32_t)i)) { + sig = i; + break; + } + } + if (sig < 0) return (uint64_t)regs; + + proc->signal_pending &= ~(1ULL << (uint32_t)sig); + uint64_t handler = proc->signal_handlers[sig]; + int flags = proc->signal_action_flags[sig]; + + if (handler == 1) { + return (uint64_t)regs; + } + + if (handler == 0 || sig == 9) { + process_terminate_with_status(proc, 128 + sig); + return process_schedule((uint64_t)regs); + } + + if (flags & SA_RESETHAND) { + proc->signal_handlers[sig] = 0; + proc->signal_action_mask[sig] = 0; + proc->signal_action_flags[sig] = 0; + } + + uint64_t new_rsp = regs->rsp - sizeof(uint64_t); + *((uint64_t *)new_rsp) = regs->rip; + regs->rsp = new_rsp; + regs->rip = handler; + regs->rdi = (uint64_t)sig; + return (uint64_t)regs; +} + uint64_t syscall_handler_c(registers_t *regs) { uint64_t syscall_num = regs->rax; @@ -1899,13 +2343,13 @@ uint64_t syscall_handler_c(registers_t *regs) { } } - if (syscall_num == 5 && regs->rdi == 43) { // SYSTEM_CMD_YIELD + if (syscall_num == SYS_SYSTEM && regs->rdi == SYSTEM_CMD_YIELD) { extern uint64_t process_schedule(uint64_t current_rsp); regs->rax = 0; return process_schedule((uint64_t)regs); } - if (syscall_num == 5 && regs->rdi == 46) { // SYSTEM_CMD_SLEEP + if (syscall_num == SYS_SYSTEM && regs->rdi == SYSTEM_CMD_SLEEP) { uint32_t ms = (uint32_t)regs->rsi; process_t *proc = process_get_current(); extern uint32_t wm_get_ticks(void); @@ -1918,7 +2362,11 @@ uint64_t syscall_handler_c(registers_t *regs) { // Normal syscalls regs->rax = syscall_handler_inner(regs); - - // Return current RSP to assembly wrapper - return (uint64_t)regs; + + if (syscall_num == SYS_SYSTEM && regs->rdi == SYSTEM_CMD_WAITPID && regs->rax == (uint64_t)-2) { + regs->rax = 0; + return process_schedule((uint64_t)regs); + } + + return syscall_maybe_deliver_signal(regs); } diff --git a/src/sys/syscall.h b/src/sys/syscall.h index 03a5c12..d98d030 100644 --- a/src/sys/syscall.h +++ b/src/sys/syscall.h @@ -31,6 +31,7 @@ typedef struct { #define SYS_WRITE 1 #define SYS_GUI 3 #define SYS_FS 4 +#define SYS_SYSTEM 5 #define SYS_EXIT 60 // FS Commands @@ -48,13 +49,74 @@ typedef struct { #define FS_CMD_GETCWD 12 #define FS_CMD_CHDIR 13 #define FS_CMD_GET_INFO 14 +#define FS_CMD_DUP 15 +#define FS_CMD_DUP2 16 +#define FS_CMD_PIPE 17 +#define FS_CMD_FCNTL 18 +#define SYSTEM_CMD_SET_BG_COLOR 1 +#define SYSTEM_CMD_SET_BG_PATTERN 2 +#define SYSTEM_CMD_SET_WALLPAPER 3 +#define SYSTEM_CMD_SET_DESKTOP_PROP 4 +#define SYSTEM_CMD_SET_MOUSE_SPEED 5 +#define SYSTEM_CMD_NETWORK_INIT 6 +#define SYSTEM_CMD_GET_DESKTOP_PROP 7 +#define SYSTEM_CMD_GET_MOUSE_SPEED 8 +#define SYSTEM_CMD_GET_WALLPAPER_THUMB 9 +#define SYSTEM_CMD_CLEAR_SCREEN 10 +#define SYSTEM_CMD_RTC_GET 11 +#define SYSTEM_CMD_REBOOT 12 +#define SYSTEM_CMD_SHUTDOWN 13 +#define SYSTEM_CMD_BEEP 14 +#define SYSTEM_CMD_GET_MEM_INFO 15 +#define SYSTEM_CMD_GET_TICKS 16 +#define SYSTEM_CMD_PCI_LIST 17 +#define SYSTEM_CMD_NETWORK_DHCP 18 +#define SYSTEM_CMD_NETWORK_GET_MAC 19 +#define SYSTEM_CMD_NETWORK_GET_IP 20 +#define SYSTEM_CMD_NETWORK_SET_IP 21 +#define SYSTEM_CMD_UDP_SEND 22 +#define SYSTEM_CMD_NETWORK_GET_STATS 23 +#define SYSTEM_CMD_NETWORK_GET_GATEWAY 24 +#define SYSTEM_CMD_NETWORK_GET_DNS 25 +#define SYSTEM_CMD_ICMP_PING 26 +#define SYSTEM_CMD_NETWORK_IS_INIT 27 +#define SYSTEM_CMD_GET_SHELL_CONFIG 28 +#define SYSTEM_CMD_SET_TEXT_COLOR 29 +#define SYSTEM_CMD_NETWORK_HAS_IP 30 +#define SYSTEM_CMD_SET_WALLPAPER_PATH 31 +#define SYSTEM_CMD_RTC_SET 32 +#define SYSTEM_CMD_TCP_CONNECT 33 +#define SYSTEM_CMD_TCP_SEND 34 +#define SYSTEM_CMD_TCP_RECV 35 +#define SYSTEM_CMD_TCP_CLOSE 36 +#define SYSTEM_CMD_DNS_LOOKUP 37 +#define SYSTEM_CMD_SET_DNS 38 +#define SYSTEM_CMD_NET_UNLOCK 39 +#define SYSTEM_CMD_SET_FONT 40 #define SYSTEM_CMD_SET_RAW_MODE 41 #define SYSTEM_CMD_TCP_RECV_NB 42 #define SYSTEM_CMD_YIELD 43 #define SYSTEM_CMD_SLEEP 46 #define SYSTEM_CMD_SET_RESOLUTION 47 +#define SYSTEM_CMD_NETWORK_GET_NIC_NAME 48 #define SYSTEM_CMD_PARALLEL_RUN 50 +#define SYSTEM_CMD_TTY_CREATE 60 +#define SYSTEM_CMD_TTY_READ_OUT 61 +#define SYSTEM_CMD_TTY_WRITE_IN 62 +#define SYSTEM_CMD_TTY_READ_IN 63 +#define SYSTEM_CMD_SPAWN 64 +#define SYSTEM_CMD_TTY_SET_FG 65 +#define SYSTEM_CMD_TTY_GET_FG 66 +#define SYSTEM_CMD_TTY_KILL_FG 67 +#define SYSTEM_CMD_TTY_KILL_ALL 68 +#define SYSTEM_CMD_TTY_DESTROY 69 +#define SYSTEM_CMD_EXEC 70 +#define SYSTEM_CMD_WAITPID 71 +#define SYSTEM_CMD_KILL_SIGNAL 72 +#define SYSTEM_CMD_SIGACTION 73 +#define SYSTEM_CMD_SIGPROCMASK 74 +#define SYSTEM_CMD_SIGPENDING 75 void syscall_init(void); uint64_t syscall_handler_c(registers_t *regs); diff --git a/src/userland/Makefile b/src/userland/Makefile index a3d4207..6e2f2fa 100644 --- a/src/userland/Makefile +++ b/src/userland/Makefile @@ -59,7 +59,7 @@ $(BIN_DIR)/screenshot.elf: $(LIBC_OBJS) $(BIN_DIR)/screenshot.o $(BIN_DIR)/stb_i $(LD) $(LDFLAGS) $^ -o $@ $(BIN_DIR)/%.o: games/doom/%.c | $(BIN_DIR) - $(CC) $(CFLAGS) -Wno-error -Igames/doom -c $< -o $@ + $(CC) $(CFLAGS) -Wno-error -DBOREDOS -Igames/doom -c $< -o $@ $(BIN_DIR)/doom.elf: $(LIBC_OBJS) $(DOOM_OBJS) $(BIN_DIR)/stb_image.o $(LD) $(LDFLAGS) $^ -o $@ diff --git a/src/userland/cli/bsh.c b/src/userland/cli/bsh.c index c5bfe80..564766e 100644 --- a/src/userland/cli/bsh.c +++ b/src/userland/cli/bsh.c @@ -564,7 +564,7 @@ static void history_add(const char *line) { static void get_time_string(char *out, int max_len) { int dt[6] = {0}; - sys_system(11, (uint64_t)dt, 0, 0, 0); + sys_system(SYSTEM_CMD_RTC_GET, (uint64_t)dt, 0, 0, 0); char hh[4], mm[4]; itoa(dt[3], hh); itoa(dt[4], mm); diff --git a/src/userland/cli/clear.c b/src/userland/cli/clear.c index 6a1de8b..d0211e8 100644 --- a/src/userland/cli/clear.c +++ b/src/userland/cli/clear.c @@ -6,6 +6,6 @@ int main(int argc, char **argv) { (void)argc; (void)argv; - sys_system(10, 0, 0, 0, 0); + sys_system(SYSTEM_CMD_CLEAR_SCREEN, 0, 0, 0, 0); return 0; } diff --git a/src/userland/cli/date.c b/src/userland/cli/date.c index f19a51d..cc32754 100644 --- a/src/userland/cli/date.c +++ b/src/userland/cli/date.c @@ -7,7 +7,7 @@ int main(int argc, char **argv) { (void)argc; (void)argv; int dt[6]; - if (sys_system(11, (uint64_t)dt, 0, 0, 0) == 0) { + if (sys_system(SYSTEM_CMD_RTC_GET, (uint64_t)dt, 0, 0, 0) == 0) { printf("Current Date: %d-%d-%d %d:%d:%d\n", dt[0], dt[1], dt[2], dt[3], dt[4], dt[5]); } else { printf("Error: Could not retrieve date.\n"); diff --git a/src/userland/cli/sort.c b/src/userland/cli/sort.c index d8cd07e..f1fab48 100644 --- a/src/userland/cli/sort.c +++ b/src/userland/cli/sort.c @@ -85,7 +85,7 @@ int main(void) { if (!fb) return 1; // Seed PRNG with system time (ticks) - my_srand((unsigned int)sys_system(16, 0, 0, 0, 0)); + my_srand((unsigned int)sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0)); // Initialize perfect slope int max_h = WIN_H - 40; // max height diff --git a/src/userland/cli/third_party/lua/boredos_stubs.h b/src/userland/cli/third_party/lua/boredos_stubs.h index d78767a..3b44cea 100644 --- a/src/userland/cli/third_party/lua/boredos_stubs.h +++ b/src/userland/cli/third_party/lua/boredos_stubs.h @@ -1,1033 +1,7 @@ #ifndef BOREDOS_LUA_STUBS_H #define BOREDOS_LUA_STUBS_H -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include - -#include "stdlib.h" -#include "string.h" #include "syscall.h" -#include "math.h" - -int errno = 0; - -static FILE boredos_stdin_obj = {0, 0, 0, 0, 0}; -static FILE boredos_stdout_obj = {1, 0, 0, 0, 0}; -static FILE boredos_stderr_obj = {2, 0, 0, 0, 0}; -FILE *stdin = &boredos_stdin_obj; -FILE *stdout = &boredos_stdout_obj; -FILE *stderr = &boredos_stderr_obj; - -static int _b_is_leap(int year) { - return ((year % 4) == 0 && (year % 100) != 0) || ((year % 400) == 0); -} - -static int _b_days_in_month(int year, int month) { - static const int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - if (month == 1 && _b_is_leap(year)) { - return 29; - } - return mdays[month]; -} - -static long long _b_days_before_year(int year) { - long long y = (long long)year - 1; - return y * 365 + y / 4 - y / 100 + y / 400; -} - -static long long _b_days_since_epoch(int year, int month, int day) { - long long days = _b_days_before_year(year) - _b_days_before_year(1970); - int m; - for (m = 0; m < month - 1; m++) { - days += _b_days_in_month(year, m); - } - days += (day - 1); - return days; -} - -static void _b_civil_from_days(long long z, int *year, int *month, int *day) { - z += 719468; - long long era = (z >= 0 ? z : z - 146096) / 146097; - unsigned doe = (unsigned)(z - era * 146097); - unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; - int y = (int)yoe + (int)era * 400; - unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); - unsigned mp = (5 * doy + 2) / 153; - unsigned d = doy - (153 * mp + 2) / 5 + 1; - unsigned m = mp + (mp < 10 ? 3 : (unsigned)-9); - y += (m <= 2); - *year = y; - *month = (int)m; - *day = (int)d; -} - -int setjmp(jmp_buf env) { - __asm__ volatile( - "movq %%rbx, 0(%0)\n\t" - "movq %%rbp, 8(%0)\n\t" - "movq %%r12, 16(%0)\n\t" - "movq %%r13, 24(%0)\n\t" - "movq %%r14, 32(%0)\n\t" - "movq %%r15, 40(%0)\n\t" - "leaq 8(%%rsp), %%rax\n\t" - "movq %%rax, 48(%0)\n\t" - "movq (%%rsp), %%rax\n\t" - "movq %%rax, 56(%0)\n\t" - : - : "r"(env) - : "rax", "memory"); - return 0; -} - -void longjmp(jmp_buf env, int val) { - int r = (val == 0) ? 1 : val; - __asm__ volatile( - "movq 0(%0), %%rbx\n\t" - "movq 8(%0), %%rbp\n\t" - "movq 16(%0), %%r12\n\t" - "movq 24(%0), %%r13\n\t" - "movq 32(%0), %%r14\n\t" - "movq 40(%0), %%r15\n\t" - "movq 48(%0), %%rsp\n\t" - "movl %1, %%eax\n\t" - "movq 56(%0), %%rdx\n\t" - "jmp *%%rdx\n\t" - : - : "r"(env), "r"(r) - : "rax", "rdx", "memory"); - __builtin_unreachable(); -} - -FILE *fopen(const char *path, const char *mode) { - int fd = sys_open(path, mode); - FILE *f; - if (fd < 0) { - errno = EINVAL; - return NULL; - } - f = (FILE *)malloc(sizeof(FILE)); - if (!f) { - sys_close(fd); - errno = ERANGE; - return NULL; - } - f->fd = fd; - f->eof = 0; - f->err = 0; - f->has_ungetc = 0; - f->ungetc_char = 0; - return f; -} - -FILE *freopen(const char *path, const char *mode, FILE *stream) { - int fd; - if (!stream) { - return fopen(path, mode); - } - if (stream->fd >= 0) { - sys_close(stream->fd); - } - fd = sys_open(path, mode); - if (fd < 0) { - stream->err = 1; - errno = EINVAL; - return NULL; - } - stream->fd = fd; - stream->eof = 0; - stream->err = 0; - stream->has_ungetc = 0; - return stream; -} - -int fclose(FILE *stream) { - if (!stream) { - return EOF; - } - if (stream != stdin && stream != stdout && stream != stderr) { - if (stream->fd >= 0) { - sys_close(stream->fd); - } - free(stream); - } - return 0; -} - -size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { - size_t total; - int n; - if (!stream || !ptr || size == 0 || nmemb == 0) { - return 0; - } - total = size * nmemb; - n = sys_read(stream->fd, ptr, (uint32_t)total); - if (n <= 0) { - if (n == 0) { - stream->eof = 1; - } else { - stream->err = 1; - } - return 0; - } - if ((size_t)n < total) { - stream->eof = 1; - } - return (size_t)n / size; -} - -size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { - size_t total; - int n; - if (!stream || !ptr || size == 0 || nmemb == 0) { - return 0; - } - total = size * nmemb; - if (stream->fd <= 2) { - n = sys_write(stream->fd, (const char *)ptr, (int)total); - } else { - n = sys_write_fs(stream->fd, ptr, (uint32_t)total); - } - if (n < 0) { - stream->err = 1; - return 0; - } - return (size_t)n / size; -} - -int fseek(FILE *stream, long offset, int whence) { - if (!stream) { - return -1; - } - if (sys_seek(stream->fd, (int)offset, whence) < 0) { - stream->err = 1; - return -1; - } - stream->eof = 0; - stream->has_ungetc = 0; - return 0; -} - -long ftell(FILE *stream) { - if (!stream) { - return -1; - } - return (long)sys_tell(stream->fd); -} - -int getc(FILE *stream) { - unsigned char ch; - int n; - if (!stream) { - return EOF; - } - if (stream->has_ungetc) { - stream->has_ungetc = 0; - return stream->ungetc_char; - } - n = sys_read(stream->fd, &ch, 1); - if (n <= 0) { - if (n == 0) { - stream->eof = 1; - } else { - stream->err = 1; - } - return EOF; - } - return (int)ch; -} - -int ungetc(int c, FILE *stream) { - if (!stream || c == EOF) { - return EOF; - } - stream->has_ungetc = 1; - stream->ungetc_char = (unsigned char)c; - stream->eof = 0; - return c; -} - -char *fgets(char *s, int n, FILE *stream) { - int i; - if (!s || n <= 0 || !stream) { - return NULL; - } - for (i = 0; i < n - 1; i++) { - int c = getc(stream); - if (c == EOF) { - break; - } - s[i] = (char)c; - if (c == '\n') { - i++; - break; - } - } - if (i == 0) { - return NULL; - } - s[i] = '\0'; - return s; -} - -int fputs(const char *s, FILE *stream) { - size_t len; - size_t written; - if (!s || !stream) { - return EOF; - } - len = strlen(s); - written = fwrite(s, 1, len, stream); - return (written == len) ? (int)len : EOF; -} - -int feof(FILE *stream) { - return stream ? stream->eof : 1; -} - -int ferror(FILE *stream) { - return stream ? stream->err : 1; -} - -void clearerr(FILE *stream) { - if (stream) { - stream->eof = 0; - stream->err = 0; - } -} - -int fflush(FILE *stream) { - (void)stream; - return 0; -} - -int remove(const char *path) { - return sys_delete(path); -} - -int rename(const char *oldpath, const char *newpath) { - (void)oldpath; - (void)newpath; - errno = EINVAL; - return -1; -} - -FILE *tmpfile(void) { - errno = EINVAL; - return NULL; -} - -char *tmpnam(char *s) { - (void)s; - errno = EINVAL; - return NULL; -} - -int isdigit(int c) { return (c >= '0' && c <= '9'); } -int isalpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); } -int isalnum(int c) { return isalpha(c) || isdigit(c); } -int isspace(int c) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; -} -int isupper(int c) { return (c >= 'A' && c <= 'Z'); } -int islower(int c) { return (c >= 'a' && c <= 'z'); } -int isxdigit(int c) { - return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} -int iscntrl(int c) { return ((c >= 0 && c < 32) || c == 127); } -int ispunct(int c) { - return isprint(c) && !isalnum(c) && !isspace(c); -} -int isprint(int c) { return (c >= 32 && c < 127); } -int isgraph(int c) { return (c > 32 && c < 127); } -int tolower(int c) { return isupper(c) ? (c - 'A' + 'a') : c; } -int toupper(int c) { return islower(c) ? (c - 'a' + 'A') : c; } - -int abs(int x) { - return (x < 0) ? -x : x; -} - -int strncmp(const char *s1, const char *s2, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - unsigned char c1 = (unsigned char)s1[i]; - unsigned char c2 = (unsigned char)s2[i]; - if (c1 != c2) { - return (int)c1 - (int)c2; - } - if (c1 == '\0') { - return 0; - } - } - return 0; -} - -char *strncpy(char *dest, const char *src, size_t n) { - size_t i; - for (i = 0; i < n && src[i] != '\0'; i++) { - dest[i] = src[i]; - } - for (; i < n; i++) { - dest[i] = '\0'; - } - return dest; -} - -char *strncat(char *dest, const char *src, size_t n) { - size_t dlen = strlen(dest); - size_t i; - for (i = 0; i < n && src[i] != '\0'; i++) { - dest[dlen + i] = src[i]; - } - dest[dlen + i] = '\0'; - return dest; -} - -char *strrchr(const char *s, int c) { - const char *last = NULL; - for (;; s++) { - if (*s == (char)c) { - last = s; - } - if (*s == '\0') { - break; - } - } - return (char *)last; -} - -char *strpbrk(const char *s, const char *accept) { - for (; *s; s++) { - const char *a; - for (a = accept; *a; a++) { - if (*s == *a) { - return (char *)s; - } - } - } - return NULL; -} - -size_t strspn(const char *s, const char *accept) { - size_t n = 0; - while (*s) { - if (!strchr(accept, *s)) { - break; - } - n++; - s++; - } - return n; -} - -size_t strcspn(const char *s, const char *reject) { - size_t n = 0; - while (*s) { - if (strchr(reject, *s)) { - break; - } - n++; - s++; - } - return n; -} - -void *memchr(const void *s, int c, size_t n) { - const unsigned char *p = (const unsigned char *)s; - size_t i; - for (i = 0; i < n; i++) { - if (p[i] == (unsigned char)c) { - return (void *)(p + i); - } - } - return NULL; -} - -int strcoll(const char *s1, const char *s2) { - return strcmp(s1, s2); -} - -char *strerror(int errnum) { - switch (errnum) { - case 0: return "no error"; - case EDOM: return "domain error"; - case ERANGE: return "range error"; - case EINVAL: return "invalid argument"; - default: return "unknown error"; - } -} - -static int _b_hex_digit(unsigned value, int upper) { - if (value < 10U) { - return (int)('0' + value); - } - return (int)((upper ? 'A' : 'a') + (value - 10U)); -} - -static void _b_append_char(char *out, size_t cap, size_t *idx, int c) { - if (*idx + 1 < cap) { - out[*idx] = (char)c; - } - (*idx)++; -} - -static void _b_append_strn(char *out, size_t cap, size_t *idx, const char *s, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - _b_append_char(out, cap, idx, s[i]); - } -} - -static void _b_utoa(unsigned long long v, unsigned base, int upper, char *buf, size_t *len) { - char tmp[64]; - size_t i = 0; - if (v == 0) { - tmp[i++] = '0'; - } else { - while (v && i < sizeof(tmp)) { - tmp[i++] = (char)_b_hex_digit((unsigned)(v % base), upper); - v /= base; - } - } - *len = i; - while (i > 0) { - *buf++ = tmp[--i]; - } -} - -static void _b_itoa(long long v, char *buf, size_t *len) { - unsigned long long uv; - size_t n = 0; - if (v < 0) { - *buf++ = '-'; - n++; - uv = (unsigned long long)(-(v + 1)) + 1ULL; - } else { - uv = (unsigned long long)v; - } - _b_utoa(uv, 10U, 0, buf, len); - *len += n; -} - -static void _b_ftoa(double d, int precision, char *buf, size_t *len) { - long long ip; - double frac; - size_t n = 0; - if (precision < 0) { - precision = 6; - } - if (d < 0.0) { - buf[n++] = '-'; - d = -d; - } - ip = (long long)d; - frac = d - (double)ip; - { - char ibuf[64]; - size_t ilen = 0; - _b_utoa((unsigned long long)ip, 10U, 0, ibuf, &ilen); - memcpy(buf + n, ibuf, ilen); - n += ilen; - } - if (precision > 0) { - int i; - buf[n++] = '.'; - for (i = 0; i < precision; i++) { - int digit; - frac *= 10.0; - digit = (int)frac; - if (digit < 0) digit = 0; - if (digit > 9) digit = 9; - buf[n++] = (char)('0' + digit); - frac -= (double)digit; - } - } - *len = n; -} - -int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { - size_t out_i = 0; - while (*fmt) { - if (*fmt != '%') { - _b_append_char(str, size, &out_i, *fmt++); - continue; - } - - fmt++; - if (*fmt == '%') { - _b_append_char(str, size, &out_i, '%'); - fmt++; - continue; - } - - while (*fmt == '-' || *fmt == '+' || *fmt == ' ' || *fmt == '#' || *fmt == '0') { - fmt++; - } - - if (*fmt == '*') { - (void)va_arg(ap, int); - fmt++; - } else { - while (isdigit((unsigned char)*fmt)) { - fmt++; - } - } - - { - int precision = -1; - if (*fmt == '.') { - fmt++; - precision = 0; - if (*fmt == '*') { - precision = va_arg(ap, int); - fmt++; - } else { - while (isdigit((unsigned char)*fmt)) { - precision = precision * 10 + (*fmt - '0'); - fmt++; - } - } - } - - int lcount = 0; - while (*fmt == 'l') { - lcount++; - fmt++; - } - - switch (*fmt) { - case 'd': - case 'i': { - long long v; - char nbuf[64]; - size_t nlen = 0; - if (lcount >= 2) v = va_arg(ap, long long); - else if (lcount == 1) v = va_arg(ap, long); - else v = va_arg(ap, int); - _b_itoa(v, nbuf, &nlen); - _b_append_strn(str, size, &out_i, nbuf, nlen); - break; - } - case 'u': - case 'x': - case 'X': - case 'o': { - unsigned long long v; - unsigned base = (*fmt == 'o') ? 8U : ((*fmt == 'u') ? 10U : 16U); - char nbuf[64]; - size_t nlen = 0; - int upper = (*fmt == 'X'); - if (lcount >= 2) v = va_arg(ap, unsigned long long); - else if (lcount == 1) v = va_arg(ap, unsigned long); - else v = va_arg(ap, unsigned int); - _b_utoa(v, base, upper, nbuf, &nlen); - _b_append_strn(str, size, &out_i, nbuf, nlen); - break; - } - case 'c': { - int c = va_arg(ap, int); - _b_append_char(str, size, &out_i, c); - break; - } - case 's': { - const char *s = va_arg(ap, const char *); - size_t slen; - if (!s) s = "(null)"; - slen = strlen(s); - if (precision >= 0 && (size_t)precision < slen) { - slen = (size_t)precision; - } - _b_append_strn(str, size, &out_i, s, slen); - break; - } - case 'p': { - uintptr_t v = (uintptr_t)va_arg(ap, void *); - char nbuf[32]; - size_t nlen = 0; - _b_append_strn(str, size, &out_i, "0x", 2); - _b_utoa((unsigned long long)v, 16U, 0, nbuf, &nlen); - _b_append_strn(str, size, &out_i, nbuf, nlen); - break; - } - case 'f': - case 'g': - case 'e': { - double v = va_arg(ap, double); - char nbuf[96]; - size_t nlen = 0; - _b_ftoa(v, precision, nbuf, &nlen); - _b_append_strn(str, size, &out_i, nbuf, nlen); - break; - } - default: - _b_append_char(str, size, &out_i, '%'); - _b_append_char(str, size, &out_i, *fmt); - break; - } - } - if (*fmt) { - fmt++; - } - } - - if (size > 0) { - size_t term = (out_i < size - 1) ? out_i : (size - 1); - str[term] = '\0'; - } - return (int)out_i; -} - -int snprintf(char *str, size_t size, const char *fmt, ...) { - int n; - va_list ap; - va_start(ap, fmt); - n = vsnprintf(str, size, fmt, ap); - va_end(ap); - return n; -} - -int sprintf(char *str, const char *fmt, ...) { - int n; - va_list ap; - va_start(ap, fmt); - n = vsnprintf(str, (size_t)-1, fmt, ap); - va_end(ap); - return n; -} - -int fprintf(FILE *stream, const char *fmt, ...) { - char buf[1024]; - int len; - va_list ap; - va_start(ap, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - if (len <= 0) { - return len; - } - if ((size_t)len > sizeof(buf)) { - len = (int)sizeof(buf); - } - if (fwrite(buf, 1, (size_t)len, stream) == 0) { - return -1; - } - return len; -} - -int sscanf(const char *str, const char *fmt, ...) { - (void)str; - (void)fmt; - return 0; -} - -double ldexp(double x, int expn) { - double v = x; - int i; - if (expn >= 0) { - for (i = 0; i < expn; i++) { - v *= 2.0; - } - } else { - for (i = 0; i < -expn; i++) { - v *= 0.5; - } - } - return v; -} - -double frexp(double x, int *expn) { - int e = 0; - double v = x; - if (x == 0.0) { - *expn = 0; - return 0.0; - } - while (fabs(v) >= 1.0) { - v *= 0.5; - e++; - } - while (fabs(v) > 0.0 && fabs(v) < 0.5) { - v *= 2.0; - e--; - } - *expn = e; - return v; -} - -static double _b_atan_series(double x) { - double x2 = x * x; - double term = x; - double sum = x; - for (int n = 3; n <= 23; n += 2) { - term *= -x2; - sum += term / (double)n; - } - return sum; -} - -static double _b_atan_precise(double x) { - if (x < 0.0) { - return -_b_atan_precise(-x); - } - if (x > 1.0) { - return (M_PI / 2.0) - _b_atan_precise(1.0 / x); - } - if (x > 0.5) { - double y = (x - 1.0) / (x + 1.0); - return (M_PI / 4.0) + _b_atan_series(y); - } - return _b_atan_series(x); -} - -double atan2(double y, double x) { - if (x > 0.0) { - return _b_atan_precise(y / x); - } - if (x < 0.0) { - if (y >= 0.0) return _b_atan_precise(y / x) + M_PI; - return _b_atan_precise(y / x) - M_PI; - } - if (y > 0.0) return M_PI / 2.0; - if (y < 0.0) return -M_PI / 2.0; - return 0.0; -} - -double asin(double x) { - if (x > 1.0 || x < -1.0) { - errno = EDOM; - return 0.0; - } - return atan2(x, sqrt(1.0 - x * x)); -} - -double acos(double x) { - if (x > 1.0 || x < -1.0) { - errno = EDOM; - return 0.0; - } - return M_PI / 2.0 - asin(x); -} - -static time_t _b_seconds_from_ymdhms(int year, int month, int day, int hour, int minute, int second) { - long long days = _b_days_since_epoch(year, month, day); - return (time_t)(days * 86400LL + hour * 3600LL + minute * 60LL + second); -} - -static void _b_fill_tm_from_epoch(time_t t, struct tm *out) { - long long sec = (long long)t; - long long days; - int sod; - int year, month, day; - - if (sec < 0) { - long long d = ((-sec) + 86399LL) / 86400LL; - sec += d * 86400LL; - } - - days = sec / 86400LL; - sod = (int)(sec % 86400LL); - if (sod < 0) { - sod += 86400; - days--; - } - - _b_civil_from_days(days, &year, &month, &day); - - out->tm_year = year - 1900; - out->tm_mon = month - 1; - out->tm_mday = day; - out->tm_hour = sod / 3600; - out->tm_min = (sod % 3600) / 60; - out->tm_sec = sod % 60; - out->tm_wday = (int)((days + 4) % 7); - if (out->tm_wday < 0) out->tm_wday += 7; - - { - long long jan1 = _b_days_since_epoch(year, 1, 1); - out->tm_yday = (int)(days - jan1); - } - out->tm_isdst = 0; -} - -time_t time(time_t *out) { - int dt[6] = {1970, 1, 1, 0, 0, 0}; - time_t t; - if (sys_system(SYSTEM_CMD_RTC_GET, 0, (uint64_t)dt, 0, 0) != 0) { - t = 0; - } else { - t = _b_seconds_from_ymdhms(dt[0], dt[1], dt[2], dt[3], dt[4], dt[5]); - } - if (out) { - *out = t; - } - return t; -} - -clock_t clock(void) { - static uint64_t start_tsc = 0; - unsigned int lo; - unsigned int hi; - uint64_t now_tsc; - - __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); - now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo; - - if (start_tsc == 0) { - start_tsc = now_tsc; - __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); - now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo; - } - - return (clock_t)(now_tsc - start_tsc); -} - -struct tm *gmtime(const time_t *timer) { - static struct tm tmv; - if (!timer) { - return NULL; - } - _b_fill_tm_from_epoch(*timer, &tmv); - return &tmv; -} - -struct tm *localtime(const time_t *timer) { - return gmtime(timer); -} - -size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm) { - (void)fmt; - if (!s || max == 0 || !tm) { - return 0; - } - { - int n = snprintf(s, max, "%04d-%02d-%02d %02d:%02d:%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - if (n < 0 || (size_t)n >= max) { - if (max > 0) s[0] = '\0'; - return 0; - } - return (size_t)n; - } -} - -time_t mktime(struct tm *tm) { - if (!tm) { - return (time_t)-1; - } - return _b_seconds_from_ymdhms( - tm->tm_year + 1900, - tm->tm_mon + 1, - tm->tm_mday, - tm->tm_hour, - tm->tm_min, - tm->tm_sec); -} - -static struct lconv _b_lconv = { - ".", "", "", "", "", ".", "", "", "", "", - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -char *setlocale(int category, const char *locale) { - (void)category; - if (locale == NULL || strcmp(locale, "C") == 0 || strcmp(locale, "") == 0) { - return "C"; - } - return NULL; -} - -struct lconv *localeconv(void) { - return &_b_lconv; -} - -sighandler_t signal(int sig, sighandler_t handler) { - (void)sig; - return handler; -} - -int system(const char *command) { - (void)command; - errno = EINVAL; - return -1; -} - -char *getenv(const char *name) { - (void)name; - return NULL; -} - -void abort(void) { - sys_exit(1); - while (1) {} -} - -double strtod(const char *nptr, char **endptr) { - const char *p = nptr; - int sign = 1; - double value = 0.0; - double frac = 0.0; - double scale = 1.0; - int exp_sign = 1; - int exp_val = 0; - - while (isspace((unsigned char)*p)) p++; - - if (*p == '-') { - sign = -1; - p++; - } else if (*p == '+') { - p++; - } - - while (isdigit((unsigned char)*p)) { - value = value * 10.0 + (double)(*p - '0'); - p++; - } - - if (*p == '.') { - p++; - while (isdigit((unsigned char)*p)) { - frac = frac * 10.0 + (double)(*p - '0'); - scale *= 10.0; - p++; - } - value += frac / scale; - } - - if (*p == 'e' || *p == 'E') { - const char *ep = p + 1; - if (*ep == '-') { - exp_sign = -1; - ep++; - } else if (*ep == '+') { - ep++; - } - if (isdigit((unsigned char)*ep)) { - p = ep; - while (isdigit((unsigned char)*p)) { - exp_val = exp_val * 10 + (*p - '0'); - p++; - } - } - } - - if (endptr) { - *endptr = (char *)p; - } - - if (exp_val != 0) { - value = ldexp(value, exp_sign * exp_val); - } - return sign * value; -} #endif diff --git a/src/userland/cli/third_party/lua/sysinclude/time.h b/src/userland/cli/third_party/lua/sysinclude/time.h index 8fb30c3..69d4123 100644 --- a/src/userland/cli/third_party/lua/sysinclude/time.h +++ b/src/userland/cli/third_party/lua/sysinclude/time.h @@ -3,6 +3,9 @@ #include +#ifndef BOREDOS_LIBC_TIME_H +#define BOREDOS_LIBC_TIME_H + typedef long long time_t; typedef unsigned long long clock_t; @@ -28,3 +31,5 @@ size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm); time_t mktime(struct tm *tm); #endif + +#endif diff --git a/src/userland/games/doom/boredos_libc.c b/src/userland/games/doom/boredos_libc.c deleted file mode 100644 index 165bd0f..0000000 --- a/src/userland/games/doom/boredos_libc.c +++ /dev/null @@ -1,219 +0,0 @@ -#include "boredos_libc.h" -#include - -int errno = 0; - -static FILE _stderr = {2, 0, 0}; -static FILE _stdout = {1, 0, 0}; -static FILE _stdin = {0, 0, 0}; - -FILE* stderr = &_stderr; -FILE* stdout = &_stdout; -FILE* stdin = &_stdin; - -FILE *fopen(const char *path, const char *mode) { - int fd = sys_open(path, mode); - if (fd < 0) return NULL; - FILE *f = malloc(sizeof(FILE)); - f->fd = fd; - f->eof = 0; - f->error = 0; - return f; -} - -int fclose(FILE *stream) { - if (!stream) return EOF; - if (stream != stderr && stream != stdout && stream != stdin) { - sys_close(stream->fd); - free(stream); - } - return 0; -} - -size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { - if (!stream) return 0; - int bytes = sys_read(stream->fd, ptr, size * nmemb); - if (bytes < 0) { - stream->error = 1; - return 0; - } - if (bytes == 0) stream->eof = 1; - return bytes / size; -} - -size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { - if (!stream) return 0; - if (stream == stdout || stream == stderr) { - sys_write(stream->fd, ptr, size * nmemb); - return nmemb; - } - int bytes = sys_write_fs(stream->fd, ptr, size * nmemb); - if (bytes < 0) { - stream->error = 1; - return 0; - } - return bytes / size; -} - -int fseek(FILE *stream, long offset, int whence) { - if (!stream) return -1; - stream->eof = 0; - return sys_seek(stream->fd, offset, whence); -} - -long ftell(FILE *stream) { - if (!stream) return -1; - return sys_tell(stream->fd); -} - -int remove(const char *pathname) { - return sys_delete(pathname); -} - -int rename(const char *oldpath, const char *newpath) { - return -1; -} - -int fputc(int c, FILE *stream) { - unsigned char ch = c; - if (fwrite(&ch, 1, 1, stream) != 1) return EOF; - return ch; -} - -int fputs(const char *s, FILE *stream) { - size_t len = strlen(s); - if (fwrite(s, 1, len, stream) != len) return EOF; - return 0; -} - -long filelength(FILE *f) { - if (!f) return -1; - return sys_size(f->fd); -} - -int mkdir(const char *pathname, int mode) { - return sys_mkdir(pathname); -} - -int access(const char *pathname, int mode) { - if (sys_exists(pathname)) return 0; - return -1; -} - -int stat(const char *pathname, struct stat *statbuf) { - if (sys_exists(pathname)) { - if (statbuf) { - statbuf->st_size = 0; - statbuf->st_mode = 0; - } - return 0; - } - return -1; -} - -int strncasecmp(const char *s1, const char *s2, size_t n) { - while (n--) { - char c1 = tolower(*s1++); - char c2 = tolower(*s2++); - if (c1 != c2) return c1 - c2; - if (!c1) break; - } - return 0; -} -int strcasecmp(const char *s1, const char *s2) { - while (1) { - char c1 = tolower(*s1++); - char c2 = tolower(*s2++); - if (c1 != c2) return c1 - c2; - if (!c1) break; - } - return 0; -} -char *strncpy(char *dest, const char *src, size_t n) { - char *ret = dest; - while (n && *src) { *dest++ = *src++; n--; } - while (n) { *dest++ = 0; n--; } - return ret; -} -int strncmp(const char *s1, const char *s2, size_t n) { - while (n--) { - if (*s1 != *s2) return *s1 - *s2; - if (!*s1) break; - s1++; s2++; - } - return 0; -} -char *strrchr(const char *s, int c) { - const char *last = NULL; - while (*s) { if (*s == c) last = s; s++; } - if (c == 0) last = s; - return (char*)last; -} -char *strdup(const char *s) { - size_t len = strlen(s) + 1; - char *dup = malloc(len); - if (dup) memcpy(dup, s, len); - return dup; -} - -int toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 32 : c; } -int tolower(int c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; } -int isspace(int c) { return c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '\v' || c == '\f'; } -int isdigit(int c) { return c >= '0' && c <= '9'; } -int isprint(int c) { return c >= 32 && c <= 126; } -int isalpha(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } -int isalnum(int c) { return isalpha(c) || isdigit(c); } -int isgraph(int c) { return c > 32 && c <= 126; } -int ispunct(int c) { return isprint(c) && !isspace(c) && !isalnum(c); } -int isupper(int c) { return c >= 'A' && c <= 'Z'; } - -void _exit(int status) { - exit(status); -} - - - -int fflush(FILE *stream) { return 0; } -int abs(int x) { return x < 0 ? -x : x; } -int putchar(int c) { return fputc(c, stdout); } -int system(const char *command) { return -1; } -#define STB_SPRINTF_IMPLEMENTATION -#define STB_SPRINTF_NOFLOAT -#include "stb_sprintf.h" - -int vfprintf(FILE *stream, const char *format, va_list ap) { - char buf[1024]; - int len = stbsp_vsnprintf(buf, sizeof(buf), format, ap); - if (len > 0) fwrite(buf, 1, len, stream); - return len; -} - -int fprintf(FILE *stream, const char *format, ...) { - va_list ap; - va_start(ap, format); - int len = vfprintf(stream, format, ap); - va_end(ap); - return len; -} - -int sprintf(char *str, const char *format, ...) { - va_list ap; - va_start(ap, format); - int len = stbsp_vsprintf(str, format, ap); - va_end(ap); - return len; -} - -int snprintf(char *str, size_t size, const char *format, ...) { - va_list ap; - va_start(ap, format); - int len = stbsp_vsnprintf(str, size, format, ap); - va_end(ap); - return len; -} - -int vsnprintf(char *str, size_t size, const char *format, va_list ap) { - return stbsp_vsnprintf(str, size, format, ap); -} - -int sscanf(const char *str, const char *format, ...) { return 0; } diff --git a/src/userland/games/doom/boredos_libc.h b/src/userland/games/doom/boredos_libc.h index ff63838..46e428d 100644 --- a/src/userland/games/doom/boredos_libc.h +++ b/src/userland/games/doom/boredos_libc.h @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include #define SEEK_SET 0 #define SEEK_CUR 1 @@ -12,12 +15,6 @@ #define EOF (-1) -typedef struct { - int fd; - int eof; - int error; -} FILE; - extern FILE* stderr; extern FILE* stdout; extern FILE* stdin; @@ -34,6 +31,14 @@ extern FILE* stdin; #define W_OK 2 #define X_OK 1 +int open(const char *pathname, int flags, ...); +int close(int fd); +ssize_t read(int fd, void *buf, size_t count); +ssize_t write(int fd, const void *buf, size_t count); +off_t lseek(int fd, off_t offset, int whence); +int unlink(const char *pathname); +int isatty(int fd); + FILE *fopen(const char *path, const char *mode); int fclose(FILE *stream); size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); @@ -73,11 +78,6 @@ int isupper(int c); int mkdir(const char *pathname, int mode); int access(const char *pathname, int mode); - -struct stat { - int st_size; - int st_mode; -}; int stat(const char *pathname, struct stat *statbuf); char *strstr(const char *haystack, const char *needle); diff --git a/src/userland/games/doom/hu_stuff.c b/src/userland/games/doom/hu_stuff.c index b63cac7..253e4c7 100644 --- a/src/userland/games/doom/hu_stuff.c +++ b/src/userland/games/doom/hu_stuff.c @@ -25,6 +25,7 @@ #include "deh_main.h" #include "i_swap.h" +#include "i_system.h" #include "i_video.h" #include "hu_stuff.h" @@ -288,14 +289,47 @@ void HU_Init(void) int i; int j; + int fallback_lump = -1; char buffer[9]; // load the heads-up font j = HU_FONTSTART; for (i=0;i= 0) + { + break; + } + } + + if (fallback_lump < 0) + { + fallback_lump = W_CheckNumForName("STTNUM0"); + } + } + + if (lump < 0) + { + lump = fallback_lump; + } + + if (lump < 0) + { + I_Error("HU_Init: no usable HUD font lumps (missing STCFNxxx/STTNUM0)"); + } + + hu_font[i] = (patch_t *) W_CacheLumpNum(lump, PU_STATIC); } } diff --git a/src/userland/games/doom/i_system.c b/src/userland/games/doom/i_system.c index 53ab2c9..adfa390 100644 --- a/src/userland/games/doom/i_system.c +++ b/src/userland/games/doom/i_system.c @@ -264,7 +264,7 @@ void I_Quit (void) #endif } -#if !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__) +#if !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__) && !defined(BOREDOS) #define ZENITY_BINARY "/usr/bin/zenity" // returns non-zero if zenity is available @@ -347,7 +347,7 @@ static int ZenityErrorBox(char *message) return result; } -#endif /* !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__) */ +#endif /* !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__) && !defined(BOREDOS) */ // @@ -456,7 +456,9 @@ void I_Error (char *error, ...) #else { +#if !defined(BOREDOS) ZenityErrorBox(msgbuf); +#endif } #endif diff --git a/src/userland/games/doom/sys/stat.h b/src/userland/games/doom/sys/stat.h index 9300dee..73aacfe 100644 --- a/src/userland/games/doom/sys/stat.h +++ b/src/userland/games/doom/sys/stat.h @@ -1 +1,15 @@ -#include "../boredos_libc.h" +#ifndef DOOM_COMPAT_SYS_STAT_H +#define DOOM_COMPAT_SYS_STAT_H + +#include "types.h" + +struct stat { + int st_size; + int st_mode; +}; + +int stat(const char *pathname, struct stat *statbuf); +int fstat(int fd, struct stat *statbuf); +int mkdir(const char *pathname, int mode); + +#endif diff --git a/src/userland/games/doom/sys/types.h b/src/userland/games/doom/sys/types.h index 9300dee..0cdc230 100644 --- a/src/userland/games/doom/sys/types.h +++ b/src/userland/games/doom/sys/types.h @@ -1 +1,8 @@ -#include "../boredos_libc.h" +#ifndef DOOM_COMPAT_SYS_TYPES_H +#define DOOM_COMPAT_SYS_TYPES_H + +typedef long ssize_t; +typedef long off_t; +typedef unsigned int mode_t; + +#endif diff --git a/src/userland/gui/browser.c b/src/userland/gui/browser.c index ab7aa7f..7c29386 100644 --- a/src/userland/gui/browser.c +++ b/src/userland/gui/browser.c @@ -313,7 +313,7 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog int total = 0; int last_render = 0; if (progressive) inc_parse_offset = 0; - long long last_data_tick = sys_system(16, 0, 0, 0, 0); + long long last_data_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); while (1) { int len = sys_tcp_recv_nb(dest_buf + total, max_len - 1 - total); @@ -321,7 +321,7 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog if (len == -2) break; if (len == 0) { - long long now = sys_system(16, 0, 0, 0, 0); + long long now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); if (now > last_data_tick + 1800) break; // 30 sec timeout gui_event_t ev; @@ -353,7 +353,7 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog continue; } - last_data_tick = sys_system(16, 0, 0, 0, 0); + last_data_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); total += len; if (total >= max_len - 1) break; @@ -461,7 +461,7 @@ static void decode_image(unsigned char *data, int len, RenderElement *el) { el->img_delays = malloc(frame_count * sizeof(int)); el->img_frame_count = frame_count; el->img_current_frame = 0; - el->next_frame_tick = sys_system(16, 0, 0, 0, 0) + (delays[0] * 60 / 1000); + el->next_frame_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0) + (delays[0] * 60 / 1000); uint32_t step_x = (img_w_orig << 16) / fit_w; uint32_t step_y = (img_h_orig << 16) / fit_h; @@ -2116,7 +2116,7 @@ int main(int argc, char **argv) { // Animated GIF progress bool gif_updated = false; - long long now = sys_system(16, 0, 0, 0, 0); + long long now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); for (int i = 0; i < element_count; i++) { if (elements[i].tag == TAG_IMG && elements[i].img_frames && elements[i].img_frame_count > 1) { if (now >= elements[i].next_frame_tick) { diff --git a/src/userland/gui/settings.c b/src/userland/gui/settings.c index 38b136c..3d9d690 100644 --- a/src/userland/gui/settings.c +++ b/src/userland/gui/settings.c @@ -769,14 +769,14 @@ static void control_panel_paint(ui_window_t win) { } static void save_desktop_config(void) { - sys_system(4 /*SET_DESKTOP_PROP*/, 1, desktop_snap_to_grid, 0, 0); - sys_system(4, 2, desktop_auto_align, 0, 0); - sys_system(4, 3, desktop_max_rows_per_col, 0, 0); - sys_system(4, 4, desktop_max_cols, 0, 0); + sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 1, desktop_snap_to_grid, 0, 0); + sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 2, desktop_auto_align, 0, 0); + sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 3, desktop_max_rows_per_col, 0, 0); + sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 4, desktop_max_cols, 0, 0); } static void save_mouse_config(void) { - sys_system(5 /*SET_MOUSE_SPEED*/, mouse_speed, 0, 0, 0); + sys_system(SYSTEM_CMD_SET_MOUSE_SPEED, mouse_speed, 0, 0, 0); } static int parse_ip(const char* str, net_ipv4_address_t* ip) { @@ -802,11 +802,11 @@ static int parse_ip(const char* str, net_ipv4_address_t* ip) { } static void fetch_kernel_state(void) { - desktop_snap_to_grid = sys_system(7 /*GET_DESKTOP_PROP*/, 1, 0, 0, 0); - desktop_auto_align = sys_system(7, 2, 0, 0, 0); - desktop_max_rows_per_col = sys_system(7, 3, 0, 0, 0); - desktop_max_cols = sys_system(7, 4, 0, 0, 0); - mouse_speed = sys_system(8 /*GET_MOUSE_SPEED*/, 0, 0, 0, 0); + desktop_snap_to_grid = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 1, 0, 0, 0); + desktop_auto_align = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 2, 0, 0, 0); + desktop_max_rows_per_col = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 3, 0, 0, 0); + desktop_max_cols = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 4, 0, 0, 0); + mouse_speed = sys_system(SYSTEM_CMD_GET_MOUSE_SPEED, 0, 0, 0, 0); net_ipv4_address_t kip; if (sys_network_get_ip(&kip) == 0) { @@ -889,7 +889,7 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click if (disp_sel_color == 3) { bpp = 8; mode = 1; } if (disp_sel_color == 4) { bpp = 8; mode = 2; } - sys_system(47 /*SET_RESOLUTION*/, w, h, bpp, mode); + sys_system(SYSTEM_CMD_SET_RESOLUTION, w, h, bpp, mode); } return; } @@ -912,28 +912,28 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click uint32_t c = 0; if (i==0) c = COLOR_COFFEE; else if(i==1) c = COLOR_TEAL; else if(i==2) c = COLOR_GREEN; else if(i==3) c = COLOR_BLUE_BG; else if(i==4) c = COLOR_PURPLE; else if(i==5) c = COLOR_GREY; - sys_system(1, c, 0, 0, 0); btn_wp_colors[i].pressed=false; + sys_system(SYSTEM_CMD_SET_BG_COLOR, c, 0, 0, 0); btn_wp_colors[i].pressed=false; } return; } } if (widget_button_handle_mouse(&btn_wp_patterns[0], x, y, is_down, is_click, NULL)) { - if (is_click) { sys_system(2, (uint64_t)pattern_lumberjack, 0, 0, 0); btn_wp_patterns[0].pressed=false;} return; + if (is_click) { sys_system(SYSTEM_CMD_SET_BG_PATTERN, (uint64_t)pattern_lumberjack, 0, 0, 0); btn_wp_patterns[0].pressed=false;} return; } if (widget_button_handle_mouse(&btn_wp_patterns[1], x, y, is_down, is_click, NULL)) { - if (is_click) { sys_system(2, (uint64_t)pattern_blue_diamond, 0, 0, 0); btn_wp_patterns[1].pressed=false;} return; + if (is_click) { sys_system(SYSTEM_CMD_SET_BG_PATTERN, (uint64_t)pattern_blue_diamond, 0, 0, 0); btn_wp_patterns[1].pressed=false;} return; } if (widget_button_handle_mouse(&btn_wp_apply, x, y, is_down, is_click, NULL)) { if (is_click) { uint32_t cust = parse_rgb_separate(rgb_r, rgb_g, rgb_b); - sys_system(1, cust, 0, 0, 0); + sys_system(SYSTEM_CMD_SET_BG_COLOR, cust, 0, 0, 0); btn_wp_apply.pressed=false; } return; } for (int i=0; i 0); if (viewer_has_image) { - viewer_next_frame_tick = sys_system(16, 0, 0, 0, 0) + (viewer_delays[0] * 60 / 1000); + viewer_next_frame_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0) + (viewer_delays[0] * 60 / 1000); } } free(delays); @@ -337,7 +337,7 @@ int main(int argc, char **argv) { win_w = ev.arg1; win_h = ev.arg2; resize_pending = true; - last_resize_tick = sys_system(16, 0, 0, 0, 0); + last_resize_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); // Fast background clear during active resize ui_draw_rect(win, 0, 0, win_w, win_h, 0xFF000000); ui_mark_dirty(win, 0, 0, win_w, win_h - 20); @@ -348,7 +348,7 @@ int main(int argc, char **argv) { } } else { if (resize_pending) { - uint64_t now = sys_system(16, 0, 0, 0, 0); + uint64_t now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); if (now > last_resize_tick + 10) { viewer_paint(win); ui_mark_dirty(win, 0, 0, win_w, win_h - 20); @@ -357,7 +357,7 @@ int main(int argc, char **argv) { } if (viewer_has_image && viewer_frame_count > 1) { - uint64_t now = sys_system(16, 0, 0, 0, 0); + uint64_t now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); if (now >= viewer_next_frame_tick) { viewer_current_frame = (viewer_current_frame + 1) % viewer_frame_count; viewer_next_frame_tick = now + (viewer_delays[viewer_current_frame] * 60 / 1000); diff --git a/src/userland/libc/ctype.c b/src/userland/libc/ctype.c new file mode 100644 index 0000000..7783d36 --- /dev/null +++ b/src/userland/libc/ctype.c @@ -0,0 +1,21 @@ +#include "ctype.h" + +__attribute__((weak)) int isdigit(int c) { return (c >= '0' && c <= '9'); } +__attribute__((weak)) int isalpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); } +__attribute__((weak)) int isalnum(int c) { return isalpha(c) || isdigit(c); } +__attribute__((weak)) int isspace(int c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; +} +__attribute__((weak)) int isupper(int c) { return (c >= 'A' && c <= 'Z'); } +__attribute__((weak)) int islower(int c) { return (c >= 'a' && c <= 'z'); } +__attribute__((weak)) int isxdigit(int c) { + return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} +__attribute__((weak)) int iscntrl(int c) { return ((c >= 0 && c < 32) || c == 127); } +__attribute__((weak)) int isprint(int c) { return (c >= 32 && c < 127); } +__attribute__((weak)) int isgraph(int c) { return (c > 32 && c < 127); } +__attribute__((weak)) int ispunct(int c) { + return isprint(c) && !isalnum(c) && !isspace(c); +} +__attribute__((weak)) int tolower(int c) { return isupper(c) ? (c - 'A' + 'a') : c; } +__attribute__((weak)) int toupper(int c) { return islower(c) ? (c - 'a' + 'A') : c; } diff --git a/src/userland/libc/ctype.h b/src/userland/libc/ctype.h new file mode 100644 index 0000000..4960be3 --- /dev/null +++ b/src/userland/libc/ctype.h @@ -0,0 +1,18 @@ +#ifndef BOREDOS_LIBC_CTYPE_H +#define BOREDOS_LIBC_CTYPE_H + +int isdigit(int c); +int isalpha(int c); +int isalnum(int c); +int isspace(int c); +int isupper(int c); +int islower(int c); +int isxdigit(int c); +int iscntrl(int c); +int ispunct(int c); +int isprint(int c); +int isgraph(int c); +int tolower(int c); +int toupper(int c); + +#endif diff --git a/src/userland/libc/errno.c b/src/userland/libc/errno.c new file mode 100644 index 0000000..8a8ab21 --- /dev/null +++ b/src/userland/libc/errno.c @@ -0,0 +1,3 @@ +#include "errno.h" + +__attribute__((weak)) int errno = 0; diff --git a/src/userland/libc/errno.h b/src/userland/libc/errno.h new file mode 100644 index 0000000..396ca00 --- /dev/null +++ b/src/userland/libc/errno.h @@ -0,0 +1,27 @@ +#ifndef BOREDOS_LIBC_ERRNO_H +#define BOREDOS_LIBC_ERRNO_H + +extern int errno; + +#define EDOM 33 +#define E2BIG 7 +#define ERANGE 34 +#define EINVAL 22 +#define EISDIR 21 +#define ENOENT 2 +#define ENOMEM 12 +#define EACCES 13 +#define EBADF 9 +#define EIO 5 +#define EEXIST 17 +#define EAGAIN 11 +#define EINTR 4 +#define ECHILD 10 +#define ENOTSUP 95 +#define EPIPE 32 +#define ENOTDIR 20 +#define EBUSY 16 +#define ESPIPE 29 +#define ENOSYS 38 + +#endif diff --git a/src/userland/libc/fcntl.h b/src/userland/libc/fcntl.h new file mode 100644 index 0000000..6bb56c1 --- /dev/null +++ b/src/userland/libc/fcntl.h @@ -0,0 +1,27 @@ +#ifndef BOREDOS_LIBC_FCNTL_H +#define BOREDOS_LIBC_FCNTL_H + +#include "sys/types.h" + +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +#define O_ACCMODE 0x0003 +#define O_CREAT 0x0040 +#define O_EXCL 0x0080 +#define O_TRUNC 0x0200 +#define O_APPEND 0x0400 +#define O_NONBLOCK 0x0800 + +#define F_GETFL 3 +#define F_SETFL 4 + +#define FD_CLOEXEC 1 + +int open(const char *pathname, int flags, ...); +int fcntl(int fd, int cmd, ...); +int dup(int oldfd); +int dup2(int oldfd, int newfd); +int pipe(int pipefd[2]); + +#endif diff --git a/src/userland/libc/libui.c b/src/userland/libc/libui.c index 7366992..3a87672 100644 --- a/src/userland/libc/libui.c +++ b/src/userland/libc/libui.c @@ -10,7 +10,6 @@ extern uint64_t syscall6(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_ // sys_gui uses syscall #3 #define SYS_GUI 3 -#define GUI_CMD_GET_SCREEN_SIZE 17 ui_window_t ui_window_create(const char *title, int x, int y, int w, int h) { uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h }; diff --git a/src/userland/libc/libui.h b/src/userland/libc/libui.h index e96c237..6d98091 100644 --- a/src/userland/libc/libui.h +++ b/src/userland/libc/libui.h @@ -22,6 +22,10 @@ #define GUI_CMD_WINDOW_SET_TITLE 15 #define GUI_CMD_SET_FONT 16 #define GUI_CMD_DRAW_STRING_SCALED_SLOPED 18 +#define GUI_CMD_GET_SCREEN_SIZE 50 +#define GUI_CMD_GET_SCREENBUFFER 51 +#define GUI_CMD_SHOW_NOTIFICATION 52 +#define GUI_CMD_GET_DATETIME 53 // Event Types #define GUI_EVENT_NONE 0 diff --git a/src/userland/libc/limits.h b/src/userland/libc/limits.h new file mode 100644 index 0000000..760f213 --- /dev/null +++ b/src/userland/libc/limits.h @@ -0,0 +1,22 @@ +#ifndef BOREDOS_LIBC_LIMITS_H +#define BOREDOS_LIBC_LIMITS_H + +#define CHAR_BIT 8 +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 +#define UCHAR_MAX 255 +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 +#define INT_MIN (-2147483647 - 1) +#define INT_MAX 2147483647 +#define UINT_MAX 4294967295U +#define LONG_MIN (-9223372036854775807L - 1) +#define LONG_MAX 9223372036854775807L +#define ULONG_MAX 18446744073709551615UL +#define LLONG_MIN (-9223372036854775807LL - 1) +#define LLONG_MAX 9223372036854775807LL +#define ULLONG_MAX 18446744073709551615ULL +#define DBL_MAX 1.7976931348623157e+308 + +#endif diff --git a/src/userland/libc/locale.c b/src/userland/libc/locale.c new file mode 100644 index 0000000..4276e7d --- /dev/null +++ b/src/userland/libc/locale.c @@ -0,0 +1,19 @@ +#include "locale.h" +#include "string.h" + +static struct lconv _b_lconv = { + ".", "", "", "", "", ".", "", "", "", "", + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +__attribute__((weak)) char *setlocale(int category, const char *locale) { + (void)category; + if (locale == NULL || strcmp(locale, "C") == 0 || strcmp(locale, "") == 0) { + return "C"; + } + return NULL; +} + +__attribute__((weak)) struct lconv *localeconv(void) { + return &_b_lconv; +} diff --git a/src/userland/libc/locale.h b/src/userland/libc/locale.h new file mode 100644 index 0000000..46158da --- /dev/null +++ b/src/userland/libc/locale.h @@ -0,0 +1,30 @@ +#ifndef BOREDOS_LIBC_LOCALE_H +#define BOREDOS_LIBC_LOCALE_H + +struct lconv { + char *decimal_point; + char *thousands_sep; + char *grouping; + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; +}; + +#define LC_ALL 0 + +char *setlocale(int category, const char *locale); +struct lconv *localeconv(void); + +#endif diff --git a/src/userland/libc/math.h b/src/userland/libc/math.h index acd193c..25d91ab 100644 --- a/src/userland/libc/math.h +++ b/src/userland/libc/math.h @@ -28,7 +28,12 @@ double log(double x); double log2(double x); double log10(double x); double exp(double x); +double ldexp(double x, int expn); +double frexp(double x, int *expn); double pow(double base, double exponent); +double atan2(double y, double x); +double asin(double x); +double acos(double x); double sinh(double x); double cosh(double x); double tanh(double x); diff --git a/src/userland/libc/math_ext.c b/src/userland/libc/math_ext.c new file mode 100644 index 0000000..180b6af --- /dev/null +++ b/src/userland/libc/math_ext.c @@ -0,0 +1,91 @@ +#include "math.h" +#include "errno.h" + +__attribute__((weak)) double ldexp(double x, int expn) { + double v = x; + int i; + if (expn >= 0) { + for (i = 0; i < expn; i++) { + v *= 2.0; + } + } else { + for (i = 0; i < -expn; i++) { + v *= 0.5; + } + } + return v; +} + +__attribute__((weak)) double frexp(double x, int *expn) { + int e = 0; + double v = x; + if (x == 0.0) { + *expn = 0; + return 0.0; + } + while (fabs(v) >= 1.0) { + v *= 0.5; + e++; + } + while (fabs(v) > 0.0 && fabs(v) < 0.5) { + v *= 2.0; + e--; + } + *expn = e; + return v; +} + +static double _b_atan_series(double x) { + double x2 = x * x; + double term = x; + double sum = x; + int n; + for (n = 3; n <= 23; n += 2) { + term *= -x2; + sum += term / (double)n; + } + return sum; +} + +static double _b_atan_precise(double x) { + if (x < 0.0) { + return -_b_atan_precise(-x); + } + if (x > 1.0) { + return (M_PI / 2.0) - _b_atan_precise(1.0 / x); + } + if (x > 0.5) { + double y = (x - 1.0) / (x + 1.0); + return (M_PI / 4.0) + _b_atan_series(y); + } + return _b_atan_series(x); +} + +__attribute__((weak)) double atan2(double y, double x) { + if (x > 0.0) { + return _b_atan_precise(y / x); + } + if (x < 0.0) { + if (y >= 0.0) return _b_atan_precise(y / x) + M_PI; + return _b_atan_precise(y / x) - M_PI; + } + if (y > 0.0) return M_PI / 2.0; + if (y < 0.0) return -M_PI / 2.0; + return 0.0; +} + +__attribute__((weak)) double asin(double x) { + if (x > 1.0 || x < -1.0) { + errno = EDOM; + return 0.0; + } + return atan2(x, sqrt(1.0 - x * x)); +} + +__attribute__((weak)) double acos(double x) { + if (x > 1.0 || x < -1.0) { + errno = EDOM; + return 0.0; + } + return M_PI / 2.0 - asin(x); +} diff --git a/src/userland/libc/posix_fs.c b/src/userland/libc/posix_fs.c new file mode 100644 index 0000000..66c877b --- /dev/null +++ b/src/userland/libc/posix_fs.c @@ -0,0 +1,87 @@ +#include "errno.h" +#include "syscall.h" +#include "sys/stat.h" + +static void _b_stat_init(struct stat *st) { + st->st_dev = 0; + st->st_ino = 0; + st->st_mode = 0; + st->st_nlink = 1; + st->st_uid = 0; + st->st_gid = 0; + st->st_rdev = 0; + st->st_size = 0; + st->st_blksize = 512; + st->st_blocks = 0; + st->st_atime = 0; + st->st_mtime = 0; + st->st_ctime = 0; +} + +__attribute__((weak)) int mkdir(const char *pathname, int mode) { + (void)mode; + if (!pathname || pathname[0] == '\0') { + errno = EINVAL; + return -1; + } + if (sys_exists(pathname)) { + errno = EEXIST; + return -1; + } + if (sys_mkdir(pathname) == 0) { + return 0; + } + errno = EIO; + return -1; +} + +__attribute__((weak)) int access(const char *pathname, int mode) { + (void)mode; + if (!pathname || pathname[0] == '\0') { + errno = EINVAL; + return -1; + } + if (sys_exists(pathname)) { + return 0; + } + errno = ENOENT; + return -1; +} + +__attribute__((weak)) int stat(const char *pathname, struct stat *statbuf) { + FAT32_FileInfo info; + if (!pathname || pathname[0] == '\0') { + errno = EINVAL; + return -1; + } + if (!sys_exists(pathname)) { + errno = ENOENT; + return -1; + } + + if (statbuf) { + _b_stat_init(statbuf); + if (sys_get_file_info(pathname, &info) == 0) { + statbuf->st_size = (int)info.size; + if (info.is_directory) { + statbuf->st_mode = S_IFDIR | 0755; + } else { + statbuf->st_mode = S_IFREG | 0644; + } + statbuf->st_blocks = (statbuf->st_size + 511) / 512; + } else { + int fd = sys_open(pathname, "rb"); + if (fd >= 0) { + statbuf->st_size = (int)sys_size(fd); + statbuf->st_mode = S_IFREG | 0644; + statbuf->st_blocks = (statbuf->st_size + 511) / 512; + sys_close(fd); + } else { + errno = EIO; + return -1; + } + } + } + + return 0; +} diff --git a/src/userland/libc/posix_io.c b/src/userland/libc/posix_io.c new file mode 100644 index 0000000..d970590 --- /dev/null +++ b/src/userland/libc/posix_io.c @@ -0,0 +1,650 @@ +#include +#include +#include + +#include "errno.h" +#include "fcntl.h" +#include "stdlib.h" +#include "string.h" +#include "sys/stat.h" +#include "syscall.h" +#include "unistd.h" + +#define POSIX_MAX_FDS 256 +#define PIPE_BUF_SIZE 4096 + +typedef enum { + HANDLE_UNUSED = 0, + HANDLE_KERNEL_FD, + HANDLE_PIPE_READ, + HANDLE_PIPE_WRITE +} handle_type_t; + +typedef struct { + unsigned char data[PIPE_BUF_SIZE]; + size_t read_pos; + size_t write_pos; + size_t count; + int readers; + int writers; +} pipe_state_t; + +typedef struct { + handle_type_t type; + int refcount; + int flags; + int kernel_fd; + pipe_state_t *pipe; +} fd_handle_t; + +static fd_handle_t *g_fd_table[POSIX_MAX_FDS]; +static fd_handle_t g_stdio_handles[3]; +static int g_fd_initialized = 0; + +static void _b_fd_init(void) { + int i; + if (g_fd_initialized) { + return; + } + for (i = 0; i < POSIX_MAX_FDS; i++) { + g_fd_table[i] = NULL; + } + for (i = 0; i < 3; i++) { + g_stdio_handles[i].type = HANDLE_KERNEL_FD; + g_stdio_handles[i].refcount = 1; + g_stdio_handles[i].flags = O_RDWR; + g_stdio_handles[i].kernel_fd = i; + g_stdio_handles[i].pipe = NULL; + g_fd_table[i] = &g_stdio_handles[i]; + } + g_fd_initialized = 1; +} + +static int _b_alloc_fd_from(int start) { + int fd; + for (fd = start; fd < POSIX_MAX_FDS; fd++) { + if (g_fd_table[fd] == NULL) { + return fd; + } + } + return -1; +} + +static fd_handle_t *_b_get_handle(int fd) { + if (fd < 0 || fd >= POSIX_MAX_FDS) { + return NULL; + } + return g_fd_table[fd]; +} + +static void _b_reset_stat_common(struct stat *st) { + memset(st, 0, sizeof(*st)); + st->st_blksize = 512; +} + +static int _b_fill_kernel_fstat(int kfd, struct stat *statbuf) { + _b_reset_stat_common(statbuf); + statbuf->st_mode = (kfd <= 2) ? (S_IFCHR | 0666) : (S_IFREG | 0644); + statbuf->st_size = (kfd <= 2) ? 0 : (int)sys_size(kfd); + statbuf->st_blocks = (statbuf->st_size + 511) / 512; + statbuf->st_nlink = 1; + return 0; +} + +static const char *_b_mode_from_flags(int flags) { + int accmode = flags & O_ACCMODE; + + if (accmode == O_RDONLY) { + return "rb"; + } + + if (accmode == O_RDWR) { + if (flags & O_TRUNC) { + return "w+"; + } + if (flags & O_APPEND) { + return "a+"; + } + return "r+"; + } + + if (flags & O_APPEND) { + return "ab"; + } + if (flags & O_TRUNC) { + return "wb"; + } + return "wb"; +} + +static int _b_pipe_read(fd_handle_t *h, void *buf, size_t count) { + size_t n = 0; + pipe_state_t *p = h->pipe; + unsigned char *out = (unsigned char *)buf; + + if (!p || !buf) { + errno = EINVAL; + return -1; + } + + while (n < count) { + if (p->count == 0) { + if (p->writers == 0) { + break; + } + if (h->flags & O_NONBLOCK) { + if (n == 0) { + errno = EAGAIN; + return -1; + } + break; + } + sys_yield(); + continue; + } + + out[n++] = p->data[p->read_pos]; + p->read_pos = (p->read_pos + 1) % PIPE_BUF_SIZE; + p->count--; + } + + return (int)n; +} + +static int _b_pipe_write(fd_handle_t *h, const void *buf, size_t count) { + size_t n = 0; + pipe_state_t *p = h->pipe; + const unsigned char *in = (const unsigned char *)buf; + + if (!p || !buf) { + errno = EINVAL; + return -1; + } + if (p->readers == 0) { + errno = EPIPE; + return -1; + } + + while (n < count) { + if (p->count == PIPE_BUF_SIZE) { + if (h->flags & O_NONBLOCK) { + if (n == 0) { + errno = EAGAIN; + return -1; + } + break; + } + sys_yield(); + continue; + } + + p->data[p->write_pos] = in[n++]; + p->write_pos = (p->write_pos + 1) % PIPE_BUF_SIZE; + p->count++; + } + + return (int)n; +} + +__attribute__((weak)) int open(const char *pathname, int flags, ...) { + int fd; + int kfd; + int exists; + mode_t mode = 0; + fd_handle_t *h; + + _b_fd_init(); + + if (!pathname || pathname[0] == '\0') { + errno = EINVAL; + return -1; + } + + if ((flags & O_ACCMODE) > O_RDWR) { + errno = EINVAL; + return -1; + } + + if ((flags & O_TRUNC) && ((flags & O_ACCMODE) == O_RDONLY)) { + errno = EINVAL; + return -1; + } + + exists = sys_exists(pathname); + + if ((flags & O_CREAT) && (flags & O_EXCL) && exists) { + errno = EEXIST; + return -1; + } + + if (!(flags & O_CREAT) && !exists) { + errno = ENOENT; + return -1; + } + + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + mode = (mode_t)va_arg(ap, int); + va_end(ap); + (void)mode; + } + + kfd = sys_open(pathname, _b_mode_from_flags(flags)); + if (kfd < 0) { + errno = EIO; + return -1; + } + + h = (fd_handle_t *)malloc(sizeof(fd_handle_t)); + if (!h) { + sys_close(kfd); + errno = ENOMEM; + return -1; + } + + fd = _b_alloc_fd_from(3); + if (fd < 0) { + free(h); + sys_close(kfd); + errno = EBUSY; + return -1; + } + + h->type = HANDLE_KERNEL_FD; + h->refcount = 1; + h->flags = flags; + h->kernel_fd = kfd; + h->pipe = NULL; + g_fd_table[fd] = h; + + if (flags & O_APPEND) { + (void)sys_seek(kfd, 0, SEEK_END); + } + + return fd; +} + +__attribute__((weak)) int close(int fd) { + fd_handle_t *h; + + _b_fd_init(); + h = _b_get_handle(fd); + if (!h) { + errno = EBADF; + return -1; + } + + g_fd_table[fd] = NULL; + if (--h->refcount > 0) { + return 0; + } + + if (h->type == HANDLE_KERNEL_FD) { + if (h->kernel_fd >= 3) { + sys_close(h->kernel_fd); + } + } else if (h->type == HANDLE_PIPE_READ || h->type == HANDLE_PIPE_WRITE) { + pipe_state_t *p = h->pipe; + if (p) { + if (h->type == HANDLE_PIPE_READ) { + p->readers--; + } else { + p->writers--; + } + if (p->readers <= 0 && p->writers <= 0) { + free(p); + } + } + } + + if (h < &g_stdio_handles[0] || h > &g_stdio_handles[2]) { + free(h); + } + return 0; +} + +__attribute__((weak)) ssize_t read(int fd, void *buf, size_t count) { + fd_handle_t *h; + int n; + + _b_fd_init(); + + if (!buf && count != 0) { + errno = EINVAL; + return -1; + } + + h = _b_get_handle(fd); + if (!h) { + errno = EBADF; + return -1; + } + + if (h->type == HANDLE_PIPE_WRITE) { + errno = EBADF; + return -1; + } + + if (h->type == HANDLE_PIPE_READ) { + n = _b_pipe_read(h, buf, count); + return (ssize_t)n; + } + + n = sys_read(h->kernel_fd, buf, (uint32_t)count); + if (n < 0) { + errno = EIO; + return -1; + } + return (ssize_t)n; +} + +__attribute__((weak)) ssize_t write(int fd, const void *buf, size_t count) { + fd_handle_t *h; + int n; + + _b_fd_init(); + if (!buf && count != 0) { + errno = EINVAL; + return -1; + } + + h = _b_get_handle(fd); + if (!h) { + errno = EBADF; + return -1; + } + + if (h->type == HANDLE_PIPE_READ) { + errno = EBADF; + return -1; + } + + if (h->type == HANDLE_PIPE_WRITE) { + n = _b_pipe_write(h, buf, count); + return (ssize_t)n; + } + + if (h->kernel_fd <= 2) { + n = sys_write(h->kernel_fd, (const char *)buf, (int)count); + } else { + n = sys_write_fs(h->kernel_fd, buf, (uint32_t)count); + } + + if (n < 0) { + errno = EIO; + return -1; + } + return (ssize_t)n; +} + +__attribute__((weak)) off_t lseek(int fd, off_t offset, int whence) { + fd_handle_t *h; + + _b_fd_init(); + h = _b_get_handle(fd); + if (!h) { + errno = EBADF; + return -1; + } + if (h->type != HANDLE_KERNEL_FD) { + errno = ESPIPE; + return -1; + } + if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) { + errno = EINVAL; + return -1; + } + + if (sys_seek(h->kernel_fd, (int)offset, whence) < 0) { + errno = EIO; + return -1; + } + return (off_t)sys_tell(h->kernel_fd); +} + +__attribute__((weak)) int unlink(const char *pathname) { + if (!pathname || pathname[0] == '\0') { + errno = EINVAL; + return -1; + } + if (sys_delete(pathname) != 0) { + errno = ENOENT; + return -1; + } + return 0; +} + +__attribute__((weak)) int isatty(int fd) { + fd_handle_t *h; + _b_fd_init(); + h = _b_get_handle(fd); + if (!h) { + errno = EBADF; + return 0; + } + return (h->type == HANDLE_KERNEL_FD && h->kernel_fd <= 2) ? 1 : 0; +} + +__attribute__((weak)) int fstat(int fd, struct stat *statbuf) { + fd_handle_t *h; + _b_fd_init(); + if (!statbuf) { + errno = EINVAL; + return -1; + } + + h = _b_get_handle(fd); + if (!h) { + errno = EBADF; + return -1; + } + + if (h->type == HANDLE_PIPE_READ || h->type == HANDLE_PIPE_WRITE) { + _b_reset_stat_common(statbuf); + statbuf->st_mode = S_IFIFO | 0666; + statbuf->st_size = (int)(h->pipe ? h->pipe->count : 0); + return 0; + } + + return _b_fill_kernel_fstat(h->kernel_fd, statbuf); +} + +__attribute__((weak)) int dup(int oldfd) { + fd_handle_t *h; + int newfd; + int newkfd; + _b_fd_init(); + + h = _b_get_handle(oldfd); + if (!h) { + errno = EBADF; + return -1; + } + + if (h->type != HANDLE_KERNEL_FD) { + errno = ENOTSUP; + return -1; + } + + newkfd = sys_dup(h->kernel_fd); + if (newkfd < 0) { + errno = EBADF; + return -1; + } + + newfd = _b_alloc_fd_from(0); + if (newfd < 0) { + sys_close(newkfd); + errno = EBUSY; + return -1; + } + + h = (fd_handle_t *)malloc(sizeof(fd_handle_t)); + if (!h) { + sys_close(newkfd); + errno = ENOMEM; + return -1; + } + + h->type = HANDLE_KERNEL_FD; + h->refcount = 1; + h->flags = O_RDWR; + h->kernel_fd = newkfd; + h->pipe = NULL; + g_fd_table[newfd] = h; + return newfd; +} + +__attribute__((weak)) int dup2(int oldfd, int newfd) { + fd_handle_t *h; + fd_handle_t *nh; + int newkfd; + _b_fd_init(); + + h = _b_get_handle(oldfd); + if (!h || newfd < 0 || newfd >= POSIX_MAX_FDS) { + errno = EBADF; + return -1; + } + + if (oldfd == newfd) { + return newfd; + } + + if (h->type != HANDLE_KERNEL_FD) { + errno = ENOTSUP; + return -1; + } + + if (g_fd_table[newfd]) { + if (close(newfd) != 0) { + return -1; + } + } + + newkfd = sys_dup(h->kernel_fd); + if (newkfd < 0) { + errno = EBADF; + return -1; + } + + nh = (fd_handle_t *)malloc(sizeof(fd_handle_t)); + if (!nh) { + errno = ENOMEM; + return -1; + } + + nh->type = HANDLE_KERNEL_FD; + nh->refcount = 1; + nh->flags = h->flags; + nh->kernel_fd = newkfd; + nh->pipe = NULL; + + g_fd_table[newfd] = nh; + return newfd; +} + +__attribute__((weak)) int pipe(int pipefd[2]) { + fd_handle_t *rh; + fd_handle_t *wh; + int rfd; + int wfd; + int kpipe[2]; + + _b_fd_init(); + if (!pipefd) { + errno = EINVAL; + return -1; + } + + if (sys_pipe(kpipe) < 0) { + errno = EIO; + return -1; + } + + rfd = _b_alloc_fd_from(3); + if (rfd < 0) { + sys_close(kpipe[0]); + sys_close(kpipe[1]); + errno = EBUSY; + return -1; + } + wfd = _b_alloc_fd_from(rfd + 1); + if (wfd < 0) { + sys_close(kpipe[0]); + sys_close(kpipe[1]); + errno = EBUSY; + return -1; + } + + rh = (fd_handle_t *)malloc(sizeof(fd_handle_t)); + wh = (fd_handle_t *)malloc(sizeof(fd_handle_t)); + if (!rh || !wh) { + free(rh); + free(wh); + sys_close(kpipe[0]); + sys_close(kpipe[1]); + errno = ENOMEM; + return -1; + } + + rh->type = HANDLE_KERNEL_FD; + rh->refcount = 1; + rh->flags = O_RDONLY; + rh->kernel_fd = kpipe[0]; + rh->pipe = NULL; + + wh->type = HANDLE_KERNEL_FD; + wh->refcount = 1; + wh->flags = O_WRONLY; + wh->kernel_fd = kpipe[1]; + wh->pipe = NULL; + + g_fd_table[rfd] = rh; + g_fd_table[wfd] = wh; + pipefd[0] = rfd; + pipefd[1] = wfd; + return 0; +} + +__attribute__((weak)) int fcntl(int fd, int cmd, ...) { + fd_handle_t *h; + va_list ap; + int val; + + _b_fd_init(); + h = _b_get_handle(fd); + if (!h) { + errno = EBADF; + return -1; + } + + switch (cmd) { + case F_GETFL: + if (h->type == HANDLE_KERNEL_FD) { + int k = sys_fcntl(h->kernel_fd, cmd, 0); + if (k < 0) { + errno = ENOSYS; + return -1; + } + h->flags = k; + } + return h->flags; + case F_SETFL: + va_start(ap, cmd); + val = va_arg(ap, int); + va_end(ap); + if (h->type == HANDLE_KERNEL_FD) { + if (sys_fcntl(h->kernel_fd, cmd, val) < 0) { + errno = ENOSYS; + return -1; + } + } + h->flags = (h->flags & ~(O_APPEND | O_NONBLOCK)) | (val & (O_APPEND | O_NONBLOCK)); + return 0; + default: + errno = ENOSYS; + return -1; + } +} diff --git a/src/userland/libc/posix_process.c b/src/userland/libc/posix_process.c new file mode 100644 index 0000000..0d15f05 --- /dev/null +++ b/src/userland/libc/posix_process.c @@ -0,0 +1,224 @@ +#include +#include + +#include "errno.h" +#include "stdio.h" +#include "string.h" +#include "sys/types.h" +#include "sys/wait.h" +#include "syscall.h" +#include "unistd.h" + +static int _b_is_space(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; +} + +static int _b_path_exists(const char *path) { + return path && path[0] && sys_exists(path); +} + +static const char *_b_resolve_exec_path(const char *file, char *out, size_t cap) { + if (!file || !file[0]) { + return NULL; + } + + if (file[0] == '/') { + return _b_path_exists(file) ? file : NULL; + } + + snprintf(out, cap, "/bin/%s", file); + if (_b_path_exists(out)) { + return out; + } + + snprintf(out, cap, "/bin/%s.elf", file); + if (_b_path_exists(out)) { + return out; + } + + return NULL; +} + +static int _b_join_argv(char *buf, size_t cap, char *const argv[]) { + size_t used = 0; + + if (!argv || !argv[0]) { + if (cap) { + buf[0] = '\0'; + } + return 0; + } + + for (int i = 1; argv[i]; i++) { + const char *a = argv[i]; + size_t len = strlen(a); + int need_quote = 0; + + for (size_t j = 0; j < len; j++) { + if (_b_is_space(a[j]) || a[j] == '"') { + need_quote = 1; + break; + } + } + + if (used && used + 1 < cap) { + buf[used++] = ' '; + } + + if (need_quote && used + 1 < cap) { + buf[used++] = '"'; + } + + for (size_t j = 0; j < len; j++) { + if (a[j] == '"' && used + 2 < cap) { + buf[used++] = '\\'; + } + if (used + 1 < cap) { + buf[used++] = a[j]; + } + } + + if (need_quote && used + 1 < cap) { + buf[used++] = '"'; + } + + if (used >= cap) { + errno = E2BIG; + return -1; + } + } + + if (cap) { + buf[used < cap ? used : cap - 1] = '\0'; + } + return 0; +} + +static int _b_exec_common(const char *path, char *const argv[]) { + char resolved[260]; + char args[512]; + const char *exec_path = _b_resolve_exec_path(path, resolved, sizeof(resolved)); + + if (!exec_path) { + errno = ENOENT; + return -1; + } + + if (_b_join_argv(args, sizeof(args), argv) != 0) { + return -1; + } + + if (sys_exec(exec_path, args[0] ? args : NULL) < 0) { + errno = EIO; + return -1; + } + + return 0; +} + +__attribute__((weak)) int execv(const char *path, char *const argv[]) { + return _b_exec_common(path, argv); +} + +__attribute__((weak)) int execve(const char *path, char *const argv[], char *const envp[]) { + (void)envp; + return _b_exec_common(path, argv); +} + +__attribute__((weak)) int execvp(const char *file, char *const argv[]) { + return _b_exec_common(file, argv); +} + +__attribute__((weak)) int execl(const char *path, const char *arg, ...) { + va_list ap; + char *argv[64]; + int i = 0; + + argv[i++] = (char *)arg; + va_start(ap, arg); + while (i < 63) { + char *v = va_arg(ap, char *); + argv[i++] = v; + if (!v) { + break; + } + } + va_end(ap); + + if (argv[i - 1] != NULL) { + argv[63] = NULL; + } + + return execv(path, argv); +} + +__attribute__((weak)) int execlp(const char *file, const char *arg, ...) { + va_list ap; + char *argv[64]; + int i = 0; + + argv[i++] = (char *)arg; + va_start(ap, arg); + while (i < 63) { + char *v = va_arg(ap, char *); + argv[i++] = v; + if (!v) { + break; + } + } + va_end(ap); + + if (argv[i - 1] != NULL) { + argv[63] = NULL; + } + + return execvp(file, argv); +} + +__attribute__((weak)) int execle(const char *path, const char *arg, ...) { + va_list ap; + char *argv[64]; + int i = 0; + char *envp; + + argv[i++] = (char *)arg; + va_start(ap, arg); + while (i < 63) { + char *v = va_arg(ap, char *); + argv[i++] = v; + if (!v) { + break; + } + } + envp = va_arg(ap, char *); + va_end(ap); + (void)envp; + + if (argv[i - 1] != NULL) { + argv[63] = NULL; + } + + return execv(path, argv); +} + +__attribute__((weak)) pid_t waitpid(pid_t pid, int *status, int options) { + int st = 0; + + for (;;) { + int rc = sys_waitpid((int)pid, &st, options); + if (rc > 0) { + if (status) { + *status = st; + } + return (pid_t)rc; + } + if (rc == 0 && (options & WNOHANG)) { + return 0; + } + if (rc < 0) { + errno = ECHILD; + return -1; + } + sys_yield(); + } +} diff --git a/src/userland/libc/runtime.c b/src/userland/libc/runtime.c new file mode 100644 index 0000000..9d21b32 --- /dev/null +++ b/src/userland/libc/runtime.c @@ -0,0 +1,178 @@ +#include "stdlib.h" +#include "string.h" +#include "errno.h" +#include "syscall.h" +#include "stdio.h" +#include "math.h" + +static int _b_is_space_char(int c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; +} + +static int _b_try_spawn_command(const char *command) { + char cmd[128]; + char args[256]; + int i = 0; + int j = 0; + int pid; + + while (command[i] && _b_is_space_char((unsigned char)command[i])) i++; + while (command[i] && !_b_is_space_char((unsigned char)command[i]) && j < (int)sizeof(cmd) - 1) { + cmd[j++] = command[i++]; + } + cmd[j] = '\0'; + + if (cmd[0] == '\0') { + return 0; + } + + while (command[i] && _b_is_space_char((unsigned char)command[i])) i++; + { + int k = 0; + while (command[i] && k < (int)sizeof(args) - 1) { + args[k++] = command[i++]; + } + args[k] = '\0'; + } + + pid = sys_spawn(cmd, args[0] ? args : NULL, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_INHERIT_TTY, 0); + if (pid >= 0) { + return 0; + } + + if (cmd[0] != '/') { + char path[160]; + snprintf(path, sizeof(path), "/bin/%s", cmd); + pid = sys_spawn(path, args[0] ? args : NULL, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_INHERIT_TTY, 0); + if (pid >= 0) { + return 0; + } + + snprintf(path, sizeof(path), "/bin/%s.elf", cmd); + pid = sys_spawn(path, args[0] ? args : NULL, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_INHERIT_TTY, 0); + if (pid >= 0) { + return 0; + } + } + + errno = ENOENT; + return -1; +} + +static int _b_streq(const char *a, const char *b) { + while (*a && *b) { + if (*a != *b) { + return 0; + } + a++; + b++; + } + return *a == '\0' && *b == '\0'; +} + +__attribute__((weak)) int abs(int x) { + return (x < 0) ? -x : x; +} + +__attribute__((weak)) int system(const char *command) { + if (command == NULL) { + return 1; + } + return _b_try_spawn_command(command); +} + +__attribute__((weak)) char *getenv(const char *name) { + const char *cfg; + static char cwd_buf[256]; + if (!name || name[0] == '\0') { + return NULL; + } + + if (_b_streq(name, "PWD")) { + if (sys_getcwd(cwd_buf, (int)sizeof(cwd_buf)) >= 0) { + return cwd_buf; + } + return NULL; + } + + if (_b_streq(name, "PATH")) return "/bin:/"; + if (_b_streq(name, "HOME")) return "/"; + if (_b_streq(name, "SHELL")) return "/bin/bsh"; + if (_b_streq(name, "TERM")) return "boredos"; + + cfg = (const char *)(uintptr_t)sys_get_shell_config(name); + if ((uintptr_t)cfg > 0x10000ULL) { + return (char *)cfg; + } + return NULL; +} + +__attribute__((weak)) void abort(void) { + sys_exit(1); + while (1) {} +} + +__attribute__((weak)) void _exit(int status) { + sys_exit(status); + while (1) {} +} + +__attribute__((weak)) double strtod(const char *nptr, char **endptr) { + const char *p = nptr; + int sign = 1; + double value = 0.0; + double frac = 0.0; + double scale = 1.0; + int exp_sign = 1; + int exp_val = 0; + + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v') p++; + + if (*p == '-') { + sign = -1; + p++; + } else if (*p == '+') { + p++; + } + + while (*p >= '0' && *p <= '9') { + value = value * 10.0 + (double)(*p - '0'); + p++; + } + + if (*p == '.') { + p++; + while (*p >= '0' && *p <= '9') { + frac = frac * 10.0 + (double)(*p - '0'); + scale *= 10.0; + p++; + } + value += frac / scale; + } + + if (*p == 'e' || *p == 'E') { + const char *ep = p + 1; + if (*ep == '-') { + exp_sign = -1; + ep++; + } else if (*ep == '+') { + ep++; + } + if (*ep >= '0' && *ep <= '9') { + p = ep; + while (*p >= '0' && *p <= '9') { + exp_val = exp_val * 10 + (*p - '0'); + p++; + } + } + } + + if (endptr) { + *endptr = (char *)p; + } + + if (exp_val != 0) { + value = ldexp(value, exp_sign * exp_val); + } + return sign * value; +} diff --git a/src/userland/libc/setjmp.c b/src/userland/libc/setjmp.c new file mode 100644 index 0000000..9a57aa8 --- /dev/null +++ b/src/userland/libc/setjmp.c @@ -0,0 +1,38 @@ +#include "setjmp.h" + +__attribute__((weak)) int setjmp(jmp_buf env) { + __asm__ volatile( + "movq %%rbx, 0(%0)\n\t" + "movq %%rbp, 8(%0)\n\t" + "movq %%r12, 16(%0)\n\t" + "movq %%r13, 24(%0)\n\t" + "movq %%r14, 32(%0)\n\t" + "movq %%r15, 40(%0)\n\t" + "leaq 8(%%rsp), %%rax\n\t" + "movq %%rax, 48(%0)\n\t" + "movq (%%rsp), %%rax\n\t" + "movq %%rax, 56(%0)\n\t" + : + : "r"(env) + : "rax", "memory"); + return 0; +} + +__attribute__((weak)) void longjmp(jmp_buf env, int val) { + int r = (val == 0) ? 1 : val; + __asm__ volatile( + "movq 0(%0), %%rbx\n\t" + "movq 8(%0), %%rbp\n\t" + "movq 16(%0), %%r12\n\t" + "movq 24(%0), %%r13\n\t" + "movq 32(%0), %%r14\n\t" + "movq 40(%0), %%r15\n\t" + "movq 48(%0), %%rsp\n\t" + "movl %1, %%eax\n\t" + "movq 56(%0), %%rdx\n\t" + "jmp *%%rdx\n\t" + : + : "r"(env), "r"(r) + : "rax", "rdx", "memory"); + __builtin_unreachable(); +} diff --git a/src/userland/libc/setjmp.h b/src/userland/libc/setjmp.h new file mode 100644 index 0000000..4bbc400 --- /dev/null +++ b/src/userland/libc/setjmp.h @@ -0,0 +1,20 @@ +#ifndef BOREDOS_LIBC_SETJMP_H +#define BOREDOS_LIBC_SETJMP_H + +#include + +typedef struct boredos_jmp_buf_s { + uint64_t rbx; + uint64_t rbp; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rsp; + uint64_t rip; +} jmp_buf[1]; + +int setjmp(jmp_buf env) __attribute__((returns_twice, noinline)); +void longjmp(jmp_buf env, int val) __attribute__((noreturn, noinline)); + +#endif diff --git a/src/userland/libc/signal.c b/src/userland/libc/signal.c new file mode 100644 index 0000000..8c3b95c --- /dev/null +++ b/src/userland/libc/signal.c @@ -0,0 +1,108 @@ +#include "signal.h" +#include "errno.h" +#include "stdlib.h" +#include "syscall.h" + +typedef struct { + unsigned long sa_handler; + unsigned long sa_mask; + int sa_flags; +} k_sigaction_t; + +__attribute__((weak)) sighandler_t signal(int sig, sighandler_t handler) { + struct sigaction act; + struct sigaction old; + + if (sig <= 0 || sig >= 32) { + errno = EINVAL; + return SIG_ERR; + } + + act.sa_handler = handler; + act.sa_mask = 0; + act.sa_flags = 0; + if (sigaction(sig, &act, &old) != 0) { + return SIG_ERR; + } + return old.sa_handler; +} + +__attribute__((weak)) int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) { + k_sigaction_t kact; + k_sigaction_t kold; + int rc; + + if (sig <= 0 || sig >= 32) { + errno = EINVAL; + return -1; + } + + if (act) { + kact.sa_handler = (unsigned long)act->sa_handler; + kact.sa_mask = (unsigned long)act->sa_mask; + kact.sa_flags = act->sa_flags; + } + + rc = sys_sigaction(sig, act ? &kact : NULL, oldact ? &kold : NULL); + if (rc < 0) { + errno = EINVAL; + return -1; + } + + if (oldact) { + oldact->sa_handler = (sighandler_t)kold.sa_handler; + oldact->sa_mask = (sigset_t)kold.sa_mask; + oldact->sa_flags = kold.sa_flags; + } + + return 0; +} + +__attribute__((weak)) int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { + if (sys_sigprocmask(how, + (const unsigned long *)set, + (unsigned long *)oldset) < 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +__attribute__((weak)) int sigpending(sigset_t *set) { + if (!set) { + errno = EINVAL; + return -1; + } + if (sys_sigpending((unsigned long *)set) < 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +__attribute__((weak)) int raise(int sig) { + if (sig <= 0 || sig >= 32) { + errno = EINVAL; + return -1; + } + + if (sys_kill_signal(-1, sig) < 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +__attribute__((weak)) int kill(pid_t pid, int sig) { + if (pid <= 0) { + errno = EINVAL; + return -1; + } + + if (sys_kill_signal((int)pid, sig) < 0) { + errno = ENOTSUP; + return -1; + } + + return 0; +} diff --git a/src/userland/libc/signal.h b/src/userland/libc/signal.h new file mode 100644 index 0000000..671f8c1 --- /dev/null +++ b/src/userland/libc/signal.h @@ -0,0 +1,37 @@ +#ifndef BOREDOS_LIBC_SIGNAL_H +#define BOREDOS_LIBC_SIGNAL_H + +#include "sys/types.h" + +typedef void (*sighandler_t)(int); +typedef unsigned long sigset_t; + +struct sigaction { + sighandler_t sa_handler; + sigset_t sa_mask; + int sa_flags; +}; + +#define SIG_DFL ((sighandler_t)0) +#define SIG_IGN ((sighandler_t)1) +#define SIG_ERR ((sighandler_t)-1) +#define SIGKILL 9 +#define SIGTERM 15 +#define SIGINT 2 + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 + +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 + +sighandler_t signal(int sig, sighandler_t handler); +int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact); +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); +int sigpending(sigset_t *set); +int raise(int sig); +int kill(pid_t pid, int sig); + +#endif diff --git a/src/userland/libc/stdio.c b/src/userland/libc/stdio.c new file mode 100644 index 0000000..510d589 --- /dev/null +++ b/src/userland/libc/stdio.c @@ -0,0 +1,1046 @@ +#include +#include +#include + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "ctype.h" +#include "errno.h" +#include "syscall.h" +#include "time.h" + +static FILE boredos_stdin_obj = {0, 0, 0, 0, 0}; +static FILE boredos_stdout_obj = {1, 0, 0, 0, 0}; +static FILE boredos_stderr_obj = {2, 0, 0, 0, 0}; +__attribute__((weak)) FILE *stdin = &boredos_stdin_obj; +__attribute__((weak)) FILE *stdout = &boredos_stdout_obj; +__attribute__((weak)) FILE *stderr = &boredos_stderr_obj; + +static int _b_streq(const char *a, const char *b) { + while (*a && *b) { + if (*a != *b) { + return 0; + } + a++; + b++; + } + return *a == '\0' && *b == '\0'; +} + +static int _b_is_space_char(int c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; +} + +static char _b_tmpname_buf[FILENAME_MAX]; +static unsigned _b_tmp_counter = 0; + +static char *_b_tmpname_generate(char *out, size_t out_cap) { + unsigned tries; + if (out_cap == 0) { + errno = EINVAL; + return NULL; + } + + (void)sys_mkdir("/tmp"); + for (tries = 0; tries < 256; tries++) { + unsigned long long t = (unsigned long long)clock(); + snprintf(out, out_cap, "/tmp/tmp_%llu_%u.tmp", t, _b_tmp_counter++); + if (!sys_exists(out)) { + return out; + } + } + + errno = EEXIST; + return NULL; +} + +__attribute__((weak)) FILE *fopen(const char *path, const char *mode) { + int fd = sys_open(path, mode); + FILE *f; + if (fd < 0) { + errno = EINVAL; + return NULL; + } + f = (FILE *)malloc(sizeof(FILE)); + if (!f) { + sys_close(fd); + errno = ENOMEM; + return NULL; + } + f->fd = fd; + f->eof = 0; + f->err = 0; + f->has_ungetc = 0; + f->ungetc_char = 0; + return f; +} + +__attribute__((weak)) FILE *freopen(const char *path, const char *mode, FILE *stream) { + int fd; + if (!stream) { + return fopen(path, mode); + } + if (stream->fd >= 0) { + sys_close(stream->fd); + } + fd = sys_open(path, mode); + if (fd < 0) { + stream->err = 1; + errno = EINVAL; + return NULL; + } + stream->fd = fd; + stream->eof = 0; + stream->err = 0; + stream->has_ungetc = 0; + return stream; +} + +__attribute__((weak)) int fclose(FILE *stream) { + if (!stream) { + return EOF; + } + if (stream != stdin && stream != stdout && stream != stderr) { + if (stream->fd >= 0) { + sys_close(stream->fd); + } + free(stream); + } + return 0; +} + +__attribute__((weak)) size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { + size_t total; + int n; + if (!stream || !ptr || size == 0 || nmemb == 0) { + return 0; + } + total = size * nmemb; + n = sys_read(stream->fd, ptr, (uint32_t)total); + if (n <= 0) { + if (n == 0) { + stream->eof = 1; + } else { + stream->err = 1; + } + return 0; + } + if ((size_t)n < total) { + stream->eof = 1; + } + return (size_t)n / size; +} + +__attribute__((weak)) size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { + size_t total; + int n; + if (!stream || !ptr || size == 0 || nmemb == 0) { + return 0; + } + total = size * nmemb; + if (stream->fd <= 2) { + n = sys_write(stream->fd, (const char *)ptr, (int)total); + } else { + n = sys_write_fs(stream->fd, ptr, (uint32_t)total); + } + if (n < 0) { + stream->err = 1; + return 0; + } + return (size_t)n / size; +} + +__attribute__((weak)) int fseek(FILE *stream, long offset, int whence) { + if (!stream) { + return -1; + } + if (sys_seek(stream->fd, (int)offset, whence) < 0) { + stream->err = 1; + return -1; + } + stream->eof = 0; + stream->has_ungetc = 0; + return 0; +} + +__attribute__((weak)) long ftell(FILE *stream) { + if (!stream) { + return -1; + } + return (long)sys_tell(stream->fd); +} + +__attribute__((weak)) int getc(FILE *stream) { + unsigned char ch; + int n; + if (!stream) { + return EOF; + } + if (stream->has_ungetc) { + stream->has_ungetc = 0; + return stream->ungetc_char; + } + n = sys_read(stream->fd, &ch, 1); + if (n <= 0) { + if (n == 0) { + stream->eof = 1; + } else { + stream->err = 1; + } + return EOF; + } + return (int)ch; +} + +__attribute__((weak)) int ungetc(int c, FILE *stream) { + if (!stream || c == EOF) { + return EOF; + } + stream->has_ungetc = 1; + stream->ungetc_char = (unsigned char)c; + stream->eof = 0; + return c; +} + +__attribute__((weak)) char *fgets(char *s, int n, FILE *stream) { + int i; + if (!s || n <= 0 || !stream) { + return NULL; + } + for (i = 0; i < n - 1; i++) { + int c = getc(stream); + if (c == EOF) { + break; + } + s[i] = (char)c; + if (c == '\n') { + i++; + break; + } + } + if (i == 0) { + return NULL; + } + s[i] = '\0'; + return s; +} + +__attribute__((weak)) int fputs(const char *s, FILE *stream) { + size_t len; + size_t written; + if (!s || !stream) { + return EOF; + } + len = strlen(s); + written = fwrite(s, 1, len, stream); + return (written == len) ? (int)len : EOF; +} + +__attribute__((weak)) int feof(FILE *stream) { + return stream ? stream->eof : 1; +} + +__attribute__((weak)) int ferror(FILE *stream) { + return stream ? stream->err : 1; +} + +__attribute__((weak)) void clearerr(FILE *stream) { + if (stream) { + stream->eof = 0; + stream->err = 0; + } +} + +__attribute__((weak)) int fflush(FILE *stream) { + (void)stream; + return 0; +} + +__attribute__((weak)) int remove(const char *path) { + return sys_delete(path); +} + +__attribute__((weak)) int rename(const char *oldpath, const char *newpath) { + FILE *src; + FILE *dst; + char buf[1024]; + size_t nread; + + if (!oldpath || !newpath || oldpath[0] == '\0' || newpath[0] == '\0') { + errno = EINVAL; + return -1; + } + if (_b_streq(oldpath, newpath)) { + return 0; + } + + src = fopen(oldpath, "rb"); + if (!src) { + errno = ENOENT; + return -1; + } + dst = fopen(newpath, "wb"); + if (!dst) { + fclose(src); + return -1; + } + + for (;;) { + nread = fread(buf, 1, sizeof(buf), src); + if (nread == 0) { + break; + } + if (fwrite(buf, 1, nread, dst) != nread) { + fclose(src); + fclose(dst); + sys_delete(newpath); + errno = EIO; + return -1; + } + } + + if (ferror(src) || ferror(dst)) { + fclose(src); + fclose(dst); + sys_delete(newpath); + errno = EIO; + return -1; + } + + fclose(src); + fclose(dst); + if (sys_delete(oldpath) != 0) { + errno = EIO; + return -1; + } + return 0; +} + +__attribute__((weak)) FILE *tmpfile(void) { + char path[FILENAME_MAX]; + if (!_b_tmpname_generate(path, sizeof(path))) { + return NULL; + } + return fopen(path, "w+"); +} + +__attribute__((weak)) char *tmpnam(char *s) { + char *dst = s ? s : _b_tmpname_buf; + return _b_tmpname_generate(dst, FILENAME_MAX); +} + +static int _b_hex_digit(unsigned value, int upper) { + if (value < 10U) { + return (int)('0' + value); + } + return (int)((upper ? 'A' : 'a') + (value - 10U)); +} + +static void _b_append_char(char *out, size_t cap, size_t *idx, int c) { + if (*idx + 1 < cap) { + out[*idx] = (char)c; + } + (*idx)++; +} + +static void _b_append_strn(char *out, size_t cap, size_t *idx, const char *s, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + _b_append_char(out, cap, idx, s[i]); + } +} + +static void _b_append_repeat(char *out, size_t cap, size_t *idx, char ch, int count) { + int i; + for (i = 0; i < count; i++) { + _b_append_char(out, cap, idx, ch); + } +} + +static void _b_utoa(unsigned long long v, unsigned base, int upper, char *buf, size_t *len) { + char tmp[64]; + size_t i = 0; + if (v == 0) { + tmp[i++] = '0'; + } else { + while (v && i < sizeof(tmp)) { + tmp[i++] = (char)_b_hex_digit((unsigned)(v % base), upper); + v /= base; + } + } + *len = i; + while (i > 0) { + *buf++ = tmp[--i]; + } +} + +static void _b_itoa(long long v, char *buf, size_t *len) { + unsigned long long uv; + size_t n = 0; + if (v < 0) { + *buf++ = '-'; + n++; + uv = (unsigned long long)(-(v + 1)) + 1ULL; + } else { + uv = (unsigned long long)v; + } + _b_utoa(uv, 10U, 0, buf, len); + *len += n; +} + +static void _b_ftoa(double d, int precision, char *buf, size_t *len) { + long long ip; + double frac; + size_t n = 0; + if (precision < 0) { + precision = 6; + } + if (d < 0.0) { + buf[n++] = '-'; + d = -d; + } + ip = (long long)d; + frac = d - (double)ip; + { + char ibuf[64]; + size_t ilen = 0; + _b_utoa((unsigned long long)ip, 10U, 0, ibuf, &ilen); + memcpy(buf + n, ibuf, ilen); + n += ilen; + } + if (precision > 0) { + int i; + buf[n++] = '.'; + for (i = 0; i < precision; i++) { + int digit; + frac *= 10.0; + digit = (int)frac; + if (digit < 0) digit = 0; + if (digit > 9) digit = 9; + buf[n++] = (char)('0' + digit); + frac -= (double)digit; + } + } + *len = n; +} + +__attribute__((weak)) int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { + size_t out_i = 0; + + while (*fmt) { + if (*fmt != '%') { + _b_append_char(str, size, &out_i, *fmt++); + continue; + } + + fmt++; + if (*fmt == '%') { + _b_append_char(str, size, &out_i, '%'); + fmt++; + continue; + } + + { + int left = 0; + int plus = 0; + int space = 0; + int alt = 0; + int zero = 0; + int width = -1; + int precision = -1; + int lcount = 0; + char spec; + + while (*fmt == '-' || *fmt == '+' || *fmt == ' ' || *fmt == '#' || *fmt == '0') { + if (*fmt == '-') left = 1; + else if (*fmt == '+') plus = 1; + else if (*fmt == ' ') space = 1; + else if (*fmt == '#') alt = 1; + else if (*fmt == '0') zero = 1; + fmt++; + } + + if (*fmt == '*') { + width = va_arg(ap, int); + if (width < 0) { + left = 1; + width = -width; + } + fmt++; + } else if (isdigit((unsigned char)*fmt)) { + width = 0; + while (isdigit((unsigned char)*fmt)) { + width = width * 10 + (*fmt - '0'); + fmt++; + } + } + + if (*fmt == '.') { + fmt++; + precision = 0; + if (*fmt == '*') { + precision = va_arg(ap, int); + if (precision < 0) { + precision = -1; + } + fmt++; + } else { + while (isdigit((unsigned char)*fmt)) { + precision = precision * 10 + (*fmt - '0'); + fmt++; + } + } + } + + while (*fmt == 'l') { + lcount++; + fmt++; + } + + spec = *fmt; + if (!spec) { + break; + } + + switch (spec) { + case 'd': + case 'i': { + long long sv; + unsigned long long uv; + char digits[64]; + size_t dlen = 0; + int neg = 0; + char signch = '\0'; + int zeros = 0; + int spaces = 0; + int total; + + if (lcount >= 2) sv = va_arg(ap, long long); + else if (lcount == 1) sv = va_arg(ap, long); + else sv = va_arg(ap, int); + + if (sv < 0) { + neg = 1; + uv = (unsigned long long)(-(sv + 1)) + 1ULL; + } else { + uv = (unsigned long long)sv; + } + + if (neg) signch = '-'; + else if (plus) signch = '+'; + else if (space) signch = ' '; + + if (!(precision == 0 && uv == 0ULL)) { + _b_utoa(uv, 10U, 0, digits, &dlen); + } + + if (precision > 0 && (size_t)precision > dlen) { + zeros = precision - (int)dlen; + } + + if (precision < 0 && zero && !left && width > 0) { + int signw = (signch != '\0') ? 1 : 0; + int need = width - (signw + (int)dlen); + if (need > zeros) { + zeros = need; + } + } + + total = ((signch != '\0') ? 1 : 0) + zeros + (int)dlen; + if (width > total) { + spaces = width - total; + } + + if (!left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + if (signch != '\0') { + _b_append_char(str, size, &out_i, signch); + } + _b_append_repeat(str, size, &out_i, '0', zeros); + _b_append_strn(str, size, &out_i, digits, dlen); + if (left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + break; + } + + case 'u': + case 'x': + case 'X': + case 'o': { + unsigned long long uv; + unsigned base = (spec == 'o') ? 8U : ((spec == 'u') ? 10U : 16U); + int upper = (spec == 'X'); + char digits[64]; + size_t dlen = 0; + int zeros = 0; + int spaces = 0; + int prefix_len = 0; + char p1 = '\0'; + char p2 = '\0'; + int total; + + if (lcount >= 2) uv = va_arg(ap, unsigned long long); + else if (lcount == 1) uv = va_arg(ap, unsigned long); + else uv = va_arg(ap, unsigned int); + + if (!(precision == 0 && uv == 0ULL)) { + _b_utoa(uv, base, upper, digits, &dlen); + } + + if (alt && uv != 0ULL) { + if (spec == 'x' || spec == 'X') { + p1 = '0'; + p2 = upper ? 'X' : 'x'; + prefix_len = 2; + } else if (spec == 'o') { + p1 = '0'; + prefix_len = 1; + } + } + + if (precision > 0 && (size_t)precision > dlen) { + zeros = precision - (int)dlen; + } + + if (precision < 0 && zero && !left && width > 0) { + int need = width - (prefix_len + (int)dlen); + if (need > zeros) { + zeros = need; + } + } + + total = prefix_len + zeros + (int)dlen; + if (width > total) { + spaces = width - total; + } + + if (!left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + if (prefix_len >= 1) { + _b_append_char(str, size, &out_i, p1); + } + if (prefix_len >= 2) { + _b_append_char(str, size, &out_i, p2); + } + _b_append_repeat(str, size, &out_i, '0', zeros); + _b_append_strn(str, size, &out_i, digits, dlen); + if (left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + break; + } + + case 'c': { + int c = va_arg(ap, int); + int spaces = (width > 1) ? (width - 1) : 0; + if (!left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + _b_append_char(str, size, &out_i, c); + if (left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + break; + } + + case 's': { + const char *s = va_arg(ap, const char *); + size_t slen; + int spaces; + if (!s) s = "(null)"; + slen = strlen(s); + if (precision >= 0 && (size_t)precision < slen) { + slen = (size_t)precision; + } + spaces = (width > (int)slen) ? (width - (int)slen) : 0; + if (!left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + _b_append_strn(str, size, &out_i, s, slen); + if (left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + break; + } + + case 'p': { + uintptr_t v = (uintptr_t)va_arg(ap, void *); + char nbuf[32]; + size_t nlen = 0; + _b_append_strn(str, size, &out_i, "0x", 2); + _b_utoa((unsigned long long)v, 16U, 0, nbuf, &nlen); + _b_append_strn(str, size, &out_i, nbuf, nlen); + break; + } + + case 'f': + case 'g': + case 'e': { + double v = va_arg(ap, double); + char nbuf[96]; + size_t nlen = 0; + int spaces; + _b_ftoa(v, precision, nbuf, &nlen); + spaces = (width > (int)nlen) ? (width - (int)nlen) : 0; + if (!left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + _b_append_strn(str, size, &out_i, nbuf, nlen); + if (left) { + _b_append_repeat(str, size, &out_i, ' ', spaces); + } + break; + } + + default: + _b_append_char(str, size, &out_i, '%'); + _b_append_char(str, size, &out_i, spec); + break; + } + } + + if (*fmt) { + fmt++; + } + } + + if (size > 0) { + size_t term = (out_i < size - 1) ? out_i : (size - 1); + str[term] = '\0'; + } + + return (int)out_i; +} + +__attribute__((weak)) int snprintf(char *str, size_t size, const char *fmt, ...) { + int n; + va_list ap; + va_start(ap, fmt); + n = vsnprintf(str, size, fmt, ap); + va_end(ap); + return n; +} + +__attribute__((weak)) int sprintf(char *str, const char *fmt, ...) { + int n; + va_list ap; + va_start(ap, fmt); + n = vsnprintf(str, (size_t)-1, fmt, ap); + va_end(ap); + return n; +} + +__attribute__((weak)) int fprintf(FILE *stream, const char *fmt, ...) { + char buf[1024]; + int len; + va_list ap; + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (len <= 0) { + return len; + } + if ((size_t)len > sizeof(buf)) { + len = (int)sizeof(buf); + } + if (fwrite(buf, 1, (size_t)len, stream) == 0) { + return -1; + } + return len; +} + +__attribute__((weak)) int vfprintf(FILE *stream, const char *fmt, va_list ap) { + char buf[1024]; + int len = vsnprintf(buf, sizeof(buf), fmt, ap); + if (len <= 0) { + return len; + } + if ((size_t)len > sizeof(buf)) { + len = (int)sizeof(buf); + } + if (fwrite(buf, 1, (size_t)len, stream) == 0) { + return -1; + } + return len; +} + +__attribute__((weak)) int fputc(int c, FILE *stream) { + unsigned char ch = (unsigned char)c; + if (!stream) { + return EOF; + } + if (fwrite(&ch, 1, 1, stream) != 1) { + return EOF; + } + return c; +} + +__attribute__((weak)) int putchar(int c) { + return fputc(c, stdout); +} + +__attribute__((weak)) long filelength(FILE *f) { + if (!f) { + return -1; + } + return (long)sys_size(f->fd); +} + +static const char *_b_skip_spaces(const char *p) { + while (*p && _b_is_space_char((unsigned char)*p)) { + p++; + } + return p; +} + +static int _b_digit_val(int c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); + if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); + return -1; +} + +static int _b_parse_u64(const char **sp, int base, int width, unsigned long long *out) { + const char *p = *sp; + unsigned long long v = 0; + int any = 0; + + if (width != 0 && base == 16 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + p += 2; + if (width > 0) { + width -= 2; + } + } + + while (*p && (width < 0 || width > 0)) { + int d = _b_digit_val((unsigned char)*p); + if (d < 0 || d >= base) { + break; + } + v = v * (unsigned long long)base + (unsigned long long)d; + any = 1; + p++; + if (width > 0) { + width--; + } + } + + if (!any) { + return 0; + } + *sp = p; + *out = v; + return 1; +} + +static int _b_parse_auto_int(const char **sp, int width, long long *out) { + const char *p = *sp; + int neg = 0; + int base = 10; + unsigned long long uv; + + if (*p == '+' || *p == '-') { + neg = (*p == '-'); + p++; + if (width > 0) { + width--; + } + } + + if ((width < 0 || width >= 2) && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + base = 16; + } else if ((width < 0 || width >= 1) && p[0] == '0') { + base = 8; + } + + if (!_b_parse_u64(&p, base, width, &uv)) { + return 0; + } + *sp = p; + *out = neg ? -(long long)uv : (long long)uv; + return 1; +} + +static int _b_parse_s64(const char **sp, int base, int width, long long *out) { + const char *p = *sp; + int neg = 0; + unsigned long long uv; + + if (*p == '+' || *p == '-') { + neg = (*p == '-'); + p++; + if (width > 0) { + width--; + } + } + if (!_b_parse_u64(&p, base, width, &uv)) { + return 0; + } + *sp = p; + *out = neg ? -(long long)uv : (long long)uv; + return 1; +} + +static int _b_vsscanf_core(const char *str, const char *fmt, va_list ap) { + const char *s = str; + const char *f = fmt; + int assigned = 0; + + while (*f) { + if (_b_is_space_char((unsigned char)*f)) { + while (_b_is_space_char((unsigned char)*f)) f++; + s = _b_skip_spaces(s); + continue; + } + + if (*f != '%') { + if (*s != *f) { + break; + } + s++; + f++; + continue; + } + + f++; + if (*f == '%') { + if (*s != '%') { + break; + } + s++; + f++; + continue; + } + + { + int suppress = 0; + int width = -1; + int lcount = 0; + + if (*f == '*') { + suppress = 1; + f++; + } + + if (isdigit((unsigned char)*f)) { + width = 0; + while (isdigit((unsigned char)*f)) { + width = width * 10 + (*f - '0'); + f++; + } + } + + while (*f == 'l') { + lcount++; + f++; + } + + if (*f != 'c' && *f != 'n') { + s = _b_skip_spaces(s); + } + + switch (*f) { + case 'd': { + long long v; + if (!_b_parse_s64(&s, 10, width, &v)) return assigned; + if (!suppress) { + if (lcount >= 1) *va_arg(ap, long *) = (long)v; + else *va_arg(ap, int *) = (int)v; + assigned++; + } + break; + } + case 'i': { + long long v; + if (!_b_parse_auto_int(&s, width, &v)) return assigned; + if (!suppress) { + if (lcount >= 1) *va_arg(ap, long *) = (long)v; + else *va_arg(ap, int *) = (int)v; + assigned++; + } + break; + } + case 'u': + case 'x': + case 'X': + case 'o': { + int base = (*f == 'o') ? 8 : ((*f == 'u') ? 10 : 16); + unsigned long long v; + if (!_b_parse_u64(&s, base, width, &v)) return assigned; + if (!suppress) { + if (lcount >= 1) *va_arg(ap, unsigned long *) = (unsigned long)v; + else *va_arg(ap, unsigned *) = (unsigned)v; + assigned++; + } + break; + } + case 'c': { + int count = (width > 0) ? width : 1; + if (!*s) return assigned; + if (!suppress) { + char *dst = va_arg(ap, char *); + while (count-- > 0 && *s) { + *dst++ = *s++; + } + assigned++; + } else { + while (count-- > 0 && *s) s++; + } + break; + } + case 's': { + int wrote = 0; + if (!*s) return assigned; + if (!suppress) { + char *dst = va_arg(ap, char *); + while (*s && !_b_is_space_char((unsigned char)*s) && (width < 0 || width-- > 0)) { + *dst++ = *s++; + wrote = 1; + } + if (!wrote) return assigned; + *dst = '\0'; + assigned++; + } else { + while (*s && !_b_is_space_char((unsigned char)*s) && (width < 0 || width-- > 0)) { + s++; + wrote = 1; + } + if (!wrote) return assigned; + } + break; + } + case 'n': { + if (!suppress) { + *va_arg(ap, int *) = (int)(s - str); + } + break; + } + default: + return assigned; + } + } + + if (*f) { + f++; + } + } + + return assigned; +} + +__attribute__((weak)) int sscanf(const char *str, const char *fmt, ...) { + int n; + va_list ap; + if (!str || !fmt) { + errno = EINVAL; + return 0; + } + va_start(ap, fmt); + n = _b_vsscanf_core(str, fmt, ap); + va_end(ap); + return n; +} diff --git a/src/userland/libc/stdio.h b/src/userland/libc/stdio.h new file mode 100644 index 0000000..4d8447f --- /dev/null +++ b/src/userland/libc/stdio.h @@ -0,0 +1,56 @@ +#ifndef BOREDOS_LIBC_STDIO_H +#define BOREDOS_LIBC_STDIO_H + +#include +#include + +typedef struct BOREDOS_FILE { + int fd; + int eof; + int err; + int has_ungetc; + int ungetc_char; +} FILE; + +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +#define EOF (-1) +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#define BUFSIZ 1024 +#define FILENAME_MAX 260 +#define TMP_MAX 32 + +FILE *fopen(const char *path, const char *mode); +FILE *freopen(const char *path, const char *mode, FILE *stream); +int fclose(FILE *stream); +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +int fseek(FILE *stream, long offset, int whence); +long ftell(FILE *stream); +int getc(FILE *stream); +int ungetc(int c, FILE *stream); +char *fgets(char *s, int n, FILE *stream); +int fputs(const char *s, FILE *stream); +int feof(FILE *stream); +int ferror(FILE *stream); +void clearerr(FILE *stream); +int fflush(FILE *stream); +int fputc(int c, FILE *stream); +int putchar(int c); +int fprintf(FILE *stream, const char *fmt, ...); +int vfprintf(FILE *stream, const char *fmt, va_list ap); +long filelength(FILE *f); +int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); +int snprintf(char *str, size_t size, const char *fmt, ...); +int sprintf(char *str, const char *fmt, ...); +int sscanf(const char *str, const char *fmt, ...); +int remove(const char *path); +int rename(const char *oldpath, const char *newpath); +FILE *tmpfile(void); +char *tmpnam(char *s); + +#endif diff --git a/src/userland/libc/stdlib.c b/src/userland/libc/stdlib.c index 2df15fb..a471ec4 100644 --- a/src/userland/libc/stdlib.c +++ b/src/userland/libc/stdlib.c @@ -334,7 +334,7 @@ char* getcwd(char *buf, int size) { } void sleep(int ms) { - sys_system(46, ms, 0, 0, 0); + sys_system(SYSTEM_CMD_SLEEP, ms, 0, 0, 0); } void exit(int status) { diff --git a/src/userland/libc/stdlib.h b/src/userland/libc/stdlib.h index 2b4818d..8e87f46 100644 --- a/src/userland/libc/stdlib.h +++ b/src/userland/libc/stdlib.h @@ -16,15 +16,24 @@ void *memcpy(void *dest, const void *src, size_t n); // Math/Utility functions int atoi(const char *nptr); void itoa(int n, char *buf); +int abs(int x); +double strtod(const char *nptr, char **endptr); // IO functions void puts(const char *s); void printf(const char *fmt, ...); +// Runtime stubs +int system(const char *command); +char *getenv(const char *name); +void abort(void); + // System/Process functions int chdir(const char *path); char* getcwd(char *buf, int size); +int access(const char *pathname, int mode); void sleep(int ms); void exit(int status); +void _exit(int status); #endif diff --git a/src/userland/libc/string.h b/src/userland/libc/string.h index a0e32ec..a5eab52 100644 --- a/src/userland/libc/string.h +++ b/src/userland/libc/string.h @@ -7,11 +7,22 @@ void *memmove(void *dest, const void *src, size_t n); int memcmp(const void *s1, const void *s2, size_t n); void *memcpy(void *dest, const void *src, size_t n); void *memset(void *s, int c, size_t n); +void *memchr(const void *s, int c, size_t n); char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +char *strpbrk(const char *s, const char *accept); char *strstr(const char *haystack, const char *needle); +size_t strspn(const char *s, const char *accept); +size_t strcspn(const char *s, const char *reject); size_t strlen(const char *s); int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +int strncasecmp(const char *s1, const char *s2, size_t n); +int strcasecmp(const char *s1, const char *s2); +int strcoll(const char *s1, const char *s2); char* strcpy(char *dest, const char *src); char* strcat(char *dest, const char *src); +char *strdup(const char *s); +char *strerror(int errnum); #endif diff --git a/src/userland/libc/string_ext.c b/src/userland/libc/string_ext.c new file mode 100644 index 0000000..06abb5e --- /dev/null +++ b/src/userland/libc/string_ext.c @@ -0,0 +1,158 @@ +#include "string.h" +#include "errno.h" +#include "ctype.h" +#include "stdlib.h" + +__attribute__((weak)) int strncmp(const char *s1, const char *s2, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + unsigned char c1 = (unsigned char)s1[i]; + unsigned char c2 = (unsigned char)s2[i]; + if (c1 != c2) { + return (int)c1 - (int)c2; + } + if (c1 == '\0') { + return 0; + } + } + return 0; +} + +__attribute__((weak)) char *strncpy(char *dest, const char *src, size_t n) { + size_t i; + for (i = 0; i < n && src[i] != '\0'; i++) { + dest[i] = src[i]; + } + for (; i < n; i++) { + dest[i] = '\0'; + } + return dest; +} + +__attribute__((weak)) char *strncat(char *dest, const char *src, size_t n) { + size_t dlen = strlen(dest); + size_t i; + for (i = 0; i < n && src[i] != '\0'; i++) { + dest[dlen + i] = src[i]; + } + dest[dlen + i] = '\0'; + return dest; +} + +__attribute__((weak)) char *strrchr(const char *s, int c) { + const char *last = NULL; + for (;; s++) { + if (*s == (char)c) { + last = s; + } + if (*s == '\0') { + break; + } + } + return (char *)last; +} + +__attribute__((weak)) char *strpbrk(const char *s, const char *accept) { + for (; *s; s++) { + const char *a; + for (a = accept; *a; a++) { + if (*s == *a) { + return (char *)s; + } + } + } + return NULL; +} + +__attribute__((weak)) size_t strspn(const char *s, const char *accept) { + size_t n = 0; + while (*s) { + if (!strchr(accept, *s)) { + break; + } + n++; + s++; + } + return n; +} + +__attribute__((weak)) size_t strcspn(const char *s, const char *reject) { + size_t n = 0; + while (*s) { + if (strchr(reject, *s)) { + break; + } + n++; + s++; + } + return n; +} + +__attribute__((weak)) void *memchr(const void *s, int c, size_t n) { + const unsigned char *p = (const unsigned char *)s; + size_t i; + for (i = 0; i < n; i++) { + if (p[i] == (unsigned char)c) { + return (void *)(p + i); + } + } + return NULL; +} + +__attribute__((weak)) int strcoll(const char *s1, const char *s2) { + return strcmp(s1, s2); +} + +__attribute__((weak)) char *strerror(int errnum) { + switch (errnum) { + case 0: return "no error"; + case EDOM: return "domain error"; + case ERANGE: return "range error"; + case EINVAL: return "invalid argument"; + case ENOENT: return "no such file or directory"; + case ENOMEM: return "not enough memory"; + case EEXIST: return "file exists"; + case EIO: return "i/o error"; + case ENOSYS: return "function not implemented"; + default: return "unknown error"; + } +} + +__attribute__((weak)) int strncasecmp(const char *s1, const char *s2, size_t n) { + while (n--) { + char c1 = (char)tolower((unsigned char)*s1++); + char c2 = (char)tolower((unsigned char)*s2++); + if (c1 != c2) { + return c1 - c2; + } + if (!c1) { + break; + } + } + return 0; +} + +__attribute__((weak)) int strcasecmp(const char *s1, const char *s2) { + while (1) { + char c1 = (char)tolower((unsigned char)*s1++); + char c2 = (char)tolower((unsigned char)*s2++); + if (c1 != c2) { + return c1 - c2; + } + if (!c1) { + break; + } + } + return 0; +} + +__attribute__((weak)) char *strdup(const char *s) { + size_t len = strlen(s) + 1; + char *dup = (char *)malloc(len); + if (!dup) { + errno = ENOMEM; + return NULL; + } + memcpy(dup, s, len); + return dup; +} diff --git a/src/userland/libc/sys/stat.h b/src/userland/libc/sys/stat.h new file mode 100644 index 0000000..e3cfdb8 --- /dev/null +++ b/src/userland/libc/sys/stat.h @@ -0,0 +1,47 @@ +#ifndef BOREDOS_LIBC_SYS_STAT_H +#define BOREDOS_LIBC_SYS_STAT_H + +#include "types.h" + +typedef long time_t; + +struct stat { + unsigned long st_dev; + unsigned long st_ino; + mode_t st_mode; + unsigned long st_nlink; + uid_t st_uid; + gid_t st_gid; + unsigned long st_rdev; + int st_size; + long st_blksize; + long st_blocks; + time_t st_atime; + time_t st_mtime; + time_t st_ctime; +}; + +#define S_IFMT 0170000 +#define S_IFIFO 0010000 +#define S_IFCHR 0020000 +#define S_IFDIR 0040000 +#define S_IFREG 0100000 + +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +#define S_IRGRP 0040 +#define S_IWGRP 0020 +#define S_IXGRP 0010 +#define S_IROTH 0004 +#define S_IWOTH 0002 +#define S_IXOTH 0001 + +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) + +int stat(const char *pathname, struct stat *statbuf); +int fstat(int fd, struct stat *statbuf); +int mkdir(const char *pathname, int mode); + +#endif diff --git a/src/userland/libc/sys/types.h b/src/userland/libc/sys/types.h new file mode 100644 index 0000000..da5815b --- /dev/null +++ b/src/userland/libc/sys/types.h @@ -0,0 +1,11 @@ +#ifndef BOREDOS_LIBC_SYS_TYPES_H +#define BOREDOS_LIBC_SYS_TYPES_H + +typedef long ssize_t; +typedef long off_t; +typedef unsigned int mode_t; +typedef int pid_t; +typedef unsigned int uid_t; +typedef unsigned int gid_t; + +#endif diff --git a/src/userland/libc/sys/wait.h b/src/userland/libc/sys/wait.h new file mode 100644 index 0000000..6e0133c --- /dev/null +++ b/src/userland/libc/sys/wait.h @@ -0,0 +1,15 @@ +#ifndef BOREDOS_LIBC_SYS_WAIT_H +#define BOREDOS_LIBC_SYS_WAIT_H + +#include "../sys/types.h" + +#define WNOHANG 1 + +#define WEXITSTATUS(status) (((status) >> 8) & 0xff) +#define WIFEXITED(status) ((((status) & 0x7f) == 0) ? 1 : 0) +#define WTERMSIG(status) ((status) & 0x7f) +#define WIFSIGNALED(status) (((status) & 0x7f) != 0) + +pid_t waitpid(pid_t pid, int *status, int options); + +#endif diff --git a/src/userland/libc/syscall.c b/src/userland/libc/syscall.c index 5e433c3..f5068c4 100644 --- a/src/userland/libc/syscall.c +++ b/src/userland/libc/syscall.c @@ -145,6 +145,22 @@ int sys_chdir(const char *path) { return (int)syscall2(SYS_FS, FS_CMD_CHDIR, (uint64_t)path); } +int sys_dup(int oldfd) { + return (int)syscall2(SYS_FS, FS_CMD_DUP, (uint64_t)oldfd); +} + +int sys_dup2(int oldfd, int newfd) { + return (int)syscall3(SYS_FS, FS_CMD_DUP2, (uint64_t)oldfd, (uint64_t)newfd); +} + +int sys_pipe(int pipefd[2]) { + return (int)syscall2(SYS_FS, FS_CMD_PIPE, (uint64_t)pipefd); +} + +int sys_fcntl(int fd, int cmd, int val) { + return (int)syscall4(SYS_FS, FS_CMD_FCNTL, (uint64_t)fd, (uint64_t)cmd, (uint64_t)val); +} + int sys_tty_create(void) { return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0); } @@ -165,6 +181,30 @@ int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_i return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_SPAWN, (uint64_t)path, (uint64_t)args, flags, (uint64_t)tty_id); } +int sys_exec(const char *path, const char *args) { + return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_EXEC, (uint64_t)path, (uint64_t)args, 0); +} + +int sys_waitpid(int pid, int *status, int options) { + return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_WAITPID, (uint64_t)pid, (uint64_t)status, (uint64_t)options); +} + +int sys_kill_signal(int pid, int sig) { + return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_KILL_SIGNAL, (uint64_t)pid, (uint64_t)sig, 0); +} + +int sys_sigaction(int sig, const void *act, void *oldact) { + return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_SIGACTION, (uint64_t)sig, (uint64_t)act, (uint64_t)oldact); +} + +int sys_sigprocmask(int how, const unsigned long *set, unsigned long *oldset) { + return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_SIGPROCMASK, (uint64_t)how, (uint64_t)set, (uint64_t)oldset); +} + +int sys_sigpending(unsigned long *set) { + return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_SIGPENDING, (uint64_t)set, 0); +} + int sys_tty_set_fg(int tty_id, int pid) { return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_SET_FG, (uint64_t)tty_id, (uint64_t)pid, 0); } diff --git a/src/userland/libc/syscall.h b/src/userland/libc/syscall.h index f6f168a..6b03bd9 100644 --- a/src/userland/libc/syscall.h +++ b/src/userland/libc/syscall.h @@ -28,6 +28,10 @@ #define FS_CMD_GETCWD 12 #define FS_CMD_CHDIR 13 #define FS_CMD_GET_INFO 14 +#define FS_CMD_DUP 15 +#define FS_CMD_DUP2 16 +#define FS_CMD_PIPE 17 +#define FS_CMD_FCNTL 18 // System Commands (via SYS_SYSTEM) #define SYSTEM_CMD_SET_BG_COLOR 1 @@ -44,6 +48,8 @@ #define SYSTEM_CMD_REBOOT 12 #define SYSTEM_CMD_SHUTDOWN 13 #define SYSTEM_CMD_BEEP 14 +#define SYSTEM_CMD_GET_MEM_INFO 15 +#define SYSTEM_CMD_GET_TICKS 16 #define SYSTEM_CMD_PCI_LIST 17 #define SYSTEM_CMD_NETWORK_DHCP 18 #define SYSTEM_CMD_NETWORK_GET_MAC 19 @@ -60,6 +66,7 @@ #define SYSTEM_CMD_NETWORK_GET_NIC_NAME 48 #define SYSTEM_CMD_SET_TEXT_COLOR 29 #define SYSTEM_CMD_SET_WALLPAPER_PATH 31 +#define SYSTEM_CMD_RTC_SET 32 #define SYSTEM_CMD_TCP_CONNECT 33 #define SYSTEM_CMD_TCP_SEND 34 #define SYSTEM_CMD_TCP_RECV 35 @@ -67,10 +74,12 @@ #define SYSTEM_CMD_DNS_LOOKUP 37 #define SYSTEM_CMD_SET_DNS 38 #define SYSTEM_CMD_NET_UNLOCK 39 +#define SYSTEM_CMD_SET_FONT 40 #define SYSTEM_CMD_SLEEP 46 #define SYSTEM_CMD_SET_RAW_MODE 41 #define SYSTEM_CMD_TCP_RECV_NB 42 #define SYSTEM_CMD_YIELD 43 +#define SYSTEM_CMD_SET_RESOLUTION 47 #define SYSTEM_CMD_PARALLEL_RUN 50 #define SYSTEM_CMD_TTY_CREATE 60 #define SYSTEM_CMD_TTY_READ_OUT 61 @@ -82,10 +91,17 @@ #define SYSTEM_CMD_TTY_KILL_FG 67 #define SYSTEM_CMD_TTY_KILL_ALL 68 #define SYSTEM_CMD_TTY_DESTROY 69 +#define SYSTEM_CMD_EXEC 70 +#define SYSTEM_CMD_WAITPID 71 +#define SYSTEM_CMD_KILL_SIGNAL 72 +#define SYSTEM_CMD_SIGACTION 73 +#define SYSTEM_CMD_SIGPROCMASK 74 +#define SYSTEM_CMD_SIGPENDING 75 #define SPAWN_FLAG_TERMINAL 0x1 #define SPAWN_FLAG_INHERIT_TTY 0x2 #define SPAWN_FLAG_TTY_ID 0x4 +#define SPAWN_FLAG_BACKGROUND 0x8 // Internal assembly entry into Ring 0 extern uint64_t syscall0(uint64_t sys_num); @@ -128,12 +144,22 @@ int sys_mkdir(const char *path); int sys_exists(const char *path); int sys_getcwd(char *buf, int size); int sys_chdir(const char *path); +int sys_dup(int oldfd); +int sys_dup2(int oldfd, int newfd); +int sys_pipe(int pipefd[2]); +int sys_fcntl(int fd, int cmd, int val); int sys_tty_create(void); int sys_tty_read_out(int tty_id, char *buf, int len); int sys_tty_write_in(int tty_id, const char *buf, int len); int sys_tty_read_in(char *buf, int len); int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id); +int sys_exec(const char *path, const char *args); +int sys_waitpid(int pid, int *status, int options); +int sys_kill_signal(int pid, int sig); +int sys_sigaction(int sig, const void *act, void *oldact); +int sys_sigprocmask(int how, const unsigned long *set, unsigned long *oldset); +int sys_sigpending(unsigned long *set); int sys_tty_set_fg(int tty_id, int pid); int sys_tty_get_fg(int tty_id); int sys_tty_kill_fg(int tty_id); diff --git a/src/userland/libc/time.c b/src/userland/libc/time.c new file mode 100644 index 0000000..6bb1248 --- /dev/null +++ b/src/userland/libc/time.c @@ -0,0 +1,166 @@ +#include + +#include "time.h" +#include "stdio.h" +#include "syscall.h" + +static int _b_is_leap(int year) { + return ((year % 4) == 0 && (year % 100) != 0) || ((year % 400) == 0); +} + +static int _b_days_in_month(int year, int month) { + static const int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + if (month == 1 && _b_is_leap(year)) { + return 29; + } + return mdays[month]; +} + +static long long _b_days_before_year(int year) { + long long y = (long long)year - 1; + return y * 365 + y / 4 - y / 100 + y / 400; +} + +static long long _b_days_since_epoch(int year, int month, int day) { + long long days = _b_days_before_year(year) - _b_days_before_year(1970); + int m; + for (m = 0; m < month - 1; m++) { + days += _b_days_in_month(year, m); + } + days += (day - 1); + return days; +} + +static void _b_civil_from_days(long long z, int *year, int *month, int *day) { + z += 719468; + long long era = (z >= 0 ? z : z - 146096) / 146097; + unsigned doe = (unsigned)(z - era * 146097); + unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + int y = (int)yoe + (int)era * 400; + unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + unsigned mp = (5 * doy + 2) / 153; + unsigned d = doy - (153 * mp + 2) / 5 + 1; + unsigned m = mp + (mp < 10 ? 3 : (unsigned)-9); + y += (m <= 2); + *year = y; + *month = (int)m; + *day = (int)d; +} + +static time_t _b_seconds_from_ymdhms(int year, int month, int day, int hour, int minute, int second) { + long long days = _b_days_since_epoch(year, month, day); + return (time_t)(days * 86400LL + hour * 3600LL + minute * 60LL + second); +} + +static void _b_fill_tm_from_epoch(time_t t, struct tm *out) { + long long sec = (long long)t; + long long days; + int sod; + int year; + int month; + int day; + + if (sec < 0) { + long long d = ((-sec) + 86399LL) / 86400LL; + sec += d * 86400LL; + } + + days = sec / 86400LL; + sod = (int)(sec % 86400LL); + if (sod < 0) { + sod += 86400; + days--; + } + + _b_civil_from_days(days, &year, &month, &day); + + out->tm_year = year - 1900; + out->tm_mon = month - 1; + out->tm_mday = day; + out->tm_hour = sod / 3600; + out->tm_min = (sod % 3600) / 60; + out->tm_sec = sod % 60; + out->tm_wday = (int)((days + 4) % 7); + if (out->tm_wday < 0) out->tm_wday += 7; + + { + long long jan1 = _b_days_since_epoch(year, 1, 1); + out->tm_yday = (int)(days - jan1); + } + out->tm_isdst = 0; +} + +__attribute__((weak)) time_t time(time_t *out) { + int dt[6] = {1970, 1, 1, 0, 0, 0}; + time_t t; + if (sys_system(SYSTEM_CMD_RTC_GET, 0, (uint64_t)dt, 0, 0) != 0) { + t = 0; + } else { + t = _b_seconds_from_ymdhms(dt[0], dt[1], dt[2], dt[3], dt[4], dt[5]); + } + if (out) { + *out = t; + } + return t; +} + +__attribute__((weak)) clock_t clock(void) { + static uint64_t start_tsc = 0; + unsigned int lo; + unsigned int hi; + uint64_t now_tsc; + + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo; + + if (start_tsc == 0) { + start_tsc = now_tsc; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo; + } + + return (clock_t)(now_tsc - start_tsc); +} + +__attribute__((weak)) struct tm *gmtime(const time_t *timer) { + static struct tm tmv; + if (!timer) { + return NULL; + } + _b_fill_tm_from_epoch(*timer, &tmv); + return &tmv; +} + +__attribute__((weak)) struct tm *localtime(const time_t *timer) { + return gmtime(timer); +} + +__attribute__((weak)) size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm) { + (void)fmt; + if (!s || max == 0 || !tm) { + return 0; + } + { + int n = snprintf(s, max, "%04d-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + if (n < 0 || (size_t)n >= max) { + if (max > 0) s[0] = '\0'; + return 0; + } + return (size_t)n; + } +} + +__attribute__((weak)) time_t mktime(struct tm *tm) { + if (!tm) { + return (time_t)-1; + } + return _b_seconds_from_ymdhms( + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); +} diff --git a/src/userland/libc/time.h b/src/userland/libc/time.h new file mode 100644 index 0000000..b3e7c3c --- /dev/null +++ b/src/userland/libc/time.h @@ -0,0 +1,30 @@ +#ifndef BOREDOS_LIBC_TIME_H +#define BOREDOS_LIBC_TIME_H + +#include + +typedef long long time_t; +typedef unsigned long long clock_t; + +#define CLOCKS_PER_SEC 3000000000ULL + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +time_t time(time_t *out); +clock_t clock(void); +struct tm *localtime(const time_t *timer); +struct tm *gmtime(const time_t *timer); +size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm); +time_t mktime(struct tm *tm); + +#endif diff --git a/src/userland/libc/unistd.h b/src/userland/libc/unistd.h new file mode 100644 index 0000000..36d7081 --- /dev/null +++ b/src/userland/libc/unistd.h @@ -0,0 +1,30 @@ +#ifndef BOREDOS_LIBC_UNISTD_H +#define BOREDOS_LIBC_UNISTD_H + +#include +#include "sys/types.h" + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define F_OK 0 +#define X_OK 1 +#define W_OK 2 +#define R_OK 4 + +int close(int fd); +ssize_t read(int fd, void *buf, size_t count); +ssize_t write(int fd, const void *buf, size_t count); +off_t lseek(int fd, off_t offset, int whence); +int unlink(const char *pathname); +int isatty(int fd); +int execv(const char *path, char *const argv[]); +int execve(const char *path, char *const argv[], char *const envp[]); +int execvp(const char *file, char *const argv[]); +int execl(const char *path, const char *arg, ...); +int execlp(const char *file, const char *arg, ...); +int execle(const char *path, const char *arg, ...); +pid_t waitpid(pid_t pid, int *status, int options); + +#endif diff --git a/src/userland/sys/beep.c b/src/userland/sys/beep.c index 7ae4355..5745bcb 100644 --- a/src/userland/sys/beep.c +++ b/src/userland/sys/beep.c @@ -6,7 +6,7 @@ int main(int argc, char **argv) { (void)argc; (void)argv; - sys_system(14, 392, 400, 0, 0); + sys_system(SYSTEM_CMD_BEEP, 392, 400, 0, 0); printf("BEEP!\n"); return 0; } diff --git a/src/userland/sys/clock.c b/src/userland/sys/clock.c index 882cb08..a733c7d 100644 --- a/src/userland/sys/clock.c +++ b/src/userland/sys/clock.c @@ -40,6 +40,10 @@ static long long sw_start_ticks = 0; static long long sw_elapsed_ticks = 0; static bool sw_running = false; +static long long sys_ticks_now(void) { + return sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0); +} + static void format_time(char *buf, int h, int m, int s) { buf[0] = '0' + (h / 10); buf[1] = '0' + (h % 10); @@ -55,7 +59,7 @@ static void format_time(char *buf, int h, int m, int s) { static void update_rtc_state(void) { int dt[6]; int old_s = cur_s; - sys_system(11, (uint64_t)dt, 0, 0, 0); + sys_system(SYSTEM_CMD_RTC_GET, (uint64_t)dt, 0, 0, 0); cur_y = dt[0]; cur_mon = dt[1]; cur_d = dt[2]; cur_h = dt[3]; cur_m = dt[4]; cur_s = dt[5]; @@ -123,7 +127,7 @@ static void clock_paint(void) { draw_btn((WIN_W - 120) / 2, 195, 120, 28, "Apply changes", false); } else if (current_tab == TAB_TIMER) { - long long now = sys_system(16, 0, 0, 0, 0); + long long now = sys_ticks_now(); int display_h = timer_h, display_m = timer_m, display_s = timer_s; if (timer_running) { @@ -131,7 +135,7 @@ static void clock_paint(void) { if (rem <= 0) { timer_running = false; display_h = display_m = display_s = 0; - for(int i=0; i<3; i++) sys_system(14, 440, 200, 0, 0); + for (int i = 0; i < 3; i++) sys_system(SYSTEM_CMD_BEEP, 440, 200, 0, 0); } else { int s = rem / 60; display_h = s / 3600; @@ -157,7 +161,7 @@ static void clock_paint(void) { } else if (current_tab == TAB_STOPWATCH) { long long elapsed = sw_elapsed_ticks; - if (sw_running) elapsed += (sys_system(16, 0, 0, 0, 0) - sw_start_ticks); + if (sw_running) elapsed += (sys_ticks_now() - sw_start_ticks); int ms_val = (elapsed % 60) * 1000 / 60; int s_val = (elapsed / 60) % 60; @@ -207,7 +211,7 @@ static void clock_click(int x, int y) { else if (x >= ax_clock + 73 && x <= ax_clock + 110) adj_s = (adj_s + 59) % 60; } else if (x >= (WIN_W - 120) / 2 && x <= (WIN_W + 120) / 2 && y >= 195 && y <= 223) { int dt[6] = {cur_y, cur_mon, cur_d, adj_h, adj_m, adj_s}; - sys_system(32, (uint64_t)dt, 0, 0, 0); + sys_system(SYSTEM_CMD_RTC_SET, (uint64_t)dt, 0, 0, 0); } } else if (current_tab == TAB_TIMER) { if (!timer_running) { @@ -225,16 +229,16 @@ static void clock_click(int x, int y) { if (!timer_running) { if (timer_h > 0 || timer_m > 0 || timer_s > 0) { timer_running = true; - timer_end_ticks = sys_system(16, 0, 0, 0, 0) + (long long)(timer_h * 3600 + timer_m * 60 + timer_s) * 60; + timer_end_ticks = sys_ticks_now() + (long long)(timer_h * 3600 + timer_m * 60 + timer_s) * 60; } } else timer_running = false; } } else if (current_tab == TAB_STOPWATCH) { if (x >= 20 && x <= 110 && y >= 150 && y <= 180) { - if (sw_running) { sw_elapsed_ticks += (sys_system(16, 0, 0, 0, 0) - sw_start_ticks); sw_running = false; } - else { sw_start_ticks = sys_system(16, 0, 0, 0, 0); sw_running = true; } + if (sw_running) { sw_elapsed_ticks += (sys_ticks_now() - sw_start_ticks); sw_running = false; } + else { sw_start_ticks = sys_ticks_now(); sw_running = true; } } else if (x >= 140 && x <= 230 && y >= 150 && y <= 180) { - sw_elapsed_ticks = 0; sw_start_ticks = sys_system(16, 0, 0, 0, 0); + sw_elapsed_ticks = 0; sw_start_ticks = sys_ticks_now(); } } clock_paint(); @@ -254,7 +258,7 @@ int main(void) { else if (ev.type == GUI_EVENT_CLICK) clock_click(ev.arg1, ev.arg2); else if (ev.type == GUI_EVENT_CLOSE) sys_exit(0); } else { - long long now = sys_system(16, 0, 0, 0, 0); + long long now = sys_ticks_now(); if (now - last_rep >= 6) { clock_paint(); ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H); last_rep = now; } sleep(10); } diff --git a/src/userland/sys/meminfo.c b/src/userland/sys/meminfo.c index 1e11822..fa4f15f 100644 --- a/src/userland/sys/meminfo.c +++ b/src/userland/sys/meminfo.c @@ -7,7 +7,7 @@ int main(int argc, char **argv) { (void)argc; (void)argv; uint64_t mem[2]; - if (sys_system(15, (uint64_t)mem, 0, 0, 0) == 0) { + if (sys_system(SYSTEM_CMD_GET_MEM_INFO, (uint64_t)mem, 0, 0, 0) == 0) { printf("Memory Info:\n"); printf("Total: %d MB\n", (int)(mem[0] / 1024 / 1024)); printf("Used: %d MB\n", (int)(mem[1] / 1024 / 1024)); diff --git a/src/userland/sys/pci_list.c b/src/userland/sys/pci_list.c index faeeabc..1198f60 100644 --- a/src/userland/sys/pci_list.c +++ b/src/userland/sys/pci_list.c @@ -13,7 +13,7 @@ typedef struct { int main(int argc, char **argv) { (void)argc; (void)argv; - int count = sys_system(17, 0, 0, 0, 0); + int count = sys_system(SYSTEM_CMD_PCI_LIST, 0, 0, 0, 0); if (count < 0) { printf("Error: Could not retrieve PCI device count.\n"); return 1; @@ -23,7 +23,7 @@ int main(int argc, char **argv) { printf("---------------------------\n"); for (int i = 0; i < count; i++) { pci_info_t info; - if (sys_system(17, (uint64_t)&info, i, 0, 0) == 0) { + if (sys_system(SYSTEM_CMD_PCI_LIST, (uint64_t)&info, i, 0, 0) == 0) { printf("[%d] Vendor:%04x Device:%04x Class:%02x Sub:%02x\n", i, info.vendor, info.device, info.class_code, info.subclass); } diff --git a/src/userland/sys/reboot.c b/src/userland/sys/reboot.c index 214afd6..edf5eea 100644 --- a/src/userland/sys/reboot.c +++ b/src/userland/sys/reboot.c @@ -7,6 +7,6 @@ int main(int argc, char **argv) { (void)argc; (void)argv; printf("Rebooting...\n"); - sys_system(12, 0, 0, 0, 0); // SYSTEM_CMD_REBOOT + sys_system(SYSTEM_CMD_REBOOT, 0, 0, 0, 0); return 0; } diff --git a/src/userland/sys/shutdown.c b/src/userland/sys/shutdown.c index 8825593..f13bfcd 100644 --- a/src/userland/sys/shutdown.c +++ b/src/userland/sys/shutdown.c @@ -7,6 +7,6 @@ int main(int argc, char **argv) { (void)argc; (void)argv; printf("Shutting down...\n"); - sys_system(13, 0, 0, 0, 0); // SYSTEM_CMD_SHUTDOWN + sys_system(SYSTEM_CMD_SHUTDOWN, 0, 0, 0, 0); return 0; } diff --git a/src/wm/wm.c b/src/wm/wm.c index 5f16fb9..2137981 100644 --- a/src/wm/wm.c +++ b/src/wm/wm.c @@ -246,6 +246,9 @@ static uint32_t timer_ticks = 0; static int last_cursor_x = 400; static int last_cursor_y = 300; +#define CURSOR_W 18 +#define CURSOR_H 18 + static bool periodic_refresh_pending = false; // --- Desktop State --- @@ -1344,27 +1347,38 @@ void draw_window(Window *win) { } } -// Draw Mouse Cursor (Simple Arrow) +// Draw Mouse Cursor (classic outlined arrow) void draw_cursor(int x, int y) { - // 0 = Transparent (skip), 1 = Black, 2 = White - static const uint8_t cursor_bitmap[10][10] = { - {1,1,0,0,0,0,0,0,0,0}, - {1,2,1,0,0,0,0,0,0,0}, - {1,2,2,1,0,0,0,0,0,0}, - {1,2,2,2,1,0,0,0,0,0}, - {1,2,2,2,2,1,0,0,0,0}, - {1,2,2,2,2,2,1,0,0,0}, - {1,2,2,1,1,1,1,0,0,0}, - {1,1,1,0,1,2,1,0,0,0}, - {0,0,0,0,0,1,2,1,0,0}, - {0,0,0,0,0,0,1,0,0,0} + // '.' transparent, 'w' white outline, 'b' black fill + static const char cursor_bitmap[CURSOR_H][CURSOR_W + 1] = { + "w.................", + "ww................", + "wbw...............", + "wbbw..............", + "wbbbw.............", + "wbbbbw............", + "wbbbbbw...........", + "wbbbbbbw..........", + "wbbbbbbbw.........", + "wbbbbbbbbw........", + "wbbbbbbbbw........", + "wbbbbbbbw.........", + "wbbbbbbbw.........", + "wbbbbbbw..........", + "wwwwbbbw..........", + "....wbbw..........", + ".....wbw..........", + "......ww..........." }; - - for (int r = 0; r < 10; r++) { - for (int c = 0; c < 10; c++) { - uint8_t p = cursor_bitmap[r][c]; - if (p == 1) put_pixel(x + c, y + r, COLOR_BLACK); - else if (p == 2) put_pixel(x + c, y + r, COLOR_WHITE); + + for (int r = 0; r < CURSOR_H; r++) { + for (int c = 0; c < CURSOR_W; c++) { + char p = cursor_bitmap[r][c]; + if (p == 'w') { + put_pixel(x + c, y + r, COLOR_WHITE); + } else if (p == 'b') { + put_pixel(x + c, y + r, COLOR_BLACK); + } } } } @@ -1377,8 +1391,8 @@ static void erase_cursor(int x, int y) { // Clamp to screen int x1 = x < 0 ? 0 : x; int y1 = y < 0 ? 0 : y; - int x2 = x + 10 > sw ? sw : x + 10; - int y2 = y + 10 > sh ? sh : y + 10; + int x2 = x + CURSOR_W > sw ? sw : x + CURSOR_W; + int y2 = y + CURSOR_H > sh ? sh : y + CURSOR_H; int w = x2 - x1; int h = y2 - y1; @@ -1750,8 +1764,8 @@ void wm_paint(void) { int sh = get_screen_height(); uint64_t rflags; rflags = wm_lock_acquire(); - wm_mark_dirty(last_cursor_x, last_cursor_y, 12, 12); - wm_mark_dirty(mx, my, 12, 12); + wm_mark_dirty(last_cursor_x, last_cursor_y, CURSOR_W, CURSOR_H); + wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H); DirtyRect dirty = graphics_get_dirty_rect(); if (menubar_dirty_pending) { @@ -2294,8 +2308,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) { if (my >= sh) my = sh - 1; if (move_x != 0 || move_y != 0) { - wm_mark_dirty(prev_mx, prev_my, 12, 12); // Extra padding for safety - wm_mark_dirty(mx, my, 12, 12); + wm_mark_dirty(prev_mx, prev_my, CURSOR_W, CURSOR_H); + wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H); } if (dz != 0) { @@ -2897,8 +2911,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) { if (prev_mx != mx || prev_my != my) { // Cursor moved - just mark dirty cursor areas - wm_mark_dirty(prev_mx, prev_my, 10, 10); - wm_mark_dirty(mx, my, 10, 10); + wm_mark_dirty(prev_mx, prev_my, CURSOR_W, CURSOR_H); + wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H); } prev_left = left;