Make EFI port primary
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,4 @@
|
||||
_o/
|
||||
build/
|
||||
picolibc/
|
||||
picolibc-install/
|
||||
/lib/
|
||||
old/
|
||||
out/
|
||||
*.iso
|
||||
*.img
|
||||
@@ -31,14 +31,7 @@ int kernel_main(EFI_SYSTEM_TABLE *st) {
|
||||
duk_push_c_function(ctx, native_log, 1);
|
||||
duk_put_global_string(ctx, "$log");
|
||||
|
||||
duk_push_object(ctx);
|
||||
|
||||
duk_push_pointer(ctx, (void *)st);
|
||||
duk_put_prop_string(ctx, -2, "st");
|
||||
|
||||
duk_put_global_string(ctx, "$st");
|
||||
|
||||
duk_push_string(ctx, "$log($st.st.ConOut)");
|
||||
duk_push_string(ctx, "var a = 1 + 2; $log(a);");
|
||||
duk_int_t returnCode = duk_peval(ctx);
|
||||
|
||||
if (returnCode != 0)
|
||||
@@ -1,21 +0,0 @@
|
||||
FROM fedora:41
|
||||
|
||||
RUN dnf install -y \
|
||||
gcc \
|
||||
gcc-c++ \
|
||||
make \
|
||||
nasm \
|
||||
python3 \
|
||||
git \
|
||||
qemu-system-x86 \
|
||||
wget \
|
||||
meson \
|
||||
unzip \
|
||||
&& dnf clean all
|
||||
|
||||
RUN curl -fsSL https://bun.sh/install | bash
|
||||
ENV PATH="/root/.bun/bin:${PATH}"
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
CMD ["bash", "-c", "/workspace/scripts/build-picolibc.sh && /workspace/scripts/build-kernel.sh"]
|
||||
1
efi-port/.gitignore
vendored
1
efi-port/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
out/
|
||||
@@ -1,18 +0,0 @@
|
||||
# Meson cross-compilation file for i686-elf with picolibc
|
||||
[binaries]
|
||||
c = 'gcc'
|
||||
ar = 'ar'
|
||||
as = 'as'
|
||||
ld = 'ld'
|
||||
strip = 'strip'
|
||||
|
||||
[host_machine]
|
||||
system = 'none'
|
||||
cpu_family = 'x86'
|
||||
cpu = 'i686'
|
||||
endian = 'little'
|
||||
|
||||
[properties]
|
||||
c_args = ['-m32', '-march=i686']
|
||||
c_link_args = ['-m32']
|
||||
skip_sanity_check = true
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Build and run kernel with picolibc
|
||||
|
||||
set -e
|
||||
|
||||
mkdir -p lib
|
||||
|
||||
./scripts/get_duktape.sh
|
||||
|
||||
# Paths
|
||||
PICOLIBC_INSTALL="$(pwd)/picolibc-install"
|
||||
BUILD_DIR="build"
|
||||
OUT_DIR="$BUILD_DIR/out"
|
||||
|
||||
# Check if picolibc is installed
|
||||
if [ ! -d "$PICOLIBC_INSTALL" ]; then
|
||||
echo "Error: Picolibc not found at $PICOLIBC_INSTALL"
|
||||
echo "Please run: make picolibc"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create build directories
|
||||
mkdir -p "$BUILD_DIR"
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
# Build TypeScript to JavaScript
|
||||
cd src/os
|
||||
echo "Compiling TypeScript to JavaScript..."
|
||||
bun build --outdir ../../build --target node --bundle src/index.ts
|
||||
cd ../../
|
||||
|
||||
# Generate embedded JavaScript header
|
||||
echo "Generating embedded JavaScript..."
|
||||
bun scripts/build_js.js
|
||||
python3 scripts/embed_js.py build/index.js > "$BUILD_DIR/embedded_js.h"
|
||||
|
||||
|
||||
# Compiler flags
|
||||
CFLAGS="-m32 -march=i686 -ffreestanding -nostdlib -fno-builtin"
|
||||
CFLAGS="$CFLAGS -mno-sse -mno-sse2 -mno-mmx -mno-3dnow"
|
||||
CFLAGS="$CFLAGS -I$PICOLIBC_INSTALL/include"
|
||||
CFLAGS="$CFLAGS -I./lib/duktape/src"
|
||||
CFLAGS="$CFLAGS -I./src/lib"
|
||||
CFLAGS="$CFLAGS -I./src"
|
||||
CFLAGS="$CFLAGS -I$BUILD_DIR"
|
||||
CFLAGS="$CFLAGS -Wall -Wextra"
|
||||
|
||||
LDFLAGS="-m elf_i386 -nostdlib"
|
||||
LDFLAGS="$LDFLAGS -L$PICOLIBC_INSTALL/lib"
|
||||
|
||||
# Get libgcc path
|
||||
LIBGCC=$(gcc -m32 -print-libgcc-file-name)
|
||||
|
||||
echo "=== Building kernel with picolibc ==="
|
||||
|
||||
# Build boot assembly
|
||||
echo "Assembling boot code..."
|
||||
nasm -f elf32 src/boot/kernel.asm -o "$BUILD_DIR/kasm.o"
|
||||
|
||||
# Build interrupt assembly
|
||||
echo "Assembling interrupt handlers..."
|
||||
nasm -f elf32 src/kernel/interrupt/interrupt.asm -o "$BUILD_DIR/interrupt.o"
|
||||
|
||||
# Build duktape
|
||||
echo "Building Duktape..."
|
||||
gcc $CFLAGS -c lib/duktape/src/duktape.c -o "$BUILD_DIR/duktape.o"
|
||||
|
||||
# Build interrupt system
|
||||
echo "Building interrupt system..."
|
||||
gcc $CFLAGS -c src/kernel/interrupt/idt.c -o "$BUILD_DIR/idt.o"
|
||||
gcc $CFLAGS -c src/kernel/interrupt/isr.c -o "$BUILD_DIR/isr.o"
|
||||
|
||||
# Build kernel
|
||||
echo "Building kernel..."
|
||||
gcc $CFLAGS -c src/kernel/kernel.c -o "$BUILD_DIR/kc.o"
|
||||
|
||||
# Build syscalls
|
||||
echo "Building syscalls..."
|
||||
gcc $CFLAGS -c src/lib/syscalls.c -o "$BUILD_DIR/syscalls.o"
|
||||
|
||||
# Link everything together
|
||||
echo "Linking kernel..."
|
||||
ld $LDFLAGS -T src/link.ld -o "$OUT_DIR/kernel" \
|
||||
"$BUILD_DIR/kasm.o" \
|
||||
"$BUILD_DIR/interrupt.o" \
|
||||
"$BUILD_DIR/idt.o" \
|
||||
"$BUILD_DIR/isr.o" \
|
||||
"$BUILD_DIR/kc.o" \
|
||||
"$BUILD_DIR/duktape.o" \
|
||||
"$BUILD_DIR/syscalls.o" \
|
||||
-lc "$LIBGCC"
|
||||
|
||||
echo ""
|
||||
echo "=== Build complete! ==="
|
||||
echo ""
|
||||
echo "Running kernel in QEMU..."
|
||||
echo "(Press Ctrl+A then X to exit)"
|
||||
echo ""
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script to build picolibc for i686 freestanding environment
|
||||
|
||||
set -e
|
||||
|
||||
PICOLIBC_DIR="picolibc"
|
||||
BUILD_DIR="build/picolibc"
|
||||
INSTALL_DIR="$(pwd)/picolibc-install"
|
||||
|
||||
echo "=== Building Picolibc for i686 freestanding ==="
|
||||
|
||||
# Check if picolibc exists
|
||||
if [ ! -d "$PICOLIBC_DIR" ]; then
|
||||
echo "Error: Picolibc source not found at $PICOLIBC_DIR"
|
||||
echo "Cloning..."
|
||||
git clone --branch 1.8.10 https://github.com/picolibc/picolibc.git "$PICOLIBC_DIR"
|
||||
fi
|
||||
|
||||
# Get current version
|
||||
cd "$PICOLIBC_DIR"
|
||||
CURRENT_VERSION=$(git describe --tags 2>/dev/null || echo "unknown")
|
||||
echo "Using picolibc version: $CURRENT_VERSION"
|
||||
cd ..
|
||||
|
||||
# Create build directory
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
echo "Configuring picolibc with meson..."
|
||||
meson setup "$BUILD_DIR" "$PICOLIBC_DIR" \
|
||||
--cross-file picolibc-i686.txt \
|
||||
--prefix="$INSTALL_DIR" \
|
||||
--wipe \
|
||||
-Dmultilib=false \
|
||||
-Dpicocrt=false \
|
||||
-Dsemihost=false \
|
||||
-Dspecsdir=none \
|
||||
-Dtinystdio=true \
|
||||
-Dio-long-long=true \
|
||||
-Dformat-default=double \
|
||||
-Dtests=false
|
||||
|
||||
echo "Building picolibc..."
|
||||
meson compile -C "$BUILD_DIR"
|
||||
|
||||
echo "Installing picolibc..."
|
||||
meson install -C "$BUILD_DIR"
|
||||
|
||||
echo ""
|
||||
echo "=== Picolibc build complete! ==="
|
||||
echo "Installation directory: $INSTALL_DIR"
|
||||
echo ""
|
||||
echo "To use picolibc in your kernel:"
|
||||
echo " Include path: -I$INSTALL_DIR/include"
|
||||
echo " Library path: -L$INSTALL_DIR/lib"
|
||||
echo " Link with: -lc"
|
||||
@@ -1,2 +0,0 @@
|
||||
docker build -t lints-dev -f docker/Dockerfile .
|
||||
docker run -it --rm -v "$(pwd)":/workspace -w /workspace lints-dev
|
||||
@@ -1,11 +0,0 @@
|
||||
#! /usr/bin/env bun
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
|
||||
writeFileSync(
|
||||
"build/index.js",
|
||||
readFileSync("build/index.js", "utf-8")
|
||||
.replaceAll("let ", "var ")
|
||||
.replaceAll("const ", "var ")
|
||||
.replaceAll("\n`", "\\n`")
|
||||
.replaceAll("`", "'")
|
||||
);
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Embed JavaScript file as a C string constant
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def escape_c_string(s):
|
||||
"""Escape a string for use in C source code"""
|
||||
result = []
|
||||
for char in s:
|
||||
if char == "\n":
|
||||
result.append("\\n")
|
||||
elif char == "\r":
|
||||
result.append("\\r")
|
||||
elif char == "\t":
|
||||
result.append("\\t")
|
||||
elif char == "\\":
|
||||
result.append("\\\\")
|
||||
elif char == '"':
|
||||
result.append('\\"')
|
||||
elif ord(char) < 32 or ord(char) > 126:
|
||||
result.append(f"\\x{ord(char):02x}")
|
||||
else:
|
||||
result.append(char)
|
||||
return "".join(result)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: embed_js.py <input.js>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
input_file = sys.argv[1]
|
||||
|
||||
try:
|
||||
with open(input_file, "r") as f:
|
||||
js_code = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File not found: {input_file}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Generate C header file
|
||||
print("/* Auto-generated file - do not edit */")
|
||||
print("#ifndef EMBEDDED_JS_H")
|
||||
print("#define EMBEDDED_JS_H")
|
||||
print()
|
||||
|
||||
escaped = escape_c_string(js_code)
|
||||
print(f'const char *embedded_js_code = "{escaped}";')
|
||||
|
||||
print()
|
||||
print("#endif /* EMBEDDED_JS_H */")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script to download Duktape library if not present
|
||||
|
||||
set -e
|
||||
|
||||
DUKTAPE_DIR="lib/duktape"
|
||||
DUKTAPE_REPO="https://github.com/joeqread/arduino-duktape.git"
|
||||
TEMP_DIR="lib/temp_duktape"
|
||||
|
||||
echo "=== Checking for Duktape library ==="
|
||||
|
||||
if [ -d "$DUKTAPE_DIR" ]; then
|
||||
echo "Duktape already exists at $DUKTAPE_DIR"
|
||||
else
|
||||
echo "Duktape not found. Cloning from $DUKTAPE_REPO..."
|
||||
git clone "$DUKTAPE_REPO" "$TEMP_DIR"
|
||||
|
||||
echo "Extracting src folder..."
|
||||
mkdir -p "$DUKTAPE_DIR"
|
||||
mv "$TEMP_DIR/src" "$DUKTAPE_DIR/"
|
||||
|
||||
echo "Cleaning up temporary directory..."
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
echo "Duktape cloned successfully!"
|
||||
fi
|
||||
|
||||
echo "Done."
|
||||
@@ -1,69 +0,0 @@
|
||||
./scripts/build.sh
|
||||
|
||||
# Create disk image if it doesn't exist
|
||||
if [ ! -f hdd.img ]; then
|
||||
echo "Creating disk image..."
|
||||
qemu-img create -f raw hdd.img 10M
|
||||
|
||||
# Create a proper MBR partition table with one FAT32 partition
|
||||
# Using fdisk commands
|
||||
(
|
||||
echo o # Create new DOS partition table
|
||||
echo n # New partition
|
||||
echo p # Primary partition
|
||||
echo 1 # Partition number 1
|
||||
echo # Default first sector
|
||||
echo # Default last sector (use all space)
|
||||
echo t # Change partition type
|
||||
echo c # FAT32 LBA
|
||||
echo a # Make bootable
|
||||
echo w # Write changes
|
||||
) | fdisk hdd.img >/dev/null 2>&1
|
||||
|
||||
# Format the partition as FAT32
|
||||
# First, get the partition offset using sfdisk
|
||||
OFFSET=$(sfdisk -l hdd.img 2>/dev/null | grep 'hdd.img1' | awk '{print $2}')
|
||||
|
||||
# Check if OFFSET is empty or non-numeric and set default if needed
|
||||
if [ -z "$OFFSET" ] || ! [[ "$OFFSET" =~ ^[0-9]+$ ]]; then
|
||||
OFFSET=2048 # Default first sector for most partition tables
|
||||
fi
|
||||
|
||||
# Create FAT32 filesystem on the partition
|
||||
echo "Formatting partition as FAT32..."
|
||||
mkfs.vfat -F 32 -n "TESTDISK" --offset $OFFSET hdd.img
|
||||
|
||||
echo "Disk image created with FAT32 partition"
|
||||
fi
|
||||
|
||||
# Get the partition offset for file operations
|
||||
OFFSET=$(sfdisk -l hdd.img 2>/dev/null | grep 'hdd.img1' | awk '{print $2}')
|
||||
if [ -z "$OFFSET" ] || ! [[ "$OFFSET" =~ ^[0-9]+$ ]]; then
|
||||
OFFSET=2048
|
||||
fi
|
||||
|
||||
# Copy test-hdd contents into the disk image
|
||||
if [ -d "test-hdd" ]; then
|
||||
echo "Copying test-hdd contents to disk image..."
|
||||
|
||||
# Use mcopy to copy files to the FAT32 partition
|
||||
MTOOLS_SKIP_CHECK=1 mcopy -i hdd.img@@$((OFFSET * 512)) -s test-hdd/* :: || {
|
||||
echo "Warning: mcopy failed, retrying individual files..."
|
||||
for file in test-hdd/*; do
|
||||
if [ -f "$file" ]; then
|
||||
MTOOLS_SKIP_CHECK=1 mcopy -i hdd.img@@$((OFFSET * 512)) "$file" :: && echo "Copied $(basename "$file")"
|
||||
fi
|
||||
done
|
||||
}
|
||||
fi
|
||||
|
||||
# Create CD-ROM image if it doesn't exist
|
||||
if [ ! -f cdrom.iso ]; then
|
||||
mkdir -p /tmp/cdrom_content
|
||||
echo "Test CD-ROM" > /tmp/cdrom_content/readme.txt
|
||||
genisoimage -o cdrom.iso -V "TESTCD" -r -J /tmp/cdrom_content 2>/dev/null || touch cdrom.iso
|
||||
fi
|
||||
|
||||
qemu-system-i386 -serial stdio -kernel "build/out/kernel" \
|
||||
-drive file=hdd.img,if=ide,format=raw,index=0,media=disk \
|
||||
-drive file=cdrom.iso,if=ide,format=raw,index=1,media=cdrom
|
||||
@@ -1,25 +0,0 @@
|
||||
bits 32 ;nasm directive
|
||||
section .text
|
||||
;multiboot spec
|
||||
align 4
|
||||
dd 0x1BADB002 ;magic
|
||||
dd 0x00 ;flags
|
||||
dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
|
||||
|
||||
global start
|
||||
extern kmain ;kmain is defined in the kernel.c file
|
||||
extern __stack_top
|
||||
|
||||
start:
|
||||
cli ; stop interrupts during boot
|
||||
mov esp, __stack_top
|
||||
|
||||
; Initialize FPU (x87)
|
||||
finit ; Initialize FPU to default state
|
||||
|
||||
call kmain
|
||||
; Note: kmain will call sti to enable interrupts after IDT is set up
|
||||
|
||||
hang:
|
||||
hlt ; halt the CPU
|
||||
jmp hang
|
||||
@@ -1,22 +0,0 @@
|
||||
#include "idt.h"
|
||||
|
||||
/* Define the IDT and IDT register here */
|
||||
idt_gate_t idt[IDT_ENTRIES];
|
||||
idt_register_t idt_reg;
|
||||
|
||||
void set_idt_gate(int n, u32 handler)
|
||||
{
|
||||
idt[n].low_offset = low_16(handler);
|
||||
idt[n].sel = KERNEL_CS;
|
||||
idt[n].always0 = 0;
|
||||
idt[n].flags = 0x8E;
|
||||
idt[n].high_offset = high_16(handler);
|
||||
}
|
||||
|
||||
void set_idt()
|
||||
{
|
||||
idt_reg.base = (u32)&idt;
|
||||
idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;
|
||||
/* Don't make the mistake of loading &idt -- always load &idt_reg */
|
||||
__asm__ __volatile__("lidtl (%0)" : : "r"(&idt_reg));
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef IDT_H
|
||||
#define IDT_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Segment selectors */
|
||||
#define KERNEL_CS 0x08
|
||||
|
||||
/* How every interrupt gate (handler) is defined */
|
||||
typedef struct
|
||||
{
|
||||
u16 low_offset; /* Lower 16 bits of handler function address */
|
||||
u16 sel; /* Kernel segment selector */
|
||||
u8 always0;
|
||||
/* First byte
|
||||
* Bit 7: "Interrupt is present"
|
||||
* Bits 6-5: Privilege level of caller (0=kernel..3=user)
|
||||
* Bit 4: Set to 0 for interrupt gates
|
||||
* Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */
|
||||
u8 flags;
|
||||
u16 high_offset; /* Higher 16 bits of handler function address */
|
||||
} __attribute__((packed)) idt_gate_t;
|
||||
|
||||
/* A pointer to the array of interrupt handlers.
|
||||
* Assembly instruction 'lidt' will read it */
|
||||
typedef struct
|
||||
{
|
||||
u16 limit;
|
||||
u32 base;
|
||||
} __attribute__((packed)) idt_register_t;
|
||||
|
||||
#define IDT_ENTRIES 256
|
||||
extern idt_gate_t idt[IDT_ENTRIES];
|
||||
extern idt_register_t idt_reg;
|
||||
|
||||
/* Functions implemented in idt.c */
|
||||
void set_idt_gate(int n, u32 handler);
|
||||
void set_idt();
|
||||
|
||||
#endif
|
||||
@@ -1,446 +0,0 @@
|
||||
; Defined in isr.c
|
||||
[extern isr_handler]
|
||||
[extern irq_handler]
|
||||
|
||||
; Common ISR code
|
||||
isr_common_stub:
|
||||
; 1. Save CPU state
|
||||
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
|
||||
mov ax, ds ; Lower 16-bits of eax = ds.
|
||||
push eax ; save the data segment descriptor
|
||||
mov ax, 0x10 ; kernel data segment descriptor
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; 2. Call C handler - pass pointer to registers_t struct on stack
|
||||
push esp
|
||||
call isr_handler
|
||||
add esp, 4
|
||||
|
||||
; 3. Restore state
|
||||
pop eax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
popa
|
||||
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
|
||||
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP (and restores interrupt flag)
|
||||
|
||||
; Common IRQ code
|
||||
irq_common_stub:
|
||||
; 1. Save CPU state
|
||||
pusha
|
||||
mov ax, ds
|
||||
push eax
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; 2. Call C handler - pass pointer to registers_t struct on stack
|
||||
push esp
|
||||
call irq_handler
|
||||
add esp, 4
|
||||
|
||||
; 3. Restore state
|
||||
pop ebx
|
||||
mov ds, bx
|
||||
mov es, bx
|
||||
mov fs, bx
|
||||
mov gs, bx
|
||||
popa
|
||||
add esp, 8
|
||||
iret ; iret restores the interrupt flag automatically from saved EFLAGS
|
||||
|
||||
; We don't get information about which interrupt was caller
|
||||
; when the handler is run, so we will need to have a different handler
|
||||
; for every interrupt.
|
||||
; Furthermore, some interrupts push an error code onto the stack but others
|
||||
; don't, so we will push a dummy error code for those which don't, so that
|
||||
; we have a consistent stack for all of them.
|
||||
|
||||
; First make the ISRs global
|
||||
global isr0
|
||||
global isr1
|
||||
global isr2
|
||||
global isr3
|
||||
global isr4
|
||||
global isr5
|
||||
global isr6
|
||||
global isr7
|
||||
global isr8
|
||||
global isr9
|
||||
global isr10
|
||||
global isr11
|
||||
global isr12
|
||||
global isr13
|
||||
global isr14
|
||||
global isr15
|
||||
global isr16
|
||||
global isr17
|
||||
global isr18
|
||||
global isr19
|
||||
global isr20
|
||||
global isr21
|
||||
global isr22
|
||||
global isr23
|
||||
global isr24
|
||||
global isr25
|
||||
global isr26
|
||||
global isr27
|
||||
global isr28
|
||||
global isr29
|
||||
global isr30
|
||||
global isr31
|
||||
|
||||
; 0: Divide By Zero Exception
|
||||
isr0:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 0
|
||||
jmp isr_common_stub
|
||||
|
||||
; 1: Debug Exception
|
||||
isr1:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 1
|
||||
jmp isr_common_stub
|
||||
|
||||
; 2: Non Maskable Interrupt Exception
|
||||
isr2:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 2
|
||||
jmp isr_common_stub
|
||||
|
||||
; 3: Int 3 Exception
|
||||
isr3:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 3
|
||||
jmp isr_common_stub
|
||||
|
||||
; 4: INTO Exception
|
||||
isr4:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 4
|
||||
jmp isr_common_stub
|
||||
|
||||
; 5: Out of Bounds Exception
|
||||
isr5:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 5
|
||||
jmp isr_common_stub
|
||||
|
||||
; 6: Invalid Opcode Exception
|
||||
isr6:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 6
|
||||
jmp isr_common_stub
|
||||
|
||||
; 7: Coprocessor Not Available Exception
|
||||
isr7:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 7
|
||||
jmp isr_common_stub
|
||||
|
||||
; 8: Double Fault Exception (With Error Code!)
|
||||
isr8:
|
||||
cli
|
||||
push byte 8
|
||||
jmp isr_common_stub
|
||||
|
||||
; 9: Coprocessor Segment Overrun Exception
|
||||
isr9:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 9
|
||||
jmp isr_common_stub
|
||||
|
||||
; 10: Bad TSS Exception (With Error Code!)
|
||||
isr10:
|
||||
cli
|
||||
push byte 10
|
||||
jmp isr_common_stub
|
||||
|
||||
; 11: Segment Not Present Exception (With Error Code!)
|
||||
isr11:
|
||||
cli
|
||||
push byte 11
|
||||
jmp isr_common_stub
|
||||
|
||||
; 12: Stack Fault Exception (With Error Code!)
|
||||
isr12:
|
||||
cli
|
||||
push byte 12
|
||||
jmp isr_common_stub
|
||||
|
||||
; 13: General Protection Fault Exception (With Error Code!)
|
||||
isr13:
|
||||
cli
|
||||
push byte 13
|
||||
jmp isr_common_stub
|
||||
|
||||
; 14: Page Fault Exception (With Error Code!)
|
||||
isr14:
|
||||
cli
|
||||
push byte 14
|
||||
jmp isr_common_stub
|
||||
|
||||
; 15: Reserved Exception
|
||||
isr15:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 15
|
||||
jmp isr_common_stub
|
||||
|
||||
; 16: Floating Point Exception
|
||||
isr16:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 16
|
||||
jmp isr_common_stub
|
||||
|
||||
; 17: Alignment Check Exception
|
||||
isr17:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 17
|
||||
jmp isr_common_stub
|
||||
|
||||
; 18: Machine Check Exception
|
||||
isr18:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 18
|
||||
jmp isr_common_stub
|
||||
|
||||
; 19: Reserved
|
||||
isr19:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 19
|
||||
jmp isr_common_stub
|
||||
|
||||
; 20: Reserved
|
||||
isr20:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 20
|
||||
jmp isr_common_stub
|
||||
|
||||
; 21: Reserved
|
||||
isr21:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 21
|
||||
jmp isr_common_stub
|
||||
|
||||
; 22: Reserved
|
||||
isr22:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 22
|
||||
jmp isr_common_stub
|
||||
|
||||
; 23: Reserved
|
||||
isr23:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 23
|
||||
jmp isr_common_stub
|
||||
|
||||
; 24: Reserved
|
||||
isr24:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 24
|
||||
jmp isr_common_stub
|
||||
|
||||
; 25: Reserved
|
||||
isr25:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 25
|
||||
jmp isr_common_stub
|
||||
|
||||
; 26: Reserved
|
||||
isr26:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 26
|
||||
jmp isr_common_stub
|
||||
|
||||
; 27: Reserved
|
||||
isr27:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 27
|
||||
jmp isr_common_stub
|
||||
|
||||
; 28: Reserved
|
||||
isr28:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 28
|
||||
jmp isr_common_stub
|
||||
|
||||
; 29: Reserved
|
||||
isr29:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 29
|
||||
jmp isr_common_stub
|
||||
|
||||
; 30: Reserved
|
||||
isr30:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 30
|
||||
jmp isr_common_stub
|
||||
|
||||
; 31: Reserved
|
||||
isr31:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 31
|
||||
jmp isr_common_stub
|
||||
|
||||
; IRQ handlers
|
||||
global irq0
|
||||
global irq1
|
||||
global irq2
|
||||
global irq3
|
||||
global irq4
|
||||
global irq5
|
||||
global irq6
|
||||
global irq7
|
||||
global irq8
|
||||
global irq9
|
||||
global irq10
|
||||
global irq11
|
||||
global irq12
|
||||
global irq13
|
||||
global irq14
|
||||
global irq15
|
||||
|
||||
; IRQ 0: System Timer
|
||||
irq0:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 32
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 1: Keyboard
|
||||
irq1:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 33
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 2: Cascade (never raised)
|
||||
irq2:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 34
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 3: COM2
|
||||
irq3:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 35
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 4: COM1
|
||||
irq4:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 36
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 5: LPT2
|
||||
irq5:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 37
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 6: Floppy Disk
|
||||
irq6:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 38
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 7: LPT1
|
||||
irq7:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 39
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 8: CMOS Real-time Clock
|
||||
irq8:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 40
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 9: Free for peripherals
|
||||
irq9:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 41
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 10: Free for peripherals
|
||||
irq10:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 42
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 11: Free for peripherals
|
||||
irq11:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 43
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 12: PS/2 Mouse
|
||||
irq12:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 44
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 13: FPU
|
||||
irq13:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 45
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 14: Primary ATA Hard Disk
|
||||
irq14:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 46
|
||||
jmp irq_common_stub
|
||||
|
||||
; IRQ 15: Secondary ATA Hard Disk
|
||||
irq15:
|
||||
cli
|
||||
push byte 0
|
||||
push byte 47
|
||||
jmp irq_common_stub
|
||||
@@ -1,317 +0,0 @@
|
||||
#include "isr.h"
|
||||
#include "idt.h"
|
||||
|
||||
/* Port I/O functions */
|
||||
static inline void outb(u16 port, u8 val)
|
||||
{
|
||||
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline u8 inb(u16 port)
|
||||
{
|
||||
u8 ret;
|
||||
__asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* PIC (Programmable Interrupt Controller) constants */
|
||||
#define PIC1_COMMAND 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_COMMAND 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
#define PIC_EOI 0x20
|
||||
|
||||
/* Array to store custom IRQ handlers */
|
||||
static irq_handler_t irq_handlers[16] = {0};
|
||||
|
||||
/* Can't do this with a loop because we need the address
|
||||
* of the function names */
|
||||
void isr_install()
|
||||
{
|
||||
set_idt_gate(0, (u32)isr0);
|
||||
set_idt_gate(1, (u32)isr1);
|
||||
set_idt_gate(2, (u32)isr2);
|
||||
set_idt_gate(3, (u32)isr3);
|
||||
set_idt_gate(4, (u32)isr4);
|
||||
set_idt_gate(5, (u32)isr5);
|
||||
set_idt_gate(6, (u32)isr6);
|
||||
set_idt_gate(7, (u32)isr7);
|
||||
set_idt_gate(8, (u32)isr8);
|
||||
set_idt_gate(9, (u32)isr9);
|
||||
set_idt_gate(10, (u32)isr10);
|
||||
set_idt_gate(11, (u32)isr11);
|
||||
set_idt_gate(12, (u32)isr12);
|
||||
set_idt_gate(13, (u32)isr13);
|
||||
set_idt_gate(14, (u32)isr14);
|
||||
set_idt_gate(15, (u32)isr15);
|
||||
set_idt_gate(16, (u32)isr16);
|
||||
set_idt_gate(17, (u32)isr17);
|
||||
set_idt_gate(18, (u32)isr18);
|
||||
set_idt_gate(19, (u32)isr19);
|
||||
set_idt_gate(20, (u32)isr20);
|
||||
set_idt_gate(21, (u32)isr21);
|
||||
set_idt_gate(22, (u32)isr22);
|
||||
set_idt_gate(23, (u32)isr23);
|
||||
set_idt_gate(24, (u32)isr24);
|
||||
set_idt_gate(25, (u32)isr25);
|
||||
set_idt_gate(26, (u32)isr26);
|
||||
set_idt_gate(27, (u32)isr27);
|
||||
set_idt_gate(28, (u32)isr28);
|
||||
set_idt_gate(29, (u32)isr29);
|
||||
set_idt_gate(30, (u32)isr30);
|
||||
set_idt_gate(31, (u32)isr31);
|
||||
|
||||
set_idt(); // Load with ASM
|
||||
}
|
||||
|
||||
/* Remap the PIC to avoid conflicts with CPU exceptions */
|
||||
static void pic_remap(void)
|
||||
{
|
||||
// Start initialization
|
||||
outb(PIC1_COMMAND, 0x11);
|
||||
outb(PIC2_COMMAND, 0x11);
|
||||
|
||||
// Set vector offsets (IRQs 0-7 -> interrupts 32-39, IRQs 8-15 -> interrupts 40-47)
|
||||
outb(PIC1_DATA, 0x20);
|
||||
outb(PIC2_DATA, 0x28);
|
||||
|
||||
// Tell master PIC there's a slave PIC at IRQ2
|
||||
outb(PIC1_DATA, 0x04);
|
||||
// Tell slave PIC its cascade identity
|
||||
outb(PIC2_DATA, 0x02);
|
||||
|
||||
// Set to 8086 mode
|
||||
outb(PIC1_DATA, 0x01);
|
||||
outb(PIC2_DATA, 0x01);
|
||||
|
||||
// Mask all interrupts initially
|
||||
outb(PIC1_DATA, 0xFF);
|
||||
outb(PIC2_DATA, 0xFF);
|
||||
}
|
||||
|
||||
/* Install IRQ handlers */
|
||||
void irq_install()
|
||||
{
|
||||
// Remap the PIC
|
||||
pic_remap();
|
||||
|
||||
// Install IRQ handlers (32-47)
|
||||
set_idt_gate(32, (u32)irq0);
|
||||
set_idt_gate(33, (u32)irq1);
|
||||
set_idt_gate(34, (u32)irq2);
|
||||
set_idt_gate(35, (u32)irq3);
|
||||
set_idt_gate(36, (u32)irq4);
|
||||
set_idt_gate(37, (u32)irq5);
|
||||
set_idt_gate(38, (u32)irq6);
|
||||
set_idt_gate(39, (u32)irq7);
|
||||
set_idt_gate(40, (u32)irq8);
|
||||
set_idt_gate(41, (u32)irq9);
|
||||
set_idt_gate(42, (u32)irq10);
|
||||
set_idt_gate(43, (u32)irq11);
|
||||
set_idt_gate(44, (u32)irq12);
|
||||
set_idt_gate(45, (u32)irq13);
|
||||
set_idt_gate(46, (u32)irq14);
|
||||
set_idt_gate(47, (u32)irq15);
|
||||
|
||||
set_idt(); // Reload IDT
|
||||
}
|
||||
|
||||
/* To print the message which defines every exception */
|
||||
char *exception_messages[] = {
|
||||
"Division By Zero",
|
||||
"Debug",
|
||||
"Non Maskable Interrupt",
|
||||
"Breakpoint",
|
||||
"Into Detected Overflow",
|
||||
"Out of Bounds",
|
||||
"Invalid Opcode",
|
||||
"No Coprocessor",
|
||||
|
||||
"Double Fault",
|
||||
"Coprocessor Segment Overrun",
|
||||
"Bad TSS",
|
||||
"Segment Not Present",
|
||||
"Stack Fault",
|
||||
"General Protection Fault",
|
||||
"Page Fault",
|
||||
"Unknown Interrupt",
|
||||
|
||||
"Coprocessor Fault",
|
||||
"Alignment Check",
|
||||
"Machine Check",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved",
|
||||
"Reserved"};
|
||||
|
||||
void isr_handler(registers_t *r)
|
||||
{
|
||||
// Write exception to VGA memory
|
||||
char *vidmem = (char *)0xb8000;
|
||||
const char *msg = "EXCEPTION: ";
|
||||
int i = 0;
|
||||
|
||||
// Clear first line
|
||||
for (i = 0; i < 80 * 2; i++)
|
||||
{
|
||||
vidmem[i] = 0;
|
||||
}
|
||||
|
||||
// Write message
|
||||
i = 0;
|
||||
while (msg[i])
|
||||
{
|
||||
vidmem[i * 2] = msg[i];
|
||||
vidmem[i * 2 + 1] = 0x4F; // White on red
|
||||
i++;
|
||||
}
|
||||
|
||||
// Write exception number
|
||||
vidmem[i * 2] = '0' + (r->int_no / 10);
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = '0' + (r->int_no % 10);
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
|
||||
// Write error code
|
||||
vidmem[i * 2] = ' ';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = 'E';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = 'R';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = 'R';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = ':';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
|
||||
// Write error code in hex
|
||||
u32 err = r->err_code;
|
||||
for (int j = 7; j >= 0; j--)
|
||||
{
|
||||
u8 nibble = (err >> (j * 4)) & 0xF;
|
||||
vidmem[i * 2] = nibble < 10 ? '0' + nibble : 'A' + (nibble - 10);
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Write EIP
|
||||
i++;
|
||||
vidmem[i * 2] = 'E';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = 'I';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = 'P';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
vidmem[i * 2] = ':';
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
|
||||
// Write EIP in hex
|
||||
u32 eip = r->eip;
|
||||
for (int j = 7; j >= 0; j--)
|
||||
{
|
||||
u8 nibble = (eip >> (j * 4)) & 0xF;
|
||||
vidmem[i * 2] = nibble < 10 ? '0' + nibble : 'A' + (nibble - 10);
|
||||
vidmem[i * 2 + 1] = 0x4F;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Halt
|
||||
__asm__ volatile("cli; hlt");
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
/* IRQ handler */
|
||||
void irq_handler(registers_t *r)
|
||||
{
|
||||
// Calculate IRQ number (interrupts 32-47 map to IRQ 0-15)
|
||||
u8 irq_no = r->int_no - 32;
|
||||
|
||||
// Handle spurious IRQ7 (from master PIC)
|
||||
if (irq_no == 7)
|
||||
{
|
||||
// Read In-Service Register (ISR)
|
||||
outb(PIC1_COMMAND, 0x0B);
|
||||
u8 isr = inb(PIC1_COMMAND);
|
||||
if (!(isr & 0x80))
|
||||
{
|
||||
// Spurious interrupt, don't send EOI
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle spurious IRQ15 (from slave PIC)
|
||||
if (irq_no == 15)
|
||||
{
|
||||
// Read In-Service Register (ISR) of slave PIC
|
||||
outb(PIC2_COMMAND, 0x0B);
|
||||
u8 isr = inb(PIC2_COMMAND);
|
||||
if (!(isr & 0x80))
|
||||
{
|
||||
// Spurious interrupt from slave, send EOI to master only
|
||||
outb(PIC1_COMMAND, PIC_EOI);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Call custom handler if registered
|
||||
if (irq_handlers[irq_no] != 0)
|
||||
{
|
||||
irq_handlers[irq_no](r);
|
||||
}
|
||||
|
||||
// Send EOI to PIC
|
||||
if (r->int_no >= 40)
|
||||
{
|
||||
// IRQ came from slave PIC, send EOI to both
|
||||
outb(PIC2_COMMAND, PIC_EOI);
|
||||
}
|
||||
outb(PIC1_COMMAND, PIC_EOI);
|
||||
}
|
||||
|
||||
/* Register a custom IRQ handler */
|
||||
void irq_register_handler(u8 irq, irq_handler_t handler)
|
||||
{
|
||||
if (irq < 16)
|
||||
{
|
||||
irq_handlers[irq] = handler;
|
||||
|
||||
// Unmask the IRQ on the PIC
|
||||
u16 port;
|
||||
u8 value;
|
||||
|
||||
if (irq < 8)
|
||||
{
|
||||
port = PIC1_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
port = PIC2_DATA;
|
||||
irq -= 8;
|
||||
}
|
||||
|
||||
value = inb(port) & ~(1 << irq);
|
||||
outb(port, value);
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
#ifndef ISR_H
|
||||
#define ISR_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* ISRs reserved for CPU exceptions */
|
||||
extern void isr0();
|
||||
extern void isr1();
|
||||
extern void isr2();
|
||||
extern void isr3();
|
||||
extern void isr4();
|
||||
extern void isr5();
|
||||
extern void isr6();
|
||||
extern void isr7();
|
||||
extern void isr8();
|
||||
extern void isr9();
|
||||
extern void isr10();
|
||||
extern void isr11();
|
||||
extern void isr12();
|
||||
extern void isr13();
|
||||
extern void isr14();
|
||||
extern void isr15();
|
||||
extern void isr16();
|
||||
extern void isr17();
|
||||
extern void isr18();
|
||||
extern void isr19();
|
||||
extern void isr20();
|
||||
extern void isr21();
|
||||
extern void isr22();
|
||||
extern void isr23();
|
||||
extern void isr24();
|
||||
extern void isr25();
|
||||
extern void isr26();
|
||||
extern void isr27();
|
||||
extern void isr28();
|
||||
extern void isr29();
|
||||
extern void isr30();
|
||||
extern void isr31();
|
||||
|
||||
/* IRQ handlers */
|
||||
extern void irq0();
|
||||
extern void irq1();
|
||||
extern void irq2();
|
||||
extern void irq3();
|
||||
extern void irq4();
|
||||
extern void irq5();
|
||||
extern void irq6();
|
||||
extern void irq7();
|
||||
extern void irq8();
|
||||
extern void irq9();
|
||||
extern void irq10();
|
||||
extern void irq11();
|
||||
extern void irq12();
|
||||
extern void irq13();
|
||||
extern void irq14();
|
||||
extern void irq15();
|
||||
|
||||
/* Struct which aggregates many registers */
|
||||
typedef struct
|
||||
{
|
||||
u32 ds; /* Data segment selector */
|
||||
u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
|
||||
u32 int_no, err_code; /* Interrupt number and error code (if applicable) */
|
||||
u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */
|
||||
} registers_t;
|
||||
|
||||
/* Function pointer type for IRQ handlers */
|
||||
typedef void (*irq_handler_t)(registers_t *);
|
||||
|
||||
void isr_install();
|
||||
void isr_handler(registers_t *r);
|
||||
void irq_install();
|
||||
void irq_handler(registers_t *r);
|
||||
|
||||
/* Register a custom IRQ handler */
|
||||
void irq_register_handler(u8 irq, irq_handler_t handler);
|
||||
|
||||
#endif
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
/* Instead of using 'chars' to allocate non-character bytes,
|
||||
* we will use these new type with no semantic meaning */
|
||||
typedef unsigned int u32;
|
||||
typedef int s32;
|
||||
typedef unsigned short u16;
|
||||
typedef short s16;
|
||||
typedef unsigned char u8;
|
||||
typedef char s8;
|
||||
|
||||
#define low_16(address) (u16)((address) & 0xFFFF)
|
||||
#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)
|
||||
|
||||
#endif
|
||||
@@ -1,762 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <duktape.h>
|
||||
#include "embedded_js.h"
|
||||
#include "interrupt/isr.h"
|
||||
|
||||
#define WHITE_TXT 0x0F
|
||||
|
||||
// Forward declaration
|
||||
unsigned int k_printf(char *message, unsigned int line);
|
||||
|
||||
duk_context *ctx;
|
||||
|
||||
duk_ret_t native_addrw(duk_context *ctx)
|
||||
{
|
||||
// Get the address (first argument)
|
||||
uint32_t address = (uint32_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
// Get the value to write (second argument)
|
||||
uint8_t value = (uint8_t)duk_to_uint32(ctx, 1);
|
||||
|
||||
// Write the value to the address
|
||||
uint8_t *ptr = (uint8_t *)address;
|
||||
*ptr = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Port byte in
|
||||
duk_ret_t native_ptin(duk_context *ctx)
|
||||
{
|
||||
// Get the port (first argument)
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
uint8_t result;
|
||||
__asm__ volatile("inb %1, %0"
|
||||
: "=a"(result)
|
||||
: "Nd"(port));
|
||||
|
||||
duk_push_uint(ctx, (duk_uint_t)result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Port byte out
|
||||
duk_ret_t native_ptout(duk_context *ctx)
|
||||
{
|
||||
// Get the port (first argument)
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
// Get the value to write (second argument)
|
||||
uint8_t value = (uint8_t)duk_to_uint32(ctx, 1);
|
||||
|
||||
__asm__ volatile("outb %0, %1"
|
||||
:
|
||||
: "a"(value), "Nd"(port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
duk_ret_t native_byte_in(duk_context *ctx)
|
||||
{
|
||||
// Get the port (first argument)
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
uint8_t result;
|
||||
__asm__ volatile("inb %1, %0"
|
||||
: "=a"(result)
|
||||
: "Nd"(port));
|
||||
|
||||
duk_push_uint(ctx, (duk_uint_t)result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duk_ret_t native_byte_out(duk_context *ctx)
|
||||
{
|
||||
// Get the port (first argument)
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
// Get the value to write (second argument)
|
||||
uint8_t value = (uint8_t)duk_to_uint32(ctx, 1);
|
||||
|
||||
__asm__ volatile("outb %0, %1"
|
||||
:
|
||||
: "a"(value), "Nd"(port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
duk_ret_t native_dword_in(duk_context *ctx)
|
||||
{
|
||||
// Get the port (first argument)
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
uint32_t result;
|
||||
__asm__ volatile("inl %1, %0"
|
||||
: "=a"(result)
|
||||
: "Nd"(port));
|
||||
|
||||
duk_push_uint(ctx, (duk_uint_t)result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duk_ret_t native_dword_out(duk_context *ctx)
|
||||
{
|
||||
// Get the port (first argument)
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
// Get the value to write (second argument)
|
||||
uint32_t value = (uint32_t)duk_to_uint32(ctx, 1);
|
||||
|
||||
__asm__ volatile("outl %0, %1"
|
||||
:
|
||||
: "a"(value), "Nd"(port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
duk_ret_t native_word_in(duk_context *ctx)
|
||||
{
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
uint16_t result;
|
||||
__asm__ volatile("inw %1, %0"
|
||||
: "=a"(result)
|
||||
: "Nd"(port));
|
||||
|
||||
duk_push_uint(ctx, (duk_uint_t)result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duk_ret_t native_word_out(duk_context *ctx)
|
||||
{
|
||||
uint16_t port = (uint16_t)duk_to_uint32(ctx, 0);
|
||||
|
||||
// Get the value to write (second argument)
|
||||
uint16_t value = (uint16_t)duk_to_uint32(ctx, 1);
|
||||
|
||||
__asm__ volatile("outw %0, %1"
|
||||
:
|
||||
: "a"(value), "Nd"(port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Global context for IRQ handlers
|
||||
duk_context *global_ctx = NULL;
|
||||
|
||||
// Pending heaps to destroy
|
||||
duk_context *pending_heaps[16];
|
||||
int pending_heaps_count = 0;
|
||||
|
||||
void process_pending_heaps()
|
||||
{
|
||||
for (int i = 0; i < pending_heaps_count; i++)
|
||||
{
|
||||
if (pending_heaps[i] != NULL)
|
||||
{
|
||||
duk_destroy_heap(pending_heaps[i]);
|
||||
}
|
||||
}
|
||||
pending_heaps_count = 0;
|
||||
}
|
||||
|
||||
// JavaScript IRQ wrapper
|
||||
void js_irq_callback(registers_t *r)
|
||||
{
|
||||
if (global_ctx == NULL)
|
||||
return;
|
||||
|
||||
// Get IRQ number
|
||||
u8 irq_no = r->int_no - 32;
|
||||
|
||||
// Build the key for this IRQ handler manually (avoid snprintf in interrupt)
|
||||
char key[32] = "irq_handler_";
|
||||
int key_len = 12;
|
||||
if (irq_no >= 10)
|
||||
{
|
||||
key[key_len++] = '0' + (irq_no / 10);
|
||||
key[key_len++] = '0' + (irq_no % 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
key[key_len++] = '0' + irq_no;
|
||||
}
|
||||
key[key_len] = '\0';
|
||||
|
||||
// Call the JavaScript handler
|
||||
duk_push_global_stash(global_ctx);
|
||||
|
||||
if (duk_get_prop_string(global_ctx, -1, key))
|
||||
{
|
||||
// Function found, call it with IRQ number
|
||||
duk_push_uint(global_ctx, irq_no);
|
||||
|
||||
// Try protected call with error handler
|
||||
duk_int_t rc = duk_pcall(global_ctx, 1);
|
||||
duk_pop(global_ctx); // Pop result or error
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_pop(global_ctx); // Pop undefined value
|
||||
}
|
||||
|
||||
duk_pop(global_ctx); // Pop stash
|
||||
|
||||
// Process any pending heaps
|
||||
process_pending_heaps();
|
||||
}
|
||||
|
||||
// Native function to register IRQ from JavaScript
|
||||
duk_ret_t native_irq_register(duk_context *ctx)
|
||||
{
|
||||
// Get the IRQ number
|
||||
u8 irq = (u8)duk_to_uint32(ctx, 0);
|
||||
|
||||
// Get the handler function
|
||||
if (!duk_is_function(ctx, 1))
|
||||
{
|
||||
duk_push_boolean(ctx, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Store function in global stash
|
||||
char key[32];
|
||||
snprintf(key, sizeof(key), "irq_handler_%d", irq);
|
||||
|
||||
duk_push_global_stash(ctx);
|
||||
duk_dup(ctx, 1); // Duplicate the function
|
||||
duk_put_prop_string(ctx, -2, key);
|
||||
duk_pop(ctx); // Pop stash
|
||||
|
||||
// Register the C wrapper
|
||||
irq_register_handler(irq, js_irq_callback);
|
||||
|
||||
duk_push_boolean(ctx, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Proxy getter for __oskrnl that forwards to root context's globalThis.__oskrnl
|
||||
duk_ret_t oskrnl_proxy_getter(duk_context *isolated_ctx)
|
||||
{
|
||||
// Get the property name being accessed
|
||||
const char *prop_name = duk_require_string(isolated_ctx, 1);
|
||||
|
||||
// Get the root context from the hidden symbol in the isolated context
|
||||
duk_push_global_stash(isolated_ctx);
|
||||
duk_get_prop_string(isolated_ctx, -1, "\xFF"
|
||||
"root_ctx_ptr");
|
||||
duk_context *root_ctx = (duk_context *)duk_get_pointer(isolated_ctx, -1);
|
||||
duk_pop_2(isolated_ctx);
|
||||
|
||||
if (root_ctx == NULL)
|
||||
{
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Access globalThis.__oskrnl[prop_name] in the root context
|
||||
duk_push_global_object(root_ctx);
|
||||
duk_get_prop_string(root_ctx, -1, "__oskrnl");
|
||||
|
||||
if (!duk_is_object(root_ctx, -1))
|
||||
{
|
||||
duk_pop_2(root_ctx);
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duk_get_prop_string(root_ctx, -1, prop_name);
|
||||
|
||||
// If it's a function, we need to create a wrapper that calls it in root context
|
||||
if (duk_is_function(root_ctx, -1))
|
||||
{
|
||||
// Store the function reference in root context's stash
|
||||
char stash_key[128];
|
||||
snprintf(stash_key, sizeof(stash_key), "\xFF"
|
||||
"oskrnl_fn_%s",
|
||||
prop_name);
|
||||
|
||||
duk_push_global_stash(root_ctx);
|
||||
duk_dup(root_ctx, -2); // Duplicate the function
|
||||
duk_put_prop_string(root_ctx, -2, stash_key);
|
||||
duk_pop(root_ctx); // Pop stash
|
||||
|
||||
duk_pop_3(root_ctx); // Pop function, __oskrnl, globalThis
|
||||
|
||||
// Create a wrapper function in the isolated context
|
||||
// Use ES5 syntax compatible with Duktape
|
||||
const char *wrapper_code =
|
||||
"(function(propName) {"
|
||||
" return function() {"
|
||||
" var args = Array.prototype.slice.call(arguments);"
|
||||
" return __oskrnl_call_native(propName, args);"
|
||||
" };"
|
||||
"})";
|
||||
|
||||
duk_push_string(isolated_ctx, wrapper_code);
|
||||
if (duk_peval(isolated_ctx) != 0)
|
||||
{
|
||||
duk_pop(isolated_ctx);
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duk_push_string(isolated_ctx, prop_name);
|
||||
if (duk_pcall(isolated_ctx, 1) != 0)
|
||||
{
|
||||
duk_pop(isolated_ctx);
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// For non-function properties, just get the value (primitives)
|
||||
if (duk_is_number(root_ctx, -1))
|
||||
{
|
||||
double val = duk_get_number(root_ctx, -1);
|
||||
duk_pop_3(root_ctx);
|
||||
duk_push_number(isolated_ctx, val);
|
||||
return 1;
|
||||
}
|
||||
else if (duk_is_string(root_ctx, -1))
|
||||
{
|
||||
const char *val = duk_get_string(root_ctx, -1);
|
||||
duk_pop_3(root_ctx);
|
||||
duk_push_string(isolated_ctx, val);
|
||||
return 1;
|
||||
}
|
||||
else if (duk_is_boolean(root_ctx, -1))
|
||||
{
|
||||
int val = duk_get_boolean(root_ctx, -1);
|
||||
duk_pop_3(root_ctx);
|
||||
duk_push_boolean(isolated_ctx, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duk_pop_3(root_ctx);
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Callback bridge to call from root context back to isolated context
|
||||
duk_ret_t native_bridge_callback(duk_context *root_ctx)
|
||||
{
|
||||
// Get metadata from the current function (the bridge)
|
||||
duk_push_current_function(root_ctx);
|
||||
|
||||
duk_get_prop_string(root_ctx, -1, "\xFF"
|
||||
"isolated_ctx_ptr");
|
||||
duk_context *isolated_ctx = (duk_context *)duk_get_pointer(root_ctx, -1);
|
||||
duk_pop(root_ctx);
|
||||
|
||||
duk_get_prop_string(root_ctx, -1, "\xFF"
|
||||
"fn_id");
|
||||
const char *fn_id = duk_get_string(root_ctx, -1);
|
||||
duk_pop(root_ctx);
|
||||
|
||||
duk_pop(root_ctx); // Pop current function
|
||||
|
||||
if (isolated_ctx == NULL || fn_id == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the target function from isolated context stash
|
||||
duk_push_global_stash(isolated_ctx);
|
||||
duk_get_prop_string(isolated_ctx, -1, fn_id);
|
||||
duk_remove(isolated_ctx, -2); // Remove stash
|
||||
|
||||
if (!duk_is_function(isolated_ctx, -1))
|
||||
{
|
||||
duk_pop(isolated_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Marshal arguments from root to isolated
|
||||
duk_idx_t nargs = duk_get_top(root_ctx);
|
||||
for (duk_idx_t i = 0; i < nargs; i++)
|
||||
{
|
||||
if (duk_is_number(root_ctx, i))
|
||||
{
|
||||
duk_push_number(isolated_ctx, duk_get_number(root_ctx, i));
|
||||
}
|
||||
else if (duk_is_string(root_ctx, i))
|
||||
{
|
||||
duk_push_string(isolated_ctx, duk_get_string(root_ctx, i));
|
||||
}
|
||||
else if (duk_is_boolean(root_ctx, i))
|
||||
{
|
||||
duk_push_boolean(isolated_ctx, duk_get_boolean(root_ctx, i));
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_undefined(isolated_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the isolated function
|
||||
if (duk_pcall(isolated_ctx, nargs) != 0)
|
||||
{
|
||||
// Error occurred
|
||||
const char *err = duk_safe_to_string(isolated_ctx, -1);
|
||||
// k_printf((char *)"[Bridge] Error: ", 24);
|
||||
// k_printf((char *)err, 25);
|
||||
duk_pop(isolated_ctx); // Pop error
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Marshal return value back to root
|
||||
if (duk_is_number(isolated_ctx, -1))
|
||||
{
|
||||
duk_push_number(root_ctx, duk_get_number(isolated_ctx, -1));
|
||||
}
|
||||
else if (duk_is_string(isolated_ctx, -1))
|
||||
{
|
||||
duk_push_string(root_ctx, duk_get_string(isolated_ctx, -1));
|
||||
}
|
||||
else if (duk_is_boolean(isolated_ctx, -1))
|
||||
{
|
||||
duk_push_boolean(root_ctx, duk_get_boolean(isolated_ctx, -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_undefined(root_ctx);
|
||||
}
|
||||
|
||||
duk_pop(isolated_ctx); // Pop result
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Native helper to call root context oskrnl functions
|
||||
duk_ret_t oskrnl_call_native(duk_context *isolated_ctx)
|
||||
{
|
||||
// Get function name and arguments
|
||||
const char *fn_name = duk_require_string(isolated_ctx, 0);
|
||||
|
||||
if (!duk_is_array(isolated_ctx, 1))
|
||||
{
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get the root context
|
||||
duk_push_global_stash(isolated_ctx);
|
||||
duk_get_prop_string(isolated_ctx, -1, "\xFF"
|
||||
"root_ctx_ptr");
|
||||
duk_context *root_ctx = (duk_context *)duk_get_pointer(isolated_ctx, -1);
|
||||
duk_pop_2(isolated_ctx);
|
||||
|
||||
if (root_ctx == NULL)
|
||||
{
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get the function from root context stash
|
||||
char stash_key[128];
|
||||
snprintf(stash_key, sizeof(stash_key), "\xFF"
|
||||
"oskrnl_fn_%s",
|
||||
fn_name);
|
||||
|
||||
duk_push_global_stash(root_ctx);
|
||||
duk_get_prop_string(root_ctx, -1, stash_key);
|
||||
|
||||
if (!duk_is_function(root_ctx, -1))
|
||||
{
|
||||
duk_pop_2(root_ctx);
|
||||
duk_push_undefined(isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Transfer arguments from isolated context to root context
|
||||
duk_size_t arg_count = duk_get_length(isolated_ctx, 1);
|
||||
|
||||
for (duk_size_t i = 0; i < arg_count; i++)
|
||||
{
|
||||
duk_get_prop_index(isolated_ctx, 1, i);
|
||||
|
||||
if (duk_is_number(isolated_ctx, -1))
|
||||
{
|
||||
duk_push_number(root_ctx, duk_get_number(isolated_ctx, -1));
|
||||
}
|
||||
else if (duk_is_string(isolated_ctx, -1))
|
||||
{
|
||||
duk_push_string(root_ctx, duk_get_string(isolated_ctx, -1));
|
||||
}
|
||||
else if (duk_is_boolean(isolated_ctx, -1))
|
||||
{
|
||||
duk_push_boolean(root_ctx, duk_get_boolean(isolated_ctx, -1));
|
||||
}
|
||||
else if (duk_is_function(isolated_ctx, -1))
|
||||
{
|
||||
// Create a bridge function in root context
|
||||
static int bridge_cnt = 0;
|
||||
char bridge_id[32];
|
||||
snprintf(bridge_id, sizeof(bridge_id), "b_%d", bridge_cnt++);
|
||||
|
||||
// Stash isolated function
|
||||
duk_push_global_stash(isolated_ctx);
|
||||
duk_dup(isolated_ctx, -2);
|
||||
duk_put_prop_string(isolated_ctx, -2, bridge_id);
|
||||
duk_pop(isolated_ctx);
|
||||
|
||||
// Create bridge in root
|
||||
duk_push_c_function(root_ctx, native_bridge_callback, DUK_VARARGS);
|
||||
|
||||
// Attach metadata
|
||||
duk_push_pointer(root_ctx, isolated_ctx);
|
||||
duk_put_prop_string(root_ctx, -2, "\xFF"
|
||||
"isolated_ctx_ptr");
|
||||
|
||||
duk_push_string(root_ctx, bridge_id);
|
||||
duk_put_prop_string(root_ctx, -2, "\xFF"
|
||||
"fn_id");
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_undefined(root_ctx);
|
||||
}
|
||||
|
||||
duk_pop(isolated_ctx);
|
||||
}
|
||||
|
||||
// Call the function in root context
|
||||
duk_int_t rc = duk_pcall(root_ctx, arg_count);
|
||||
|
||||
// Transfer return value back to isolated context
|
||||
if (rc == 0)
|
||||
{
|
||||
if (duk_is_number(root_ctx, -1))
|
||||
{
|
||||
duk_push_number(isolated_ctx, duk_get_number(root_ctx, -1));
|
||||
}
|
||||
else if (duk_is_string(root_ctx, -1))
|
||||
{
|
||||
duk_push_string(isolated_ctx, duk_get_string(root_ctx, -1));
|
||||
}
|
||||
else if (duk_is_boolean(root_ctx, -1))
|
||||
{
|
||||
duk_push_boolean(isolated_ctx, duk_get_boolean(root_ctx, -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_push_undefined(isolated_ctx);
|
||||
}
|
||||
duk_pop(root_ctx); // Pop result
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_pop(root_ctx); // Pop error
|
||||
duk_push_undefined(isolated_ctx);
|
||||
}
|
||||
|
||||
duk_pop(root_ctx); // Pop stash
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Native function to execute JS code in isolated context with __oskrnl access
|
||||
duk_ret_t native_isolated_exec(duk_context *ctx)
|
||||
{
|
||||
// Get the JS code to execute
|
||||
const char *js_code = duk_require_string(ctx, 0);
|
||||
|
||||
// Create a new isolated Duktape heap
|
||||
duk_context *isolated_ctx = duk_create_heap_default();
|
||||
|
||||
if (isolated_ctx == NULL)
|
||||
{
|
||||
duk_push_boolean(ctx, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Store reference to root context in isolated context's stash
|
||||
duk_push_global_stash(isolated_ctx);
|
||||
duk_push_pointer(isolated_ctx, ctx);
|
||||
duk_put_prop_string(isolated_ctx, -2, "\xFF"
|
||||
"root_ctx_ptr");
|
||||
duk_pop(isolated_ctx);
|
||||
|
||||
// Register the native call helper in isolated context
|
||||
duk_push_c_function(isolated_ctx, oskrnl_call_native, 2);
|
||||
duk_put_global_string(isolated_ctx, "__oskrnl_call_native");
|
||||
|
||||
// Register the getter function
|
||||
duk_push_c_function(isolated_ctx, oskrnl_proxy_getter, 2);
|
||||
duk_put_global_string(isolated_ctx, "__oskrnl_get_prop");
|
||||
|
||||
// Create __oskrnl proxy object using ES6 Proxy
|
||||
const char *proxy_code =
|
||||
"(function() {"
|
||||
" var handler = {"
|
||||
" get: function(target, prop) {"
|
||||
" if (typeof prop === 'symbol') return undefined;"
|
||||
" return __oskrnl_get_prop(null, String(prop));"
|
||||
" }"
|
||||
" };"
|
||||
" return new Proxy({}, handler);"
|
||||
"})()";
|
||||
|
||||
duk_push_string(isolated_ctx, proxy_code);
|
||||
|
||||
if (duk_peval(isolated_ctx) != 0)
|
||||
{
|
||||
duk_pop(isolated_ctx);
|
||||
duk_destroy_heap(isolated_ctx);
|
||||
duk_push_boolean(ctx, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
duk_put_global_string(isolated_ctx, "__oskrnl");
|
||||
|
||||
// Execute the provided JS code
|
||||
duk_push_string(isolated_ctx, js_code);
|
||||
duk_int_t rc = duk_peval(isolated_ctx);
|
||||
|
||||
// Check for errors
|
||||
int success = (rc == 0);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
// Transfer error message back to root context for display
|
||||
const char *err = duk_safe_to_string(isolated_ctx, -1);
|
||||
k_printf((char *)"[Isolated] Execution error: ", 12);
|
||||
k_printf((char *)err, 13);
|
||||
}
|
||||
|
||||
duk_pop(isolated_ctx); // Pop result or error
|
||||
|
||||
// Clean up the isolated heap
|
||||
// duk_destroy_heap(isolated_ctx);
|
||||
|
||||
// Return success/failure
|
||||
// Return the heap pointer as a number (uint32_t)
|
||||
duk_push_uint(ctx, (duk_uint_t)isolated_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Native function to destroy an isolated heap
|
||||
duk_ret_t native_destroy_isolated_heap(duk_context *ctx)
|
||||
{
|
||||
// Get the heap pointer
|
||||
duk_uint_t heap_ptr = duk_require_uint(ctx, 0);
|
||||
duk_context *isolated_ctx = (duk_context *)heap_ptr;
|
||||
|
||||
if (isolated_ctx != NULL)
|
||||
{
|
||||
if (pending_heaps_count < 16)
|
||||
{
|
||||
pending_heaps[pending_heaps_count++] = isolated_ctx;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kmain()
|
||||
{
|
||||
// Initialize IDT and ISRs
|
||||
isr_install();
|
||||
irq_install();
|
||||
|
||||
// Initialize Duktape heap
|
||||
ctx = duk_create_heap_default();
|
||||
global_ctx = ctx;
|
||||
|
||||
// Register native memory write function
|
||||
duk_push_c_function(ctx, native_addrw, 2);
|
||||
duk_put_global_string(ctx, "$addrw");
|
||||
|
||||
// Register native port in function
|
||||
duk_push_c_function(ctx, native_ptin, 1);
|
||||
duk_put_global_string(ctx, "$ptin");
|
||||
|
||||
// Register native port out function
|
||||
duk_push_c_function(ctx, native_ptout, 2);
|
||||
duk_put_global_string(ctx, "$ptout");
|
||||
|
||||
// Register native byte in function
|
||||
duk_push_c_function(ctx, native_byte_in, 1);
|
||||
duk_put_global_string(ctx, "$bytein");
|
||||
|
||||
// Register native byte out function
|
||||
duk_push_c_function(ctx, native_byte_out, 2);
|
||||
duk_put_global_string(ctx, "$byteout");
|
||||
|
||||
// Register native dword in function
|
||||
duk_push_c_function(ctx, native_dword_in, 1);
|
||||
duk_put_global_string(ctx, "$dwordin");
|
||||
|
||||
// Register native dword out function
|
||||
duk_push_c_function(ctx, native_dword_out, 2);
|
||||
duk_put_global_string(ctx, "$dwordout");
|
||||
|
||||
// Register native dword in function
|
||||
duk_push_c_function(ctx, native_word_in, 1);
|
||||
duk_put_global_string(ctx, "$wordin");
|
||||
|
||||
// Register native dword out function
|
||||
duk_push_c_function(ctx, native_word_out, 2);
|
||||
duk_put_global_string(ctx, "$wordout");
|
||||
|
||||
// Register native IRQ registration function
|
||||
duk_push_c_function(ctx, native_irq_register, 2);
|
||||
duk_put_global_string(ctx, "$irqregister");
|
||||
|
||||
// Register native isolated execution function
|
||||
duk_push_c_function(ctx, native_isolated_exec, 1);
|
||||
duk_put_global_string(ctx, "$isolatedExec");
|
||||
|
||||
// Register native isolated heap destruction function
|
||||
duk_push_c_function(ctx, native_destroy_isolated_heap, 1);
|
||||
duk_put_global_string(ctx, "$destroyIsolatedHeap");
|
||||
|
||||
// Enable interrupts before JavaScript execution
|
||||
__asm__ volatile("sti");
|
||||
|
||||
// Execute embedded JavaScript code from build/index.js
|
||||
duk_push_string(ctx, embedded_js_code);
|
||||
duk_int_t returnCode = duk_peval(ctx);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
// Error occurred - display stack trace
|
||||
duk_safe_to_stacktrace(ctx, -1);
|
||||
k_printf((char *)duk_safe_to_string(ctx, -1), 1);
|
||||
}
|
||||
|
||||
duk_pop(ctx);
|
||||
}
|
||||
|
||||
unsigned int k_printf(char *message, unsigned int line)
|
||||
{
|
||||
char *vidmem = (char *)0xb8000;
|
||||
unsigned int i = 0;
|
||||
|
||||
i = (line * 80 * 2);
|
||||
|
||||
while (*message != 0)
|
||||
{
|
||||
if (*message == '\n') // check for a new line
|
||||
{
|
||||
line++;
|
||||
i = (line * 80 * 2);
|
||||
*message++;
|
||||
}
|
||||
else
|
||||
{
|
||||
vidmem[i] = *message;
|
||||
*message++;
|
||||
i++;
|
||||
vidmem[i] = WHITE_TXT;
|
||||
i++;
|
||||
};
|
||||
};
|
||||
|
||||
return (1);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Minimal system stubs for freestanding picolibc
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Exit function - halt the system */
|
||||
void _exit(int status)
|
||||
{
|
||||
(void)status;
|
||||
/* Halt the CPU */
|
||||
while (1)
|
||||
{
|
||||
__asm__ volatile("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
/* Get time of day - not supported in freestanding */
|
||||
int gettimeofday(struct timeval *tv, void *tz)
|
||||
{
|
||||
(void)tz;
|
||||
if (tv)
|
||||
{
|
||||
tv->tv_sec = 0;
|
||||
tv->tv_usec = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sbrk - memory allocation (picolibc uses __heap_start and __heap_end from linker script) */
|
||||
/* No need to implement sbrk as picolibc uses the linker symbols directly */
|
||||
40
src/link.ld
40
src/link.ld
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* link.ld
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT(elf32-i386)
|
||||
ENTRY(start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000;
|
||||
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
/* Stack grows downward from high memory - place before heap */
|
||||
. = ALIGN(4096);
|
||||
__stack_bottom = .;
|
||||
. = . + 0x100000; /* 1MB stack */
|
||||
__stack_top = .;
|
||||
|
||||
/* Heap for malloc */
|
||||
. = ALIGN(4096);
|
||||
__heap_start = .;
|
||||
. = . + 0x10000000; /* 256MB heap */
|
||||
__heap_end = .;
|
||||
}
|
||||
34
src/os/.gitignore
vendored
34
src/os/.gitignore
vendored
@@ -1,34 +0,0 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "os",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
|
||||
|
||||
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
|
||||
|
||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "os",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
export const Keycode = {
|
||||
Enter: 28,
|
||||
Backspace: 14,
|
||||
Q: 16,
|
||||
W: 17,
|
||||
E: 18,
|
||||
R: 19,
|
||||
T: 20,
|
||||
Y: 21,
|
||||
U: 22,
|
||||
I: 23,
|
||||
O: 24,
|
||||
P: 25,
|
||||
A: 30,
|
||||
S: 31,
|
||||
D: 32,
|
||||
F: 33,
|
||||
G: 34,
|
||||
H: 35,
|
||||
J: 36,
|
||||
K: 37,
|
||||
L: 38,
|
||||
Z: 44,
|
||||
X: 45,
|
||||
C: 46,
|
||||
V: 47,
|
||||
B: 48,
|
||||
N: 49,
|
||||
M: 50,
|
||||
".": 52,
|
||||
"/": 53,
|
||||
" ": 57,
|
||||
};
|
||||
|
||||
export const RKeycode = {
|
||||
28: "Enter",
|
||||
14: "Backspace",
|
||||
16: "Q",
|
||||
17: "W",
|
||||
18: "E",
|
||||
19: "R",
|
||||
20: "T",
|
||||
21: "Y",
|
||||
22: "U",
|
||||
23: "I",
|
||||
24: "O",
|
||||
25: "P",
|
||||
30: "A",
|
||||
31: "S",
|
||||
32: "D",
|
||||
33: "F",
|
||||
34: "G",
|
||||
35: "H",
|
||||
36: "J",
|
||||
37: "K",
|
||||
38: "L",
|
||||
44: "Z",
|
||||
45: "X",
|
||||
46: "C",
|
||||
47: "V",
|
||||
48: "B",
|
||||
49: "N",
|
||||
50: "M",
|
||||
52: ".",
|
||||
53: "/",
|
||||
57: " ",
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
import { kmain } from "./kernel";
|
||||
import { kpanic } from "./kernel/panic";
|
||||
|
||||
try {
|
||||
kmain();
|
||||
} catch (e) {
|
||||
kpanic(e instanceof Error ? e.message : String(e));
|
||||
}
|
||||
@@ -1,365 +0,0 @@
|
||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||
import { bytein, byteout } from "../../../lib/libts/byte";
|
||||
import { dwordin } from "../../../lib/libts/dword";
|
||||
import { wordin } from "../../../lib/libts/word";
|
||||
import type { AtaDriveDescriptor } from "../../../types/dev/ata/drive";
|
||||
import type { BlockCache } from "../../../types/utils/block_cache";
|
||||
import {
|
||||
blockCache_block,
|
||||
blockCache_create,
|
||||
blockCache_init,
|
||||
} from "../../utils/block_cache";
|
||||
|
||||
const KDRIVER_DEV_ATA_PRIMARY = 0x1f0;
|
||||
const KDRIVER_DEV_ATA_SECONDARY = 0x170;
|
||||
const KDRIVER_DEV_ATA_PRIMARY_CTL = 0x3f6;
|
||||
const KDRIVER_DEV_ATA_SECONDARY_CTL = 0x376;
|
||||
const KDRIVER_DEV_ATA_CTL_nIEN = 0x02;
|
||||
const KDRIVER_DEV_ATA_MASTER_BIT = 0;
|
||||
const KDRIVER_DEV_ATA_SLAVE_BIT = 1;
|
||||
const KDRIVER_DEV_ATA_DRV_HEAD = 6;
|
||||
const KDRIVER_DEV_ATA_NSECTOR = 2;
|
||||
const KDRIVER_DEV_ATA_SECTOR = 3;
|
||||
const KDRIVER_DEV_ATA_LCYL = 4;
|
||||
const KDRIVER_DEV_ATA_HCYL = 5;
|
||||
const KDRIVER_DEV_ATA_STATUS = 7;
|
||||
const KDRIVER_DEV_ATA_COMMAND = 7;
|
||||
const KDRIVER_DEV_ATA_IDENTIFY = 0xec;
|
||||
const KDRIVER_DEV_ATA_READ_SECTORS = 0x20;
|
||||
const KDRIVER_DEV_ATA_STATUS_ERROR = 0x01;
|
||||
const KDRIVER_DEV_ATA_STATUS_BSY = 0x80;
|
||||
const KDRIVER_DEV_ATA_STATUS_DRQ = 0x08;
|
||||
const KDRIVER_DEV_ATA_DATA = 0;
|
||||
const KDRIVER_DEV_ATA_BLOCK_SIZE = 512;
|
||||
|
||||
let drives: AtaDriveDescriptor[] = [];
|
||||
let cache = blockCache_create();
|
||||
|
||||
export function kdriver_dev_ata_init(): void {}
|
||||
|
||||
export function kdriver_dev_ata_400ns_delay(controller: number) {
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
bytein(controller + KDRIVER_DEV_ATA_STATUS);
|
||||
}
|
||||
|
||||
function ide_string(info: number[], start: number, size: number): string {
|
||||
const buffer = new Array(50);
|
||||
|
||||
const t = info.slice(start);
|
||||
for (let i = 0; i < size; ++i) {
|
||||
const wordIndex = Math.floor(i / 2);
|
||||
const isHighByte = i % 2 === 1;
|
||||
|
||||
buffer[i] = isHighByte ? (t[wordIndex]! >> 8) & 0xff : t[wordIndex]! & 0xff;
|
||||
}
|
||||
|
||||
for (let i = 0; i < size; i += 2) {
|
||||
const c = buffer[i];
|
||||
buffer[i] = buffer[i + 1];
|
||||
buffer[i + 1] = c;
|
||||
}
|
||||
|
||||
let end = size - 1;
|
||||
while (true) {
|
||||
const c = buffer[end];
|
||||
|
||||
if (c > 32 && c < 127) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (end == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
--end;
|
||||
}
|
||||
|
||||
//buffer[end + 1] = 0;
|
||||
|
||||
let target = "";
|
||||
for (let i = 0; i <= end; i++) {
|
||||
target += String.fromCharCode(buffer[i]);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
export function kdriver_dev_ata_identify(drive: AtaDriveDescriptor) {
|
||||
byteout(
|
||||
drive.controller + KDRIVER_DEV_ATA_DRV_HEAD,
|
||||
0xa0 | (drive.slave << 4)
|
||||
);
|
||||
kdriver_dev_ata_400ns_delay(drive.controller);
|
||||
|
||||
const floatingBus = bytein(drive.controller + KDRIVER_DEV_ATA_STATUS);
|
||||
if (floatingBus == 0xff) {
|
||||
return;
|
||||
}
|
||||
|
||||
byteout(drive.controller + KDRIVER_DEV_ATA_COMMAND, KDRIVER_DEV_ATA_IDENTIFY);
|
||||
kdriver_dev_ata_400ns_delay(drive.controller);
|
||||
|
||||
if (bytein(drive.controller + KDRIVER_DEV_ATA_STATUS) == 0) return;
|
||||
|
||||
let notAta = false;
|
||||
let iterations = 0;
|
||||
while (1) {
|
||||
const status = bytein(drive.controller + KDRIVER_DEV_ATA_STATUS);
|
||||
iterations++;
|
||||
|
||||
if (status & KDRIVER_DEV_ATA_STATUS_ERROR) {
|
||||
notAta = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
!(status & KDRIVER_DEV_ATA_STATUS_BSY) &&
|
||||
status & KDRIVER_DEV_ATA_STATUS_DRQ
|
||||
)
|
||||
break;
|
||||
|
||||
if (iterations > 100000) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
drive.atapi = false;
|
||||
|
||||
if (notAta) {
|
||||
const cl = bytein(drive.controller + KDRIVER_DEV_ATA_LCYL);
|
||||
const ch = bytein(drive.controller + KDRIVER_DEV_ATA_HCYL);
|
||||
|
||||
if (cl == 0x14 && ch == 0xeb) drive.atapi = true;
|
||||
else if (cl == 0x69 && ch == 0x96) drive.atapi = true;
|
||||
else return;
|
||||
|
||||
byteout(drive.controller + KDRIVER_DEV_ATA_COMMAND, 0xa1);
|
||||
kdriver_dev_ata_400ns_delay(drive.controller);
|
||||
}
|
||||
|
||||
drive.present = true;
|
||||
|
||||
const info = new Array(256);
|
||||
for (let i = 0; i < 256; i++) {
|
||||
info[i] = wordin(drive.controller + KDRIVER_DEV_ATA_DATA);
|
||||
}
|
||||
|
||||
drive.model = ide_string(info, 27, 40);
|
||||
drive.serial = ide_string(info, 10, 20);
|
||||
drive.firmware = ide_string(info, 23, 8);
|
||||
|
||||
const sectors = info[0] + 114;
|
||||
drive.size = sectors * 4096;
|
||||
|
||||
Logger.log(
|
||||
"[ATA] Found drive " +
|
||||
drive.model +
|
||||
" (" +
|
||||
drive.serial +
|
||||
") FW: " +
|
||||
drive.firmware +
|
||||
" Size: " +
|
||||
drive.size
|
||||
);
|
||||
}
|
||||
|
||||
export function kdriver_dev_ata_numberOfDrives(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
export function kdriver_dev_ata_getDrive(n: number) {
|
||||
return drives[n]!;
|
||||
}
|
||||
|
||||
export function kdriver_dev_ata_detectDisks(): void {
|
||||
blockCache_init(cache, KDRIVER_DEV_ATA_BLOCK_SIZE, 256);
|
||||
|
||||
drives = new Array<AtaDriveDescriptor>(4);
|
||||
|
||||
drives[0] = {
|
||||
controller: KDRIVER_DEV_ATA_PRIMARY,
|
||||
drive: 0xe0,
|
||||
present: false,
|
||||
slave: KDRIVER_DEV_ATA_MASTER_BIT,
|
||||
atapi: false,
|
||||
model: "",
|
||||
serial: "",
|
||||
firmware: "",
|
||||
size: 0,
|
||||
};
|
||||
drives[1] = {
|
||||
controller: KDRIVER_DEV_ATA_PRIMARY,
|
||||
drive: 0xf0,
|
||||
present: false,
|
||||
slave: KDRIVER_DEV_ATA_SLAVE_BIT,
|
||||
atapi: false,
|
||||
model: "",
|
||||
serial: "",
|
||||
firmware: "",
|
||||
size: 0,
|
||||
};
|
||||
drives[2] = {
|
||||
controller: KDRIVER_DEV_ATA_SECONDARY,
|
||||
drive: 0xe0,
|
||||
present: false,
|
||||
slave: KDRIVER_DEV_ATA_MASTER_BIT,
|
||||
atapi: false,
|
||||
model: "",
|
||||
serial: "",
|
||||
firmware: "",
|
||||
size: 0,
|
||||
};
|
||||
drives[3] = {
|
||||
controller: KDRIVER_DEV_ATA_SECONDARY,
|
||||
drive: 0xf0,
|
||||
present: false,
|
||||
slave: KDRIVER_DEV_ATA_SLAVE_BIT,
|
||||
atapi: false,
|
||||
model: "",
|
||||
serial: "",
|
||||
firmware: "",
|
||||
size: 0,
|
||||
};
|
||||
|
||||
byteout(KDRIVER_DEV_ATA_PRIMARY_CTL, KDRIVER_DEV_ATA_CTL_nIEN);
|
||||
byteout(KDRIVER_DEV_ATA_SECONDARY_CTL, KDRIVER_DEV_ATA_CTL_nIEN);
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const drive = drives[i]!;
|
||||
|
||||
kdriver_dev_ata_identify(drive);
|
||||
}
|
||||
|
||||
byteout(KDRIVER_DEV_ATA_PRIMARY_CTL, 0);
|
||||
byteout(KDRIVER_DEV_ATA_SECONDARY_CTL, 0);
|
||||
|
||||
// TODO: INTERRUPTS
|
||||
}
|
||||
|
||||
export function kdriver_dev_ata_readSectors(
|
||||
descriptor: AtaDriveDescriptor,
|
||||
start: number,
|
||||
count: number,
|
||||
read: number
|
||||
) {
|
||||
let buffer: number[] = [];
|
||||
|
||||
for (let i = 0; i < count; ++i) {
|
||||
const sectorNum = start + i;
|
||||
const valid = { valid: false };
|
||||
const block = blockCache_block(
|
||||
cache,
|
||||
(descriptor.controller << 8) + descriptor.drive,
|
||||
sectorNum,
|
||||
valid
|
||||
);
|
||||
|
||||
if (!valid.valid) {
|
||||
// Need to actually read from disk
|
||||
byteout(
|
||||
descriptor.controller + KDRIVER_DEV_ATA_DRV_HEAD,
|
||||
0xe0 | (descriptor.slave << 4) | ((sectorNum >> 24) & 0x0f)
|
||||
);
|
||||
byteout(descriptor.controller + KDRIVER_DEV_ATA_NSECTOR, 1);
|
||||
byteout(descriptor.controller + KDRIVER_DEV_ATA_SECTOR, sectorNum & 0xff);
|
||||
byteout(
|
||||
descriptor.controller + KDRIVER_DEV_ATA_LCYL,
|
||||
(sectorNum >> 8) & 0xff
|
||||
);
|
||||
byteout(
|
||||
descriptor.controller + KDRIVER_DEV_ATA_HCYL,
|
||||
(sectorNum >> 16) & 0xff
|
||||
);
|
||||
byteout(
|
||||
descriptor.controller + KDRIVER_DEV_ATA_COMMAND,
|
||||
KDRIVER_DEV_ATA_READ_SECTORS
|
||||
);
|
||||
|
||||
// Wait for drive to be ready
|
||||
let timeout = 0;
|
||||
while (timeout < 100000) {
|
||||
const status = bytein(descriptor.controller + KDRIVER_DEV_ATA_STATUS);
|
||||
if (
|
||||
!(status & KDRIVER_DEV_ATA_STATUS_BSY) &&
|
||||
status & KDRIVER_DEV_ATA_STATUS_DRQ
|
||||
) {
|
||||
break;
|
||||
}
|
||||
timeout++;
|
||||
}
|
||||
|
||||
// Read 256 words (512 bytes)
|
||||
for (let j = 0; j < 256; j++) {
|
||||
const word = wordin(descriptor.controller + KDRIVER_DEV_ATA_DATA);
|
||||
block[j * 2] = word & 0xff;
|
||||
block[j * 2 + 1] = (word >> 8) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the block to the output buffer
|
||||
for (let n = 0; n < block.length; n++) {
|
||||
buffer.push(block[n]!);
|
||||
}
|
||||
|
||||
read += 512;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function kdriver_dev_ata_fsDriver_read(
|
||||
data: AtaDriveDescriptor,
|
||||
count: number,
|
||||
offset: number
|
||||
) {
|
||||
if (count % KDRIVER_DEV_ATA_BLOCK_SIZE != 0) throw new Error("Invalid count");
|
||||
if (offset % KDRIVER_DEV_ATA_BLOCK_SIZE != 0)
|
||||
throw new Error("Invalid offset");
|
||||
|
||||
const sectors = count / KDRIVER_DEV_ATA_BLOCK_SIZE;
|
||||
const start = offset / KDRIVER_DEV_ATA_BLOCK_SIZE;
|
||||
|
||||
const buffer = kdriver_dev_ata_readSectors(data, start, sectors, 0);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function kdriver_dev_ata_partition_fsDriver_read(
|
||||
data: any,
|
||||
count: number,
|
||||
offset: number
|
||||
) {
|
||||
if (count % KDRIVER_DEV_ATA_BLOCK_SIZE != 0) throw new Error("Invalid count");
|
||||
if (offset % KDRIVER_DEV_ATA_BLOCK_SIZE != 0)
|
||||
throw new Error("Invalid offset");
|
||||
|
||||
const partition = data;
|
||||
const disk = partition.disk as { descriptor: AtaDriveDescriptor };
|
||||
|
||||
const sectors = count / KDRIVER_DEV_ATA_BLOCK_SIZE;
|
||||
const start = partition.lbaStart + offset / KDRIVER_DEV_ATA_BLOCK_SIZE;
|
||||
|
||||
const buffer = kdriver_dev_ata_readSectors(
|
||||
disk.descriptor,
|
||||
start,
|
||||
sectors,
|
||||
0
|
||||
);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export const kdriver_dev_ata_fsDriver = {
|
||||
read: kdriver_dev_ata_fsDriver_read,
|
||||
};
|
||||
|
||||
export const kdriver_dev_ata_partition_fsDriver = {
|
||||
read: kdriver_dev_ata_partition_fsDriver_read,
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Keycode, RKeycode } from "../../../config/keyboard";
|
||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||
import { bytein } from "../../../lib/libts/byte";
|
||||
import { kmod_cpu_irq_register } from "../../modules/cpu/interrupts";
|
||||
import { kmod_terminal_input_handleKeyboardInput } from "../../modules/terminal/input";
|
||||
|
||||
export function kdriver_dev_keyboard_handleInterrupt(): void {
|
||||
const scancode = bytein(0x60);
|
||||
|
||||
const key = RKeycode[scancode as keyof typeof RKeycode] as
|
||||
| keyof typeof Keycode
|
||||
| undefined;
|
||||
|
||||
//Logger.log(String(scancode));
|
||||
|
||||
if (key == undefined) return;
|
||||
|
||||
kmod_terminal_input_handleKeyboardInput(key);
|
||||
}
|
||||
|
||||
export function kdriver_dev_keyboard_init(): void {
|
||||
if (!kmod_cpu_irq_register(1, kdriver_dev_keyboard_handleInterrupt, null)) {
|
||||
Logger.log("[keyboard] Failed to register keyboard interrupt");
|
||||
return;
|
||||
}
|
||||
|
||||
let key = 0;
|
||||
while ((key = bytein(0x64) & 1) == 1) {
|
||||
bytein(0x60);
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import { dwordin, dwordout } from "../../../lib/libts/dword";
|
||||
import { portin, portout } from "../../../lib/libts/port";
|
||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||
import { sysfs_mkdir, sysfs_writeFile } from "../../filesystem/sysfs";
|
||||
import { Path } from "../../../lib/libstd/path";
|
||||
|
||||
const KDRIVER_PCI_CONFIG_ADDR = 0xcf8;
|
||||
const KDRIVER_PCI_CONFIG_DATA = 0xcfc;
|
||||
|
||||
export function kdriver_dev_pci_init() {}
|
||||
|
||||
export function kdriver_dev_pci_detectDevices() {
|
||||
Logger.log("[PCI] Scanning...");
|
||||
|
||||
for (let bus = 0; bus < 256; bus++) {
|
||||
for (let device = 0; device < 32; device++) {
|
||||
kdriver_dev_pci_checkDevice(bus, device, 0);
|
||||
|
||||
const headerType = kdriver_dev_pci_getHeaderType(bus, device, 0);
|
||||
if ((headerType & 0x80) !== 0) {
|
||||
for (let func = 1; func < 8; func++) {
|
||||
kdriver_dev_pci_checkDevice(bus, device, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function kdriver_dev_pci_checkDevice(
|
||||
bus: number,
|
||||
device: number,
|
||||
func: number
|
||||
) {
|
||||
const vendorId = kdriver_dev_pci_getVendorId(bus, device, func);
|
||||
|
||||
if (vendorId === 0xffff) return;
|
||||
|
||||
const deviceId = kdriver_dev_pci_getDeviceId(bus, device, func);
|
||||
const classCode = kdriver_dev_pci_getClassCode(bus, device, func);
|
||||
const subclass = kdriver_dev_pci_getSubclass(bus, device, func);
|
||||
|
||||
const filename = bus + ":" + device + ":" + func;
|
||||
|
||||
Logger.log(
|
||||
"[PCI] Found device pci:" +
|
||||
filename +
|
||||
" (vendor:" +
|
||||
vendorId.toString(16) +
|
||||
", device:" +
|
||||
deviceId.toString(16) +
|
||||
", class:" +
|
||||
classCode.toString(16) +
|
||||
", subclass:" +
|
||||
subclass.toString(16) +
|
||||
")"
|
||||
);
|
||||
|
||||
const dirname = Path.join("/pci", filename);
|
||||
|
||||
sysfs_mkdir(dirname);
|
||||
sysfs_writeFile(Path.join(dirname, "vendor"), vendorId);
|
||||
sysfs_writeFile(Path.join(dirname, "device"), deviceId);
|
||||
sysfs_writeFile(Path.join(dirname, "class"), classCode);
|
||||
sysfs_writeFile(Path.join(dirname, "subclass"), subclass);
|
||||
}
|
||||
|
||||
export function kdriver_dev_pci_getDeviceId(
|
||||
bus: number,
|
||||
device: number,
|
||||
func: number
|
||||
) {
|
||||
const data = kdriver_dev_pci_readConfigDword(bus, device, func, 0);
|
||||
|
||||
return data >> 16;
|
||||
}
|
||||
|
||||
export function kdriver_dev_pci_getVendorId(
|
||||
bus: number,
|
||||
device: number,
|
||||
func: number
|
||||
) {
|
||||
const data = kdriver_dev_pci_readConfigDword(bus, device, func, 0);
|
||||
|
||||
return data & 0xffff;
|
||||
}
|
||||
|
||||
export function kdriver_dev_pci_getClassCode(
|
||||
bus: number,
|
||||
device: number,
|
||||
func: number
|
||||
) {
|
||||
const data = kdriver_dev_pci_readConfigDword(bus, device, func, 8);
|
||||
|
||||
return (data >> 24) & 0xff;
|
||||
}
|
||||
|
||||
export function kdriver_dev_pci_getSubclass(
|
||||
bus: number,
|
||||
device: number,
|
||||
func: number
|
||||
) {
|
||||
const data = kdriver_dev_pci_readConfigDword(bus, device, func, 8);
|
||||
|
||||
return (data >> 16) & 0xff;
|
||||
}
|
||||
|
||||
export function kdriver_dev_pci_getHeaderType(
|
||||
bus: number,
|
||||
device: number,
|
||||
func: number
|
||||
) {
|
||||
const data = kdriver_dev_pci_readConfigDword(bus, device, func, 12);
|
||||
|
||||
return (data >> 16) & 0xff;
|
||||
}
|
||||
|
||||
export function kdriver_dev_pci_readConfigDword(
|
||||
bus: number,
|
||||
device: number,
|
||||
func: number,
|
||||
offset: number
|
||||
) {
|
||||
const address =
|
||||
(1 << 31) | (bus << 16) | (device << 11) | (func << 8) | (offset & 0xfc);
|
||||
|
||||
dwordout(KDRIVER_PCI_CONFIG_ADDR, address);
|
||||
const data = dwordin(KDRIVER_PCI_CONFIG_DATA);
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { portin, portout } from "../../../lib/libts/port";
|
||||
|
||||
const SERIAL_PORT_COM1 = 0x3f8;
|
||||
|
||||
export function kdriver_etc_serial_init() {
|
||||
portout(SERIAL_PORT_COM1 + 1, 0x00); // Disable all interrupts
|
||||
portout(SERIAL_PORT_COM1 + 3, 0x80); // Enable DLAB
|
||||
portout(SERIAL_PORT_COM1 + 0, 0x03); // 38400 baud
|
||||
portout(SERIAL_PORT_COM1 + 1, 0x00);
|
||||
portout(SERIAL_PORT_COM1 + 3, 0x03); // 8 bits, no parity, one stop bit
|
||||
portout(SERIAL_PORT_COM1 + 2, 0xc7); // Enable FIFO, clear them, with 14-byte threshold
|
||||
portout(SERIAL_PORT_COM1 + 4, 0x0b);
|
||||
}
|
||||
|
||||
export function kdriver_etc_serial_isTransmitBufferEmpty(): boolean {
|
||||
return (portin(SERIAL_PORT_COM1 + 5) & 0x20) !== 0;
|
||||
}
|
||||
|
||||
export function kdriver_etc_serial_transmit(byte: number): void {
|
||||
while (!kdriver_etc_serial_isTransmitBufferEmpty()) {}
|
||||
|
||||
portout(SERIAL_PORT_COM1, byte);
|
||||
}
|
||||
|
||||
export function kdriver_etc_serial_read(): number {
|
||||
return portin(SERIAL_PORT_COM1);
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
import { Path } from "../../lib/libstd/path";
|
||||
import type { AtaDriveDescriptor } from "../../types/dev/ata/drive";
|
||||
import type { DeviceType } from "../../types/dev/device";
|
||||
import type {
|
||||
KFilesystemDevice,
|
||||
KFilesystemDirectReadResponse,
|
||||
KFilesystemDriver,
|
||||
KFilesystemEntity,
|
||||
KFilesystemMemdata,
|
||||
KFilesystemMemdev,
|
||||
KFilesystemStat,
|
||||
} from "../../types/filesystem/fs";
|
||||
|
||||
const devfs_data: KFilesystemMemdev = [];
|
||||
|
||||
export function devfs_driver(): KFilesystemDriver {
|
||||
return {
|
||||
id: "devfs",
|
||||
init: devfs_init,
|
||||
listDir(path: string): (KFilesystemEntity | KFilesystemDevice)[] | null {
|
||||
return devfs_listDir(path);
|
||||
},
|
||||
readFile(path: string): unknown {
|
||||
throw new Error("Cannot read files in devfs.");
|
||||
},
|
||||
writeFile(path: string, content: unknown): void {
|
||||
throw new Error("Cannot write files in devfs.");
|
||||
},
|
||||
createFile(path: string, content: unknown): void {
|
||||
throw new Error("Cannot create files in devfs.");
|
||||
},
|
||||
mkdir(path: string): void {
|
||||
throw new Error("Cannot make directories in devfs.");
|
||||
},
|
||||
stat(path: string): KFilesystemStat | null {
|
||||
return devfs_statEntity(path);
|
||||
},
|
||||
rm(path: string): void {
|
||||
throw new Error("Cannot remove objects in devfs.")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function devfs_init(): void {
|
||||
devfs_data.push({
|
||||
name: "root",
|
||||
path: "/",
|
||||
type: "folder",
|
||||
size: 0,
|
||||
contents: null,
|
||||
});
|
||||
}
|
||||
|
||||
export function devfs_getEntity(
|
||||
path: string
|
||||
): KFilesystemDevice | KFilesystemEntity | null {
|
||||
for (let i = 0; i < devfs_data.length; i++) {
|
||||
const entity = devfs_data[i]!;
|
||||
|
||||
if (entity.path === path) return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function devfs_statEntity(path: string): KFilesystemStat | null {
|
||||
const entity = devfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
return {
|
||||
name: entity.name,
|
||||
type: entity.type,
|
||||
size: entity.size,
|
||||
};
|
||||
}
|
||||
|
||||
export function devfs_mkdir(path: string): void {
|
||||
if (devfs_getEntity(path)) return;
|
||||
|
||||
const parent = Path.dirname(path);
|
||||
if (!devfs_getEntity(parent)) return;
|
||||
|
||||
devfs_data.push({
|
||||
name: Path.filename(path),
|
||||
path,
|
||||
type: "folder",
|
||||
size: 0,
|
||||
contents: null,
|
||||
});
|
||||
}
|
||||
|
||||
export function devfs_listDir(
|
||||
path: string
|
||||
): (KFilesystemEntity | KFilesystemDevice)[] | null {
|
||||
const entity = devfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
if (entity.type !== "folder") return null;
|
||||
|
||||
const contents: (KFilesystemEntity | KFilesystemDevice)[] = [];
|
||||
|
||||
for (let i = 0; i < devfs_data.length; i++) {
|
||||
const e = devfs_data[i]!;
|
||||
|
||||
if (Path.dirname(e.path) === path) {
|
||||
contents.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
export function devfs_registerDevice(
|
||||
path: string,
|
||||
type: DeviceType,
|
||||
driver: unknown,
|
||||
data: unknown
|
||||
) {
|
||||
if (devfs_getEntity(path)) return;
|
||||
|
||||
const parent = Path.dirname(path);
|
||||
if (!devfs_getEntity(parent)) return;
|
||||
|
||||
devfs_data.push({
|
||||
name: Path.filename(path),
|
||||
path,
|
||||
type: "file",
|
||||
size: 0,
|
||||
contents: "",
|
||||
dtype: type,
|
||||
driver: driver,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
export function devfs_getDevice(path: string): KFilesystemDevice | null {
|
||||
const e = devfs_getEntity(path);
|
||||
if (!e) return null;
|
||||
if (!("driver" in e)) return null;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
export function devfs_directRead(
|
||||
path: string,
|
||||
count: number,
|
||||
offset: number
|
||||
): KFilesystemDirectReadResponse | null {
|
||||
const device = devfs_getDevice(path);
|
||||
if (!device) return null;
|
||||
|
||||
//return (device.driver as any).read(device.data, count, offset);
|
||||
return [];
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
import { Logger } from "../../lib/libstd/logger/logger.kmod";
|
||||
import type {
|
||||
KFilesystemDriver,
|
||||
KFilesystemEntity,
|
||||
KFilesystemStat,
|
||||
} from "../../types/filesystem/fs";
|
||||
|
||||
interface Fat32BootSector {
|
||||
bytesPerSector: number;
|
||||
sectorsPerCluster: number;
|
||||
reservedSectors: number;
|
||||
numberOfFATs: number;
|
||||
sectorsPerFAT: number;
|
||||
rootCluster: number;
|
||||
totalSectors: number;
|
||||
}
|
||||
|
||||
interface Fat32State {
|
||||
driver: any;
|
||||
data: any;
|
||||
boot: Fat32BootSector;
|
||||
fatStart: number;
|
||||
dataStart: number;
|
||||
}
|
||||
|
||||
interface DirectoryEntry {
|
||||
name: string;
|
||||
extension: string;
|
||||
attributes: number;
|
||||
firstCluster: number;
|
||||
fileSize: number;
|
||||
}
|
||||
|
||||
const ATTR_READ_ONLY = 0x01;
|
||||
const ATTR_HIDDEN = 0x02;
|
||||
const ATTR_SYSTEM = 0x04;
|
||||
const ATTR_VOLUME_ID = 0x08;
|
||||
const ATTR_DIRECTORY = 0x10;
|
||||
const ATTR_ARCHIVE = 0x20;
|
||||
const ATTR_LONG_NAME =
|
||||
ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID;
|
||||
|
||||
export function fat32_driver(driver: any, data: any): KFilesystemDriver {
|
||||
const state: Fat32State = {
|
||||
driver,
|
||||
data,
|
||||
boot: {} as Fat32BootSector,
|
||||
fatStart: 0,
|
||||
dataStart: 0,
|
||||
};
|
||||
|
||||
return {
|
||||
id: "fat32",
|
||||
init() {
|
||||
return fat32_init(state);
|
||||
},
|
||||
listDir(path: string) {
|
||||
return fat32_listDir(state, path);
|
||||
},
|
||||
readFile(path: string) {
|
||||
return fat32_readFile(state, path);
|
||||
},
|
||||
writeFile(path: string, content: unknown) {
|
||||
throw new Error("FAT32 write not implemented");
|
||||
},
|
||||
createFile(path: string, content: unknown) {
|
||||
throw new Error("FAT32 createFile not implemented");
|
||||
},
|
||||
mkdir(path: string) {
|
||||
throw new Error("FAT32 mkdir not implemented");
|
||||
},
|
||||
stat(path: string) {
|
||||
return fat32_stat(state, path);
|
||||
},
|
||||
directRead(path: string, count: number, offset: number) {
|
||||
return fat32_directRead(state, path, count, offset);
|
||||
},
|
||||
rm(path: string): void {
|
||||
throw new Error("Cannot remove objects in fat32.");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function fat32_init(state: Fat32State): void {
|
||||
const bootSector = state.driver.read(state.data, 512, 0);
|
||||
|
||||
state.boot = {
|
||||
bytesPerSector: bootSector[11] | (bootSector[12] << 8),
|
||||
sectorsPerCluster: bootSector[13],
|
||||
reservedSectors: bootSector[14] | (bootSector[15] << 8),
|
||||
numberOfFATs: bootSector[16],
|
||||
sectorsPerFAT:
|
||||
bootSector[36] |
|
||||
(bootSector[37] << 8) |
|
||||
(bootSector[38] << 16) |
|
||||
(bootSector[39] << 24),
|
||||
rootCluster:
|
||||
bootSector[44] |
|
||||
(bootSector[45] << 8) |
|
||||
(bootSector[46] << 16) |
|
||||
(bootSector[47] << 24),
|
||||
totalSectors:
|
||||
bootSector[32] |
|
||||
(bootSector[33] << 8) |
|
||||
(bootSector[34] << 16) |
|
||||
(bootSector[35] << 24),
|
||||
};
|
||||
|
||||
state.fatStart = state.boot.reservedSectors;
|
||||
state.dataStart =
|
||||
state.boot.reservedSectors +
|
||||
state.boot.numberOfFATs * state.boot.sectorsPerFAT;
|
||||
}
|
||||
|
||||
function fat32_clusterToSector(state: Fat32State, cluster: number): number {
|
||||
return state.dataStart + (cluster - 2) * state.boot.sectorsPerCluster;
|
||||
}
|
||||
|
||||
function fat32_readCluster(state: Fat32State, cluster: number): number[] {
|
||||
const sector = fat32_clusterToSector(state, cluster);
|
||||
const size = state.boot.sectorsPerCluster * state.boot.bytesPerSector;
|
||||
return state.driver.read(
|
||||
state.data,
|
||||
size,
|
||||
sector * state.boot.bytesPerSector
|
||||
);
|
||||
}
|
||||
|
||||
function fat32_getNextCluster(state: Fat32State, cluster: number): number {
|
||||
const fatOffset = cluster * 4;
|
||||
const fatSector =
|
||||
state.fatStart + Math.floor(fatOffset / state.boot.bytesPerSector);
|
||||
const entryOffset = fatOffset % state.boot.bytesPerSector;
|
||||
|
||||
const sector = state.driver.read(
|
||||
state.data,
|
||||
state.boot.bytesPerSector,
|
||||
fatSector * state.boot.bytesPerSector
|
||||
);
|
||||
|
||||
const nextCluster =
|
||||
sector[entryOffset] |
|
||||
(sector[entryOffset + 1] << 8) |
|
||||
(sector[entryOffset + 2] << 16) |
|
||||
(sector[entryOffset + 3] << 24);
|
||||
|
||||
return nextCluster & 0x0fffffff;
|
||||
}
|
||||
|
||||
function fat32_parseDirectoryEntry(
|
||||
data: number[],
|
||||
offset: number
|
||||
): DirectoryEntry | null {
|
||||
if (data[offset] === 0x00) return null; // End of directory
|
||||
if (data[offset] === 0xe5) return null; // Deleted entry
|
||||
|
||||
const attr = data[offset + 11];
|
||||
if ((attr & ATTR_LONG_NAME) === ATTR_LONG_NAME) return null; // LFN entry
|
||||
|
||||
let name = "";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const c = data[offset + i];
|
||||
if (c !== 0x20) name += String.fromCharCode(c);
|
||||
}
|
||||
|
||||
let extension = "";
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const c = data[offset + 8 + i];
|
||||
if (c !== 0x20) extension += String.fromCharCode(c);
|
||||
}
|
||||
|
||||
const firstClusterLow = data[offset + 26] | (data[offset + 27] << 8);
|
||||
const firstClusterHigh = data[offset + 20] | (data[offset + 21] << 8);
|
||||
const firstCluster = (firstClusterHigh << 16) | firstClusterLow;
|
||||
|
||||
const fileSize =
|
||||
data[offset + 28] |
|
||||
(data[offset + 29] << 8) |
|
||||
(data[offset + 30] << 16) |
|
||||
(data[offset + 31] << 24);
|
||||
|
||||
return {
|
||||
name: name.trim(),
|
||||
extension: extension.trim(),
|
||||
attributes: attr,
|
||||
firstCluster,
|
||||
fileSize,
|
||||
};
|
||||
}
|
||||
|
||||
function fat32_listDir(
|
||||
state: Fat32State,
|
||||
path: string
|
||||
): KFilesystemEntity[] | null {
|
||||
let cluster: number | undefined;
|
||||
|
||||
if (path === "/") cluster = state.boot.rootCluster;
|
||||
else {
|
||||
const entry = fat32_findEntry(state, path);
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
cluster = entry.firstCluster;
|
||||
}
|
||||
|
||||
if (!cluster) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const entries: KFilesystemEntity[] = [];
|
||||
let currentCluster = cluster;
|
||||
|
||||
while (currentCluster < 0x0ffffff8) {
|
||||
const clusterData = fat32_readCluster(state, currentCluster);
|
||||
|
||||
for (let i = 0; i < clusterData.length; i += 32) {
|
||||
const entry = fat32_parseDirectoryEntry(clusterData, i);
|
||||
if (!entry) continue;
|
||||
|
||||
// Skip volume label entries
|
||||
if ((entry.attributes & ATTR_VOLUME_ID) !== 0) continue;
|
||||
|
||||
const fullName = entry.extension
|
||||
? entry.name + "." + entry.extension
|
||||
: entry.name;
|
||||
const isDir = (entry.attributes & ATTR_DIRECTORY) !== 0;
|
||||
|
||||
entries.push({
|
||||
name: fullName,
|
||||
path: path === "/" ? "/" + fullName : path + "/" + fullName,
|
||||
type: isDir ? "folder" : "file",
|
||||
size: entry.fileSize,
|
||||
contents: null,
|
||||
});
|
||||
}
|
||||
|
||||
currentCluster = fat32_getNextCluster(state, currentCluster);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
function fat32_findEntry(
|
||||
state: Fat32State,
|
||||
path: string
|
||||
): DirectoryEntry | null {
|
||||
const parts = path.split("/").filter(function (p) {
|
||||
return p.length > 0;
|
||||
});
|
||||
if (parts.length === 0) return null;
|
||||
|
||||
let currentCluster = state.boot.rootCluster;
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const targetName = parts[i]!.toUpperCase();
|
||||
let found = false;
|
||||
|
||||
while (currentCluster < 0x0ffffff8) {
|
||||
const clusterData = fat32_readCluster(state, currentCluster);
|
||||
|
||||
for (let j = 0; j < clusterData.length; j += 32) {
|
||||
const entry = fat32_parseDirectoryEntry(clusterData, j);
|
||||
if (!entry) continue;
|
||||
|
||||
const entryName = entry.extension
|
||||
? entry.name + "." + entry.extension
|
||||
: entry.name;
|
||||
|
||||
if (entryName.toUpperCase() === targetName) {
|
||||
if (i === parts.length - 1) return entry;
|
||||
currentCluster = entry.firstCluster;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) break;
|
||||
currentCluster = fat32_getNextCluster(state, currentCluster);
|
||||
}
|
||||
|
||||
if (!found) return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function fat32_readFile(state: Fat32State, path: string): number[] {
|
||||
const entry = fat32_findEntry(state, path);
|
||||
if (!entry || (entry.attributes & ATTR_DIRECTORY) !== 0) {
|
||||
throw new Error("File not found or is a directory");
|
||||
}
|
||||
|
||||
const data: number[] = [];
|
||||
let currentCluster = entry.firstCluster;
|
||||
let remaining = entry.fileSize;
|
||||
|
||||
while (currentCluster < 0x0ffffff8 && remaining > 0) {
|
||||
const clusterData = fat32_readCluster(state, currentCluster);
|
||||
const bytesToCopy = Math.min(remaining, clusterData.length);
|
||||
|
||||
for (let i = 0; i < bytesToCopy; i++) {
|
||||
data.push(clusterData[i]!);
|
||||
}
|
||||
|
||||
remaining -= bytesToCopy;
|
||||
currentCluster = fat32_getNextCluster(state, currentCluster);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function fat32_stat(state: Fat32State, path: string): KFilesystemStat | null {
|
||||
if (path === "/") {
|
||||
return { name: "/", type: "folder", size: 0 };
|
||||
}
|
||||
|
||||
const entry = fat32_findEntry(state, path);
|
||||
if (!entry) return null;
|
||||
|
||||
const fullName = entry.extension
|
||||
? entry.name + "." + entry.extension
|
||||
: entry.name;
|
||||
|
||||
return {
|
||||
name: fullName,
|
||||
type: (entry.attributes & ATTR_DIRECTORY) !== 0 ? "folder" : "file",
|
||||
size: entry.fileSize,
|
||||
};
|
||||
}
|
||||
|
||||
function fat32_directRead(
|
||||
state: Fat32State,
|
||||
path: string,
|
||||
count: number,
|
||||
offset: number
|
||||
): number[] {
|
||||
const entry = fat32_findEntry(state, path);
|
||||
if (!entry || (entry.attributes & ATTR_DIRECTORY) !== 0) {
|
||||
throw new Error("File not found or is a directory");
|
||||
}
|
||||
|
||||
const clusterSize = state.boot.sectorsPerCluster * state.boot.bytesPerSector;
|
||||
let currentCluster = entry.firstCluster;
|
||||
let currentOffset = 0;
|
||||
const data: number[] = [];
|
||||
|
||||
// Skip to the cluster containing the offset
|
||||
while (offset >= currentOffset + clusterSize && currentCluster < 0x0ffffff8) {
|
||||
currentOffset += clusterSize;
|
||||
currentCluster = fat32_getNextCluster(state, currentCluster);
|
||||
}
|
||||
|
||||
let remaining = count;
|
||||
const startOffsetInCluster = offset - currentOffset;
|
||||
|
||||
while (remaining > 0 && currentCluster < 0x0ffffff8) {
|
||||
const clusterData = fat32_readCluster(state, currentCluster);
|
||||
const start = data.length === 0 ? startOffsetInCluster : 0;
|
||||
const bytesToCopy = Math.min(remaining, clusterData.length - start);
|
||||
|
||||
for (let i = start; i < start + bytesToCopy; i++) {
|
||||
data.push(clusterData[i]!);
|
||||
}
|
||||
|
||||
remaining -= bytesToCopy;
|
||||
currentCluster = fat32_getNextCluster(state, currentCluster);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
import { Logger } from "../../lib/libstd/logger/logger.kmod";
|
||||
import { Path } from "../../lib/libstd/path";
|
||||
import type {
|
||||
KFilesystemDriver,
|
||||
KFilesystemEntity,
|
||||
KFilesystemMemdata,
|
||||
KFilesystemStat,
|
||||
} from "../../types/filesystem/fs";
|
||||
|
||||
const procfs_data: KFilesystemMemdata = [];
|
||||
|
||||
export function procfs_driver(): KFilesystemDriver {
|
||||
return {
|
||||
id: "procfs",
|
||||
init: procfs_init,
|
||||
listDir(path: string): KFilesystemEntity[] | null {
|
||||
return procfs_listDir(path);
|
||||
},
|
||||
readFile(path: string): unknown {
|
||||
return procfs_readFile(path);
|
||||
},
|
||||
writeFile(path: string, content: unknown): void {
|
||||
throw new Error("Cannot write files in procfs.");
|
||||
},
|
||||
createFile(path: string, content: unknown): void {
|
||||
throw new Error("Cannot create files in procfs.");
|
||||
},
|
||||
mkdir(path: string): void {
|
||||
throw new Error("Cannot make directories in procfs.");
|
||||
},
|
||||
stat(path: string): KFilesystemStat | null {
|
||||
return procfs_statEntity(path);
|
||||
},
|
||||
rm(path: string): void {
|
||||
procfs_rm(path);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function procfs_init(): void {
|
||||
procfs_data.push({
|
||||
name: "root",
|
||||
path: "/",
|
||||
type: "folder",
|
||||
size: 0,
|
||||
contents: null,
|
||||
});
|
||||
}
|
||||
|
||||
export function procfs_getEntity(path: string): KFilesystemEntity | null {
|
||||
for (let i = 0; i < procfs_data.length; i++) {
|
||||
const entity = procfs_data[i]!;
|
||||
|
||||
if (entity.path === path) return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function procfs_statEntity(path: string): KFilesystemStat | null {
|
||||
const entity = procfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
return {
|
||||
name: entity.name,
|
||||
type: entity.type,
|
||||
size: entity.size,
|
||||
};
|
||||
}
|
||||
|
||||
export function procfs_mkdir(path: string): void {
|
||||
if (procfs_getEntity(path)) return;
|
||||
|
||||
const parent = Path.dirname(path);
|
||||
if (!procfs_getEntity(parent)) return;
|
||||
|
||||
procfs_data.push({
|
||||
name: Path.filename(path),
|
||||
path,
|
||||
type: "folder",
|
||||
size: 0,
|
||||
contents: null,
|
||||
});
|
||||
}
|
||||
|
||||
export function procfs_createFile(path: string, content: unknown): void {
|
||||
if (procfs_getEntity(path)) return;
|
||||
|
||||
const parent = Path.dirname(path);
|
||||
if (!procfs_getEntity(parent)) return;
|
||||
|
||||
const size = String(content).length;
|
||||
|
||||
procfs_data.push({
|
||||
name: Path.filename(path),
|
||||
path,
|
||||
type: "file",
|
||||
size,
|
||||
contents: content,
|
||||
});
|
||||
}
|
||||
|
||||
export function procfs_writeFile(path: string, content: unknown): void {
|
||||
const entity = procfs_getEntity(path);
|
||||
if (!entity) return procfs_createFile(path, content);
|
||||
|
||||
if (entity.type !== "file") return;
|
||||
|
||||
entity.contents = content;
|
||||
entity.size = String(content).length;
|
||||
}
|
||||
|
||||
export function procfs_readFile(path: string): unknown {
|
||||
const entity = procfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
if (entity.type !== "file") return null;
|
||||
|
||||
return entity.contents;
|
||||
}
|
||||
|
||||
export function procfs_listDir(path: string): KFilesystemEntity[] | null {
|
||||
const entity = procfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
if (entity.type !== "folder") return null;
|
||||
|
||||
const contents: KFilesystemEntity[] = [];
|
||||
|
||||
for (let i = 0; i < procfs_data.length; i++) {
|
||||
const e = procfs_data[i]!;
|
||||
|
||||
if (Path.dirname(e.path) === path) {
|
||||
contents.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
export function procfs_rm(path: string): void {
|
||||
const entity = procfs_getEntity(path);
|
||||
if (!entity) return;
|
||||
|
||||
if (entity.type === "file") {
|
||||
const index = procfs_data.indexOf(entity);
|
||||
|
||||
if (index !== -1) procfs_data.splice(index, 1);
|
||||
} else if (entity.type === "folder") {
|
||||
const entitiesToRemove: KFilesystemEntity[] = [];
|
||||
const pathPrefix = path === "/" ? "/" : path + "/";
|
||||
|
||||
for (let i = 0; i < procfs_data.length; i++) {
|
||||
const e = procfs_data[i]!;
|
||||
|
||||
if (e.path === path || e.path.startsWith(pathPrefix)) {
|
||||
entitiesToRemove.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < entitiesToRemove.length; i++) {
|
||||
const toRemove = entitiesToRemove[i]!;
|
||||
const index = procfs_data.indexOf(toRemove);
|
||||
|
||||
if (index !== -1) procfs_data.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
import { Logger } from "../../lib/libstd/logger/logger.kmod";
|
||||
import { Path } from "../../lib/libstd/path";
|
||||
import type {
|
||||
KFilesystemDriver,
|
||||
KFilesystemEntity,
|
||||
KFilesystemMemdata,
|
||||
KFilesystemStat,
|
||||
} from "../../types/filesystem/fs";
|
||||
|
||||
const sysfs_data: KFilesystemMemdata = [];
|
||||
|
||||
export function sysfs_driver(): KFilesystemDriver {
|
||||
return {
|
||||
id: "sysfs",
|
||||
init: sysfs_init,
|
||||
listDir(path: string): KFilesystemEntity[] | null {
|
||||
return sysfs_listDir(path);
|
||||
},
|
||||
readFile(path: string): unknown {
|
||||
return sysfs_readFile(path);
|
||||
},
|
||||
writeFile(path: string, content: unknown): void {
|
||||
throw new Error("Cannot write to sysfs files.");
|
||||
},
|
||||
createFile(path: string, content: unknown): void {
|
||||
throw new Error("Cannot create files in sysfs.");
|
||||
},
|
||||
mkdir(path: string): void {
|
||||
throw new Error("Cannot create directories in sysfs.");
|
||||
},
|
||||
stat(path: string): KFilesystemStat | null {
|
||||
return sysfs_statEntity(path);
|
||||
},
|
||||
rm(path: string): void {
|
||||
throw new Error("Cannot remove objects in sysfs.");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function sysfs_init(): void {
|
||||
sysfs_data.push({
|
||||
name: "root",
|
||||
path: "/",
|
||||
type: "folder",
|
||||
size: 0,
|
||||
contents: null,
|
||||
});
|
||||
|
||||
sysfs_mkdir("/pci");
|
||||
}
|
||||
|
||||
export function sysfs_getEntity(path: string): KFilesystemEntity | null {
|
||||
for (let i = 0; i < sysfs_data.length; i++) {
|
||||
const entity = sysfs_data[i]!;
|
||||
|
||||
if (entity.path === path) return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function sysfs_statEntity(path: string): KFilesystemStat | null {
|
||||
const entity = sysfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
return {
|
||||
name: entity.name,
|
||||
type: entity.type,
|
||||
size: entity.size,
|
||||
};
|
||||
}
|
||||
|
||||
export function sysfs_mkdir(path: string): void {
|
||||
if (sysfs_getEntity(path)) return;
|
||||
|
||||
const parent = Path.dirname(path);
|
||||
if (!sysfs_getEntity(parent)) return;
|
||||
|
||||
sysfs_data.push({
|
||||
name: Path.filename(path),
|
||||
path,
|
||||
type: "folder",
|
||||
size: 0,
|
||||
contents: null,
|
||||
});
|
||||
}
|
||||
|
||||
export function sysfs_createFile(path: string, content: unknown): void {
|
||||
if (sysfs_getEntity(path)) return;
|
||||
|
||||
const parent = Path.dirname(path);
|
||||
if (!sysfs_getEntity(parent)) return;
|
||||
|
||||
const size = String(content).length;
|
||||
|
||||
sysfs_data.push({
|
||||
name: Path.filename(path),
|
||||
path,
|
||||
type: "file",
|
||||
size,
|
||||
contents: content,
|
||||
});
|
||||
}
|
||||
|
||||
export function sysfs_writeFile(path: string, content: unknown): void {
|
||||
const entity = sysfs_getEntity(path);
|
||||
if (!entity) return sysfs_createFile(path, content);
|
||||
|
||||
if (entity.type !== "file") return;
|
||||
|
||||
entity.contents = content;
|
||||
entity.size = String(content).length;
|
||||
}
|
||||
|
||||
export function sysfs_readFile(path: string): unknown {
|
||||
const entity = sysfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
if (entity.type !== "file") return null;
|
||||
|
||||
return entity.contents;
|
||||
}
|
||||
|
||||
export function sysfs_listDir(path: string): KFilesystemEntity[] | null {
|
||||
const entity = sysfs_getEntity(path);
|
||||
if (!entity) return null;
|
||||
|
||||
if (entity.type !== "folder") return null;
|
||||
|
||||
const contents: KFilesystemEntity[] = [];
|
||||
|
||||
for (let i = 0; i < sysfs_data.length; i++) {
|
||||
const e = sysfs_data[i]!;
|
||||
|
||||
if (Path.dirname(e.path) === path) {
|
||||
contents.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { Logger } from "../lib/libstd/logger/logger.kmod";
|
||||
import { charc } from "../lib/libts/byte";
|
||||
import { padStart } from "../lib/libts/string";
|
||||
import { uiarrtostr } from "../lib/libts/uint_arr";
|
||||
import { getDate } from "../lib/sys/date";
|
||||
import { kdriver_dev_ata_detectDisks } from "./drivers/dev/ata";
|
||||
import { kdriver_dev_pci_detectDevices } from "./drivers/dev/pci";
|
||||
import {
|
||||
kdriver_etc_serial_read,
|
||||
kdriver_etc_serial_transmit,
|
||||
} from "./drivers/etc/serial";
|
||||
import { devfs_driver, devfs_getDevice } from "./filesystem/devfs";
|
||||
import { fat32_driver } from "./filesystem/fat32";
|
||||
import { procfs_driver } from "./filesystem/procfs";
|
||||
import { sysfs_driver, sysfs_readFile } from "./filesystem/sysfs";
|
||||
import { kmod_app_init, kmod_app_run } from "./modules/app/app.kmod";
|
||||
import { kmod_disks_detectDisks } from "./modules/disks/disks.kmod";
|
||||
import {
|
||||
kmod_drivers_init,
|
||||
kmod_drivers_register,
|
||||
} from "./modules/drivers/drivers.kmod";
|
||||
import {
|
||||
kmod_filesystem_init,
|
||||
kmod_filesystem_listDir,
|
||||
kmod_filesystem_mount,
|
||||
kmod_filesystem_readFile,
|
||||
} from "./modules/filesystem/filesystem.kmod";
|
||||
import {
|
||||
kmod_graphics_vga_clear,
|
||||
kmod_graphics_vga_init,
|
||||
kmod_graphics_vga_pushLine,
|
||||
kmod_graphics_vga_setLastLine,
|
||||
} from "./modules/graphics/graphics.kmod";
|
||||
import {
|
||||
kmod_terminal_input_init,
|
||||
kmod_terminal_input_onKeyboardInput,
|
||||
} from "./modules/terminal/input";
|
||||
|
||||
export function kmain() {
|
||||
kmod_drivers_init();
|
||||
|
||||
kmod_drivers_register();
|
||||
Logger.log("[Kernel] Drivers initialized.");
|
||||
|
||||
Logger.log("[Kernel] Initializing VGA module...");
|
||||
kmod_graphics_vga_init();
|
||||
|
||||
Logger.log("[Kernel] Initializing filesystem module...");
|
||||
kmod_filesystem_init();
|
||||
kmod_filesystem_mount("/sys", sysfs_driver);
|
||||
kmod_filesystem_mount("/dev", devfs_driver);
|
||||
kmod_filesystem_mount("/proc", procfs_driver);
|
||||
|
||||
Logger.log("[Kernel] Initializing terminal module...");
|
||||
kmod_terminal_input_init();
|
||||
|
||||
Logger.log("[Kernel] Initializing PCI devices...");
|
||||
kdriver_dev_pci_detectDevices();
|
||||
|
||||
Logger.log("[Kernel] Initializing drives...");
|
||||
kmod_disks_detectDisks();
|
||||
|
||||
const dev = devfs_getDevice("/hda1");
|
||||
if (!dev) throw new Error("System drive not found");
|
||||
|
||||
kmod_filesystem_mount("/disk", function () {
|
||||
return fat32_driver(dev.driver, dev.data);
|
||||
});
|
||||
|
||||
Logger.log("[Kernel] Initializing app module...");
|
||||
kmod_app_init();
|
||||
|
||||
Logger.log("[Kernel] Kernel initialized successfully.");
|
||||
|
||||
const shell = uiarrtostr(kmod_filesystem_readFile("/disk/ushellc")!);
|
||||
|
||||
kmod_app_run(shell, "");
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||
import { Path } from "../../../lib/libstd/path";
|
||||
import { iexec } from "../../../lib/libts/exec";
|
||||
import { uiarrtostr } from "../../../lib/libts/uint_arr";
|
||||
import {
|
||||
kmod_filesystem_listDir,
|
||||
kmod_filesystem_readFile,
|
||||
kmod_filesystem_stat,
|
||||
} from "../filesystem/filesystem.kmod";
|
||||
import { oskrnl_register } from "../../../oskrnl";
|
||||
import { getPathEnv } from "../../../lib/sys/env";
|
||||
import {
|
||||
procfs_mkdir,
|
||||
procfs_readFile,
|
||||
procfs_rm,
|
||||
procfs_writeFile,
|
||||
} from "../../filesystem/procfs";
|
||||
|
||||
const exitListeners: { [pid: string]: (() => void)[] } = {};
|
||||
const heapHandles: { [pid: string]: number } = {};
|
||||
|
||||
export function kmod_app_init() {
|
||||
oskrnl_register();
|
||||
}
|
||||
|
||||
export function kmod_app_run(path: string, args: string) {
|
||||
const appPath = kmod_app_resolve(path);
|
||||
if (!appPath) throw new Error("App not found");
|
||||
|
||||
const meta = JSON.parse(
|
||||
uiarrtostr(kmod_filesystem_readFile(Path.join(appPath, "meta.lam"))!)
|
||||
);
|
||||
|
||||
if (!meta) throw new Error("App metadata not found");
|
||||
|
||||
const entrypoint = Path.join(appPath, meta["app:entrypoint"]);
|
||||
const code = uiarrtostr(kmod_filesystem_readFile(entrypoint)!);
|
||||
|
||||
const pid = kmod_app_proc_generateId().toString();
|
||||
|
||||
kmod_app_proc_init(pid, meta["app:name"]!, args);
|
||||
kmod_app_proc_registerHandle(pid, "app");
|
||||
const heapHandle = iexec("var __oskrnl_procd_pid = " + pid + ";\n" + code);
|
||||
if (heapHandle) heapHandles[pid] = heapHandle;
|
||||
kmod_app_proc_unregisterHandle(pid, "app");
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
export function kmod_app_resolve(path: string): string | null {
|
||||
if (kmod_filesystem_stat(path)) return path;
|
||||
|
||||
const pathEnv = getPathEnv();
|
||||
if (!pathEnv) return null;
|
||||
|
||||
for (let i = 0; i < pathEnv.length; i++) {
|
||||
const p = Path.join(pathEnv[i]!, path);
|
||||
if (kmod_filesystem_stat(p)) return p;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function kmod_app_proc_generateId(): number {
|
||||
return Math.floor(Math.random() * 0xffffffff);
|
||||
}
|
||||
|
||||
export function kmod_app_proc_init(id: string, name: string, args: string) {
|
||||
const root = "/" + id;
|
||||
procfs_mkdir(root);
|
||||
procfs_writeFile(Path.join(root, "name"), name);
|
||||
procfs_writeFile(Path.join(root, "args"), args);
|
||||
procfs_writeFile(Path.join(root, "pid"), id);
|
||||
procfs_writeFile(Path.join(root, "handles"), "");
|
||||
}
|
||||
|
||||
export function kmod_app_proc_getArgs(pid: string): string {
|
||||
const res = procfs_readFile(Path.join("/" + pid, "args"));
|
||||
return res as string;
|
||||
}
|
||||
|
||||
export function kmod_app_proc_registerHandle(
|
||||
pid: string,
|
||||
handle: string
|
||||
): string {
|
||||
const handles = procfs_readFile(Path.join("/" + pid, "handles")) as string;
|
||||
|
||||
const handlesArr = handles == "" ? [] : handles.split(",");
|
||||
handlesArr.push(handle);
|
||||
|
||||
procfs_writeFile(Path.join("/" + pid, "handles"), handlesArr.join(","));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
export function kmod_app_proc_unregisterHandle(
|
||||
pid: string,
|
||||
handle: string
|
||||
): void {
|
||||
const handles = procfs_readFile(Path.join("/" + pid, "handles")) as string;
|
||||
if (handles == "") return;
|
||||
|
||||
const handlesArr = handles.split(",");
|
||||
handlesArr.splice(handlesArr.indexOf(handle), 1);
|
||||
|
||||
procfs_writeFile(Path.join("/" + pid, "handles"), handlesArr.join(","));
|
||||
|
||||
if (handlesArr.length < 1) kmod_app_proc_handleExit(pid);
|
||||
}
|
||||
|
||||
export function kmod_app_proc_generateHandle(): string {
|
||||
return Math.floor(Math.random() * 0xffffffff).toString();
|
||||
}
|
||||
|
||||
export function kmod_app_proc_handleExit(pid: string): void {
|
||||
procfs_writeFile(Path.join("/", pid + "/handles"), "");
|
||||
procfs_rm("/" + pid);
|
||||
|
||||
if (exitListeners[pid]) {
|
||||
for (let i = 0; i < exitListeners[pid].length; i++) {
|
||||
exitListeners[pid][i]!();
|
||||
}
|
||||
}
|
||||
|
||||
exitListeners[pid] = [];
|
||||
delete exitListeners[pid];
|
||||
|
||||
if (heapHandles[pid]) {
|
||||
$destroyIsolatedHeap(heapHandles[pid]!);
|
||||
delete heapHandles[pid];
|
||||
}
|
||||
}
|
||||
|
||||
export function kmod_app_proc_running(pid: string): boolean {
|
||||
const handles = procfs_readFile(Path.join("/" + pid, "handles")) as string;
|
||||
|
||||
if (handles === null || handles === undefined) return false;
|
||||
|
||||
return handles !== "";
|
||||
}
|
||||
|
||||
export function kmod_app_proc_addExitListener(
|
||||
pid: string,
|
||||
listener: () => void
|
||||
): void {
|
||||
if (!exitListeners[pid]) exitListeners[pid] = [];
|
||||
exitListeners[pid].push(listener);
|
||||
}
|
||||
|
||||
export function kmod_app_proc_exit(pid: string): void {
|
||||
kmod_app_proc_handleExit(pid);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./time"
|
||||
@@ -1,2 +0,0 @@
|
||||
export const CFG_KMOD_BIOS_TIME_CMOS_ADDR = 0x70;
|
||||
export const CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR = 0x71;
|
||||
@@ -1,96 +0,0 @@
|
||||
import { portin, portout } from "../../../lib/libts/port";
|
||||
import { padStart } from "../../../lib/libts/string";
|
||||
import {
|
||||
CFG_KMOD_BIOS_TIME_CMOS_ADDR,
|
||||
CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR,
|
||||
} from "./config";
|
||||
|
||||
export function kmod_bios_time_getCentury(): number {
|
||||
const val = 0x32;
|
||||
|
||||
portout(CFG_KMOD_BIOS_TIME_CMOS_ADDR, val);
|
||||
const data = portin(CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR);
|
||||
|
||||
return (data & 0x0f) + ((data >> 4) & 0x0f) * 10;
|
||||
}
|
||||
|
||||
export function kmod_bios_time_getYear(): number {
|
||||
const val = 0x09;
|
||||
|
||||
portout(CFG_KMOD_BIOS_TIME_CMOS_ADDR, val);
|
||||
const data = portin(CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR);
|
||||
|
||||
return (data & 0x0f) + ((data >> 4) & 0x0f) * 10;
|
||||
}
|
||||
|
||||
export function kmod_bios_time_getMonth(): number {
|
||||
const val = 0x08;
|
||||
|
||||
portout(CFG_KMOD_BIOS_TIME_CMOS_ADDR, val);
|
||||
const data = portin(CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR);
|
||||
|
||||
return (data & 0x0f) + ((data >> 4) & 0x0f) * 10;
|
||||
}
|
||||
|
||||
export function kmod_bios_time_getDay(): number {
|
||||
const val = 0x07;
|
||||
|
||||
portout(CFG_KMOD_BIOS_TIME_CMOS_ADDR, val);
|
||||
const data = portin(CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR);
|
||||
|
||||
return (data & 0x0f) + ((data >> 4) & 0x0f) * 10;
|
||||
}
|
||||
|
||||
export function kmod_bios_time_getHours(): number {
|
||||
const val = 0x04;
|
||||
|
||||
portout(CFG_KMOD_BIOS_TIME_CMOS_ADDR, val);
|
||||
const data = portin(CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR);
|
||||
|
||||
return (data & 0x0f) + ((data >> 4) & 0x0f) * 10;
|
||||
}
|
||||
|
||||
export function kmod_bios_time_getMinutes(): number {
|
||||
const val = 0x02;
|
||||
|
||||
portout(CFG_KMOD_BIOS_TIME_CMOS_ADDR, val);
|
||||
const data = portin(CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR);
|
||||
|
||||
return (data & 0x0f) + ((data >> 4) & 0x0f) * 10;
|
||||
}
|
||||
|
||||
export function kmod_bios_time_getSeconds(): number {
|
||||
const val = 0x00;
|
||||
|
||||
portout(CFG_KMOD_BIOS_TIME_CMOS_ADDR, val);
|
||||
const data = portin(CFG_KMOD_BIOS_TIME_CMOS_DATA_ADDR);
|
||||
|
||||
return (data & 0x0f) + ((data >> 4) & 0x0f) * 10;
|
||||
}
|
||||
|
||||
export function kmod_bios_time_getTimestamp(): string {
|
||||
const century = kmod_bios_time_getCentury();
|
||||
const year = kmod_bios_time_getYear();
|
||||
const month = kmod_bios_time_getMonth();
|
||||
const day = kmod_bios_time_getDay();
|
||||
const hours = kmod_bios_time_getHours();
|
||||
const minutes = kmod_bios_time_getMinutes();
|
||||
const seconds = kmod_bios_time_getSeconds();
|
||||
|
||||
return (
|
||||
century.toString() +
|
||||
year.toString() +
|
||||
"-" +
|
||||
padStart(month.toString(), 2, "0") +
|
||||
"-" +
|
||||
padStart(day.toString(), 2, "0") +
|
||||
"T" +
|
||||
padStart(hours.toString(), 2, "0") +
|
||||
":" +
|
||||
padStart(minutes.toString(), 2, "0") +
|
||||
":" +
|
||||
padStart(seconds.toString(), 2, "0") +
|
||||
+".000" +
|
||||
"Z"
|
||||
);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||
import type { KInterrupt, KInterruptHandler } from "../../../types/interrupt";
|
||||
|
||||
const kmod_cpu_irq_data: KInterrupt[] = [];
|
||||
|
||||
export function kmod_cpu_irq_register(
|
||||
irq: number,
|
||||
handler: KInterruptHandler,
|
||||
data: unknown
|
||||
): boolean {
|
||||
if (kmod_cpu_irq_data[irq]) {
|
||||
Logger.log("[IRQ] IRQ " + irq + " is already registered.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (irq > 15) {
|
||||
Logger.log("[IRQ] IRQ " + irq + " is out of bounds.");
|
||||
return false;
|
||||
}
|
||||
|
||||
kmod_cpu_irq_data[irq] = {
|
||||
handler: handler,
|
||||
data: data,
|
||||
};
|
||||
|
||||
function wrapper(irqNum: number) {
|
||||
const irqData = kmod_cpu_irq_data[irqNum];
|
||||
if (irqData) {
|
||||
irqData.handler(irqNum, irqData.data);
|
||||
}
|
||||
}
|
||||
|
||||
const success = $irqregister(irq, wrapper);
|
||||
|
||||
if (!success) {
|
||||
Logger.log("[IRQ] Failed to register IRQ " + irq + " with hardware.");
|
||||
delete kmod_cpu_irq_data[irq];
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.log("[IRQ] Successfully registered IRQ " + irq);
|
||||
return true;
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||
import { Path } from "../../../lib/libstd/path";
|
||||
import { cchar, charc } from "../../../lib/libts/byte";
|
||||
import { DeviceType } from "../../../types/dev/device";
|
||||
import { DiskType, type Disk } from "../../../types/filesystem/disk";
|
||||
import {
|
||||
kdriver_dev_ata_detectDisks,
|
||||
kdriver_dev_ata_fsDriver,
|
||||
kdriver_dev_ata_getDrive,
|
||||
kdriver_dev_ata_numberOfDrives,
|
||||
kdriver_dev_ata_partition_fsDriver,
|
||||
kdriver_dev_ata_readSectors,
|
||||
} from "../../drivers/dev/ata";
|
||||
import { devfs_registerDevice } from "../../filesystem/devfs";
|
||||
import { sysfs_mkdir, sysfs_writeFile } from "../../filesystem/sysfs";
|
||||
|
||||
let disks: Disk[] = [];
|
||||
|
||||
export function kmod_disks_detectDisks() {
|
||||
kdriver_dev_ata_detectDisks();
|
||||
|
||||
let cdrom = charc("a");
|
||||
let disk = charc("a");
|
||||
let numOfDisks = 0;
|
||||
|
||||
for (let i = 0; i < kdriver_dev_ata_numberOfDrives(); ++i) {
|
||||
const descriptor = kdriver_dev_ata_getDrive(i);
|
||||
|
||||
if (!descriptor.present) continue;
|
||||
|
||||
let name = "";
|
||||
|
||||
if (descriptor.atapi) {
|
||||
name = "cd" + cchar(cdrom);
|
||||
cdrom++;
|
||||
|
||||
disks.push({
|
||||
dnum: numOfDisks,
|
||||
name: name,
|
||||
descriptor: descriptor,
|
||||
type: DiskType.ATAPI,
|
||||
});
|
||||
|
||||
devfs_registerDevice(
|
||||
"/" + name,
|
||||
DeviceType.BlockDevice,
|
||||
null,
|
||||
descriptor
|
||||
);
|
||||
} else {
|
||||
name = "hd" + cchar(disk);
|
||||
disk++;
|
||||
|
||||
disks.push({
|
||||
dnum: numOfDisks,
|
||||
name: name,
|
||||
descriptor: descriptor,
|
||||
type: DiskType.ATA,
|
||||
});
|
||||
|
||||
devfs_registerDevice(
|
||||
"/" + name,
|
||||
DeviceType.BlockDevice,
|
||||
kdriver_dev_ata_fsDriver,
|
||||
descriptor
|
||||
);
|
||||
|
||||
let part = charc("1");
|
||||
|
||||
const partitions = kmod_disks_getPartitions(disks[numOfDisks]!);
|
||||
|
||||
for (let n = 0; n < partitions.length; n++) {
|
||||
const partition = partitions[n];
|
||||
|
||||
let part_name = name + cchar(part);
|
||||
part++;
|
||||
|
||||
(partition as any).disk = disks[numOfDisks]!;
|
||||
|
||||
devfs_registerDevice(
|
||||
"/" + part_name,
|
||||
DeviceType.BlockDevice,
|
||||
kdriver_dev_ata_partition_fsDriver,
|
||||
partition
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const p = Path.join("/ata", name);
|
||||
|
||||
sysfs_mkdir(p);
|
||||
sysfs_writeFile(Path.join(p, "model"), descriptor.model);
|
||||
sysfs_writeFile(Path.join(p, "serial"), descriptor.serial);
|
||||
sysfs_writeFile(Path.join(p, "firmware"), descriptor.firmware);
|
||||
|
||||
numOfDisks++;
|
||||
}
|
||||
}
|
||||
|
||||
export function kmod_disks_getPartitions(disk: Disk) {
|
||||
const sectors = kdriver_dev_ata_readSectors(disk.descriptor, 0, 1, 0);
|
||||
|
||||
const signature = (sectors[511]! << 8) | sectors[510]!;
|
||||
|
||||
if (signature !== 0xaa55) {
|
||||
throw new Error("Invalid MBR signature");
|
||||
}
|
||||
|
||||
const partitions = [];
|
||||
|
||||
// Parse partition table (starts at offset 446, 4 entries of 16 bytes each)
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const offset = 446 + i * 16;
|
||||
|
||||
const status = sectors[offset]!;
|
||||
const partitionType = sectors[offset + 4]!;
|
||||
|
||||
// LBA of first sector (little-endian)
|
||||
const lbaStart =
|
||||
sectors[offset + 8]! |
|
||||
(sectors[offset + 9]! << 8) |
|
||||
(sectors[offset + 10]! << 16) |
|
||||
(sectors[offset + 11]! << 24);
|
||||
|
||||
// Number of sectors (little-endian)
|
||||
const sectorCount =
|
||||
sectors[offset + 12]! |
|
||||
(sectors[offset + 13]! << 8) |
|
||||
(sectors[offset + 14]! << 16) |
|
||||
(sectors[offset + 15]! << 24);
|
||||
|
||||
// Skip empty partition entries
|
||||
if (partitionType === 0 || sectorCount === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
partitions.push({
|
||||
index: i,
|
||||
bootable: status === 0x80,
|
||||
type: partitionType,
|
||||
lbaStart: lbaStart,
|
||||
sectors: sectorCount,
|
||||
size: sectorCount * 512,
|
||||
});
|
||||
|
||||
Logger.log(
|
||||
"[Disk] Partition " +
|
||||
i +
|
||||
": Type=0x" +
|
||||
partitionType.toString(16) +
|
||||
" " +
|
||||
"LBA=" +
|
||||
lbaStart +
|
||||
" Sectors=" +
|
||||
sectorCount +
|
||||
" " +
|
||||
"Bootable=" +
|
||||
(status === 0x80)
|
||||
);
|
||||
}
|
||||
|
||||
return partitions;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { kdriver_dev_ata_init } from "../../drivers/dev/ata";
|
||||
import { kdriver_dev_keyboard_init } from "../../drivers/dev/keyboard";
|
||||
import { kdriver_dev_pci_init } from "../../drivers/dev/pci";
|
||||
import { kdriver_etc_serial_init } from "../../drivers/etc/serial";
|
||||
|
||||
const drivers = [
|
||||
kdriver_etc_serial_init,
|
||||
kdriver_dev_pci_init,
|
||||
kdriver_dev_keyboard_init,
|
||||
kdriver_dev_ata_init,
|
||||
];
|
||||
|
||||
export function kmod_drivers_init(): void {}
|
||||
|
||||
export function kmod_drivers_register(): void {
|
||||
for (let i = 0; i < drivers.length; i++) {
|
||||
drivers[i]!();
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||
import { Path } from "../../../lib/libstd/path";
|
||||
import type {
|
||||
KFilesystemDriver,
|
||||
KFilesystemEntity,
|
||||
KFilesystemMount,
|
||||
KFilesystemMountdata,
|
||||
KFilesystemStat,
|
||||
} from "../../../types/filesystem/fs";
|
||||
|
||||
const kmod_filesystem_data: KFilesystemMountdata = [];
|
||||
|
||||
export function kmod_filesystem_init(): void {}
|
||||
|
||||
export function kmod_filesystem_mount(
|
||||
mpoint: string,
|
||||
drv: () => KFilesystemDriver
|
||||
): void {
|
||||
const driver = drv();
|
||||
|
||||
driver.init();
|
||||
|
||||
kmod_filesystem_data.push({
|
||||
mountpoint: mpoint,
|
||||
driver: driver,
|
||||
});
|
||||
|
||||
Logger.log("[Filesystem] Mounted " + driver.id + " at " + mpoint);
|
||||
}
|
||||
|
||||
export function kmod_filesystem_findMount(p: string): KFilesystemMount | null {
|
||||
for (let i = 0; i < kmod_filesystem_data.length; i++) {
|
||||
const mount = kmod_filesystem_data[i]!;
|
||||
|
||||
if (p.startsWith(mount.mountpoint)) {
|
||||
return mount;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function kmod_filesystem_stripMountpoint(
|
||||
mount: KFilesystemMount,
|
||||
p: string
|
||||
): string {
|
||||
if (mount.mountpoint === "/") return p;
|
||||
|
||||
return p.slice(mount.mountpoint.length) || "/";
|
||||
}
|
||||
|
||||
export function kmod_filesystem_lsMountAsFsEntity() {
|
||||
let out: KFilesystemEntity[] = [];
|
||||
|
||||
for (let i = 0; i < kmod_filesystem_data.length; i++) {
|
||||
const mount = kmod_filesystem_data[i]!;
|
||||
|
||||
out.push({
|
||||
name: Path.filename(mount.mountpoint),
|
||||
path: mount.mountpoint,
|
||||
type: "folder",
|
||||
size: 0,
|
||||
contents: 0,
|
||||
});
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
export function kmod_filesystem_listDir(
|
||||
path: string
|
||||
): KFilesystemEntity[] | null {
|
||||
const mount = kmod_filesystem_findMount(path);
|
||||
if (!mount) return kmod_filesystem_lsMountAsFsEntity();
|
||||
|
||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||
|
||||
return mount.driver.listDir(strippedPath);
|
||||
}
|
||||
|
||||
export function kmod_filesystem_readFile(path: string): number[] | null {
|
||||
const mount = kmod_filesystem_findMount(path);
|
||||
if (!mount) return null;
|
||||
|
||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||
|
||||
return mount.driver.readFile(strippedPath) as number[];
|
||||
}
|
||||
|
||||
export function kmod_filesystem_writeFile(
|
||||
path: string,
|
||||
content: unknown
|
||||
): void {
|
||||
const mount = kmod_filesystem_findMount(path);
|
||||
if (!mount) return;
|
||||
|
||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||
|
||||
mount.driver.writeFile(strippedPath, content);
|
||||
}
|
||||
|
||||
export function kmod_filesystem_createFile(
|
||||
path: string,
|
||||
content: unknown
|
||||
): void {
|
||||
const mount = kmod_filesystem_findMount(path);
|
||||
if (!mount) return;
|
||||
|
||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||
|
||||
mount.driver.createFile(strippedPath, content);
|
||||
}
|
||||
|
||||
export function kmod_filesystem_mkdir(path: string): void {
|
||||
const mount = kmod_filesystem_findMount(path);
|
||||
if (!mount) return;
|
||||
|
||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||
|
||||
mount.driver.mkdir(strippedPath);
|
||||
}
|
||||
|
||||
export function kmod_filesystem_stat(path: string): KFilesystemStat | null {
|
||||
const mount = kmod_filesystem_findMount(path);
|
||||
if (!mount) return null;
|
||||
|
||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||
|
||||
return mount.driver.stat(strippedPath);
|
||||
}
|
||||
|
||||
export function kmod_filesystem_directRead(
|
||||
path: string,
|
||||
count: number,
|
||||
offset: number
|
||||
) {}
|
||||
@@ -1,3 +0,0 @@
|
||||
export const CFG_KMOD_GRAPHICS_VGA_WIDTH = 80;
|
||||
export const CFG_KMOD_GRAPHICS_VGA_HEIGHT = 25;
|
||||
export const CFG_KMOD_GRAPGICS_VGA_MEM_ADDR = 0xb8000;
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./vga";
|
||||
@@ -1,95 +0,0 @@
|
||||
import { charc } from "../../../lib/libts/byte";
|
||||
import {
|
||||
CFG_KMOD_GRAPGICS_VGA_MEM_ADDR,
|
||||
CFG_KMOD_GRAPHICS_VGA_HEIGHT,
|
||||
CFG_KMOD_GRAPHICS_VGA_WIDTH,
|
||||
} from "./config";
|
||||
|
||||
let kmod_graphics_vga_lineBuf: string[] = [];
|
||||
let init = false;
|
||||
|
||||
export function kmod_graphics_vga_init() {
|
||||
init = true;
|
||||
|
||||
kmod_graphics_vga_clear();
|
||||
}
|
||||
|
||||
export function kmod_graphics_vga_pushLine(line: string) {
|
||||
if (!init) return;
|
||||
|
||||
kmod_graphics_vga_lineBuf.push(line);
|
||||
|
||||
if (kmod_graphics_vga_lineBuf.length > CFG_KMOD_GRAPHICS_VGA_HEIGHT) {
|
||||
kmod_graphics_vga_lineBuf.shift();
|
||||
}
|
||||
|
||||
kmod_graphics_vga_printLines();
|
||||
}
|
||||
|
||||
export function kmod_graphics_vga_setLastLine(line: string) {
|
||||
if (!init) return;
|
||||
|
||||
if (kmod_graphics_vga_lineBuf.length === 0) {
|
||||
kmod_graphics_vga_lineBuf.push(line);
|
||||
} else {
|
||||
kmod_graphics_vga_lineBuf[kmod_graphics_vga_lineBuf.length - 1] = line;
|
||||
}
|
||||
|
||||
kmod_graphics_vga_printLines();
|
||||
}
|
||||
|
||||
export function kmod_graphics_vga_printLines() {
|
||||
if (!init) return;
|
||||
|
||||
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
|
||||
const line = kmod_graphics_vga_lineBuf[y] || "";
|
||||
|
||||
for (let x = 0; x < CFG_KMOD_GRAPHICS_VGA_WIDTH; x++) {
|
||||
let char = line[x];
|
||||
if (!char) char = " ";
|
||||
|
||||
kmod_graphics_vga_writeChar(x, y, char, 0x0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function kmod_graphics_vga_getCharAddr(x: number, y: number): number {
|
||||
return (
|
||||
CFG_KMOD_GRAPGICS_VGA_MEM_ADDR + (y * CFG_KMOD_GRAPHICS_VGA_WIDTH + x) * 2
|
||||
);
|
||||
}
|
||||
|
||||
export function kmod_graphics_vga_getColorAddr(x: number, y: number): number {
|
||||
return (
|
||||
CFG_KMOD_GRAPGICS_VGA_MEM_ADDR +
|
||||
(y * CFG_KMOD_GRAPHICS_VGA_WIDTH + x) * 2 +
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
export function kmod_graphics_vga_clear() {
|
||||
if (!init) return;
|
||||
|
||||
kmod_graphics_vga_lineBuf = [];
|
||||
|
||||
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
|
||||
for (let x = 0; x < CFG_KMOD_GRAPHICS_VGA_WIDTH; x++) {
|
||||
kmod_graphics_vga_writeChar(x, y, " ", 0x07);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function kmod_graphics_vga_writeChar(
|
||||
x: number,
|
||||
y: number,
|
||||
char: string,
|
||||
color: number
|
||||
) {
|
||||
if (!init) return;
|
||||
|
||||
const charAddr = kmod_graphics_vga_getCharAddr(x, y);
|
||||
const colorAddr = kmod_graphics_vga_getColorAddr(x, y);
|
||||
|
||||
$addrw(charAddr, charc(char));
|
||||
$addrw(colorAddr, color);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { Keycode } from "../../../config/keyboard";
|
||||
|
||||
const kmod_terminal_input_keyboardInputHandlers: Array<
|
||||
(keycode: keyof typeof Keycode) => void
|
||||
> = [];
|
||||
|
||||
export function kmod_terminal_input_init(): void {}
|
||||
|
||||
export function kmod_terminal_input_onKeyboardInput(
|
||||
handler: (keycode: keyof typeof Keycode) => void
|
||||
): () => void {
|
||||
kmod_terminal_input_keyboardInputHandlers.push(handler);
|
||||
|
||||
return function () {
|
||||
const index = kmod_terminal_input_keyboardInputHandlers.indexOf(handler);
|
||||
if (index !== -1) {
|
||||
kmod_terminal_input_keyboardInputHandlers.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function kmod_terminal_input_handleKeyboardInput(
|
||||
keycode: keyof typeof Keycode
|
||||
): void {
|
||||
for (let i = 0; i < kmod_terminal_input_keyboardInputHandlers.length; i++) {
|
||||
kmod_terminal_input_keyboardInputHandlers[i]!(keycode);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./input";
|
||||
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
kmod_graphics_vga_clear,
|
||||
kmod_graphics_vga_writeChar,
|
||||
} from "./modules/graphics/graphics.kmod";
|
||||
|
||||
export function kpanic(reason?: string) {
|
||||
kmod_graphics_vga_clear();
|
||||
|
||||
const l1 = "KERNEL PANIC!";
|
||||
let l2 = "Unknown: Kernel crashed.";
|
||||
|
||||
if (reason) l2 = "Reason: " + reason;
|
||||
|
||||
for (let i = 0; i < l1.length; i++) {
|
||||
kmod_graphics_vga_writeChar(i, 0, l1[i]!, 0x0c);
|
||||
}
|
||||
|
||||
for (let i = 0; i < l2.length; i++) {
|
||||
kmod_graphics_vga_writeChar(i, 1, l2[i]!, 0x0c);
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
interface Block {
|
||||
key: number;
|
||||
hashNext: Block | null;
|
||||
freeNext: Block | null;
|
||||
freePrev: Block | null;
|
||||
payload: number[]; // Array instead of Uint8Array
|
||||
}
|
||||
|
||||
interface BlockCacheState {
|
||||
payloadSize: number;
|
||||
blocks: number;
|
||||
hashTable: (Block | null)[];
|
||||
blocksMemory: Block[];
|
||||
front: Block | null;
|
||||
rear: Block | null;
|
||||
}
|
||||
|
||||
export function blockCache_create(): BlockCacheState {
|
||||
return {
|
||||
payloadSize: 0,
|
||||
blocks: 0,
|
||||
hashTable: [],
|
||||
blocksMemory: [],
|
||||
front: null,
|
||||
rear: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function blockCache_init(
|
||||
state: BlockCacheState,
|
||||
payloadSize: number,
|
||||
blocks: number
|
||||
): void {
|
||||
state.payloadSize = payloadSize;
|
||||
state.blocks = blocks;
|
||||
|
||||
// Initialize hash table (2x the number of blocks)
|
||||
var hashTableSize = blocks * 2;
|
||||
state.hashTable = [];
|
||||
for (var i = 0; i < hashTableSize; i++) {
|
||||
state.hashTable.push(null);
|
||||
}
|
||||
|
||||
// Allocate blocks in memory
|
||||
state.blocksMemory = [];
|
||||
var previous: Block | null = null;
|
||||
|
||||
for (var i = 0; i < blocks; i++) {
|
||||
var payload: number[] = [];
|
||||
for (var j = 0; j < payloadSize; j++) {
|
||||
payload.push(0);
|
||||
}
|
||||
|
||||
var block: Block = {
|
||||
key: 0,
|
||||
hashNext: null,
|
||||
freeNext: null,
|
||||
freePrev: previous,
|
||||
payload: payload,
|
||||
};
|
||||
|
||||
if (previous) {
|
||||
previous.freeNext = block;
|
||||
}
|
||||
|
||||
state.blocksMemory.push(block);
|
||||
previous = block;
|
||||
}
|
||||
|
||||
state.front = state.blocksMemory[0];
|
||||
state.rear = previous;
|
||||
}
|
||||
|
||||
export function blockCache_blockIfPresent(
|
||||
state: BlockCacheState,
|
||||
deviceOrKey: number,
|
||||
sector?: number
|
||||
): number[] | null {
|
||||
var origKey =
|
||||
sector !== undefined ? deviceOrKey * 65536 + sector : deviceOrKey;
|
||||
|
||||
var key = origKey % (state.blocks * 2);
|
||||
|
||||
if (state.hashTable[key]) {
|
||||
var entry = state.hashTable[key];
|
||||
|
||||
while (entry) {
|
||||
if (entry.key === origKey) {
|
||||
return entry.payload;
|
||||
}
|
||||
entry = entry.hashNext;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function blockCache_block(
|
||||
state: BlockCacheState,
|
||||
deviceOrKey: number,
|
||||
sectorOrValid: number | { valid: boolean },
|
||||
valid?: { valid: boolean }
|
||||
): number[] {
|
||||
var origKey: number;
|
||||
var validRef: { valid: boolean };
|
||||
|
||||
if (valid !== undefined) {
|
||||
// block(state, device, sector, valid)
|
||||
origKey = deviceOrKey * 65536 + (sectorOrValid as number);
|
||||
validRef = valid;
|
||||
} else {
|
||||
// block(state, key, valid)
|
||||
origKey = deviceOrKey;
|
||||
validRef = sectorOrValid as { valid: boolean };
|
||||
}
|
||||
|
||||
var key = origKey % (state.blocks * 2);
|
||||
|
||||
// Try to get from hash table
|
||||
var direct = blockCache_blockIfPresent(state, origKey);
|
||||
if (direct) {
|
||||
validRef.valid = true;
|
||||
return direct;
|
||||
}
|
||||
|
||||
// Allocate new block from front of free list
|
||||
validRef.valid = false;
|
||||
|
||||
if (!state.front) {
|
||||
throw new Error("No free blocks available");
|
||||
}
|
||||
|
||||
var blk = state.front;
|
||||
|
||||
// Remove block from previous hash table entry if it was used
|
||||
if (blk.key) {
|
||||
var previousKey = blk.key % (state.blocks * 2);
|
||||
|
||||
if (state.hashTable[previousKey] === blk) {
|
||||
state.hashTable[previousKey] = blk.hashNext;
|
||||
} else {
|
||||
var entry = state.hashTable[previousKey];
|
||||
var found = false;
|
||||
|
||||
while (entry && entry.hashNext) {
|
||||
if (entry.hashNext === blk) {
|
||||
entry.hashNext = blk.hashNext;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
entry = entry.hashNext;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
throw new Error("The hash table chain did not contain the used block");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert block into hash table
|
||||
blk.key = origKey;
|
||||
|
||||
if (!state.hashTable[key]) {
|
||||
blk.hashNext = null;
|
||||
state.hashTable[key] = blk;
|
||||
} else {
|
||||
// Insert at end of chain
|
||||
var entry = state.hashTable[key]!;
|
||||
|
||||
while (entry.hashNext) {
|
||||
entry = entry.hashNext;
|
||||
}
|
||||
|
||||
entry.hashNext = blk;
|
||||
blk.hashNext = null;
|
||||
}
|
||||
|
||||
// Move block to rear of free list
|
||||
state.front = state.front.freeNext;
|
||||
if (state.front) {
|
||||
state.front.freePrev = null;
|
||||
}
|
||||
|
||||
if (state.rear) {
|
||||
state.rear.freeNext = blk;
|
||||
}
|
||||
blk.freePrev = state.rear;
|
||||
blk.freeNext = null;
|
||||
state.rear = blk;
|
||||
|
||||
return blk.payload;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { charc } from "../../libts/byte";
|
||||
import { kdriver_etc_serial_transmit } from "../../../kernel/drivers/etc/serial";
|
||||
import {
|
||||
kmod_graphics_vga_pushLine,
|
||||
kmod_graphics_vga_setLastLine,
|
||||
} from "../../../kernel/modules/graphics/vga";
|
||||
|
||||
export const Logger = {
|
||||
log(message: string) {
|
||||
kmod_graphics_vga_pushLine(message);
|
||||
|
||||
for (let i = 0; i < message.length; i++) {
|
||||
kdriver_etc_serial_transmit(charc(message[i]!));
|
||||
}
|
||||
|
||||
kdriver_etc_serial_transmit(charc("\n"));
|
||||
},
|
||||
update(message: string) {
|
||||
kmod_graphics_vga_setLastLine(message);
|
||||
|
||||
kdriver_etc_serial_transmit(charc("\x1b[2K"));
|
||||
kdriver_etc_serial_transmit(charc("\r"));
|
||||
for (let i = 0; i < message.length; i++) {
|
||||
kdriver_etc_serial_transmit(charc(message[i]!));
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
export const Path = {
|
||||
dirname(p: string): string {
|
||||
const parts = p.split("/");
|
||||
|
||||
parts.pop();
|
||||
|
||||
if (parts.length <= 1) return "/";
|
||||
|
||||
return parts.join("/");
|
||||
},
|
||||
filename(p: string): string {
|
||||
const parts = p.split("/");
|
||||
|
||||
const n = parts.pop();
|
||||
|
||||
if (!n) return "";
|
||||
return n;
|
||||
},
|
||||
join(p1: string, p2: string): string {
|
||||
if (p1.endsWith("/")) {
|
||||
p1 = p1.slice(0, -1);
|
||||
}
|
||||
|
||||
if (p2.startsWith("/")) {
|
||||
p2 = p2.slice(1);
|
||||
}
|
||||
|
||||
return p1 + "/" + p2;
|
||||
},
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
export function bytein(port: number): number {
|
||||
return $bytein(port);
|
||||
}
|
||||
|
||||
export function byteout(port: number, value: number): void {
|
||||
$byteout(port, value);
|
||||
}
|
||||
|
||||
export function charc(char: string): number {
|
||||
return char.charCodeAt(0);
|
||||
}
|
||||
|
||||
export function cchar(code: number): string {
|
||||
return String.fromCharCode(code)[0]!;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function dwordin(dw: number): number {
|
||||
return $dwordin(dw);
|
||||
}
|
||||
|
||||
export function dwordout(dw: number, value: number): void {
|
||||
$dwordout(dw, value);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export function iexec(code: string) {
|
||||
return $isolatedExec(code);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function portin(port: number): number {
|
||||
return $ptin(port);
|
||||
}
|
||||
|
||||
export function portout(port: number, value: number): void {
|
||||
$ptout(port, value);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export function padStart(
|
||||
str: string,
|
||||
targetLength: number,
|
||||
padString: string
|
||||
): string {
|
||||
while (str.length < targetLength) {
|
||||
str = padString + str;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export function uiarrtostr(a: number[]) {
|
||||
let s = "";
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
s += String.fromCharCode(a[i]!);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function wordin(w: number): number {
|
||||
return $wordin(w);
|
||||
}
|
||||
|
||||
export function wordout(w: number, value: number): void {
|
||||
$wordout(w, value);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { kmod_bios_time_getTimestamp } from "../../kernel/modules/bios/time";
|
||||
|
||||
export function getDate(): Date {
|
||||
return new Date(kmod_bios_time_getTimestamp());
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { kmod_filesystem_readFile } from "../../kernel/modules/filesystem/filesystem.kmod";
|
||||
import { uiarrtostr } from "../libts/uint_arr";
|
||||
|
||||
export function getEnv(key: string): string | null {
|
||||
const data = uiarrtostr(kmod_filesystem_readFile("/disk/uenv")!);
|
||||
const lines = data.split("\n");
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const kv = lines[i]!.split("=");
|
||||
|
||||
if (kv[0] == key) return kv[1]!;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getPathEnv(): string[] | null {
|
||||
const path = getEnv("PATH");
|
||||
if (!path) return null;
|
||||
|
||||
return path.split(":");
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { KDriverPCIClass } from "../../../types/dev/pci/class";
|
||||
|
||||
const sysPCIClassNames = {
|
||||
[KDriverPCIClass.MassStorageController]: "Mass Storage Controller",
|
||||
[KDriverPCIClass.NetworkController]: "Network Controller",
|
||||
[KDriverPCIClass.DisplayController]: "Display Controller",
|
||||
[KDriverPCIClass.MultimediaController]: "Multimedia Controller",
|
||||
[KDriverPCIClass.MemoryController]: "Memory Controller",
|
||||
[KDriverPCIClass.BridgeDevice]: "Bridge Device",
|
||||
[KDriverPCIClass.SimpleCommunicationsController]:
|
||||
"Simple Communications Controller",
|
||||
[KDriverPCIClass.BaseSystemPeripheral]: "Base System Peripheral",
|
||||
[KDriverPCIClass.InputDevice]: "Input Device",
|
||||
[KDriverPCIClass.DockingStation]: "Docking Station",
|
||||
[KDriverPCIClass.Processors]: "Processors",
|
||||
[KDriverPCIClass.SerialBusController]: "Serial Bus Controller",
|
||||
[KDriverPCIClass.WirelessController]: "Wireless Controller",
|
||||
[KDriverPCIClass.IntelligentIOController]: "Intelligent I/O Controller",
|
||||
[KDriverPCIClass.SatelliteCommunicationController]:
|
||||
"Satellite Communication Controller",
|
||||
[KDriverPCIClass.EncryptionDecryptionController]:
|
||||
"Encryption/Decryption Controller",
|
||||
[KDriverPCIClass.DataAcquisitionAndSignalProcessingController]:
|
||||
"Data Acquisition and Signal Processing Controller",
|
||||
};
|
||||
|
||||
export function sys_pci_class_name(classId: KDriverPCIClass): string {
|
||||
return sysPCIClassNames[classId] || "Unknown";
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { kmod_app_run } from "../../kernel/modules/app/app.kmod";
|
||||
|
||||
export function oskrnl_app_launcher_run(path: string, args: string) {
|
||||
return kmod_app_run(path, args);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import {
|
||||
kmod_app_proc_addExitListener,
|
||||
kmod_app_proc_exit,
|
||||
kmod_app_proc_getArgs,
|
||||
kmod_app_proc_running,
|
||||
} from "../../kernel/modules/app/app.kmod";
|
||||
|
||||
export function oskrnl_app_proc_getArgs(pid: string): string {
|
||||
return kmod_app_proc_getArgs(pid);
|
||||
}
|
||||
|
||||
export function oskrnl_app_proc_addExitListener(
|
||||
pid: string,
|
||||
listener: () => void
|
||||
): void {
|
||||
kmod_app_proc_addExitListener(pid, listener);
|
||||
}
|
||||
|
||||
export function oskrnl_app_proc_exit(pid: string): void {
|
||||
kmod_app_proc_exit(pid);
|
||||
}
|
||||
|
||||
export function oskrnl_app_proc_running(pid: string): boolean {
|
||||
return kmod_app_proc_running(pid);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Logger } from "../../lib/libstd/logger/logger.kmod";
|
||||
|
||||
export function oskrnl_console_log(data: any) {
|
||||
Logger.log(String(data));
|
||||
}
|
||||
|
||||
export function oskrnl_console_update(data: any) {
|
||||
Logger.update(String(data));
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { oskrnl_app_launcher_run } from "./app/launcher";
|
||||
import {
|
||||
oskrnl_app_proc_addExitListener,
|
||||
oskrnl_app_proc_exit,
|
||||
oskrnl_app_proc_getArgs,
|
||||
oskrnl_app_proc_running,
|
||||
} from "./app/proc";
|
||||
import { oskrnl_console_log, oskrnl_console_update } from "./console/console";
|
||||
import { oskrnl_input_onKeyPress } from "./input/input";
|
||||
|
||||
export function oskrnl_register() {
|
||||
(globalThis as any).__oskrnl = {
|
||||
console_log: oskrnl_console_log,
|
||||
console_update: oskrnl_console_update,
|
||||
input_onKeyPress: oskrnl_input_onKeyPress,
|
||||
app_launcher_run: oskrnl_app_launcher_run,
|
||||
app_proc_getArgs: oskrnl_app_proc_getArgs,
|
||||
app_proc_addExitListener: oskrnl_app_proc_addExitListener,
|
||||
app_proc_exit: oskrnl_app_proc_exit,
|
||||
app_proc_running: oskrnl_app_proc_running,
|
||||
};
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import {
|
||||
kmod_app_proc_generateHandle,
|
||||
kmod_app_proc_registerHandle,
|
||||
kmod_app_proc_running,
|
||||
kmod_app_proc_addExitListener,
|
||||
} from "../../kernel/modules/app/app.kmod";
|
||||
import { kmod_terminal_input_onKeyboardInput } from "../../kernel/modules/terminal/input";
|
||||
|
||||
export function oskrnl_input_onKeyPress(
|
||||
pid: string,
|
||||
handler: (key: string) => void
|
||||
) {
|
||||
kmod_app_proc_registerHandle(pid, kmod_app_proc_generateHandle());
|
||||
const removeHandler = kmod_terminal_input_onKeyboardInput(function (
|
||||
key: string
|
||||
) {
|
||||
const running = kmod_app_proc_running(pid);
|
||||
if (!running) return;
|
||||
|
||||
handler(key);
|
||||
});
|
||||
|
||||
kmod_app_proc_addExitListener(pid, removeHandler);
|
||||
}
|
||||
15
src/os/src/types/c/bindings.d.ts
vendored
15
src/os/src/types/c/bindings.d.ts
vendored
@@ -1,15 +0,0 @@
|
||||
declare function $addrw(address: number, value: number): void;
|
||||
declare function $ptin(port: number): number;
|
||||
declare function $ptout(port: number, value: number): void;
|
||||
declare function $bytein(port: number): number;
|
||||
declare function $byteout(port: number, value: number): void;
|
||||
declare function $dwordin(port: number): number;
|
||||
declare function $dwordout(port: number, value: number): void;
|
||||
declare function $wordin(port: number): number;
|
||||
declare function $wordout(port: number, value: number): void;
|
||||
declare const $irqregister: (
|
||||
irq: number,
|
||||
handler: (irqNum: number) => void
|
||||
) => boolean;
|
||||
declare function $isolatedExec(code: string): number;
|
||||
declare function $destroyIsolatedHeap(handle: number): void;
|
||||
@@ -1,11 +0,0 @@
|
||||
export type AtaDriveDescriptor = {
|
||||
controller: number;
|
||||
drive: number;
|
||||
present: boolean;
|
||||
slave: number;
|
||||
atapi: boolean;
|
||||
model: string;
|
||||
serial: string;
|
||||
firmware: string;
|
||||
size: number;
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum DeviceType {
|
||||
BlockDevice,
|
||||
CharDevice,
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
export enum KDriverPCIClass {
|
||||
MassStorageController = 1,
|
||||
NetworkController = 2,
|
||||
DisplayController = 3,
|
||||
MultimediaController = 4,
|
||||
MemoryController = 5,
|
||||
BridgeDevice = 6,
|
||||
SimpleCommunicationsController = 7,
|
||||
BaseSystemPeripheral = 8,
|
||||
InputDevice = 9,
|
||||
DockingStation = 10,
|
||||
Processors = 11,
|
||||
SerialBusController = 12,
|
||||
WirelessController = 13,
|
||||
IntelligentIOController = 14,
|
||||
SatelliteCommunicationController = 15,
|
||||
EncryptionDecryptionController = 16,
|
||||
DataAcquisitionAndSignalProcessingController = 17,
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { AtaDriveDescriptor } from "../dev/ata/drive";
|
||||
|
||||
export type Disk = {
|
||||
dnum: number;
|
||||
type: DiskType;
|
||||
descriptor: AtaDriveDescriptor;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export enum DiskType {
|
||||
ATA,
|
||||
ATAPI,
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { DeviceType } from "../dev/device";
|
||||
|
||||
export type KFilesystemMountdata = KFilesystemMount[];
|
||||
|
||||
export type KFilesystemMount = {
|
||||
mountpoint: string;
|
||||
driver: KFilesystemDriver;
|
||||
};
|
||||
|
||||
export type KFilesystemDirectReadResponse = number[];
|
||||
|
||||
export type KFilesystemDriver = {
|
||||
id: string;
|
||||
init: () => void;
|
||||
listDir(path: string): KFilesystemEntity[] | null;
|
||||
readFile(path: string): unknown;
|
||||
writeFile(path: string, content: unknown): void;
|
||||
createFile(path: string, content: unknown): void;
|
||||
mkdir(path: string): void;
|
||||
rm(path: string): void;
|
||||
stat(path: string): KFilesystemStat | null;
|
||||
directRead?(
|
||||
path: string,
|
||||
count: number,
|
||||
offset: number
|
||||
): KFilesystemDirectReadResponse;
|
||||
};
|
||||
|
||||
export type KFilesystemMemdata = KFilesystemEntity[];
|
||||
|
||||
export type KFilesystemEntity = {
|
||||
name: string;
|
||||
path: string;
|
||||
size: number;
|
||||
contents: unknown;
|
||||
type: "file" | "folder";
|
||||
};
|
||||
|
||||
export type KFilesystemStat = {
|
||||
name: string;
|
||||
type: "file" | "folder";
|
||||
size: number;
|
||||
};
|
||||
|
||||
export type KFilesystemDevice = KFilesystemEntity & {
|
||||
dtype: DeviceType;
|
||||
driver: unknown;
|
||||
data: unknown;
|
||||
};
|
||||
|
||||
export type KFilesystemMemdev = (KFilesystemDevice | KFilesystemEntity)[];
|
||||
@@ -1,9 +0,0 @@
|
||||
export type KInterruptHandler = (
|
||||
interruptNumber: number,
|
||||
data?: unknown
|
||||
) => void;
|
||||
|
||||
export type KInterrupt = {
|
||||
handler: KInterruptHandler;
|
||||
data: unknown;
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
export type BlockCache = {
|
||||
payload_size: number;
|
||||
blocks: number;
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
__oskrnl.console_log(__oskrnl.app_proc_getArgs(__oskrnl_procd_pid));
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"app:name": "echo",
|
||||
"app:version": "1.0.0",
|
||||
"app:entrypoint": "index.js"
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
__oskrnl.console_log("> ");
|
||||
|
||||
var input = "";
|
||||
var pid = "";
|
||||
|
||||
__oskrnl.input_onKeyPress(__oskrnl_procd_pid, function (key) {
|
||||
if (pid != "" && __oskrnl.app_proc_running(pid)) return;
|
||||
|
||||
if (key == "Backspace") input = input.slice(0, -1);
|
||||
else if (key == "Enter") {
|
||||
process();
|
||||
return;
|
||||
} else input += key.toLowerCase();
|
||||
|
||||
__oskrnl.console_update("> " + input);
|
||||
});
|
||||
|
||||
function process() {
|
||||
var app = input.split(" ")[0];
|
||||
var args = input.split(" ").slice(1);
|
||||
pid = __oskrnl.app_launcher_run(app, args.join(" "));
|
||||
__oskrnl.app_proc_addExitListener(pid, function () {
|
||||
input = "";
|
||||
__oskrnl.console_log("> ");
|
||||
});
|
||||
|
||||
if (!__oskrnl.app_proc_running(pid)) {
|
||||
input = "";
|
||||
__oskrnl.console_log("> ");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"app:name": "tysh",
|
||||
"app:version": "1.0.0",
|
||||
"app:entrypoint": "index.js"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user