Add disk driver
This commit is contained in:
@@ -1,3 +1,69 @@
|
|||||||
./scripts/build.sh
|
./scripts/build.sh
|
||||||
|
|
||||||
qemu-system-i386 -serial stdio -kernel "build/out/kernel"
|
# 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
|
||||||
@@ -116,6 +116,33 @@ duk_ret_t native_dword_out(duk_context *ctx)
|
|||||||
return 0;
|
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
|
// Global context for IRQ handlers
|
||||||
duk_context *global_ctx = NULL;
|
duk_context *global_ctx = NULL;
|
||||||
|
|
||||||
@@ -229,6 +256,14 @@ void kmain()
|
|||||||
duk_push_c_function(ctx, native_dword_out, 2);
|
duk_push_c_function(ctx, native_dword_out, 2);
|
||||||
duk_put_global_string(ctx, "$dwordout");
|
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
|
// Register native IRQ registration function
|
||||||
duk_push_c_function(ctx, native_irq_register, 2);
|
duk_push_c_function(ctx, native_irq_register, 2);
|
||||||
duk_put_global_string(ctx, "$irqregister");
|
duk_put_global_string(ctx, "$irqregister");
|
||||||
|
|||||||
14
src/link.ld
14
src/link.ld
@@ -26,15 +26,15 @@ SECTIONS
|
|||||||
*(COMMON)
|
*(COMMON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stack grows downward from high memory - place before heap */
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__stack_bottom = .;
|
||||||
|
. = . + 0x100000; /* 1MB stack */
|
||||||
|
__stack_top = .;
|
||||||
|
|
||||||
/* Heap for malloc */
|
/* Heap for malloc */
|
||||||
. = ALIGN(4096);
|
. = ALIGN(4096);
|
||||||
__heap_start = .;
|
__heap_start = .;
|
||||||
. = . + 0x100000; /* 1MB heap */
|
. = . + 0x10000000; /* 256MB heap */
|
||||||
__heap_end = .;
|
__heap_end = .;
|
||||||
|
|
||||||
/* Stack grows downward from high memory */
|
|
||||||
. = ALIGN(4096);
|
|
||||||
__stack_bottom = .;
|
|
||||||
. = . + 0x10000; /* 64KB stack */
|
|
||||||
__stack_top = .;
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export const Keycode = {
|
export const Keycode = {
|
||||||
|
Enter: 28,
|
||||||
Q: 16,
|
Q: 16,
|
||||||
W: 17,
|
W: 17,
|
||||||
E: 18,
|
E: 18,
|
||||||
@@ -25,9 +26,12 @@ export const Keycode = {
|
|||||||
B: 48,
|
B: 48,
|
||||||
N: 49,
|
N: 49,
|
||||||
M: 50,
|
M: 50,
|
||||||
|
".": 52,
|
||||||
|
"/": 53,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RKeycode = {
|
export const RKeycode = {
|
||||||
|
28: "Enter",
|
||||||
16: "Q",
|
16: "Q",
|
||||||
17: "W",
|
17: "W",
|
||||||
18: "E",
|
18: "E",
|
||||||
@@ -54,4 +58,6 @@ export const RKeycode = {
|
|||||||
48: "B",
|
48: "B",
|
||||||
49: "N",
|
49: "N",
|
||||||
50: "M",
|
50: "M",
|
||||||
|
52: ".",
|
||||||
|
53: "/",
|
||||||
};
|
};
|
||||||
|
|||||||
365
src/os/src/kernel/drivers/dev/ata.ts
Normal file
365
src/os/src/kernel/drivers/dev/ata.ts
Normal file
@@ -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<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,
|
||||||
|
};
|
||||||
@@ -11,6 +11,8 @@ export function kdriver_dev_keyboard_handleInterrupt(): void {
|
|||||||
| keyof typeof Keycode
|
| keyof typeof Keycode
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
|
//Logger.log(String(scancode));
|
||||||
|
|
||||||
if (key == undefined) return;
|
if (key == undefined) return;
|
||||||
|
|
||||||
kmod_terminal_input_handleKeyboardInput(key);
|
kmod_terminal_input_handleKeyboardInput(key);
|
||||||
|
|||||||
151
src/os/src/kernel/filesystem/devfs.ts
Normal file
151
src/os/src/kernel/filesystem/devfs.ts
Normal file
@@ -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 [];
|
||||||
|
}
|
||||||
367
src/os/src/kernel/filesystem/fat32.ts
Normal file
367
src/os/src/kernel/filesystem/fat32.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -2,18 +2,23 @@ import { Logger } from "../lib/libstd/logger/logger.kmod";
|
|||||||
import { charc } from "../lib/libts/byte";
|
import { charc } from "../lib/libts/byte";
|
||||||
import { padStart } from "../lib/libts/string";
|
import { padStart } from "../lib/libts/string";
|
||||||
import { getDate } from "../lib/sys/date";
|
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_dev_pci_detectDevices } from "./drivers/dev/pci";
|
||||||
import {
|
import {
|
||||||
kdriver_etc_serial_read,
|
kdriver_etc_serial_read,
|
||||||
kdriver_etc_serial_transmit,
|
kdriver_etc_serial_transmit,
|
||||||
} from "./drivers/etc/serial";
|
} 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 { sysfs_driver, sysfs_readFile } from "./filesystem/sysfs";
|
||||||
|
import { kmod_disks_detectDisks } from "./modules/disks/disks.kmod";
|
||||||
import {
|
import {
|
||||||
kmod_drivers_init,
|
kmod_drivers_init,
|
||||||
kmod_drivers_register,
|
kmod_drivers_register,
|
||||||
} from "./modules/drivers/drivers.kmod";
|
} from "./modules/drivers/drivers.kmod";
|
||||||
import {
|
import {
|
||||||
kmod_filesystem_init,
|
kmod_filesystem_init,
|
||||||
|
kmod_filesystem_listDir,
|
||||||
kmod_filesystem_mount,
|
kmod_filesystem_mount,
|
||||||
kmod_filesystem_readFile,
|
kmod_filesystem_readFile,
|
||||||
} from "./modules/filesystem/filesystem.kmod";
|
} from "./modules/filesystem/filesystem.kmod";
|
||||||
@@ -21,6 +26,7 @@ import {
|
|||||||
kmod_graphics_vga_clear,
|
kmod_graphics_vga_clear,
|
||||||
kmod_graphics_vga_init,
|
kmod_graphics_vga_init,
|
||||||
kmod_graphics_vga_pushLine,
|
kmod_graphics_vga_pushLine,
|
||||||
|
kmod_graphics_vga_setLastLine,
|
||||||
} from "./modules/graphics/graphics.kmod";
|
} from "./modules/graphics/graphics.kmod";
|
||||||
import {
|
import {
|
||||||
kmod_terminal_input_init,
|
kmod_terminal_input_init,
|
||||||
@@ -33,9 +39,13 @@ export function kmain() {
|
|||||||
kmod_drivers_register();
|
kmod_drivers_register();
|
||||||
Logger.log("[Kernel] Drivers initialized.");
|
Logger.log("[Kernel] Drivers initialized.");
|
||||||
|
|
||||||
|
Logger.log("[Kernel] Initializing VGA module...");
|
||||||
|
kmod_graphics_vga_init();
|
||||||
|
|
||||||
Logger.log("[Kernel] Initializing filesystem module...");
|
Logger.log("[Kernel] Initializing filesystem module...");
|
||||||
kmod_filesystem_init();
|
kmod_filesystem_init();
|
||||||
kmod_filesystem_mount("/sys", sysfs_driver);
|
kmod_filesystem_mount("/sys", sysfs_driver);
|
||||||
|
kmod_filesystem_mount("/dev", devfs_driver);
|
||||||
|
|
||||||
Logger.log("[Kernel] Initializing terminal module...");
|
Logger.log("[Kernel] Initializing terminal module...");
|
||||||
kmod_terminal_input_init();
|
kmod_terminal_input_init();
|
||||||
@@ -43,11 +53,45 @@ export function kmain() {
|
|||||||
Logger.log("[Kernel] Initializing PCI devices...");
|
Logger.log("[Kernel] Initializing PCI devices...");
|
||||||
kdriver_dev_pci_detectDevices();
|
kdriver_dev_pci_detectDevices();
|
||||||
|
|
||||||
Logger.log("[Kernel] Initializing VGA module...");
|
Logger.log("[Kernel] Initializing drives...");
|
||||||
kmod_graphics_vga_init();
|
kmod_disks_detectDisks();
|
||||||
|
|
||||||
kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully.");
|
kmod_graphics_vga_pushLine("[Kernel] Kernel initialized successfully.");
|
||||||
|
|
||||||
kmod_terminal_input_onKeyboardInput(function (keycode) {
|
const dev = devfs_getDevice("/hda1");
|
||||||
kmod_graphics_vga_pushLine(keycode);
|
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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
163
src/os/src/kernel/modules/disks/disks.kmod.ts
Normal file
163
src/os/src/kernel/modules/disks/disks.kmod.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -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_keyboard_init } from "../../drivers/dev/keyboard";
|
||||||
import { kdriver_dev_pci_init } from "../../drivers/dev/pci";
|
import { kdriver_dev_pci_init } from "../../drivers/dev/pci";
|
||||||
import { kdriver_etc_serial_init } from "../../drivers/etc/serial";
|
import { kdriver_etc_serial_init } from "../../drivers/etc/serial";
|
||||||
@@ -6,6 +7,7 @@ const drivers = [
|
|||||||
kdriver_etc_serial_init,
|
kdriver_etc_serial_init,
|
||||||
kdriver_dev_pci_init,
|
kdriver_dev_pci_init,
|
||||||
kdriver_dev_keyboard_init,
|
kdriver_dev_keyboard_init,
|
||||||
|
kdriver_dev_ata_init,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function kmod_drivers_init(): void {}
|
export function kmod_drivers_init(): void {}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
import { Logger } from "../../../lib/libstd/logger/logger.kmod";
|
||||||
|
import { Path } from "../../../lib/libstd/path";
|
||||||
import type {
|
import type {
|
||||||
KFilesystemDriver,
|
KFilesystemDriver,
|
||||||
KFilesystemEntity,
|
KFilesystemEntity,
|
||||||
@@ -48,24 +49,42 @@ export function kmod_filesystem_stripMountpoint(
|
|||||||
return p.slice(mount.mountpoint.length) || "/";
|
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(
|
export function kmod_filesystem_listDir(
|
||||||
path: string
|
path: string
|
||||||
): KFilesystemEntity[] | null {
|
): KFilesystemEntity[] | null {
|
||||||
const mount = kmod_filesystem_findMount(path);
|
const mount = kmod_filesystem_findMount(path);
|
||||||
if (!mount) return null;
|
if (!mount) return kmod_filesystem_lsMountAsFsEntity();
|
||||||
|
|
||||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||||
|
|
||||||
return mount.driver.listDir(strippedPath);
|
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);
|
const mount = kmod_filesystem_findMount(path);
|
||||||
if (!mount) return null;
|
if (!mount) return null;
|
||||||
|
|
||||||
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
const strippedPath = kmod_filesystem_stripMountpoint(mount, path);
|
||||||
|
|
||||||
return mount.driver.readFile(strippedPath);
|
return mount.driver.readFile(strippedPath) as number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function kmod_filesystem_writeFile(
|
export function kmod_filesystem_writeFile(
|
||||||
@@ -109,3 +128,9 @@ export function kmod_filesystem_stat(path: string): KFilesystemStat | null {
|
|||||||
|
|
||||||
return mount.driver.stat(strippedPath);
|
return mount.driver.stat(strippedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function kmod_filesystem_directRead(
|
||||||
|
path: string,
|
||||||
|
count: number,
|
||||||
|
offset: number
|
||||||
|
) {}
|
||||||
|
|||||||
@@ -6,12 +6,17 @@ import {
|
|||||||
} from "./config";
|
} from "./config";
|
||||||
|
|
||||||
let kmod_graphics_vga_lineBuf: string[] = [];
|
let kmod_graphics_vga_lineBuf: string[] = [];
|
||||||
|
let init = false;
|
||||||
|
|
||||||
export function kmod_graphics_vga_init() {
|
export function kmod_graphics_vga_init() {
|
||||||
|
init = true;
|
||||||
|
|
||||||
kmod_graphics_vga_clear();
|
kmod_graphics_vga_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function kmod_graphics_vga_pushLine(line: string) {
|
export function kmod_graphics_vga_pushLine(line: string) {
|
||||||
|
if (!init) return;
|
||||||
|
|
||||||
kmod_graphics_vga_lineBuf.push(line);
|
kmod_graphics_vga_lineBuf.push(line);
|
||||||
|
|
||||||
if (kmod_graphics_vga_lineBuf.length > CFG_KMOD_GRAPHICS_VGA_HEIGHT) {
|
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();
|
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() {
|
export function kmod_graphics_vga_printLines() {
|
||||||
|
if (!init) return;
|
||||||
|
|
||||||
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
|
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
|
||||||
const line = kmod_graphics_vga_lineBuf[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() {
|
export function kmod_graphics_vga_clear() {
|
||||||
|
if (!init) return;
|
||||||
|
|
||||||
kmod_graphics_vga_lineBuf = [];
|
kmod_graphics_vga_lineBuf = [];
|
||||||
|
|
||||||
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
|
for (let y = 0; y < CFG_KMOD_GRAPHICS_VGA_HEIGHT; y++) {
|
||||||
@@ -64,6 +85,8 @@ export function kmod_graphics_vga_writeChar(
|
|||||||
char: string,
|
char: string,
|
||||||
color: number
|
color: number
|
||||||
) {
|
) {
|
||||||
|
if (!init) return;
|
||||||
|
|
||||||
const charAddr = kmod_graphics_vga_getCharAddr(x, y);
|
const charAddr = kmod_graphics_vga_getCharAddr(x, y);
|
||||||
const colorAddr = kmod_graphics_vga_getColorAddr(x, y);
|
const colorAddr = kmod_graphics_vga_getColorAddr(x, y);
|
||||||
|
|
||||||
|
|||||||
192
src/os/src/kernel/utils/block_cache.ts
Normal file
192
src/os/src/kernel/utils/block_cache.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
import { charc } from "../../libts/byte";
|
import { charc } from "../../libts/byte";
|
||||||
import { kdriver_etc_serial_transmit } from "../../../kernel/drivers/etc/serial";
|
import { kdriver_etc_serial_transmit } from "../../../kernel/drivers/etc/serial";
|
||||||
|
import { kmod_graphics_vga_pushLine } from "../../../kernel/modules/graphics/vga";
|
||||||
|
|
||||||
export const Logger = {
|
export const Logger = {
|
||||||
log(message: string) {
|
log(message: string) {
|
||||||
|
kmod_graphics_vga_pushLine(message);
|
||||||
|
|
||||||
for (let i = 0; i < message.length; i++) {
|
for (let i = 0; i < message.length; i++) {
|
||||||
kdriver_etc_serial_transmit(charc(message[i]!));
|
kdriver_etc_serial_transmit(charc(message[i]!));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,3 +9,7 @@ export function byteout(port: number, value: number): void {
|
|||||||
export function charc(char: string): number {
|
export function charc(char: string): number {
|
||||||
return char.charCodeAt(0);
|
return char.charCodeAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cchar(code: number): string {
|
||||||
|
return String.fromCharCode(code)[0]!;
|
||||||
|
}
|
||||||
|
|||||||
7
src/os/src/lib/libts/word.ts
Normal file
7
src/os/src/lib/libts/word.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export function wordin(w: number): number {
|
||||||
|
return $wordin(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wordout(w: number, value: number): void {
|
||||||
|
$wordout(w, value);
|
||||||
|
}
|
||||||
2
src/os/src/types/c/bindings.d.ts
vendored
2
src/os/src/types/c/bindings.d.ts
vendored
@@ -5,6 +5,8 @@ declare function $bytein(port: number): number;
|
|||||||
declare function $byteout(port: number, value: number): void;
|
declare function $byteout(port: number, value: number): void;
|
||||||
declare function $dwordin(port: number): number;
|
declare function $dwordin(port: number): number;
|
||||||
declare function $dwordout(port: number, value: number): void;
|
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: (
|
declare const $irqregister: (
|
||||||
irq: number,
|
irq: number,
|
||||||
handler: (irqNum: number) => void
|
handler: (irqNum: number) => void
|
||||||
|
|||||||
11
src/os/src/types/dev/ata/drive.ts
Normal file
11
src/os/src/types/dev/ata/drive.ts
Normal file
@@ -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;
|
||||||
|
};
|
||||||
4
src/os/src/types/dev/device.ts
Normal file
4
src/os/src/types/dev/device.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum DeviceType {
|
||||||
|
BlockDevice,
|
||||||
|
CharDevice,
|
||||||
|
}
|
||||||
13
src/os/src/types/filesystem/disk.ts
Normal file
13
src/os/src/types/filesystem/disk.ts
Normal file
@@ -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,
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { DeviceType } from "../dev/device";
|
||||||
|
|
||||||
export type KFilesystemMountdata = KFilesystemMount[];
|
export type KFilesystemMountdata = KFilesystemMount[];
|
||||||
|
|
||||||
export type KFilesystemMount = {
|
export type KFilesystemMount = {
|
||||||
@@ -5,6 +7,8 @@ export type KFilesystemMount = {
|
|||||||
driver: KFilesystemDriver;
|
driver: KFilesystemDriver;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type KFilesystemDirectReadResponse = number[];
|
||||||
|
|
||||||
export type KFilesystemDriver = {
|
export type KFilesystemDriver = {
|
||||||
id: string;
|
id: string;
|
||||||
init: () => void;
|
init: () => void;
|
||||||
@@ -14,6 +18,11 @@ export type KFilesystemDriver = {
|
|||||||
createFile(path: string, content: unknown): void;
|
createFile(path: string, content: unknown): void;
|
||||||
mkdir(path: string): void;
|
mkdir(path: string): void;
|
||||||
stat(path: string): KFilesystemStat | null;
|
stat(path: string): KFilesystemStat | null;
|
||||||
|
directRead?(
|
||||||
|
path: string,
|
||||||
|
count: number,
|
||||||
|
offset: number
|
||||||
|
): KFilesystemDirectReadResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type KFilesystemMemdata = KFilesystemEntity[];
|
export type KFilesystemMemdata = KFilesystemEntity[];
|
||||||
@@ -31,3 +40,11 @@ export type KFilesystemStat = {
|
|||||||
type: "file" | "folder";
|
type: "file" | "folder";
|
||||||
size: number;
|
size: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type KFilesystemDevice = KFilesystemEntity & {
|
||||||
|
dtype: DeviceType;
|
||||||
|
driver: unknown;
|
||||||
|
data: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type KFilesystemMemdev = (KFilesystemDevice | KFilesystemEntity)[];
|
||||||
|
|||||||
4
src/os/src/types/utils/block_cache.ts
Normal file
4
src/os/src/types/utils/block_cache.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export type BlockCache = {
|
||||||
|
payload_size: number;
|
||||||
|
blocks: number;
|
||||||
|
};
|
||||||
1
test-hdd/hello.txt
Normal file
1
test-hdd/hello.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
It works!
|
||||||
1
test-hdd/myapp.js
Normal file
1
test-hdd/myapp.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
console.log("Hello from myApp");
|
||||||
Reference in New Issue
Block a user