Make EFI port primary
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,4 @@
|
|||||||
_o/
|
old/
|
||||||
build/
|
out/
|
||||||
picolibc/
|
|
||||||
picolibc-install/
|
|
||||||
/lib/
|
|
||||||
*.iso
|
*.iso
|
||||||
*.img
|
*.img
|
||||||
@@ -31,14 +31,7 @@ int kernel_main(EFI_SYSTEM_TABLE *st) {
|
|||||||
duk_push_c_function(ctx, native_log, 1);
|
duk_push_c_function(ctx, native_log, 1);
|
||||||
duk_put_global_string(ctx, "$log");
|
duk_put_global_string(ctx, "$log");
|
||||||
|
|
||||||
duk_push_object(ctx);
|
duk_push_string(ctx, "var a = 1 + 2; $log(a);");
|
||||||
|
|
||||||
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_int_t returnCode = duk_peval(ctx);
|
duk_int_t returnCode = duk_peval(ctx);
|
||||||
|
|
||||||
if (returnCode != 0)
|
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