From 304dda0adf20a0fcb3a14a3f1e81fb2a8df46177 Mon Sep 17 00:00:00 2001 From: Martin Petr Date: Fri, 21 Nov 2025 22:57:51 +0100 Subject: [PATCH] Add disk driver --- scripts/run-kernel.sh | 68 +++- src/kernel/kernel.c | 35 ++ src/link.ld | 14 +- src/os/src/config/keyboard.ts | 6 + src/os/src/kernel/drivers/dev/ata.ts | 365 +++++++++++++++++ src/os/src/kernel/drivers/dev/keyboard.ts | 2 + src/os/src/kernel/filesystem/devfs.ts | 151 +++++++ src/os/src/kernel/filesystem/fat32.ts | 367 ++++++++++++++++++ src/os/src/kernel/index.ts | 52 ++- src/os/src/kernel/modules/disks/disks.kmod.ts | 163 ++++++++ .../kernel/modules/drivers/drivers.kmod.ts | 2 + .../modules/filesystem/filesystem.kmod.ts | 31 +- src/os/src/kernel/modules/graphics/vga.ts | 23 ++ src/os/src/kernel/utils/block_cache.ts | 192 +++++++++ src/os/src/lib/libstd/logger/logger.kmod.ts | 3 + src/os/src/lib/libts/byte.ts | 4 + src/os/src/lib/libts/word.ts | 7 + src/os/src/types/c/bindings.d.ts | 2 + src/os/src/types/dev/ata/drive.ts | 11 + src/os/src/types/dev/device.ts | 4 + src/os/src/types/filesystem/disk.ts | 13 + src/os/src/types/filesystem/fs.ts | 17 + src/os/src/types/utils/block_cache.ts | 4 + test-hdd/hello.txt | 1 + test-hdd/myapp.js | 1 + 25 files changed, 1523 insertions(+), 15 deletions(-) create mode 100644 src/os/src/kernel/drivers/dev/ata.ts create mode 100644 src/os/src/kernel/filesystem/devfs.ts create mode 100644 src/os/src/kernel/filesystem/fat32.ts create mode 100644 src/os/src/kernel/modules/disks/disks.kmod.ts create mode 100644 src/os/src/kernel/utils/block_cache.ts create mode 100644 src/os/src/lib/libts/word.ts create mode 100644 src/os/src/types/dev/ata/drive.ts create mode 100644 src/os/src/types/dev/device.ts create mode 100644 src/os/src/types/filesystem/disk.ts create mode 100644 src/os/src/types/utils/block_cache.ts create mode 100644 test-hdd/hello.txt create mode 100644 test-hdd/myapp.js diff --git a/scripts/run-kernel.sh b/scripts/run-kernel.sh index 35d9c56..bcf14d6 100755 --- a/scripts/run-kernel.sh +++ b/scripts/run-kernel.sh @@ -1,3 +1,69 @@ ./scripts/build.sh -qemu-system-i386 -serial stdio -kernel "build/out/kernel" \ No newline at end of file +# 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 \ No newline at end of file diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 2baff03..1563af3 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -116,6 +116,33 @@ duk_ret_t native_dword_out(duk_context *ctx) 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; @@ -229,6 +256,14 @@ void kmain() 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"); diff --git a/src/link.ld b/src/link.ld index 6174ba6..192689a 100644 --- a/src/link.ld +++ b/src/link.ld @@ -26,15 +26,15 @@ SECTIONS *(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 = .; - . = . + 0x100000; /* 1MB heap */ + . = . + 0x10000000; /* 256MB heap */ __heap_end = .; - - /* Stack grows downward from high memory */ - . = ALIGN(4096); - __stack_bottom = .; - . = . + 0x10000; /* 64KB stack */ - __stack_top = .; } \ No newline at end of file diff --git a/src/os/src/config/keyboard.ts b/src/os/src/config/keyboard.ts index 3a3f8fd..0db6001 100644 --- a/src/os/src/config/keyboard.ts +++ b/src/os/src/config/keyboard.ts @@ -1,4 +1,5 @@ export const Keycode = { + Enter: 28, Q: 16, W: 17, E: 18, @@ -25,9 +26,12 @@ export const Keycode = { B: 48, N: 49, M: 50, + ".": 52, + "/": 53, }; export const RKeycode = { + 28: "Enter", 16: "Q", 17: "W", 18: "E", @@ -54,4 +58,6 @@ export const RKeycode = { 48: "B", 49: "N", 50: "M", + 52: ".", + 53: "/", }; diff --git a/src/os/src/kernel/drivers/dev/ata.ts b/src/os/src/kernel/drivers/dev/ata.ts new file mode 100644 index 0000000..457c7c2 --- /dev/null +++ b/src/os/src/kernel/drivers/dev/ata.ts @@ -0,0 +1,365 @@ +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(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, +}; diff --git a/src/os/src/kernel/drivers/dev/keyboard.ts b/src/os/src/kernel/drivers/dev/keyboard.ts index cfb952c..77fb184 100644 --- a/src/os/src/kernel/drivers/dev/keyboard.ts +++ b/src/os/src/kernel/drivers/dev/keyboard.ts @@ -11,6 +11,8 @@ export function kdriver_dev_keyboard_handleInterrupt(): void { | keyof typeof Keycode | undefined; + //Logger.log(String(scancode)); + if (key == undefined) return; kmod_terminal_input_handleKeyboardInput(key); diff --git a/src/os/src/kernel/filesystem/devfs.ts b/src/os/src/kernel/filesystem/devfs.ts new file mode 100644 index 0000000..0cb3e25 --- /dev/null +++ b/src/os/src/kernel/filesystem/devfs.ts @@ -0,0 +1,151 @@ +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); + }, + }; +} + +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 []; +} diff --git a/src/os/src/kernel/filesystem/fat32.ts b/src/os/src/kernel/filesystem/fat32.ts new file mode 100644 index 0000000..c043989 --- /dev/null +++ b/src/os/src/kernel/filesystem/fat32.ts @@ -0,0 +1,367 @@ +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); + }, + }; +} + +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; +} diff --git a/src/os/src/kernel/index.ts b/src/os/src/kernel/index.ts index 43b26ba..0ef9260 100644 --- a/src/os/src/kernel/index.ts +++ b/src/os/src/kernel/index.ts @@ -2,18 +2,23 @@ import { Logger } from "../lib/libstd/logger/logger.kmod"; import { charc } from "../lib/libts/byte"; import { padStart } from "../lib/libts/string"; 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 { sysfs_driver, sysfs_readFile } from "./filesystem/sysfs"; +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"; @@ -21,6 +26,7 @@ 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, @@ -33,9 +39,13 @@ export function kmain() { 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); Logger.log("[Kernel] Initializing terminal module..."); kmod_terminal_input_init(); @@ -43,11 +53,45 @@ export function kmain() { Logger.log("[Kernel] Initializing PCI devices..."); kdriver_dev_pci_detectDevices(); - Logger.log("[Kernel] Initializing VGA module..."); - kmod_graphics_vga_init(); + Logger.log("[Kernel] Initializing drives..."); + kmod_disks_detectDisks(); + kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully."); - kmod_terminal_input_onKeyboardInput(function (keycode) { - kmod_graphics_vga_pushLine(keycode); + const dev = devfs_getDevice("/hda1"); + if (!dev) throw new Error("Drive not found"); + + kmod_filesystem_mount("/home", function () { + return fat32_driver(dev.driver, dev.data); + }); + + let input = ""; + let running = false; + + kmod_graphics_vga_setLastLine("> "); + + kmod_terminal_input_onKeyboardInput(function (code) { + if (running) return; + + if (code == "Enter") { + running = true; + kmod_graphics_vga_pushLine("Starting " + input); + const f = kmod_filesystem_readFile(input); + if (!f) return; + + let app = ""; + + for (let i = 0; i < f.length; i++) { + app += String.fromCharCode(f[i]!); + } + + // EXTREMELY DANGEROUS -- REPLACE WITH SANDBOXING + eval("const console = { log: Logger.log } ;" + app); + Logger.log("DONE"); + running = false; + } else { + input += code.toLowerCase(); + kmod_graphics_vga_setLastLine("> " + input); + } }); } diff --git a/src/os/src/kernel/modules/disks/disks.kmod.ts b/src/os/src/kernel/modules/disks/disks.kmod.ts new file mode 100644 index 0000000..3ff7885 --- /dev/null +++ b/src/os/src/kernel/modules/disks/disks.kmod.ts @@ -0,0 +1,163 @@ +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; +} diff --git a/src/os/src/kernel/modules/drivers/drivers.kmod.ts b/src/os/src/kernel/modules/drivers/drivers.kmod.ts index e2159ec..b5d7387 100644 --- a/src/os/src/kernel/modules/drivers/drivers.kmod.ts +++ b/src/os/src/kernel/modules/drivers/drivers.kmod.ts @@ -1,3 +1,4 @@ +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"; @@ -6,6 +7,7 @@ const drivers = [ kdriver_etc_serial_init, kdriver_dev_pci_init, kdriver_dev_keyboard_init, + kdriver_dev_ata_init, ]; export function kmod_drivers_init(): void {} diff --git a/src/os/src/kernel/modules/filesystem/filesystem.kmod.ts b/src/os/src/kernel/modules/filesystem/filesystem.kmod.ts index 845d91e..6ccb03a 100644 --- a/src/os/src/kernel/modules/filesystem/filesystem.kmod.ts +++ b/src/os/src/kernel/modules/filesystem/filesystem.kmod.ts @@ -1,4 +1,5 @@ import { Logger } from "../../../lib/libstd/logger/logger.kmod"; +import { Path } from "../../../lib/libstd/path"; import type { KFilesystemDriver, KFilesystemEntity, @@ -48,24 +49,42 @@ export function kmod_filesystem_stripMountpoint( 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 null; + 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): unknown { +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); + return mount.driver.readFile(strippedPath) as number[]; } export function kmod_filesystem_writeFile( @@ -109,3 +128,9 @@ export function kmod_filesystem_stat(path: string): KFilesystemStat | null { return mount.driver.stat(strippedPath); } + +export function kmod_filesystem_directRead( + path: string, + count: number, + offset: number +) {} diff --git a/src/os/src/kernel/modules/graphics/vga.ts b/src/os/src/kernel/modules/graphics/vga.ts index 5108bee..e8661fd 100644 --- a/src/os/src/kernel/modules/graphics/vga.ts +++ b/src/os/src/kernel/modules/graphics/vga.ts @@ -6,12 +6,17 @@ import { } 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) { @@ -21,7 +26,21 @@ export function kmod_graphics_vga_pushLine(line: string) { 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] || ""; @@ -49,6 +68,8 @@ export function kmod_graphics_vga_getColorAddr(x: number, y: number): number { } export function kmod_graphics_vga_clear() { + if (!init) return; + kmod_graphics_vga_lineBuf = []; for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) { @@ -64,6 +85,8 @@ export function kmod_graphics_vga_writeChar( char: string, color: number ) { + if (!init) return; + const charAddr = kmod_graphics_vga_getCharAddr(x, y); const colorAddr = kmod_graphics_vga_getColorAddr(x, y); diff --git a/src/os/src/kernel/utils/block_cache.ts b/src/os/src/kernel/utils/block_cache.ts new file mode 100644 index 0000000..49ae851 --- /dev/null +++ b/src/os/src/kernel/utils/block_cache.ts @@ -0,0 +1,192 @@ +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; +} diff --git a/src/os/src/lib/libstd/logger/logger.kmod.ts b/src/os/src/lib/libstd/logger/logger.kmod.ts index 2e9d6c2..2eba19e 100644 --- a/src/os/src/lib/libstd/logger/logger.kmod.ts +++ b/src/os/src/lib/libstd/logger/logger.kmod.ts @@ -1,8 +1,11 @@ import { charc } from "../../libts/byte"; import { kdriver_etc_serial_transmit } from "../../../kernel/drivers/etc/serial"; +import { kmod_graphics_vga_pushLine } 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]!)); } diff --git a/src/os/src/lib/libts/byte.ts b/src/os/src/lib/libts/byte.ts index bded289..69b3beb 100644 --- a/src/os/src/lib/libts/byte.ts +++ b/src/os/src/lib/libts/byte.ts @@ -9,3 +9,7 @@ export function byteout(port: number, value: number): void { export function charc(char: string): number { return char.charCodeAt(0); } + +export function cchar(code: number): string { + return String.fromCharCode(code)[0]!; +} diff --git a/src/os/src/lib/libts/word.ts b/src/os/src/lib/libts/word.ts new file mode 100644 index 0000000..fe564e5 --- /dev/null +++ b/src/os/src/lib/libts/word.ts @@ -0,0 +1,7 @@ +export function wordin(w: number): number { + return $wordin(w); +} + +export function wordout(w: number, value: number): void { + $wordout(w, value); +} diff --git a/src/os/src/types/c/bindings.d.ts b/src/os/src/types/c/bindings.d.ts index ccedf7d..a6482f2 100644 --- a/src/os/src/types/c/bindings.d.ts +++ b/src/os/src/types/c/bindings.d.ts @@ -5,6 +5,8 @@ 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 diff --git a/src/os/src/types/dev/ata/drive.ts b/src/os/src/types/dev/ata/drive.ts new file mode 100644 index 0000000..aa59520 --- /dev/null +++ b/src/os/src/types/dev/ata/drive.ts @@ -0,0 +1,11 @@ +export type AtaDriveDescriptor = { + controller: number; + drive: number; + present: boolean; + slave: number; + atapi: boolean; + model: string; + serial: string; + firmware: string; + size: number; +}; diff --git a/src/os/src/types/dev/device.ts b/src/os/src/types/dev/device.ts new file mode 100644 index 0000000..efbf22b --- /dev/null +++ b/src/os/src/types/dev/device.ts @@ -0,0 +1,4 @@ +export enum DeviceType { + BlockDevice, + CharDevice, +} diff --git a/src/os/src/types/filesystem/disk.ts b/src/os/src/types/filesystem/disk.ts new file mode 100644 index 0000000..e118132 --- /dev/null +++ b/src/os/src/types/filesystem/disk.ts @@ -0,0 +1,13 @@ +import type { AtaDriveDescriptor } from "../dev/ata/drive"; + +export type Disk = { + dnum: number; + type: DiskType; + descriptor: AtaDriveDescriptor; + name: string; +}; + +export enum DiskType { + ATA, + ATAPI, +} diff --git a/src/os/src/types/filesystem/fs.ts b/src/os/src/types/filesystem/fs.ts index 0d2cd2e..b16ff42 100644 --- a/src/os/src/types/filesystem/fs.ts +++ b/src/os/src/types/filesystem/fs.ts @@ -1,3 +1,5 @@ +import type { DeviceType } from "../dev/device"; + export type KFilesystemMountdata = KFilesystemMount[]; export type KFilesystemMount = { @@ -5,6 +7,8 @@ export type KFilesystemMount = { driver: KFilesystemDriver; }; +export type KFilesystemDirectReadResponse = number[]; + export type KFilesystemDriver = { id: string; init: () => void; @@ -14,6 +18,11 @@ export type KFilesystemDriver = { createFile(path: string, content: unknown): void; mkdir(path: string): void; stat(path: string): KFilesystemStat | null; + directRead?( + path: string, + count: number, + offset: number + ): KFilesystemDirectReadResponse; }; export type KFilesystemMemdata = KFilesystemEntity[]; @@ -31,3 +40,11 @@ export type KFilesystemStat = { type: "file" | "folder"; size: number; }; + +export type KFilesystemDevice = KFilesystemEntity & { + dtype: DeviceType; + driver: unknown; + data: unknown; +}; + +export type KFilesystemMemdev = (KFilesystemDevice | KFilesystemEntity)[]; diff --git a/src/os/src/types/utils/block_cache.ts b/src/os/src/types/utils/block_cache.ts new file mode 100644 index 0000000..08acdb2 --- /dev/null +++ b/src/os/src/types/utils/block_cache.ts @@ -0,0 +1,4 @@ +export type BlockCache = { + payload_size: number; + blocks: number; +}; diff --git a/test-hdd/hello.txt b/test-hdd/hello.txt new file mode 100644 index 0000000..5ef648a --- /dev/null +++ b/test-hdd/hello.txt @@ -0,0 +1 @@ +It works! \ No newline at end of file diff --git a/test-hdd/myapp.js b/test-hdd/myapp.js new file mode 100644 index 0000000..3ee7f0c --- /dev/null +++ b/test-hdd/myapp.js @@ -0,0 +1 @@ +console.log("Hello from myApp");