Compare commits
4 Commits
v0.5.0
...
v0.5.3-pre
| Author | SHA1 | Date | |
|---|---|---|---|
| 888bc5abdd | |||
| 9c103218d0 | |||
| 21270a3cc8 | |||
| 7d32444da2 |
+3
-1
@@ -2,4 +2,6 @@
|
||||
build*
|
||||
.venv
|
||||
initrd/image.cpio
|
||||
initramfs/*
|
||||
initramfs/*
|
||||
|
||||
target
|
||||
+1
-1
@@ -42,7 +42,7 @@ if(MCOPY_EXE AND MKFS_EXE AND CPIO_EXE)
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}
|
||||
COMMAND sh -c "find . -mindepth 1 ! -name '*.cpio' -print0 | ${CPIO_EXE} --null -ov -H newc > \"${INITRAMFS_CPIO_FILE}\""
|
||||
WORKING_DIRECTORY ${INITRAMFS_SRC_DIR}
|
||||
DEPENDS ${INIT_FILES} init
|
||||
DEPENDS ${INIT_FILES} init debug
|
||||
VERBATIM
|
||||
COMMENT "Packing initramfs to cpio..."
|
||||
)
|
||||
|
||||
@@ -4,6 +4,39 @@
|
||||
<img src='assets/logo.png'>
|
||||
</p>
|
||||
|
||||
Just a hobby, won't be big and professional like Linux
|
||||
> "Just a hobby, won't be big and professional like Linux."
|
||||
> — *Linus Torvalds (and now me)*
|
||||
|
||||
Really WIP. And really just a hobby.
|
||||
**termOS** is a 64-bit, UNIX-hating, bespoke operating system written from scratch in C.
|
||||
|
||||
Current Kernel: **Dewar** (v0.5.x)
|
||||
|
||||
## Philosophy
|
||||
|
||||
- **Zero Bloat:** We don't port libraries; we write them.
|
||||
- **Custom Everything:** Why use ELF when you can invent **HOT!**? Why use GRUB when you can write your own bootloader (soon)?
|
||||
|
||||
## Features (v0.5.2)
|
||||
|
||||
- **Architecture:** x86_64 / UEFI.
|
||||
- **Memory Management:** PMM (Bitmap), VMM (PML4 + Higher Half Direct Map), Kernel Heap.
|
||||
- **Multitasking:** Preemptive scheduler with Round Robin.
|
||||
- **Isolation:** Ring 0 (Kernel) / Ring 3 (Userspace) protection.
|
||||
- **Binaries:** Custom **HOT!** executable format (parsed via custom `elf2hot` toolchain).
|
||||
- **Filesystem:** VFS abstraction with CPIO Initramfs support.
|
||||
- **Graphics:** `ShitGUI` (yes, really) linear framebuffer driver.
|
||||
- **Shell:** `ksh` (Kernel Shell) -> transitioning to userspace `ush`
|
||||
|
||||
## 🔥 The HOT! Format
|
||||
|
||||
termOS uses its own executable format called **HOT!**.
|
||||
Because parsing ELF headers is too mainstream. HOT! is a flat, segment-based format designed specifically for the Dewar kernel loader.
|
||||
Made only because ELF is *Not-invented-here*
|
||||
|
||||
## Roadmap
|
||||
|
||||
See [ROADMAP.md](ROADMAP.md) for the detailed plan of world domination (or at least self-hosting).
|
||||
|
||||
## License
|
||||
|
||||
**GPL-3.0-or-later**.
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
# termOS Roadmap
|
||||
|
||||
## Legend
|
||||
- ✅ **Done**: Implemented and working.
|
||||
- 🚧 **WIP**: Currently under active development.
|
||||
- 📅 **Planned**: Scheduled for future releases.
|
||||
- 🤔 **Vision**: Long-term goals for v1.0.
|
||||
|
||||
---
|
||||
|
||||
## Current Status: v0.5.2 (Alpha)
|
||||
*Where we are now.*
|
||||
- **Core:** Ring 0 / Ring 3 isolation implemented.
|
||||
- **Mem:** PMM (bitmap), VMM (PML4, HHDM).
|
||||
- **Exec:** Custom **HOT!** executable format (static loader).
|
||||
- **FS:** VFS + Initramfs (CPIO, read-only).
|
||||
- **User:** Basic jump to userspace (hardcoded stack, no heap yet).
|
||||
|
||||
---
|
||||
|
||||
## 📅 Phase 1
|
||||
*Goal: Transform the decorative userspace into a functional mechanism.*
|
||||
|
||||
### v0.5.3 (Immediate Priority)
|
||||
*Focus: Lifecycle & Memory*
|
||||
- [ ] **Syscall: Memory Management**
|
||||
- Implement `sys_sbrk` or `sys_mem`.
|
||||
- Allow userspace to dynamically allocate pages (User Heap).
|
||||
- [ ] **Syscall: Process Lifecycle**
|
||||
- `sys_spawn(path)`: Load `.hot` files via VFS, create new process structures.
|
||||
- `sys_exit(code)`: Proper termination, ZOMBIE state, resource cleanup in scheduler.
|
||||
- [ ] **Userspace Lib (mini-libc)**
|
||||
- Wrappers: `malloc`, `free`, `exec`, `exit`.
|
||||
- `crt0.asm` for proper `main()` entry.
|
||||
|
||||
### v0.5.4 (The Shell Update)
|
||||
*Focus: Interactive Userspace*
|
||||
- [ ] **Userspace Shell (`ush`)**
|
||||
- Porting `ksh` logic into a standalone `.hot` binary.
|
||||
- Standard I/O abstraction (stdin/stdout) to pass keyboard input to active process.
|
||||
- [ ] **Basic Utils**
|
||||
- `ls`, `cat`, `echo`, `ver` as standalone programs.
|
||||
- Remove built-in commands from the kernel.
|
||||
|
||||
---
|
||||
|
||||
## 📅 Phase 2
|
||||
*Goal: Total independence from external libraries and architectural hygiene.*
|
||||
|
||||
### v0.6.0 (Architecture Overhaul)
|
||||
- [ ] **Bootloader Independence**
|
||||
- Remove `posix-uefi` library.
|
||||
- Write custom UEFI entry point (pure PE).
|
||||
- Kernel itself becomes a valid `.hot` executable (or loaded by custom bootloader).
|
||||
- [ ] **HOT! Format Hardening**
|
||||
- Fix segment alignment (Page Alignment) in `elf2hot` and kernel loader.
|
||||
- Add metadata headers (stack size, permissions).
|
||||
- [ ] **Concurrency Fixes**
|
||||
- Implement spinlocks (`spinlock`).
|
||||
- Protect allocator and process lists from interrupt race conditions.
|
||||
|
||||
---
|
||||
|
||||
## 📅 Phase 3
|
||||
*Goal: Prepare the environment for self-hosting. We cannot compile on a Read-Only FS.*
|
||||
|
||||
### v0.7.0 (Filesystem & Input)
|
||||
- [ ] **Write Support**
|
||||
- Storage driver (NVMe/AHCI or Writable RAMDisk).
|
||||
- Writable Filesystem (FAT32 or custom simple FS).
|
||||
- [ ] **Input Subsystem**
|
||||
- Input buffering, Pipe (`|`) support in shell.
|
||||
- Basic TTY/PTY abstraction.
|
||||
|
||||
### v0.8.0 (The Editor)
|
||||
- [ ] **Text Editor**
|
||||
- Write a simple editor (nano-like) in userspace.
|
||||
- Capabilities: Open, Edit, Save files.
|
||||
|
||||
---
|
||||
|
||||
## 📅 Phase 4
|
||||
*Goal: Self-Hosting.*
|
||||
|
||||
### v0.9.0 (The Toolchain)
|
||||
- [ ] **Linker (`hld`)**
|
||||
- Native linker capable of outputting `.hot` files (running inside termOS).
|
||||
- [ ] **Compiler Port**
|
||||
- Port **TCC (Tiny C Compiler)** to termOS.
|
||||
- Adapt libc headers for the OS environment.
|
||||
|
||||
### 🤔 v1.0.0 (Self-Hosting)
|
||||
- [ ] **Bootstrapping**
|
||||
- Compile the Dewar kernel *inside* termOS.
|
||||
- Boot from the newly compiled kernel.
|
||||
- Total victory.
|
||||
|
||||
---
|
||||
|
||||
## 📜 History
|
||||
|
||||
| Version | Date | Key Features |
|
||||
| :--- | :--- | :--- |
|
||||
| **v0.5.2** | 2026-01-30 | **HOT!** format, Ring 3 isolation, VFS, CPIO parsing. |
|
||||
| **v0.5.1** | 2026-01-29 | Initial userspace jumps, syscall mechanics. |
|
||||
| **v0.5.0** | 2026-01-29 | Kernel officially named "Dewar". Versioning standardized. |
|
||||
| **v0.4.x** | 2026-01-28 | Basic VFS, Shell improvements, basic multitasking. |
|
||||
| **v0.3.x** | 2025-12-30 | Scheduler, ~~Composer~~, Heap manager. |
|
||||
| **v0.2.x** | 2025-12-28 | `ksh`, Keyboard driver, HHDM, PMM/VMM. |
|
||||
| **v0.1.x** | 2025-12-21 | First boot, GDT/IDT, Serial/Console, Panic handler. |
|
||||
@@ -0,0 +1,23 @@
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#define HOT_MAGIC 0x21544F48
|
||||
|
||||
typedef struct hot_segment {
|
||||
u64 type; // 1 = rx 2 = rw
|
||||
u64 vaddr;
|
||||
u64 offset;
|
||||
u64 filesz;
|
||||
u64 memsz;
|
||||
} hot_segment;
|
||||
|
||||
typedef struct hot_header {
|
||||
u32 magic; // "HOT!"
|
||||
u8 version; // 1
|
||||
u8 reserved_pad[3];
|
||||
u64 entry_point;
|
||||
u64 segments_count;
|
||||
u64 reserved;
|
||||
} hot_header;
|
||||
@@ -23,6 +23,7 @@ file(GLOB_RECURSE KERNEL_SOURCES CMAKE_CONFIGURE_DEPENDS
|
||||
"src/mm/*.c"
|
||||
"src/shell/*.c"
|
||||
"src/fs/*.c"
|
||||
"src/syscalls/*.c"
|
||||
|
||||
"data/*.c"
|
||||
)
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
|
||||
typedef enum {
|
||||
SYS_EXIT,
|
||||
SYS_EXEC,
|
||||
SYS_SPAWN,
|
||||
SYS_MEM,
|
||||
SYS_WRITE,
|
||||
SYS_READ,
|
||||
} syscalls;
|
||||
|
||||
void syscall_init();
|
||||
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
#include <core/scheduler.h>
|
||||
|
||||
u64 load_hot(process* proc, u8* data);
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
#include <types.h>
|
||||
|
||||
bool exec_init(const char* path);
|
||||
i32 process_spawn(const char* path, const char* name);
|
||||
void init_task_entry();
|
||||
@@ -3,15 +3,33 @@
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
|
||||
typedef enum process_state {
|
||||
DEAD,
|
||||
RUNNING,
|
||||
} process_state;
|
||||
|
||||
typedef struct process {
|
||||
u64 pid;
|
||||
process_state state;
|
||||
u64 pml4_phys;
|
||||
struct process* parent;
|
||||
char name[32];
|
||||
u64 heap_start;
|
||||
u64 heap_cur;
|
||||
} process;
|
||||
|
||||
typedef struct task {
|
||||
u64 rsp;
|
||||
struct task* next;
|
||||
u32 id;
|
||||
u32 sleep_ticks;
|
||||
process_state task_state; // reusing process_state cuz wn
|
||||
u64 kernel_stack_top;
|
||||
process* proc;
|
||||
} task;
|
||||
|
||||
void sched_init();
|
||||
task* sched_spawn(void(*entry)());
|
||||
task* sched_spawn(void(*entry)(), process* owner, bool is_user, u64 fixed_user_stack);
|
||||
u64 sched_next(u64 curr_rsp);
|
||||
void yield(u64 ticks);
|
||||
@@ -13,6 +13,7 @@ void console_set_color(u32 color);
|
||||
void console_set_default_color(u32 color);
|
||||
void console_set_cursor_pos(SG_Point *p);
|
||||
char console_getc();
|
||||
void console_putc(char c);
|
||||
void kprint(const char *str);
|
||||
void kprintf(const char *fmt, ...);
|
||||
void kgets(char* buff, u32 lim);
|
||||
|
||||
@@ -37,3 +37,7 @@
|
||||
|
||||
void vmm_init(Bootinfo* info);
|
||||
u64* vmm_map_page(u64* pml4, u64 phys, u64 virt, u64 flags);
|
||||
u64 vmm_create_address_space();
|
||||
u64 vmm_get_current_cr3();
|
||||
void load_cr3(u64 pml4_addr);
|
||||
void vmm_setup_user_stack(u64* pml4_phys);
|
||||
@@ -11,4 +11,5 @@ void print_regs();
|
||||
void cmd_sleep();
|
||||
void cmd_debug();
|
||||
void cmd_rand();
|
||||
void cmd_ver();
|
||||
void cmd_ver();
|
||||
void cmd_userspace();
|
||||
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
|
||||
u64 sys_write(u64 fd, u64 buff, u64 len);
|
||||
u64 sys_read(u64 fd, u64 buff, u64 count);
|
||||
@@ -2,5 +2,6 @@
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
|
||||
void jump_to_userspace(void* entry, void* user_stack_top);
|
||||
u64 sys_mem(u64 size);
|
||||
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <types.h>
|
||||
|
||||
u64 sys_exit(i32 code);
|
||||
u64 sys_spawn(const char* path);
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include "types.h"
|
||||
#include <types.h>
|
||||
#include <cpuinfo.h>
|
||||
|
||||
#define MSR_GS_BASE 0xC0000101
|
||||
|
||||
@@ -60,7 +60,6 @@ syscall_entry:
|
||||
pop r11 ; rflags
|
||||
add rsp, 8 ; skip rsp
|
||||
|
||||
mov [gs:8], rsp
|
||||
mov rsp, [gs:0]
|
||||
swapgs
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include <mm/pmm.h>
|
||||
#include <mm/vmm.h>
|
||||
|
||||
#include <syscalls/proc.h>
|
||||
#include <syscalls/mem.h>
|
||||
#include <syscalls/io.h>
|
||||
|
||||
static inline void wrmsr(u32 msr, u64 val) {
|
||||
u32 low = (u32)val;
|
||||
u32 high = (u32)(val >> 32);
|
||||
@@ -47,20 +51,14 @@ void syscall_init() {
|
||||
}
|
||||
|
||||
u64 syscall_dispatch(u64 id, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5) {
|
||||
__asm__ volatile("cli");
|
||||
switch (id) {
|
||||
case SYS_EXIT: {
|
||||
kprintf("\n[Dewar] process exited with code %d", arg1);
|
||||
while(1) __asm__ ("hlt"); // stub
|
||||
return 0;
|
||||
}
|
||||
case SYS_EXEC: {
|
||||
kprintf("\n[Dewar] process called exec syscall");
|
||||
return 0; // stub as well
|
||||
}
|
||||
case SYS_WRITE: {
|
||||
kprintf("%s", (char*)arg1);
|
||||
return 0;
|
||||
}
|
||||
case SYS_EXIT: return sys_exit(arg1);
|
||||
case SYS_SPAWN: return sys_spawn((const char*)arg1);
|
||||
case SYS_MEM: return sys_mem(arg1);
|
||||
case SYS_WRITE: return sys_write(arg1, arg2, arg3);
|
||||
case SYS_READ: return sys_read(arg1, arg2, arg3);
|
||||
default: kprintf("[Dewar] Unknown syscall %d\n", id); return -1;
|
||||
}
|
||||
__asm__ volatile("sti");
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <core/hot.h>
|
||||
#include <core/scheduler.h>
|
||||
#include <core/string.h>
|
||||
|
||||
#include <mm/pmm.h>
|
||||
#include <mm/vmm.h>
|
||||
#include <mm/heap.h>
|
||||
#include <mm/memory.h>
|
||||
|
||||
#include "../../common/hot_header.h"
|
||||
|
||||
u64 load_hot(process* proc, u8* data) {
|
||||
hot_header* header = (hot_header*)data;
|
||||
if (header->magic != HOT_MAGIC) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
hot_segment* segments = (hot_segment*)(data + sizeof(hot_header));
|
||||
for (u64 i = 0; i < header->segments_count; i++) {
|
||||
hot_segment* seg = &segments[i];
|
||||
if (seg->memsz == 0) continue;
|
||||
|
||||
u64 start = seg->vaddr & ~(0xFFFULL);
|
||||
u64 end = (seg->vaddr + seg->memsz + 0xFFF) & ~(0xFFFULL);
|
||||
|
||||
for (u64 addr = start; addr < end; addr += PAGE_SIZE) {
|
||||
void* phys = pmm_alloc_page();
|
||||
vmm_map_page((u64*)proc->pml4_phys, (u64)phys, addr, PTE_USER | PTE_RW | PTE_PRESENT);
|
||||
void* kernel_virt = (void*)((u64)phys + HHDM_OFFSET);
|
||||
memset(kernel_virt, 0, PAGE_SIZE);
|
||||
u64 page_overleap_start = (addr > seg->vaddr) ? addr : seg->vaddr;
|
||||
u64 page_overleap_end = (addr + PAGE_SIZE < seg->vaddr + seg->filesz)
|
||||
? (addr + PAGE_SIZE)
|
||||
: (seg->vaddr + seg->filesz);
|
||||
if (page_overleap_start < page_overleap_end) {
|
||||
u64 copy_size = page_overleap_end - page_overleap_start;
|
||||
u64 src_offset = seg->offset + (page_overleap_start - seg->vaddr);
|
||||
u64 dst_offset = page_overleap_start - addr;
|
||||
|
||||
memcpy((u8*)kernel_virt + dst_offset, data + src_offset, copy_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return header->entry_point;
|
||||
}
|
||||
+38
-53
@@ -1,14 +1,17 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <core/panic.h>
|
||||
#include <core/hot.h>
|
||||
#include <core/loader.h>
|
||||
#include <core/userspace.h>
|
||||
#include <core/scheduler.h>
|
||||
#include <core/string.h>
|
||||
|
||||
#include <shell/ksh.h>
|
||||
|
||||
#include <mm/pmm.h>
|
||||
#include <mm/vmm.h>
|
||||
#include <mm/heap.h>
|
||||
#include <mm/memory.h>
|
||||
|
||||
#include <fs/vfs.h>
|
||||
@@ -16,67 +19,49 @@
|
||||
#include <drivers/console.h>
|
||||
#include <types.h>
|
||||
|
||||
extern u64* pml4_kernel;
|
||||
extern task* curr_task;
|
||||
extern u32 next_pid;
|
||||
|
||||
bool exec_init(const char* path) {
|
||||
kprintf("[Loader] loading %s...\n", path);
|
||||
#define USER_STACK_TOP 0x70000000
|
||||
#define HEAP_START 0x40000000
|
||||
|
||||
i32 process_spawn(const char* path, const char* name) {
|
||||
fs_node* file = vfs_open(path);
|
||||
if (!file) {
|
||||
kprintf("[Loader] Error: %s not found in initramfs!\n", path);
|
||||
return false;
|
||||
if (!file) return -1;
|
||||
|
||||
process* new_proc = (process*)malloc(sizeof(process));
|
||||
if (!new_proc) return -2;
|
||||
memset(new_proc, 0, sizeof(process));
|
||||
new_proc->pid = next_pid++;
|
||||
new_proc->state = RUNNING;
|
||||
new_proc->pml4_phys = vmm_create_address_space();
|
||||
new_proc->heap_start = HEAP_START;
|
||||
new_proc->heap_cur = HEAP_START;
|
||||
strncpy(new_proc->name, name, 31);
|
||||
|
||||
u8* file_buffer = (u8*)malloc(file->len);
|
||||
if (!file_buffer) {
|
||||
free(new_proc);
|
||||
return -3;
|
||||
}
|
||||
vfs_read(file, 0, file->len, file_buffer);
|
||||
|
||||
u64 virt_code = 0x400000;
|
||||
u64 virt_stack = 0x800000;
|
||||
u64 stack_size = 8192;
|
||||
u64 entry = load_hot(new_proc, file_buffer);
|
||||
if (!entry) return -4;
|
||||
|
||||
u64 bytes_left = file->len;
|
||||
u64 offset = 0;
|
||||
u64 page_idx = 0;
|
||||
free(file_buffer);
|
||||
|
||||
while (bytes_left > 0) {
|
||||
void* phys = pmm_alloc_page();
|
||||
if (!phys) {
|
||||
kprintf("Loader: OOM!\n");
|
||||
return false;
|
||||
}
|
||||
vmm_setup_user_stack((u64*)new_proc->pml4_phys);
|
||||
sched_spawn((void(*)())entry, new_proc, true, USER_STACK_TOP);
|
||||
|
||||
vmm_map_page(pml4_kernel, (u64)phys, virt_code + (page_idx * 4096), PTE_PRESENT | PTE_RW | PTE_USER);
|
||||
|
||||
void* k_ptr = (void*)(HHDM_OFFSET + (u64)phys);
|
||||
memset(k_ptr, 0, 4096);
|
||||
|
||||
u64 chunk = (bytes_left > 4096) ? 4096 : bytes_left;
|
||||
vfs_read(file, offset, chunk, (u8*)k_ptr);
|
||||
|
||||
bytes_left -= chunk;
|
||||
offset += chunk;
|
||||
page_idx++;
|
||||
}
|
||||
|
||||
for (u64 i = 0; i < (stack_size / 4096); i++) {
|
||||
void* phys = pmm_alloc_page();
|
||||
vmm_map_page(pml4_kernel, (u64)phys, virt_stack + (i * 4096), PTE_PRESENT | PTE_RW | PTE_USER);
|
||||
memset((void*)(HHDM_OFFSET + (u64)phys), 0, 4096);
|
||||
}
|
||||
|
||||
__asm__ volatile(
|
||||
"mov %%cr3, %%rax\n\t"
|
||||
"mov %%rax, %%cr3\n\t"
|
||||
::: "rax", "memory"
|
||||
);
|
||||
|
||||
kprintf("[Loader] Transferring control to userspace...\n");
|
||||
jump_to_userspace((void*)virt_code, (void*)(virt_stack + stack_size));
|
||||
|
||||
return true; // unreachable
|
||||
return new_proc->pid;
|
||||
}
|
||||
|
||||
void init_task_entry() {
|
||||
if (!exec_init("/init")) {
|
||||
kprintf("FATAL: Could not load /init\n");
|
||||
sched_spawn(ksh);
|
||||
|
||||
while(1) __asm__("hlt");
|
||||
i32 pid = process_spawn("/init", "init");
|
||||
if (pid < 0) {
|
||||
panic("FATAL: Failed to spawn /init");
|
||||
}
|
||||
|
||||
while (1) { __asm__("sti; hlt"); }
|
||||
}
|
||||
@@ -32,7 +32,6 @@ const char* fun_messages[] = {
|
||||
"Code have been eaten by Aliens",
|
||||
"That's all, folks!",
|
||||
"Raiden, answer me, Raiden, respond! Raiden?! RAIDEEEEEEEEEN!",
|
||||
"Fatal error has been occurred.\n\t\t\t\t Your device will be terminated in 30 seconds.",
|
||||
"I'll be back",
|
||||
"Hastla la vista, baby",
|
||||
"Ti chego mne tut nagovoril...",
|
||||
|
||||
+51
-13
@@ -3,7 +3,10 @@
|
||||
|
||||
#include <core/panic.h>
|
||||
#include <core/scheduler.h>
|
||||
#include <core/string.h>
|
||||
#include <mm/heap.h>
|
||||
#include <mm/vmm.h>
|
||||
#include <mm/memory.h>
|
||||
#include <cpuinfo.h>
|
||||
#include <gdt.h>
|
||||
|
||||
@@ -11,30 +14,55 @@ task* curr_task = nullptr;
|
||||
u32 next_pid = 1;
|
||||
|
||||
extern void irq0_handler();
|
||||
extern u64 pml4_kernel_phys;
|
||||
|
||||
void sched_init() {
|
||||
task* kt = (task*)malloc(sizeof(task));
|
||||
static process kernel_process;
|
||||
|
||||
kt->id = 0;
|
||||
kt->sleep_ticks = 0;
|
||||
kt->next = kt;
|
||||
|
||||
curr_task = kt;
|
||||
void idle_task() {
|
||||
while (1) {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
task* sched_spawn(void(*entry)()) {
|
||||
void sched_init() {
|
||||
kernel_process.pid = 0;
|
||||
kernel_process.state = RUNNING;
|
||||
kernel_process.pml4_phys = pml4_kernel_phys;
|
||||
strcpy(kernel_process.name, "kernel");
|
||||
|
||||
task* kt = (task*)malloc(sizeof(task));
|
||||
memset(kt, 0, sizeof(task));
|
||||
kt->id = 0;
|
||||
kt->proc = &kernel_process;
|
||||
kt->sleep_ticks = 0;
|
||||
kt->next = kt;
|
||||
kt->task_state = RUNNING;
|
||||
|
||||
curr_task = kt;
|
||||
sched_spawn(idle_task, &kernel_process, false, 0);
|
||||
}
|
||||
|
||||
task* sched_spawn(void(*entry)(), process* owner, bool is_user, u64 fixed_user_stack) {
|
||||
task* t = (task*)malloc(sizeof(task));
|
||||
if (!t) return nullptr;
|
||||
|
||||
if (!owner) owner = &kernel_process;
|
||||
|
||||
u64 stack_size = 16384;
|
||||
u8* stack_base = (u8*)malloc(stack_size);
|
||||
if (!stack_base) panic("OOM for task stack");
|
||||
u64* rsp = (u64*)(stack_base + stack_size);
|
||||
|
||||
*--rsp = 0x10; // SS -- Kernel data
|
||||
*--rsp = (u64)stack_base + stack_size; // rsp
|
||||
*--rsp = 0x202; // RFLAGS -- Interrupts Enabled | Reserved bit;
|
||||
*--rsp = 0x08; // CS -- Kernel Code;
|
||||
u64 cs = is_user ? 0x23 : 0x08;
|
||||
u64 ss = is_user ? 0x1b : 0x10;
|
||||
u64 rflags = 0x202;
|
||||
u64 target_rsp = 0;
|
||||
if (is_user) target_rsp = fixed_user_stack;
|
||||
else target_rsp = (u64)stack_base + stack_size;
|
||||
|
||||
*--rsp = ss; // SS -- Kernel data
|
||||
*--rsp = target_rsp; // rsp
|
||||
*--rsp = rflags; // RFLAGS -- Interrupts Enabled | Reserved bit;
|
||||
*--rsp = cs; // CS -- Kernel Code;
|
||||
*--rsp = (u64)entry; // RIP
|
||||
|
||||
*--rsp = 0; // int no
|
||||
@@ -43,10 +71,12 @@ task* sched_spawn(void(*entry)()) {
|
||||
for (u8 i = 0; i < 15; i++) *--rsp = 0; // R15 .. RAX
|
||||
|
||||
t->rsp = (u64)rsp;
|
||||
t->proc = owner;
|
||||
t->id = next_pid++;
|
||||
t->sleep_ticks = 0;
|
||||
t->next = curr_task->next;
|
||||
t->kernel_stack_top = (u64)stack_base + stack_size;
|
||||
t->task_state = RUNNING;
|
||||
curr_task->next = t;
|
||||
return t;
|
||||
}
|
||||
@@ -65,8 +95,16 @@ u64 sched_next(u64 curr_rsp) {
|
||||
if (curr_task->sleep_ticks > 0) curr_task->sleep_ticks--;
|
||||
|
||||
task* next = curr_task->next;
|
||||
while (next->task_state == DEAD) {
|
||||
// TODO: add gc here;
|
||||
next = next->next;
|
||||
if (next == curr_task) panic("no alive tasks");
|
||||
}
|
||||
|
||||
while (next != curr_task && next->sleep_ticks > 0) next = next->next; // what the fuck i just wrote
|
||||
|
||||
if (next->proc->pml4_phys != curr_task->proc->pml4_phys) load_cr3(next->proc->pml4_phys);
|
||||
|
||||
curr_task = next;
|
||||
tss.rsp0 = curr_task->kernel_stack_top;
|
||||
g_cpu.kernel_rsp = curr_task->kernel_stack_top;
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <core/userspace.h>
|
||||
#include <types.h>
|
||||
|
||||
#define USER_DS (0x18 | 3)
|
||||
#define USER_CS (0x20 | 3)
|
||||
#define RFLAGS_IF 0x202
|
||||
|
||||
void jump_to_userspace(void* entry, void* user_stack_top) {
|
||||
__asm__ volatile (
|
||||
"mov %0, %%ds\n"
|
||||
"mov %0, %%es\n"
|
||||
"mov %0, %%fs\n"
|
||||
:: "r" ((u64)USER_DS) : "memory"
|
||||
);
|
||||
|
||||
__asm__ volatile (
|
||||
"pushq %0\n" // SS (User Data Selector)
|
||||
"pushq %1\n" // RSP (User Stack Pointer)
|
||||
"pushq %2\n" // RFLAGS
|
||||
"pushq %3\n" // CS (User Code Selector)
|
||||
"pushq %4\n" // RIP (Entry Point)
|
||||
"iretq\n"
|
||||
:
|
||||
: "r" ((u64)USER_DS),
|
||||
"r" ((u64)user_stack_top),
|
||||
"r" ((u64)RFLAGS_IF),
|
||||
"r" ((u64)USER_CS),
|
||||
"r" ((u64)entry)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
@@ -80,7 +80,7 @@ void console_set_cursor_pos(SG_Point *p) {
|
||||
s_cursor_pos.y = p->y;
|
||||
}
|
||||
|
||||
static void console_putc(char c) {
|
||||
void console_putc(char c) {
|
||||
serial_writec(c);
|
||||
if (!ctx_ptr) return;
|
||||
if (c == '\n') {
|
||||
|
||||
+2
-2
@@ -101,8 +101,8 @@ void kmain(Bootinfo* info) {
|
||||
c = console_getc();
|
||||
if (c != '\n') staying_in_ksh = true;
|
||||
|
||||
if (staying_in_ksh) sched_spawn(ksh);
|
||||
else sched_spawn(init_task_entry);
|
||||
if (staying_in_ksh) sched_spawn(ksh, nullptr, false, 0);
|
||||
else sched_spawn(init_task_entry, nullptr, false, 0);
|
||||
|
||||
__asm__ volatile("sti");
|
||||
|
||||
|
||||
+56
-13
@@ -4,13 +4,19 @@
|
||||
#include <mm/pmm.h>
|
||||
#include <mm/memory.h>
|
||||
|
||||
#include <core/panic.h>
|
||||
|
||||
#include <gdt.h>
|
||||
#include <idt.h>
|
||||
|
||||
#include <types.h>
|
||||
#include "bootinfo.h"
|
||||
|
||||
#define USER_STACK_TOP 0x70000000
|
||||
#define USER_STACK_SIZE 0x4000
|
||||
|
||||
u64* pml4_kernel = nullptr;
|
||||
u64 pml4_kernel_phys = 0;
|
||||
static bool is_initialized = false;
|
||||
|
||||
extern u64 _kernel_start;
|
||||
@@ -89,26 +95,30 @@ void vmm_unmap_page(u64* pml4, u64 virt) {
|
||||
u64 pdpt_idx = VMM_PDPT_INDEX(virt);
|
||||
u64 pml4_idx = VMM_PML4_INDEX(virt);
|
||||
|
||||
if (!(pml4[pml4_idx] & PTE_PRESENT)) return;
|
||||
u64* pdpt = get_table_virt(PTE_GET_ADDR(pml4[pml4_idx]));
|
||||
|
||||
if (!(pdpt[pdpt_idx] & PTE_PRESENT)) return;
|
||||
u64* pd = get_table_virt(PTE_GET_ADDR(pdpt[pdpt_idx]));
|
||||
|
||||
if (!(pd[pd_idx] & PTE_PRESENT)) return;
|
||||
u64* pt = get_table_virt(PTE_GET_ADDR(pd[pd_idx]));
|
||||
u64* pml4_virt = pml4;
|
||||
if (is_initialized) pml4_virt = (u64*)PHYS_TO_HHDM((u64)pml4);
|
||||
|
||||
pt[pt_idx] = 0;
|
||||
if (!(pml4_virt[pml4_idx] & PTE_PRESENT)) return;
|
||||
u64* pdpt_virt = get_table_virt(PTE_GET_ADDR(pml4_virt[pml4_idx]));
|
||||
|
||||
if (!(pdpt_virt[pdpt_idx] & PTE_PRESENT)) return;
|
||||
u64* pd_virt = get_table_virt(PTE_GET_ADDR(pdpt_virt[pdpt_idx]));
|
||||
|
||||
if (!(pd_virt[pd_idx] & PTE_PRESENT)) return;
|
||||
u64* pt_virt = get_table_virt(PTE_GET_ADDR(pd_virt[pd_idx]));
|
||||
|
||||
pt_virt[pt_idx] = 0;
|
||||
|
||||
__asm__ volatile("invlpg (%0)" :: "r" (virt) : "memory");
|
||||
}
|
||||
|
||||
static inline void load_cr3(u64 pml4_addr) {
|
||||
void load_cr3(u64 pml4_addr) {
|
||||
__asm__ volatile ("mov %0, %%cr3" :: "r"(pml4_addr) : "memory");
|
||||
}
|
||||
|
||||
void vmm_init(Bootinfo* info) {
|
||||
pml4_kernel = pmm_alloc_page();
|
||||
pml4_kernel_phys = (u64)pmm_alloc_page();
|
||||
pml4_kernel = (u64*)pml4_kernel_phys;
|
||||
memset(pml4_kernel, 0, PAGE_SIZE);
|
||||
|
||||
u64 k_virt_start = (u64)&_kernel_start;
|
||||
@@ -126,6 +136,39 @@ void vmm_init(Bootinfo* info) {
|
||||
|
||||
bitmap = (u8*)PHYS_TO_HHDM((u64)bitmap);
|
||||
info->framebuffer.base = (u32*)FB_VIRT_BASE;
|
||||
load_cr3((u64)pml4_kernel);
|
||||
load_cr3(pml4_kernel_phys);
|
||||
is_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
u64 vmm_create_address_space() {
|
||||
u64 phys = (u64)pmm_alloc_page();
|
||||
if (!phys) return 0;
|
||||
|
||||
u64* virt = (u64*)PHYS_TO_HHDM(phys);
|
||||
memset(virt, 0, PAGE_SIZE);
|
||||
|
||||
u64* kernel_pml4_virt = get_table_virt((u64)pml4_kernel);
|
||||
|
||||
for (u32 i = 256; i < 512; i++) {
|
||||
virt[i] = kernel_pml4_virt[i];
|
||||
}
|
||||
|
||||
return phys;
|
||||
}
|
||||
|
||||
u64 vmm_get_current_cr3() {
|
||||
u64 cr3;
|
||||
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
|
||||
return cr3;
|
||||
}
|
||||
|
||||
|
||||
void vmm_setup_user_stack(u64* pml4_phys) {
|
||||
u64 stack_bottom = USER_STACK_TOP - USER_STACK_SIZE;
|
||||
for (u64 addr = stack_bottom; addr < USER_STACK_TOP; addr += 4096) {
|
||||
void* phys = pmm_alloc_page();
|
||||
if (!phys) panic("OOM in user stack setup");
|
||||
memset((void*)PHYS_TO_HHDM((u64)phys), 0, 4096);
|
||||
vmm_map_page((u64*)pml4_phys, (u64)phys, addr, PTE_PRESENT | PTE_RW | PTE_USER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,19 @@
|
||||
#include <drivers/timer.h>
|
||||
|
||||
#include <core/rand.h>
|
||||
#include <core/loader.h>
|
||||
#include <core/scheduler.h>
|
||||
#include <core/string.h>
|
||||
|
||||
#include <mm/vmm.h>
|
||||
#include <mm/heap.h>
|
||||
|
||||
#include <shell/dbgcmd.h>
|
||||
|
||||
#include "../data/cats.h"
|
||||
|
||||
extern task* curr_task;
|
||||
|
||||
const char* ascii_logo[] = {
|
||||
" /\\___/\\ ",
|
||||
" | > < | ",
|
||||
@@ -48,7 +57,6 @@ void cmd_meow() {
|
||||
void cmd_help() {
|
||||
kprintf("Welcome to ^ptermOS^!'s ^gk^!ernel ^gsh^!ell!\n");
|
||||
kprintf("It loads when userspace is failed to load and acts as a basic rescue environment\n");
|
||||
kprintf("At this moment i dont have userspace so it loads always\n");
|
||||
kprintf("Available commands:\n");
|
||||
|
||||
kprintf("\t^rDebug^!:\n");
|
||||
@@ -58,6 +66,7 @@ void cmd_help() {
|
||||
kprintf("\t\t^ypanic^! \t\tPanics (lol)\n");
|
||||
kprintf("\t\t^yud2^! \t\tPanics with #UD\n");
|
||||
kprintf("\t\t^ypf^! \t\tPanics with #PF\n");
|
||||
kprintf("\t\t^yuserspace^! \t\tAttempt to jump in ring 3\n");
|
||||
|
||||
kprintf("\t^pFun^!:\n");
|
||||
kprintf("\t\t^ysplash^! \t\tShows splash (works kinda unstable)\n");
|
||||
@@ -112,4 +121,8 @@ void cmd_ver() {
|
||||
kprintf("termOS version %s\n", TERMOS_VERSION);
|
||||
kprintf("Dewar Kernel (x86_64), build: %s %s\n", __DATE__, __TIME__);
|
||||
kprintf("License: GPL-3.0-or-later\n");
|
||||
}
|
||||
|
||||
void cmd_userspace() {
|
||||
sched_spawn(init_task_entry, nullptr, false, 0);
|
||||
}
|
||||
@@ -33,6 +33,7 @@ typedef enum {
|
||||
TOKEN_PANIC,
|
||||
TOKEN_PANIC_UD2,
|
||||
TOKEN_PANIC_PF,
|
||||
TOKEN_USERSPACE,
|
||||
|
||||
TOKEN_CLEAR,
|
||||
TOKEN_BLINKING,
|
||||
@@ -61,6 +62,7 @@ static const ksh_command_map token_map[] = {
|
||||
{"panic", TOKEN_PANIC},
|
||||
{"ud2", TOKEN_PANIC_UD2},
|
||||
{"pf", TOKEN_PANIC_PF},
|
||||
{"userspace", TOKEN_USERSPACE},
|
||||
|
||||
// fun
|
||||
{"meow", TOKEN_MEOW},
|
||||
@@ -84,7 +86,7 @@ ksh_token char2token(char* token) {
|
||||
}
|
||||
|
||||
void ksh() {
|
||||
sched_spawn(cursor_blinker_sched_task);
|
||||
sched_spawn(cursor_blinker_sched_task, nullptr, false, 0);
|
||||
while (true) {
|
||||
kprintf("ksh_> ");
|
||||
char cmdbuff[256];
|
||||
@@ -103,7 +105,8 @@ void ksh() {
|
||||
case TOKEN_PANIC: panic("Manually initiated panic");
|
||||
case TOKEN_PANIC_UD2: __asm__ volatile ("ud2");
|
||||
case TOKEN_PANIC_PF: u64* bad_ptr = (u64*)0xDEADBEEF; *bad_ptr = 666;
|
||||
|
||||
case TOKEN_USERSPACE: cmd_userspace(); break;
|
||||
|
||||
case TOKEN_SPLASH: show_splash(console_get_context()); break;
|
||||
case TOKEN_KFETCH: cmd_kfetch(); break;
|
||||
case TOKEN_MEOW: cmd_meow(); break;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <syscalls/io.h>
|
||||
#include <drivers/console.h>
|
||||
|
||||
u64 sys_write(u64 fd, u64 buff, u64 len) {
|
||||
if (fd == 1 || fd == 2) {
|
||||
char* str = (char*)buff;
|
||||
for (u64 i = 0; i < len; i++) {
|
||||
console_putc(str[i]);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 sys_read(u64 fd, u64 buff, u64 count) {
|
||||
char* buf = (char*)buff;
|
||||
if (fd == 0) {
|
||||
for (u64 i = 0; i < count; i++) {
|
||||
buf[i] = console_getc();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <syscalls/mem.h>
|
||||
|
||||
#include <core/scheduler.h>
|
||||
|
||||
#include <mm/pmm.h>
|
||||
#include <mm/vmm.h>
|
||||
#include <mm/memory.h>
|
||||
|
||||
extern task* curr_task;
|
||||
|
||||
u64 sys_mem(u64 size) {
|
||||
if (size == 0) return 0;
|
||||
process* proc = curr_task->proc;
|
||||
u64 addr_to_ret = proc->heap_cur;
|
||||
|
||||
u64 pages_needed = (size + (PAGE_SIZE-1)) / PAGE_SIZE;
|
||||
|
||||
for (u64 i = 0; i < pages_needed; i++) {
|
||||
void* phys = pmm_alloc_page();
|
||||
if (!phys) return 0;
|
||||
|
||||
vmm_map_page((u64*)proc->pml4_phys, (u64)phys, proc->heap_cur, PTE_PRESENT | PTE_RW | PTE_USER);
|
||||
memset((void*)PHYS_TO_HHDM((u64)phys), 0, PAGE_SIZE);
|
||||
|
||||
proc->heap_cur += 4096;
|
||||
}
|
||||
|
||||
return addr_to_ret;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <syscalls/proc.h>
|
||||
#include <core/scheduler.h>
|
||||
#include <drivers/console.h>
|
||||
#include <core/loader.h>
|
||||
|
||||
extern task* curr_task;
|
||||
|
||||
u64 sys_exit(i32 code) {
|
||||
kprintf("\n[Dewar] process %s exited with code %d", curr_task->proc->name, code);
|
||||
curr_task->task_state = DEAD;
|
||||
sched_next(curr_task->rsp);
|
||||
while (1) { __asm__ ("hlt"); }
|
||||
}
|
||||
|
||||
|
||||
u64 sys_spawn(const char* path) {
|
||||
return process_spawn(path, path);
|
||||
}
|
||||
|
||||
Generated
+88
@@ -0,0 +1,88 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "elf2hot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"goblin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "goblin"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4db6758c546e6f81f265638c980e5e84dfbda80cfd8e89e02f83454c8e8124bd"
|
||||
dependencies = [
|
||||
"log",
|
||||
"plain",
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add"
|
||||
dependencies = [
|
||||
"scroll_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll_derive"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed76efe62313ab6610570951494bdaa81568026e0318eaa55f167de70eeea67d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "elf2hot"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
goblin = "0.10.4"
|
||||
@@ -0,0 +1,98 @@
|
||||
use goblin::elf::program_header::PT_LOAD;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Default, Debug)]
|
||||
struct HotHeader {
|
||||
magic: u32,
|
||||
version: u8,
|
||||
reserved_pad: [u8; 3],
|
||||
entry_point: u64,
|
||||
segments_count: u64,
|
||||
reserved: u64,
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
struct HotSegment {
|
||||
stype: u64,
|
||||
vaddr: u64,
|
||||
offset: u64,
|
||||
filesz: u64,
|
||||
memsz: u64,
|
||||
}
|
||||
|
||||
const HOT_MAGIC: u32 = 0x21544F48; // "HOT!"
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
println!("Usage: elf2hot <input_elf> <output_hot>");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let input_path = &args[1];
|
||||
let output_path = &args[2];
|
||||
|
||||
let mut f = File::open(input_path)?;
|
||||
let mut buffer = Vec::new();
|
||||
f.read_to_end(&mut buffer)?;
|
||||
|
||||
let elf = goblin::elf::Elf::parse(&buffer)?;
|
||||
|
||||
let mut hot_segments = Vec::new();
|
||||
let mut segment_data = Vec::new();
|
||||
|
||||
let mut current_offset = std::mem::size_of::<HotHeader>() as u64;
|
||||
|
||||
let load_segments: Vec<_> = elf.program_headers.iter()
|
||||
.filter(|ph| ph.p_type == PT_LOAD)
|
||||
.collect();
|
||||
|
||||
current_offset += (load_segments.len() * std::mem::size_of::<HotSegment>()) as u64;
|
||||
|
||||
for ph in load_segments {
|
||||
let is_code = ph.is_executable();
|
||||
|
||||
let data = &buffer[ph.p_offset as usize..(ph.p_offset + ph.p_filesz) as usize];
|
||||
segment_data.push(data);
|
||||
|
||||
hot_segments.push(HotSegment {
|
||||
stype: if is_code { 1 } else { 2 },
|
||||
vaddr: ph.p_vaddr,
|
||||
offset: current_offset,
|
||||
filesz: ph.p_filesz,
|
||||
memsz: ph.p_memsz,
|
||||
});
|
||||
|
||||
current_offset += ph.p_filesz;
|
||||
}
|
||||
|
||||
let header = HotHeader {
|
||||
magic: HOT_MAGIC,
|
||||
version: 1,
|
||||
entry_point: elf.entry,
|
||||
segments_count: hot_segments.len() as u64,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut out = File::create(output_path)?;
|
||||
|
||||
let header_bytes: [u8; std::mem::size_of::<HotHeader>()] = unsafe { std::mem::transmute(header) };
|
||||
out.write_all(&header_bytes)?;
|
||||
|
||||
for seg in hot_segments {
|
||||
let seg_bytes: [u8; std::mem::size_of::<HotSegment>()] = unsafe { std::mem::transmute(seg) };
|
||||
out.write_all(&seg_bytes)?;
|
||||
}
|
||||
|
||||
for data in segment_data {
|
||||
out.write_all(data)?;
|
||||
}
|
||||
|
||||
println!("Successfully converted {} to {}!", input_path, output_path);
|
||||
println!("Entry point: 0x{:X}", elf.entry);
|
||||
Ok(())
|
||||
}
|
||||
@@ -7,4 +7,6 @@ set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
message(STATUS "Building termOS's userspace")
|
||||
|
||||
add_subdirectory(init)
|
||||
add_subdirectory(libterm)
|
||||
add_subdirectory(init)
|
||||
add_subdirectory(debug)
|
||||
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(termOSdbg LANGUAGES C)
|
||||
|
||||
file(GLOB_RECURSE debug_SOURCES "src/*.c")
|
||||
|
||||
add_termos_executable(debug "${debug_SOURCES}")
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#define VECTOR_BUFFER_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
i32* data;
|
||||
u64 size;
|
||||
u64 capacity;
|
||||
} Vector;
|
||||
|
||||
void vector_init(Vector* vec) {
|
||||
vec->data = nullptr;
|
||||
vec->size = 0;
|
||||
vec->capacity = 0;
|
||||
}
|
||||
|
||||
u8 vector_append(Vector* vec, const i32 num) {
|
||||
if (vec->size >= vec->capacity) {
|
||||
u64 new_cap = (vec->capacity == 0) ? VECTOR_BUFFER_SIZE : vec->capacity * 2;
|
||||
|
||||
i32* new_data = realloc(vec->data, new_cap * sizeof(i32));
|
||||
if (!new_data) return 2; // Out of memory
|
||||
|
||||
vec->data = new_data;
|
||||
vec->capacity = new_cap;
|
||||
}
|
||||
vec->data[vec->size] = num;
|
||||
vec->size++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vector_reset(Vector* vec) {
|
||||
vec->size = 0;
|
||||
}
|
||||
|
||||
u8 vector_reserve(Vector* vec, u64 new_cap) {
|
||||
if (new_cap <= vec->capacity) return 0;
|
||||
i32* new_data = realloc(vec->data, new_cap * sizeof(i32));
|
||||
if (!new_data) return 2;
|
||||
|
||||
vec->data = new_data;
|
||||
vec->capacity = new_cap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vector_free(Vector* vec) {
|
||||
free(vec->data);
|
||||
vec->data = nullptr;
|
||||
vec->size = vec->capacity = 0;
|
||||
}
|
||||
|
||||
void render_list(Vector* numbers) {
|
||||
printf("\nNumbers [%d/%d]: [", numbers->size, numbers->capacity);
|
||||
if (numbers->size == 0) printf("]");
|
||||
for (u64 i = 0; i < numbers->size; i++) {
|
||||
printf("%d", numbers->data[i]);
|
||||
if (i + 1 >= numbers->size) {
|
||||
printf("]");
|
||||
break;
|
||||
}
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
Vector nums;
|
||||
vector_init(&nums);
|
||||
printf("Heap test\n");
|
||||
printf("press any key to add 1 number press 'q' to exit\n");
|
||||
char c = 'd';
|
||||
u64 i = 0;
|
||||
while (c != 'q') {
|
||||
render_list(&nums);
|
||||
vector_append(&nums, i);
|
||||
i++;
|
||||
c = getchar();
|
||||
}
|
||||
vector_free(&nums);
|
||||
}
|
||||
@@ -1,32 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(termOSinit LANGUAGES C ASM_NASM)
|
||||
project(termOSinit LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
file(GLOB_RECURSE INIT_SOURCES "src/*.c")
|
||||
|
||||
message(STATUS "Building termOS's init")
|
||||
|
||||
file(GLOB_RECURSE INIT_SOURCES CMAKE_CONFIGURE_DEPENDS
|
||||
"src/*.asm"
|
||||
"src/*.c"
|
||||
)
|
||||
|
||||
add_executable(init ${INIT_SOURCES})
|
||||
set_target_properties(init PROPERTIES
|
||||
SUFFIX ""
|
||||
LINKER_LANGUAGE C
|
||||
)
|
||||
|
||||
target_link_options(init PRIVATE
|
||||
-nostdlib
|
||||
-static
|
||||
-Wl,--oformat=binary
|
||||
-Wl,-Ttext=0x400000
|
||||
-Wl,-e,start
|
||||
)
|
||||
|
||||
add_custom_command(TARGET init POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:init> ${CMAKE_SOURCE_DIR}/initramfs/init
|
||||
COMMENT "Installing init binary to initramfs"
|
||||
)
|
||||
add_termos_executable(init "${INIT_SOURCES}")
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
[BITS 64]
|
||||
|
||||
start:
|
||||
mov rax, 2 ; SYS_WRITE
|
||||
mov rdi, msg_hello ; Адрес строки
|
||||
syscall
|
||||
|
||||
mov rax, 1 ; SYS_EXEC
|
||||
syscall
|
||||
|
||||
mov rax, 0 ; SYS_EXIT
|
||||
mov rdi, 1337
|
||||
syscall
|
||||
|
||||
jmp $
|
||||
|
||||
msg_hello: db "Hello from init!", 0xA, 0
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <process.h>
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
|
||||
int main() {
|
||||
printf("Launching debug\n");
|
||||
spawn("debug");
|
||||
while (1) {}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(libterm LANGUAGES C ASM_NASM)
|
||||
|
||||
set(USER_C_FLAGS
|
||||
-ffreestanding
|
||||
-nostdlib
|
||||
-nostdinc
|
||||
-fno-stack-protector
|
||||
-fno-pic
|
||||
-fno-pie
|
||||
-m64
|
||||
-mno-red-zone
|
||||
-mcmodel=small
|
||||
-Wall
|
||||
-Wextra
|
||||
-Werror
|
||||
-O2
|
||||
)
|
||||
|
||||
set(CMAKE_ASM_NASM_FLAGS "-f elf64")
|
||||
|
||||
set(LIBTERM_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls.asm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/stdio.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/malloc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/string.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ctype.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/process.c
|
||||
)
|
||||
|
||||
add_library(term STATIC ${LIBTERM_SOURCES})
|
||||
|
||||
target_compile_options(term PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
|
||||
|
||||
target_include_directories(term PUBLIC inc)
|
||||
|
||||
add_library(crt0_obj OBJECT src/crt0.asm)
|
||||
target_compile_options(crt0_obj PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
|
||||
|
||||
function(add_termos_executable NAME SOURCES)
|
||||
add_executable(${NAME} ${SOURCES})
|
||||
|
||||
target_sources(${NAME} PRIVATE $<TARGET_OBJECTS:crt0_obj>)
|
||||
|
||||
target_compile_options(${NAME} PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
|
||||
target_link_libraries(${NAME} PRIVATE term)
|
||||
|
||||
target_link_options(${NAME} PRIVATE
|
||||
-T ${libterm_SOURCE_DIR}/linker.ld
|
||||
-nostdlib
|
||||
-static
|
||||
)
|
||||
|
||||
set(ELF2HOT_DIR "${CMAKE_SOURCE_DIR}/tools/elf2hot")
|
||||
set(FINAL_HOT_PATH "${CMAKE_SOURCE_DIR}/initramfs/${NAME}")
|
||||
|
||||
add_custom_command(TARGET ${NAME} POST_BUILD
|
||||
COMMAND cargo run --release --quiet -- $<TARGET_FILE:${NAME}> ${FINAL_HOT_PATH}
|
||||
WORKING_DIRECTORY ${ELF2HOT_DIR}
|
||||
COMMENT "elf2hot: Converting ${NAME} to HOT! format..."
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
|
||||
#define HEADER_MAGIC 0x1CE1CE
|
||||
|
||||
typedef struct block_header {
|
||||
u64 magic;
|
||||
struct block_header* next;
|
||||
struct block_header* prev;
|
||||
u64 size;
|
||||
bool is_free;
|
||||
} block_header;
|
||||
|
||||
void* malloc(u64 size);
|
||||
void free(void* ptr);
|
||||
void* realloc(void* ptr, u64 new_size);
|
||||
@@ -0,0 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
|
||||
u64 spawn(const char* path);
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#define va_start(v, l) __builtin_va_start(v, l)
|
||||
#define va_end(v) __builtin_va_end(v)
|
||||
#define va_arg(v, l) __builtin_va_arg(v, l)
|
||||
#define va_copy(d, s) __builtin_va_copy(d, s)
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
|
||||
void printf(const char *fmt, ...);
|
||||
int getchar();
|
||||
char* gets(char* str);
|
||||
char* gets_s(char* str, u64 size);
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
|
||||
void *memset(void *ptr, int value, usize num);
|
||||
void* memcpy(void* dest, const void* src, u64 n);
|
||||
i32 strcmp(const char *s1, const char *s2);
|
||||
i32 strncmp(const char* s1, const char* s2, u64 n);
|
||||
char* strcpy(char* dest, const char* src);
|
||||
char* strncpy(char* dest, const char* src, u64 n);
|
||||
u64 strlen(const char* str);
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2025 0xKarinyash
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
typedef signed char i8;
|
||||
typedef signed short i16;
|
||||
typedef signed int i32;
|
||||
typedef signed long long i64;
|
||||
|
||||
typedef u64 usize;
|
||||
typedef u64 uintptr_t;
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define bool _Bool
|
||||
#define true 1
|
||||
#define false 0
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x400000;
|
||||
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.note.gnu.build-id)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.gnu.property)
|
||||
*(.comment)
|
||||
*(.interp)
|
||||
*(.dynsym)
|
||||
*(.dynstr)
|
||||
*(.hash)
|
||||
*(.gnu.hash)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
; Copyright (c) 2026 0xKarinyash
|
||||
|
||||
[bits 64]
|
||||
|
||||
section .text
|
||||
global _start
|
||||
extern main
|
||||
extern sys_exit
|
||||
|
||||
_start:
|
||||
call main
|
||||
mov rdi, rax
|
||||
call sys_exit
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
// brutally copypasting from kernel/mm/heap.c
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
static block_header* heap_list_head = nullptr;
|
||||
extern u64 sys_mem(u64 size);
|
||||
|
||||
void combine_forward(block_header* curr) {
|
||||
if (!curr->next || !curr->next->is_free) return;
|
||||
curr->size += sizeof(block_header) + curr->next->size;
|
||||
curr->next = curr->next->next;
|
||||
if (curr->next) curr->next->prev = curr; // what the fuck
|
||||
}
|
||||
|
||||
void* malloc(u64 size) {
|
||||
if (size == 0) return nullptr;
|
||||
u64 aligned_size = (size + 15) & ~15;
|
||||
|
||||
block_header* curr = heap_list_head;
|
||||
block_header* last = nullptr;
|
||||
while (curr) {
|
||||
if (curr->is_free && curr->size >= aligned_size) {
|
||||
if (curr->size > aligned_size + sizeof(block_header) + 16) {
|
||||
block_header* new_block = (block_header*)((u64)curr + sizeof(block_header) + aligned_size);
|
||||
new_block->size = curr->size - aligned_size - sizeof(block_header);
|
||||
new_block->is_free = true;
|
||||
new_block->next = curr->next;
|
||||
new_block->prev = curr;
|
||||
new_block->magic = HEADER_MAGIC;
|
||||
|
||||
if (curr->next) curr->next->prev = new_block;
|
||||
curr->next = new_block;
|
||||
curr->size = aligned_size;
|
||||
}
|
||||
curr->is_free = false;
|
||||
return (void*)((u64)curr + sizeof(block_header));
|
||||
}
|
||||
last = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
u64 need_to_alloc = aligned_size + sizeof(block_header);
|
||||
|
||||
u64 page_aligned_size = (need_to_alloc + 4095) & ~4095;
|
||||
|
||||
u64 new_mem_addr = sys_mem(page_aligned_size);
|
||||
if (new_mem_addr == 0) return nullptr;
|
||||
|
||||
block_header* new_block = (block_header*)new_mem_addr;
|
||||
new_block->size = page_aligned_size - sizeof(block_header);
|
||||
new_block->is_free = true;
|
||||
new_block->magic = HEADER_MAGIC;
|
||||
new_block->next = nullptr;
|
||||
new_block->prev = last;
|
||||
|
||||
if (last) {
|
||||
last->next = new_block;
|
||||
} else {
|
||||
heap_list_head = new_block;
|
||||
}
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void free(void* ptr) {
|
||||
if (!ptr) return;
|
||||
|
||||
block_header* curr = (block_header*)((u64)ptr - sizeof(block_header));
|
||||
if (curr->magic != HEADER_MAGIC) return;
|
||||
|
||||
curr->is_free = true;
|
||||
if (curr->next && curr->next->is_free) combine_forward(curr);
|
||||
if (curr->prev && curr->prev->is_free) combine_forward(curr->prev);
|
||||
}
|
||||
|
||||
void* realloc(void* ptr, u64 new_size) {
|
||||
if (!ptr) return malloc(new_size);
|
||||
if (new_size == 0) {
|
||||
free(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
block_header* curr = (block_header*)((u64)ptr - sizeof(block_header));
|
||||
if (curr->size >= new_size) return ptr;
|
||||
|
||||
if (curr->next &&
|
||||
curr->next->is_free &&
|
||||
(curr->size + sizeof(block_header) + curr->next->size) >= new_size) { // why ts so fucking unreadable
|
||||
combine_forward(curr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* new_ptr = malloc(new_size);
|
||||
if (!new_ptr) return nullptr;
|
||||
|
||||
memcpy(new_ptr, ptr, curr->size);
|
||||
free(ptr);
|
||||
|
||||
return new_ptr;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <process.h>
|
||||
|
||||
extern u64 sys_spawn(const char* path);
|
||||
|
||||
u64 spawn(const char* path) {
|
||||
return sys_spawn(path);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <types.h>
|
||||
#include <string.h>
|
||||
|
||||
#define EOF (-1)
|
||||
|
||||
extern u64 sys_read(u64 fd, void* buf, u64 len);
|
||||
extern u64 sys_write(u64 fd, const void* buf, u64 len);
|
||||
|
||||
static void putchar(char c) {
|
||||
sys_write(1, &c, 1);
|
||||
}
|
||||
|
||||
static void print_str(const char* str) {
|
||||
if (!str) str = "(null)";
|
||||
sys_write(1, str, strlen(str));
|
||||
}
|
||||
|
||||
static void print_dec(long long n) {
|
||||
if (n < 0) {
|
||||
putchar('-');
|
||||
n = -n;
|
||||
}
|
||||
unsigned long long u = (unsigned long long)n;
|
||||
char buffer[32];
|
||||
int i = 0;
|
||||
do {
|
||||
buffer[i++] = (u % 10) + '0';
|
||||
u /= 10;
|
||||
} while (u > 0);
|
||||
while (--i >= 0) putchar(buffer[i]);
|
||||
}
|
||||
|
||||
static void print_hex(unsigned long long u, int padding) {
|
||||
char buffer[16];
|
||||
int i = 0;
|
||||
const char* hex_chars = "0123456789ABCDEF";
|
||||
|
||||
do {
|
||||
buffer[i++] = hex_chars[u % 16];
|
||||
u /= 16;
|
||||
} while (u > 0);
|
||||
|
||||
while (i < padding) buffer[i++] = '0';
|
||||
|
||||
print_str("0x");
|
||||
while (--i >= 0) putchar(buffer[i]);
|
||||
}
|
||||
|
||||
void printf(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
for (int i = 0; fmt[i] != '\0'; i++) {
|
||||
if (fmt[i] == '%') {
|
||||
i++;
|
||||
switch (fmt[i]) {
|
||||
case 's': print_str(va_arg(args, const char*)); break;
|
||||
case 'c': putchar((char)va_arg(args, int)); break;
|
||||
case 'd': print_dec(va_arg(args, int)); break;
|
||||
case 'x': print_hex(va_arg(args, unsigned long long), 0); break;
|
||||
case 'X': print_hex(va_arg(args, unsigned long long), 16); break;
|
||||
case '%': putchar('%'); break;
|
||||
default: putchar(fmt[i]); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
putchar(fmt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
int getchar() {
|
||||
char c;
|
||||
unsigned long long res = sys_read(0, &c, 1);
|
||||
if (res <= 0) return EOF;
|
||||
return (int)(unsigned char)c;
|
||||
}
|
||||
|
||||
char* gets(char* str) {
|
||||
int i = 0;
|
||||
int c;
|
||||
|
||||
while (1) {
|
||||
c = getchar();
|
||||
|
||||
if (c == EOF || c == '\n' || c == '\r') {
|
||||
break;
|
||||
}
|
||||
|
||||
str[i++] = (char)c;
|
||||
|
||||
char ch = (char)c;
|
||||
sys_write(1, &ch, 1);
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
char nl = '\n';
|
||||
sys_write(1, &nl, 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char* gets_s(char* str, u64 size) {
|
||||
if (size == 0) return str;
|
||||
|
||||
u64 i = 0;
|
||||
int c;
|
||||
|
||||
while (i < size - 1) {
|
||||
c = getchar();
|
||||
if (c == EOF || c == '\n' || c == '\r') break;
|
||||
|
||||
str[i++] = (char)c;
|
||||
|
||||
char ch = (char)c;
|
||||
sys_write(1, &ch, 1);
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
char nl = '\n';
|
||||
sys_write(1, &nl, 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void *memset(void *ptr, int value, usize num) {
|
||||
u8 *p = (u8 *)ptr;
|
||||
while (num--) {
|
||||
*p++ = (u8)value;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* memcpy(void* dest, const void* src, u64 n) {
|
||||
u8* d = (u8*)dest;
|
||||
const u8* s = (const u8*)src;
|
||||
|
||||
while (n >= 8) {
|
||||
*(u64*)d = *(const u64*)s;
|
||||
d += 8;
|
||||
s += 8;
|
||||
n -= 8;
|
||||
}
|
||||
|
||||
while (n > 0) {
|
||||
*d++ = *s++;
|
||||
n--;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
i32 strcmp(const char *s1, const char *s2) {
|
||||
while (*s1 && (*s1 == *s2)) {
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
|
||||
}
|
||||
|
||||
i32 strncmp(const char* s1, const char* s2, u64 n) {
|
||||
while (n > 0) {
|
||||
if (*s1 != *s2) return *(unsigned char*)s1 - *(unsigned char*)s2;
|
||||
if (*s1 == '\0') return 0;
|
||||
s1++;
|
||||
s2++;
|
||||
n--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* strcpy(char* dest, const char* src) {
|
||||
char* saved = dest;
|
||||
while (*src) *dest++ = *src++;
|
||||
*dest = 0;
|
||||
return saved;
|
||||
}
|
||||
|
||||
char* strncpy(char* dest, const char* src, u64 n) {
|
||||
char* saved = dest;
|
||||
while (*src && n > 0) {
|
||||
*dest++ = *src++;
|
||||
n--;
|
||||
}
|
||||
while (n > 0) {
|
||||
*dest++ = 0;
|
||||
n--;
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
u64 strlen(const char* str) {
|
||||
u64 res = 0;
|
||||
for (res = 0; str[res]; res++);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
; SPDX-License-Identifier: GPL-3.0-or-later
|
||||
; Copyright (c) 2026 0xKarinyash
|
||||
|
||||
bits 64
|
||||
|
||||
section .text
|
||||
|
||||
global sys_exit
|
||||
global sys_spawn
|
||||
global sys_mem
|
||||
global sys_write
|
||||
global sys_read
|
||||
|
||||
sys_exit:
|
||||
mov rax, 0
|
||||
syscall
|
||||
ret
|
||||
|
||||
sys_spawn:
|
||||
mov rax, 1
|
||||
syscall
|
||||
ret
|
||||
|
||||
sys_mem:
|
||||
mov rax, 2
|
||||
syscall
|
||||
ret
|
||||
|
||||
sys_write:
|
||||
mov rax, 3
|
||||
syscall
|
||||
ret
|
||||
|
||||
sys_read:
|
||||
mov rax, 4
|
||||
syscall
|
||||
ret
|
||||
Reference in New Issue
Block a user