diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9b3aa8b --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/PROGRESS.md b/PROGRESS.md new file mode 100644 index 0000000..84ee980 --- /dev/null +++ b/PROGRESS.md @@ -0,0 +1,160 @@ +# WebVM 開発進捗 + +## 現在の進捗状況 + +### 完了した機能 + +#### 1. カーネル基盤 +- [x] WebAssemblyカーネルの基本構造 +- [x] WASI システムコールの実装 + - [x] `fd_write` (stdout/stderr) + - [x] `fd_read` (stdin) + - [x] `proc_exit` + - [x] `clock_time_get` +- [x] Linux風のディレクトリ構造 + - `/kernel/init/` - 初期化 + - `/kernel/mm/` - メモリ管理 + - `/kernel/fs/` - ファイルシステム + - `/kernel/drivers/` - デバイスドライバ + - `/kernel/shell/` - シェル + - `/kernel/lib/` - ライブラリ関数 + +#### 2. メモリ管理 +- [x] 基本的なメモリアロケータ (`kmalloc`/`kfree`) +- [x] 32MBの静的メモリプール +- [x] SharedArrayBuffer対応 (128MB) + +#### 3. ファイルシステム +- [x] VFS (Virtual File System) の基本実装 +- [x] マウントポイント管理 +- [x] 疑似ファイルシステム (スタブ) + - `/proc/version` + - `/etc/passwd` + +#### 4. デバイスドライバ +- [x] コンソールドライバ +- [x] ドライバ登録システム +- [x] ANSI エスケープシーケンス対応 + +#### 5. シェル +- [x] 基本的なコマンドラインインターフェース +- [x] ビルトインコマンド + - `help` - ヘルプ表示 + - `clear` - 画面クリア + - `echo` - テキスト出力 + - `ps` - プロセス一覧 (スタブ) + - `ls` - ファイル一覧 (スタブ) + - `cat` - ファイル表示 (スタブ) + - `about` - システム情報 + - `exit` - 終了 + +#### 6. GUI/ブラウザ統合 +- [x] ターミナルウィンドウ UI +- [x] ウィンドウマネージャー (ドラッグ、最大化) +- [x] stdin/stdout のブラウザ統合 +- [x] コマンドハンドラー経由の入力処理 + +#### 7. ビルドシステム +- [x] Ninja ビルドシステム +- [x] TypeScript/JavaScript モジュール構造 +- [x] CSS分離 + +## 🚧 やること + +### 高優先度 + +#### 1. プロセス管理 +- [ ] プロセステーブルの実装 +- [ ] `fork()` の代替実装 (`posix_spawn`) +- [ ] プロセススケジューラー +- [ ] プロセス間通信 (IPC) + +#### 2. ファイルシステム +- [ ] 実際のファイルシステム実装 +- [ ] IndexedDB バックエンド +- [ ] ディレクトリ操作 (mkdir, rmdir) +- [ ] ファイル操作 (open, read, write, close) +- [ ] パーミッション管理 + +#### 3. ネットワーク +- [ ] ソケット API の実装 +- [ ] WebSocket ベースのネットワーク層 +- [ ] 基本的なネットワークコマンド (ping, wget 相当) + +### 中優先度 + +#### 4. シェル拡張 +- [ ] パイプ機能 +- [ ] リダイレクト +- [ ] 環境変数 +- [ ] ジョブコントロール +- [ ] コマンド履歴 +- [ ] タブ補完 + +#### 5. デバイスドライバ +- [ ] キーボードドライバ (詳細な入力処理) +- [ ] マウスドライバ +- [ ] フレームバッファドライバ (グラフィックス) + +#### 6. 標準ユーティリティ +- [ ] 基本的なUNIXコマンド群 + - [ ] `cp`, `mv`, `rm` + - [ ] `grep`, `sed`, `awk` + - [ ] `vi` または `nano` エディタ + +### 低優先度 + +#### 7. 高度な機能 +- [ ] マルチスレッド (pthread) +- [ ] 共有ライブラリ +- [ ] デバッガー +- [ ] パッケージマネージャー + +#### 8. パフォーマンス最適化 +- [ ] メモリ管理の最適化 +- [ ] ファイルシステムキャッシュ +- [ ] コンパイラ最適化 + +## 既知の問題 + +1. **メモリ管理** + - `mm_stats` 構造体が未定義 (`kernel/mm/memory.c:134`) + - `kmalloc` 実装が不完全 (`kernel/lib/string.c:79`) + +2. **ファイルシステム** + - ディレクトリ作成が未実装 (`kernel/fs/vfs.c:94, 125`) + - procfs の完全な実装が必要 (`kernel/fs/vfs.c:183`) + +3. **標準ライブラリ** + - 可変引数の処理が未実装 (`kernel/lib/stdio.c:95-96, 103-104`) + - 数値変換が未実装 (`kernel/drivers/console.c:111`) + +4. **ドライバ** + - 追加ドライバが必要 (`kernel/drivers/drivers.c:69-72`) + +## 技術的な詳細 + +### メモリレイアウト +- カーネル最大メモリ: 128MB (134217728 bytes) +- メモリプール: 32MB +- スタックサイズ: 1MB +- WebAssembly ページ: 2048 (64KB/ページ) + +### ビルド要件 +- clang (wasm32 ターゲット) +- wasm-ld +- ninja +- pnpm +- TypeScript + +### アーキテクチャ +1. **カーネル層** (C/WebAssembly) +2. **システムライブラリ層** (TypeScript) +3. **GUI層** (HTML/CSS/JavaScript) + +## 次のステップ + +1. プロセス管理の基本実装を開始 +2. 実際のファイルシステムの実装 +3. より多くのシェルコマンドの追加 +4. テストスイートの作成 diff --git a/build.ninja b/build.ninja index 3194506..5f5ea14 100644 --- a/build.ninja +++ b/build.ninja @@ -1,15 +1,32 @@ rule CC command = clang -O2 -nostdlib -target wasm32-unknown-unknown -fno-builtin -matomics -mbulk-memory -c $in -o $out rule LD - command = wasm-ld -z stack-size=1048576 --export=_start --allow-undefined --import-memory --shared-memory --max-memory=67108864 $in -o $out + command = wasm-ld -z stack-size=1048576 --export=_start --export=handle_command --allow-undefined --import-memory --shared-memory --max-memory=134217728 $in -o $out rule COPY command = cp $in $out rule TSC command = npx tsc --outDir build -build build/kernel/hello.o: CC kernel/hello.c -build build/kernel.wasm: LD build/kernel/hello.o -build build/gui/index.html: COPY gui/index.html +# カーネルオブジェクトファイル +build build/kernel/init/main.o: CC kernel/init/main.c +build build/kernel/mm/memory.o: CC kernel/mm/memory.c +build build/kernel/fs/vfs.o: CC kernel/fs/vfs.c +build build/kernel/drivers/console.o: CC kernel/drivers/console.c +build build/kernel/drivers/drivers.o: CC kernel/drivers/drivers.c +build build/kernel/lib/string.o: CC kernel/lib/string.c +build build/kernel/lib/stdio.o: CC kernel/lib/stdio.c +build build/kernel/lib/wasi_wrapper.o: CC kernel/lib/wasi_wrapper.c +build build/kernel/shell/shell.o: CC kernel/shell/shell.c +build build/kernel/shell/shell_api.o: CC kernel/shell/shell_api.c + +# カーネルリンク +build build/kernel.wasm: LD build/kernel/init/main.o build/kernel/mm/memory.o build/kernel/fs/vfs.o build/kernel/drivers/console.o build/kernel/drivers/drivers.o build/kernel/lib/string.o build/kernel/lib/stdio.o build/kernel/lib/wasi_wrapper.o build/kernel/shell/shell.o build/kernel/shell/shell_api.o + +# GUI とシステムライブラリ +build build/index.html: COPY gui/index.html +build build/gui/styles.css: COPY gui/styles.css +build build/gui/terminal.js: COPY gui/terminal.js +build build/gui/window-manager.js: COPY gui/window-manager.js build build/syslib/index.js: TSC syslib/index.ts -default build/kernel.wasm build/gui/index.html build/syslib/index.js +default build/kernel.wasm build/index.html build/gui/styles.css build/gui/terminal.js build/gui/window-manager.js build/syslib/index.js diff --git a/gui/index.html b/gui/index.html index 4e76301..2aa3801 100644 --- a/gui/index.html +++ b/gui/index.html @@ -1,11 +1,46 @@ + + WebVM - POSIX-compatible WebAssembly OS + + + +
+ +
+
+
Terminal - WebVM
+
+
+
+
+
+
+
+
+
+
+ $ + + +
+
+
+
+
+ - -

Hello WebVM

- - + + \ No newline at end of file diff --git a/gui/styles.css b/gui/styles.css new file mode 100644 index 0000000..65c737e --- /dev/null +++ b/gui/styles.css @@ -0,0 +1,140 @@ +/** + * WebVM GUI スタイルシート + */ + +/* ベーススタイル */ +body { + margin: 0; + padding: 0; + background-color: #1e1e1e; + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + color: #d4d4d4; + overflow: hidden; +} + +/* デスクトップ */ +#desktop { + width: 100vw; + height: 100vh; + position: relative; +} + +/* ウィンドウシステム */ +.window { + position: absolute; + background-color: #2d2d30; + border: 1px solid #464647; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); + display: flex; + flex-direction: column; +} + +.window-header { + background-color: #3c3c3c; + padding: 8px; + cursor: move; + display: flex; + justify-content: space-between; + align-items: center; + user-select: none; +} + +.window-title { + color: #cccccc; + font-size: 14px; +} + +.window-controls { + display: flex; + gap: 8px; +} + +.window-control { + width: 12px; + height: 12px; + border-radius: 50%; + cursor: pointer; + transition: opacity 0.2s; +} + +.window-control:hover { + opacity: 0.8; +} + +.close { + background-color: #f44336; +} + +.minimize { + background-color: #ffeb3b; +} + +.maximize { + background-color: #4caf50; +} + +.window-content { + flex: 1; + overflow: auto; + padding: 0; +} + +/* ターミナルスタイル */ +.terminal { + background-color: #1e1e1e; + color: #d4d4d4; + padding: 10px; + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + font-size: 14px; + line-height: 1.4; + height: 100%; + box-sizing: border-box; + overflow-y: auto; +} + +.terminal-output { + white-space: pre-wrap; + word-wrap: break-word; +} + +.terminal-input-line { + display: flex; + align-items: center; +} + +.terminal-prompt { + color: #4ec9b0; + margin-right: 5px; +} + +.terminal-input { + background: transparent; + border: none; + color: #d4d4d4; + font-family: inherit; + font-size: inherit; + outline: none; + flex: 1; +} + +.terminal-cursor { + display: inline-block; + width: 8px; + height: 16px; + background-color: #d4d4d4; + animation: blink 1s infinite; +} + +@keyframes blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +} + +/* レスポンシブ対応 */ +@media (max-width: 768px) { + .window { + width: calc(100vw - 20px) !important; + left: 10px !important; + right: 10px !important; + } +} \ No newline at end of file diff --git a/gui/terminal.js b/gui/terminal.js new file mode 100644 index 0000000..178a253 --- /dev/null +++ b/gui/terminal.js @@ -0,0 +1,56 @@ +/** + * ターミナル機能 + */ + +// ターミナル要素 +const terminalOutput = document.getElementById('terminal-output'); +const terminalInput = document.getElementById('terminal-input'); + +/** + * ターミナルAPIを初期化 + */ +export function initializeTerminal() { + // ターミナル出力関数 + window.terminalWrite = function(text) { + terminalOutput.textContent += text; + terminalOutput.scrollTop = terminalOutput.scrollHeight; + }; + + window.terminalWriteLine = function(text) { + window.terminalWrite(text + '\n'); + }; + + window.terminalClear = function() { + terminalOutput.textContent = ''; + }; + + // 入力ハンドラを設定 + setupInputHandler(); +} + +/** + * 入力ハンドラを設定 + */ +function setupInputHandler() { + terminalInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + const command = terminalInput.value; + window.terminalWriteLine('$ ' + command); + + // カーネルにコマンドを送信 + if (window.handleCommand) { + window.handleCommand(command); + } + + terminalInput.value = ''; + } + }); + + // 自動フォーカス + terminalInput.focus(); + + // クリック時にフォーカス + document.getElementById('terminal').addEventListener('click', () => { + terminalInput.focus(); + }); +} \ No newline at end of file diff --git a/gui/window-manager.js b/gui/window-manager.js new file mode 100644 index 0000000..11046a3 --- /dev/null +++ b/gui/window-manager.js @@ -0,0 +1,131 @@ +/** + * ウィンドウマネージャー + */ + +// ドラッグ状態 +let isDragging = false; +let currentWindow = null; +let startX = 0; +let startY = 0; +let windowX = 0; +let windowY = 0; + +/** + * ウィンドウマネージャーを初期化 + */ +export function initializeWindowManager() { + // ウィンドウドラッグ機能 + setupWindowDragging(); + + // ウィンドウコントロール + setupWindowControls(); +} + +/** + * ウィンドウドラッグ機能を設定 + */ +function setupWindowDragging() { + document.querySelectorAll('.window-header').forEach(header => { + header.addEventListener('mousedown', (e) => { + if (e.target.classList.contains('window-control')) return; + + isDragging = true; + currentWindow = header.parentElement; + startX = e.clientX; + startY = e.clientY; + const rect = currentWindow.getBoundingClientRect(); + windowX = rect.left; + windowY = rect.top; + + // 最前面に移動 + bringToFront(currentWindow); + + e.preventDefault(); + }); + }); + + document.addEventListener('mousemove', (e) => { + if (!isDragging) return; + + const deltaX = e.clientX - startX; + const deltaY = e.clientY - startY; + + currentWindow.style.left = (windowX + deltaX) + 'px'; + currentWindow.style.top = (windowY + deltaY) + 'px'; + }); + + document.addEventListener('mouseup', () => { + isDragging = false; + currentWindow = null; + }); +} + +/** + * ウィンドウコントロールを設定 + */ +function setupWindowControls() { + // 閉じるボタン + document.querySelectorAll('.close').forEach(btn => { + btn.addEventListener('click', () => { + btn.closest('.window').style.display = 'none'; + }); + }); + + // 最小化ボタン + document.querySelectorAll('.minimize').forEach(btn => { + btn.addEventListener('click', () => { + // TODO: 最小化実装 + console.log('Minimize not implemented yet'); + }); + }); + + // 最大化ボタン + document.querySelectorAll('.maximize').forEach(btn => { + btn.addEventListener('click', () => { + const window = btn.closest('.window'); + toggleMaximize(window); + }); + }); +} + +/** + * ウィンドウを最前面に移動 + */ +function bringToFront(window) { + const windows = document.querySelectorAll('.window'); + let maxZ = 0; + + windows.forEach(w => { + const z = parseInt(w.style.zIndex || '0'); + if (z > maxZ) maxZ = z; + }); + + window.style.zIndex = maxZ + 1; +} + +/** + * ウィンドウの最大化をトグル + */ +function toggleMaximize(window) { + if (window.dataset.maximized === 'true') { + // 元のサイズに戻す + window.style.width = window.dataset.originalWidth; + window.style.height = window.dataset.originalHeight; + window.style.left = window.dataset.originalLeft; + window.style.top = window.dataset.originalTop; + window.dataset.maximized = 'false'; + } else { + // 現在のサイズを保存 + window.dataset.originalWidth = window.style.width; + window.dataset.originalHeight = window.style.height; + window.dataset.originalLeft = window.style.left; + window.dataset.originalTop = window.style.top; + + // 最大化 + window.style.width = '100vw'; + window.style.height = '100vh'; + window.style.left = '0'; + window.style.top = '0'; + window.dataset.maximized = 'true'; + } +} \ No newline at end of file diff --git a/kernel/drivers/console.c b/kernel/drivers/console.c new file mode 100644 index 0000000..6168214 --- /dev/null +++ b/kernel/drivers/console.c @@ -0,0 +1,141 @@ +/* + * WebVM コンソールドライバ + * + * 標準入出力の処理 + */ + +#include "../include/drivers.h" +#include "../include/kernel.h" +#include "../include/wasi_syscalls.h" + +/* 外部関数宣言 */ +int kprintf(const char *format, ...); +int kputchar(int c); +int kputs(const char *str); + +/* コンソールバッファサイズ */ +#define CONSOLE_BUFFER_SIZE 4096 + +/* コンソール構造体 */ +struct console { + char input_buffer[CONSOLE_BUFFER_SIZE]; + int input_pos; + int echo_enabled; +}; + +/* グローバル変数 */ +static struct console console_dev = { + .input_pos = 0, + .echo_enabled = 1, +}; + +/** + * コンソールドライバの初期化 + */ +int console_init(void) { + /* コンソールを初期化 */ + console_dev.input_pos = 0; + console_dev.echo_enabled = 1; + + KDEBUG("Console driver initialized"); + + return KERNEL_SUCCESS; +} + +/** + * コンソールに文字を出力 + */ +int console_putc(char c) { return kputchar(c); } + +/** + * コンソールに文字列を出力 + */ +int console_puts(const char *str) { return kputs(str); } + +/** + * コンソールから文字を読み込み + */ +int console_getc(void) { + char c; + wasi_read(STDIN_FILENO, &c, 1); + return c; +} + +/** + * コンソールから行を読み込み + */ +char *console_gets(char *buffer, int size) { + int i = 0; + char c; + + while (i < size - 1) { + if (wasi_read(STDIN_FILENO, &c, 1) <= 0) + break; + if (c == '\n') + break; + buffer[i++] = c; + } + buffer[i] = '\0'; + + return i > 0 ? buffer : NULL; +} + +/** + * コンソールをクリア + */ +void console_clear(void) { + /* ANSI エスケープシーケンスでクリア */ + const char *clear_seq = "\033[2J\033[H"; + wasi_write(STDOUT_FILENO, clear_seq, 7); +} + +/** + * カーソル位置を設定 + */ +void console_set_cursor(int x, int y) { + char buf[32]; + int len = 0; + + /* 簡易的なsprintf実装 */ + buf[len++] = '\033'; + buf[len++] = '['; + /* TODO: 数値変換 */ + buf[len++] = 'H'; + + wasi_write(STDOUT_FILENO, buf, len); +} + +/** + * テキスト色を設定 + */ +void console_set_color(int fg, int bg) { + char buf[16]; + int len = 0; + + if (fg >= 0) { + buf[len++] = '\033'; + buf[len++] = '['; + buf[len++] = '3'; + buf[len++] = '0' + fg; + buf[len++] = 'm'; + wasi_write(STDOUT_FILENO, buf, len); + } + + if (bg >= 0) { + len = 0; + buf[len++] = '\033'; + buf[len++] = '['; + buf[len++] = '4'; + buf[len++] = '0' + bg; + buf[len++] = 'm'; + wasi_write(STDOUT_FILENO, buf, len); + } +} + +/** + * テキスト属性をリセット + */ +void console_reset_attr(void) { + const char *reset_seq = "\033[0m"; + wasi_write(STDOUT_FILENO, reset_seq, 4); +} \ No newline at end of file diff --git a/kernel/drivers/drivers.c b/kernel/drivers/drivers.c new file mode 100644 index 0000000..a21508a --- /dev/null +++ b/kernel/drivers/drivers.c @@ -0,0 +1,107 @@ +/* + * WebVM デバイスドライバ管理 + * + * ドライバの登録と初期化 + */ + +#include + +#include "../include/drivers.h" +#include "../include/kernel.h" +#include "../include/wasi_syscalls.h" + +/* 外部関数宣言 */ +int kprintf(const char *format, ...); +int kfprintf(int fd, const char *format, ...); + +/* ドライバ登録構造体 */ +struct driver_entry { + const char *name; + int (*init)(void); + int (*exit)(void); + struct driver_entry *next; +}; + +/* グローバル変数 */ +static struct driver_entry *driver_list = NULL; + +/* 組み込みドライバの初期化関数 */ +extern int console_init(void); + +/** + * ドライバを登録 + */ +int register_driver(const char *name, int (*init)(void), int (*exit)(void)) { + struct driver_entry *entry; + + static struct driver_entry driver_entries[10]; + static int driver_count = 0; + + if (driver_count >= 10) { + return KERNEL_ERROR; + } + + entry = &driver_entries[driver_count++]; + + entry->name = name; + entry->init = init; + entry->exit = exit; + entry->next = driver_list; + driver_list = entry; + + KDEBUG("Registered driver"); + + return KERNEL_SUCCESS; +} + +/** + * ドライバの初期化 + */ +int drivers_init(void) { + struct driver_entry *entry; + int ret; + + /* 組み込みドライバを登録 */ + register_driver("console", console_init, NULL); + + /* TODO: 他のドライバを追加 */ + /* register_driver("keyboard", keyboard_init, NULL); */ + /* register_driver("mouse", mouse_init, NULL); */ + /* register_driver("framebuffer", fb_init, NULL); */ + + /* 登録されたドライバを初期化 */ + for (entry = driver_list; entry; entry = entry->next) { + kprintf("[DRIVER] Initializing driver...\n"); + if (entry->init) { + ret = entry->init(); + if (ret < 0) { + kfprintf(STDERR_FILENO, "[DRIVER] Failed to initialize driver\n"); + return ret; + } + } + } + + kprintf("[DRIVER] All drivers initialized\n"); + + return KERNEL_SUCCESS; +} + +/** + * ドライバの終了処理 + */ +int drivers_exit(void) { + struct driver_entry *entry; + + /* 逆順で終了処理を実行 */ + for (entry = driver_list; entry; entry = entry->next) { + if (entry->exit) { + KDEBUG("Exiting driver"); + entry->exit(); + } + } + + /* 静的配列なので解放不要 */ + driver_list = NULL; + + return KERNEL_SUCCESS; +} \ No newline at end of file diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c new file mode 100644 index 0000000..cf8b3b6 --- /dev/null +++ b/kernel/fs/vfs.c @@ -0,0 +1,178 @@ +/* + * WebVM 仮想ファイルシステム (VFS) + * + * 基本的なファイルシステム抽象化レイヤー + */ + +#include + +#include "../include/fs.h" +#include "../include/kernel.h" +#include "../include/wasi_syscalls.h" + +/* 外部関数宣言 */ +int kprintf(const char *format, ...); +int kfprintf(int fd, const char *format, ...); + +/* 文字列関数 */ +static int strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { + s1++; + s2++; + } + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +static int strncmp(const char *s1, const char *s2, size_t n) { + while (n && *s1 && (*s1 == *s2)) { + s1++; + s2++; + n--; + } + if (n == 0) + return 0; + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +static size_t strlen(const char *s) { + size_t len = 0; + while (s[len]) + len++; + return len; +} + +static char *strncpy(char *dest, const char *src, size_t n) { + size_t i; + for (i = 0; i < n && src[i] != '\0'; i++) + dest[i] = src[i]; + for (; i < n; i++) + dest[i] = '\0'; + return dest; +} + +/* ファイルシステムタイプ */ +#define FS_TYPE_ROOTFS 1 +#define FS_TYPE_DEVFS 2 +#define FS_TYPE_PROCFS 3 + +/* マウントポイント構造体 */ +struct mount_point { + char path[256]; + int fs_type; + struct mount_point *next; +}; + +/* グローバル変数 */ +static struct mount_point *mount_list = NULL; +static int vfs_initialized = 0; + +/** + * ファイルシステムの初期化 + */ +int fs_init(void) { + /* ルートファイルシステムをマウント */ + if (fs_mount("/", FS_TYPE_ROOTFS) < 0) { + kfprintf(STDERR_FILENO, "[FS] Failed to mount root filesystem\n"); + return KERNEL_ERROR; + } + + /* /dev をマウント */ + if (fs_mount("/dev", FS_TYPE_DEVFS) < 0) { + kfprintf(STDERR_FILENO, "[FS] Failed to mount /dev\n"); + return KERNEL_ERROR; + } + + /* /proc をマウント */ + if (fs_mount("/proc", FS_TYPE_PROCFS) < 0) { + kfprintf(STDERR_FILENO, "[FS] Failed to mount /proc\n"); + return KERNEL_ERROR; + } + + /* 基本ディレクトリを作成 - TODO: 実装 */ + + vfs_initialized = 1; + KDEBUG("VFS initialized"); + + return KERNEL_SUCCESS; +} + +/** + * ファイルシステムをマウント + */ +int fs_mount(const char *path, int fs_type) { + struct mount_point *mp; + + /* マウントポイントを作成 */ + static struct mount_point mount_points[10]; + static int mount_count = 0; + + if (mount_count >= 10) { + return KERNEL_ERROR; + } + + mp = &mount_points[mount_count++]; + + strncpy(mp->path, path, sizeof(mp->path) - 1); + mp->path[sizeof(mp->path) - 1] = '\0'; + mp->fs_type = fs_type; + mp->next = mount_list; + mount_list = mp; + + /* ディレクトリを作成 - TODO: 実装 */ + + KDEBUG("Mounted filesystem"); + + return KERNEL_SUCCESS; +} + +/** + * ファイルシステムをアンマウント + */ +int fs_umount(const char *path) { + struct mount_point *mp, *prev = NULL; + + /* マウントポイントを探す */ + for (mp = mount_list; mp; prev = mp, mp = mp->next) { + if (strcmp(mp->path, path) == 0) { + if (prev) { + prev->next = mp->next; + } else { + mount_list = mp->next; + } + /* 静的配列なのでfree不要 */ + return KERNEL_SUCCESS; + } + } + + return KERNEL_ERROR; +} + +/** + * ファイルシステムの種類を取得 + */ +int fs_get_type(const char *path) { + struct mount_point *mp; + int best_match_len = 0; + int fs_type = FS_TYPE_ROOTFS; + + /* 最長一致でマウントポイントを探す */ + for (mp = mount_list; mp; mp = mp->next) { + int len = strlen(mp->path); + if (strncmp(path, mp->path, len) == 0) { + if (len > best_match_len) { + best_match_len = len; + fs_type = mp->fs_type; + } + } + } + + return fs_type; +} + +/** + * /proc ファイルシステムの内容を生成 + */ +static int procfs_read(const char *path, char *buffer, size_t size) { + /* TODO: procfs実装 */ + return -1; +} \ No newline at end of file diff --git a/kernel/hello.c b/kernel/hello.c deleted file mode 100644 index b6aed21..0000000 --- a/kernel/hello.c +++ /dev/null @@ -1,36 +0,0 @@ -// WASI imports -__attribute__((import_module("wasi_snapshot_preview1"), import_name("proc_exit"))) -extern void __wasi_proc_exit(int exit_code); - -__attribute__((import_module("wasi_snapshot_preview1"), import_name("fd_write"))) -extern int __wasi_fd_write(int fd, const void* iovs, int iovs_len, int* nwritten); - -// writev を console に出力するための構造体 -typedef struct { - const void* buf; - int buf_len; -} iovec_t; - -// stdio ファイルディスクリプタ -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 - -int wasi_write(int fd, const char* str, int len) { - iovec_t iov = { .buf = str, .buf_len = len }; - int nwritten; - return __wasi_fd_write(fd, &iov, 1, &nwritten); -} - -int strlen_wasi(const char* str) { - int len = 0; - while (str[len] != '\0') len++; - return len; -} - -void _start(void) { - const char* message = "Hello, WebVM!\n"; - wasi_write(STDOUT_FILENO, message, strlen_wasi(message)); - - // 正常終了 - __wasi_proc_exit(0); -} diff --git a/kernel/include/drivers.h b/kernel/include/drivers.h new file mode 100644 index 0000000..17fce2a --- /dev/null +++ b/kernel/include/drivers.h @@ -0,0 +1,38 @@ +/* + * WebVM デバイスドライバヘッダ + */ + +#ifndef __WEBVM_DRIVERS_H__ +#define __WEBVM_DRIVERS_H__ + +/* ドライバ管理 */ +int register_driver(const char *name, int (*init)(void), int (*exit)(void)); +int drivers_exit(void); + +/* コンソールドライバ */ +int console_init(void); +int console_putc(char c); +int console_puts(const char *str); +int console_getc(void); +char *console_gets(char *buffer, int size); +void console_clear(void); +void console_set_cursor(int x, int y); +void console_set_color(int fg, int bg); +void console_reset_attr(void); + +/* 色定義 */ +#define COLOR_BLACK 0 +#define COLOR_RED 1 +#define COLOR_GREEN 2 +#define COLOR_YELLOW 3 +#define COLOR_BLUE 4 +#define COLOR_MAGENTA 5 +#define COLOR_CYAN 6 +#define COLOR_WHITE 7 + +/* デバイス番号 */ +#define MAJOR(dev) ((dev) >> 8) +#define MINOR(dev) ((dev) & 0xff) +#define MKDEV(maj, min) (((maj) << 8) | (min)) + +#endif /* __WEBVM_DRIVERS_H__ */ \ No newline at end of file diff --git a/kernel/include/fs.h b/kernel/include/fs.h new file mode 100644 index 0000000..b00f1e9 --- /dev/null +++ b/kernel/include/fs.h @@ -0,0 +1,33 @@ +/* + * WebVM ファイルシステムヘッダ + */ + +#ifndef __WEBVM_FS_H__ +#define __WEBVM_FS_H__ + +/* ファイルシステム操作 */ +int fs_mount(const char *path, int fs_type); +int fs_umount(const char *path); +int fs_get_type(const char *path); + +/* VFS構造体 (将来の拡張用) */ +struct vfs_operations { + int (*open)(const char *path, int flags); + int (*read)(int fd, void *buf, size_t count); + int (*write)(int fd, const void *buf, size_t count); + int (*close)(int fd); +}; + +/* inode構造体 (簡易版) */ +struct inode { + unsigned long ino; + unsigned int mode; + unsigned int uid; + unsigned int gid; + unsigned long size; + unsigned long atime; + unsigned long mtime; + unsigned long ctime; +}; + +#endif /* __WEBVM_FS_H__ */ \ No newline at end of file diff --git a/kernel/include/init.h b/kernel/include/init.h new file mode 100644 index 0000000..1be0fb0 --- /dev/null +++ b/kernel/include/init.h @@ -0,0 +1,31 @@ +/* + * WebVM カーネル初期化ヘッダ + */ + +#ifndef __WEBVM_INIT_H__ +#define __WEBVM_INIT_H__ + +/* 各サブシステムの初期化関数 */ +int mm_init(void); /* メモリ管理初期化 */ +int fs_init(void); /* ファイルシステム初期化 */ +int drivers_init(void); /* デバイスドライバ初期化 */ + +/* 初期化順序定義 */ +#define INIT_LEVEL_EARLY 0 +#define INIT_LEVEL_CORE 1 +#define INIT_LEVEL_DRIVERS 2 +#define INIT_LEVEL_FS 3 +#define INIT_LEVEL_LATE 4 + +/* 初期化エントリ構造体 */ +struct init_entry { + const char *name; + int (*init_func)(void); + int level; +}; + +/* 初期化マクロ */ +#define __init __attribute__((section(".init.text"))) +#define __initdata __attribute__((section(".init.data"))) + +#endif /* __WEBVM_INIT_H__ */ \ No newline at end of file diff --git a/kernel/include/kernel.h b/kernel/include/kernel.h new file mode 100644 index 0000000..b0e97be --- /dev/null +++ b/kernel/include/kernel.h @@ -0,0 +1,50 @@ +/* + * WebVM カーネル共通ヘッダ + */ + +#ifndef __WEBVM_KERNEL_H__ +#define __WEBVM_KERNEL_H__ + +#include +#include + +/* カーネル共通定義 */ +#define KERNEL_SUCCESS 0 +#define KERNEL_ERROR -1 + +/* デバッグマクロ */ +#ifdef DEBUG +#define KDEBUG(fmt, ...) printf("[DEBUG] " fmt "\n", ##__VA_ARGS__) +#else +#define KDEBUG(fmt, ...) ((void)0) +#endif + +/* カーネルパニック */ +#define kernel_panic(msg) do { \ + fprintf(stderr, "[PANIC] %s at %s:%d\n", msg, __FILE__, __LINE__); \ + abort(); \ +} while(0) + +/* アライメントマクロ */ +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define PAGE_SIZE 4096 +#define PAGE_ALIGN(x) ALIGN(x, PAGE_SIZE) + +/* 最小/最大マクロ */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* カーネル統計情報 */ +struct kernel_stats { + uint64_t boot_time; + uint64_t uptime; + uint32_t processes; + uint32_t threads; + uint64_t memory_used; + uint64_t memory_total; +}; + +/* グローバル関数宣言 */ +int get_kernel_stats(struct kernel_stats *stats); + +#endif /* __WEBVM_KERNEL_H__ */ \ No newline at end of file diff --git a/kernel/include/mm.h b/kernel/include/mm.h new file mode 100644 index 0000000..befd92c --- /dev/null +++ b/kernel/include/mm.h @@ -0,0 +1,28 @@ +/* + * WebVM メモリ管理ヘッダ + */ + +#ifndef __WEBVM_MM_H__ +#define __WEBVM_MM_H__ + +#include + +/* メモリアロケーション関数 */ +void *kmalloc(size_t size); +void kfree(void *ptr); + +/* メモリ統計構造体 */ +struct mm_stats { + size_t total_memory; + size_t used_memory; + size_t free_memory; +}; + +/* メモリ統計取得 */ +void mm_get_stats(struct mm_stats *stats); + +/* ページアロケーション (将来的な実装用) */ +void *get_free_page(void); +void free_page(void *page); + +#endif /* __WEBVM_MM_H__ */ \ No newline at end of file diff --git a/kernel/include/shell.h b/kernel/include/shell.h new file mode 100644 index 0000000..7c9a260 --- /dev/null +++ b/kernel/include/shell.h @@ -0,0 +1,11 @@ +/* + * WebVM シェルヘッダ + */ + +#ifndef __WEBVM_SHELL_H__ +#define __WEBVM_SHELL_H__ + +/* シェルのエントリポイント */ +void shell_main(void); + +#endif /* __WEBVM_SHELL_H__ */ \ No newline at end of file diff --git a/kernel/include/wasi_syscalls.h b/kernel/include/wasi_syscalls.h new file mode 100644 index 0000000..b7f2c3b --- /dev/null +++ b/kernel/include/wasi_syscalls.h @@ -0,0 +1,45 @@ +/* + * WebVM WASI システムコール定義 + */ + +#ifndef __WEBVM_WASI_SYSCALLS_H__ +#define __WEBVM_WASI_SYSCALLS_H__ + +#include +#include + +/* WASI インポート関数 */ +__attribute__((import_module("wasi_snapshot_preview1"), import_name("proc_exit"))) +extern void __wasi_proc_exit(int exit_code); + +__attribute__((import_module("wasi_snapshot_preview1"), import_name("fd_write"))) +extern int __wasi_fd_write(int fd, const void* iovs, int iovs_len, int* nwritten); + +__attribute__((import_module("wasi_snapshot_preview1"), import_name("fd_read"))) +extern int __wasi_fd_read(int fd, const void* iovs, int iovs_len, int* nread); + +__attribute__((import_module("wasi_snapshot_preview1"), import_name("clock_time_get"))) +extern int __wasi_clock_time_get(int clock_id, uint64_t precision, uint64_t* time); + +/* iovec構造体 */ +typedef struct { + const void* buf; + size_t buf_len; +} wasi_iovec_t; + +/* ファイルディスクリプタ */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +/* クロックID */ +#define WASI_CLOCK_REALTIME 0 +#define WASI_CLOCK_MONOTONIC 1 + +/* ラッパー関数 */ +int wasi_write(int fd, const char* str, size_t len); +int wasi_read(int fd, char* buf, size_t len); +void wasi_exit(int code); +uint64_t wasi_get_time_ns(void); + +#endif /* __WEBVM_WASI_SYSCALLS_H__ */ \ No newline at end of file diff --git a/kernel/init/main.c b/kernel/init/main.c new file mode 100644 index 0000000..41f3e1c --- /dev/null +++ b/kernel/init/main.c @@ -0,0 +1,111 @@ +/* + * WebVM カーネル初期化エントリポイント + * + * WASI システムコール直接実装版 + */ + +#include "../include/init.h" +#include "../include/kernel.h" +#include "../include/shell.h" +#include "../include/wasi_syscalls.h" + +/* 外部関数宣言 */ +int kprintf(const char *format, ...); +int kputs(const char *str); +int kfprintf(int fd, const char *format, ...); + +/* カーネルバージョン情報 */ +#define KERNEL_VERSION "0.1.0" +#define KERNEL_NAME "WebVM" + +/* グローバル変数 */ +static int kernel_initialized = 0; + +/** + * カーネルバナーを表示 + */ +static void print_banner(void) { + kputs("====================================="); + kputs(" WebVM Kernel v0.1.0"); + kputs(" POSIX-compatible WebAssembly OS"); + kputs("====================================="); + kputs(""); +} + +/** + * カーネル初期化のメインシーケンス + */ +static int kernel_init(void) { + kputs("[INIT] Starting kernel initialization..."); + + /* メモリ管理の初期化 */ + if (mm_init() < 0) { + kfprintf(STDERR_FILENO, "[INIT] Failed to initialize memory management\n"); + return -1; + } + kputs("[INIT] Memory management initialized"); + + /* ファイルシステムの初期化 */ + if (fs_init() < 0) { + kfprintf(STDERR_FILENO, "[INIT] Failed to initialize filesystem\n"); + return -1; + } + kputs("[INIT] Filesystem initialized"); + + /* デバイスドライバの初期化 */ + if (drivers_init() < 0) { + kfprintf(STDERR_FILENO, "[INIT] Failed to initialize drivers\n"); + return -1; + } + kputs("[INIT] Device drivers initialized"); + + kernel_initialized = 1; + kputs("[INIT] Kernel initialization complete"); + + return 0; +} + +/** + * カーネルメインループ + */ +static void kernel_main_loop(void) { + kputs("[KERNEL] Entering main loop..."); + + /* ここでシェルまたは init プロセスを起動する */ + kputs("[KERNEL] Starting shell..."); + + /* シェルを起動 */ + shell_main(); + + /* シェルが終了した場合 (通常は到達しない) */ + kputs("[KERNEL] Shell exited, halting system..."); +} + +/** + * カーネルエントリポイント + */ +void kernel_main(void) { + kputs("[DEBUG] kernel_main() started"); + + /* カーネルバナーを表示 */ + print_banner(); + + /* カーネル初期化 */ + if (kernel_init() < 0) { + kfprintf(STDERR_FILENO, "[KERNEL] Initialization failed, halting.\n"); + wasi_exit(1); + } + + /* メインループ */ + kernel_main_loop(); + + kputs("[DEBUG] kernel_main() finished"); + wasi_exit(0); +} + +/** + * WASI環境用の _start エントリポイント + */ +void _start(void) { + kernel_main(); +} \ No newline at end of file diff --git a/kernel/kernel.wat b/kernel/kernel.wat new file mode 100644 index 0000000..5e2d2a7 --- /dev/null +++ b/kernel/kernel.wat @@ -0,0 +1,1598 @@ +(module $kernel.wasm + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32 i32 i32 i32) (result i32))) + (type (;2;) (func (param i32))) + (type (;3;) (func)) + (type (;4;) (func (param i32) (result i32))) + (type (;5;) (func (param i32 i32) (result i32))) + (type (;6;) (func (param i32 i32 i32) (result i32))) + (import "env" "memory" (memory (;0;) 529 2048 shared)) + (import "wasi_snapshot_preview1" "fd_write" (func $__wasi_fd_write (type 1))) + (import "wasi_snapshot_preview1" "proc_exit" (func $__wasi_proc_exit (type 2))) + (func $__wasm_init_memory (type 3) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + i32.const 33560320 + i32.const 0 + i32.const 1 + i32.atomic.rmw.cmpxchg + br_table 0 (;@3;) 1 (;@2;) 2 (;@1;) + end + i32.const 1024 + i32.const 0 + i32.const 1746 + memory.init $.rodata + i32.const 2784 + i32.const 0 + i32.const 33557536 + memory.fill + i32.const 33560320 + i32.const 2 + i32.atomic.store + i32.const 33560320 + i32.const -1 + memory.atomic.notify + drop + br 1 (;@1;) + end + i32.const 33560320 + i32.const 1 + i64.const -1 + memory.atomic.wait32 + drop + end + data.drop $.rodata) + (func $kernel_main (type 3) + (local i32) + i32.const 1642 + call $kputs + drop + i32.const 1838 + call $kputs + drop + i32.const 1931 + call $kputs + drop + i32.const 1764 + call $kputs + drop + i32.const 1838 + call $kputs + drop + i32.const 2769 + call $kputs + drop + i32.const 2093 + call $kputs + drop + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + call $mm_init + i32.const 0 + i32.ge_s + br_if 0 (;@4;) + i32.const 2406 + local.set 0 + br 1 (;@3;) + end + i32.const 1541 + call $kputs + drop + block ;; label = @4 + call $fs_init + i32.const 0 + i32.ge_s + br_if 0 (;@4;) + i32.const 2566 + local.set 0 + br 1 (;@3;) + end + i32.const 1612 + call $kputs + drop + call $drivers_init + i32.const 0 + i32.ge_s + br_if 1 (;@2;) + i32.const 2453 + local.set 0 + end + i32.const 2 + local.get 0 + i32.const 0 + call $kfprintf + drop + i32.const 2 + i32.const 2695 + i32.const 0 + call $kfprintf + drop + i32.const 1 + call $wasi_exit + br 1 (;@1;) + end + i32.const 1578 + call $kputs + drop + i32.const 1422 + call $kputs + drop + end + i32.const 2062 + call $kputs + drop + i32.const 2175 + call $kputs + drop + call $shell_main + i32.const 2134 + call $kputs + drop + i32.const 1693 + call $kputs + drop + i32.const 0 + call $wasi_exit) + (func $_start (type 3) + call $kernel_main) + (func $mm_init (type 0) (result i32) + i32.const 0 + i64.const 0 + i64.store offset=2792 + i32.const 0 + i64.const 4328521712 + i64.store offset=2784 + i32.const 0 + i32.const 2784 + i32.store offset=33557216 + i32.const 0 + i32.const 16 + i32.store offset=33557220 + i32.const 0) + (func $fs_init (type 0) (result i32) + (local i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + i32.const 0 + i32.load offset=33559872 + local.tee 0 + i32.const 9 + i32.gt_s + br_if 0 (;@1;) + i32.const 0 + local.get 0 + i32.const 1 + i32.add + local.tee 1 + i32.store offset=33559872 + local.get 0 + i32.const 264 + i32.mul + i32.const 33557232 + i32.add + local.tee 2 + i32.const 47 + i32.store8 + i32.const 0 + local.set 3 + loop ;; label = @2 + local.get 2 + local.get 3 + i32.add + i32.const 1 + i32.add + i32.const 0 + i32.store16 align=1 + local.get 3 + i32.const 2 + i32.add + local.tee 3 + i32.const 254 + i32.ne + br_if 0 (;@2;) + end + local.get 2 + i32.const 1 + i32.store offset=256 + local.get 2 + i32.const 0 + i32.store8 offset=255 + i32.const 0 + i32.load offset=33559876 + local.set 3 + i32.const 0 + local.get 2 + i32.store offset=33559876 + local.get 2 + local.get 3 + i32.store offset=260 + block ;; label = @2 + block ;; label = @3 + local.get 0 + i32.const 9 + i32.eq + br_if 0 (;@3;) + i32.const 0 + local.get 0 + i32.const 2 + i32.add + local.tee 4 + i32.store offset=33559872 + local.get 1 + i32.const 264 + i32.mul + i32.const 33557232 + i32.add + local.tee 5 + i32.const 1986356271 + i32.store + local.get 0 + i32.const 264 + i32.mul + i32.const 33557232 + i32.add + local.set 6 + i32.const -248 + local.set 3 + loop ;; label = @4 + local.get 6 + local.get 3 + i32.add + local.tee 1 + i32.const 518 + i32.add + i32.const 0 + i32.store8 + local.get 1 + i32.const 516 + i32.add + i32.const 0 + i32.store16 + local.get 3 + i32.eqz + br_if 2 (;@2;) + local.get 1 + i32.const 523 + i32.add + i32.const 0 + i32.store8 + local.get 1 + i32.const 519 + i32.add + i32.const 0 + i32.store align=1 + local.get 3 + i32.const 8 + i32.add + local.set 3 + br 0 (;@4;) + end + end + i32.const 2 + i32.const 2379 + i32.const 0 + call $kfprintf + drop + i32.const -1 + return + end + local.get 5 + local.get 2 + i32.store offset=260 + local.get 5 + i32.const 2 + i32.store offset=256 + local.get 5 + i32.const 0 + i32.store8 offset=255 + i32.const 0 + local.get 5 + i32.store offset=33559876 + block ;; label = @2 + local.get 0 + i32.const 7 + i32.gt_s + br_if 0 (;@2;) + i32.const 0 + local.get 0 + i32.const 3 + i32.add + i32.store offset=33559872 + local.get 4 + i32.const 264 + i32.mul + local.tee 3 + i32.const 33557236 + i32.add + i32.const 99 + i32.store8 + local.get 3 + i32.const 33557232 + i32.add + local.tee 6 + i32.const 1869770799 + i32.store + local.get 0 + i32.const 264 + i32.mul + i32.const 33557232 + i32.add + local.set 2 + i32.const -250 + local.set 3 + loop ;; label = @3 + local.get 2 + local.get 3 + i32.add + local.tee 1 + i32.const 787 + i32.add + i32.const 0 + i32.store8 + local.get 1 + i32.const 783 + i32.add + i32.const 0 + i32.store align=1 + local.get 3 + i32.const 5 + i32.add + local.tee 3 + br_if 0 (;@3;) + end + local.get 6 + local.get 5 + i32.store offset=260 + local.get 6 + i32.const 3 + i32.store offset=256 + local.get 6 + i32.const 0 + i32.store8 offset=255 + i32.const 0 + local.get 6 + i32.store offset=33559876 + i32.const 0 + return + end + i32.const 2 + i32.const 2667 + i32.const 0 + call $kfprintf + drop + i32.const -1 + return + end + i32.const 2 + i32.const 2528 + i32.const 0 + call $kfprintf + drop + i32.const -1) + (func $console_init (type 0) (result i32) + i32.const 0) + (func $drivers_init (type 0) (result i32) + (local i32 i32) + i32.const 0 + i32.load offset=33560052 + local.set 0 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + i32.const 0 + i32.load offset=33560048 + local.tee 1 + i32.const 9 + i32.gt_s + br_if 0 (;@3;) + i32.const 0 + local.get 1 + i32.const 1 + i32.add + i32.store offset=33560048 + local.get 1 + i32.const 4 + i32.shl + local.tee 1 + i32.const 33559900 + i32.add + local.get 0 + i32.store + local.get 1 + i32.const 33559896 + i32.add + i32.const 0 + i32.store + local.get 1 + i32.const 33559892 + i32.add + i32.const 1 + i32.store + local.get 1 + i32.const 33559888 + i32.add + local.tee 0 + i32.const 1460 + i32.store + i32.const 0 + local.get 0 + i32.store offset=33560052 + br 1 (;@2;) + end + local.get 0 + i32.eqz + br_if 1 (;@1;) + end + loop ;; label = @2 + i32.const 2737 + i32.const 0 + call $kprintf + drop + block ;; label = @3 + local.get 0 + i32.load offset=4 + local.tee 1 + i32.eqz + br_if 0 (;@3;) + local.get 1 + call_indirect (type 0) + local.tee 1 + i32.const -1 + i32.gt_s + br_if 0 (;@3;) + i32.const 2 + i32.const 2490 + i32.const 0 + call $kfprintf + drop + local.get 1 + return + end + local.get 0 + i32.load offset=12 + local.tee 0 + br_if 0 (;@2;) + end + end + i32.const 2633 + i32.const 0 + call $kprintf + drop + i32.const 0) + (func $kputs (type 4) (param i32) (result i32) + (local i32 i32 i32) + i32.const 0 + local.set 1 + loop ;; label = @1 + local.get 0 + local.get 1 + i32.add + local.set 2 + local.get 1 + i32.const 1 + i32.add + local.tee 3 + local.set 1 + local.get 2 + i32.load8_u + br_if 0 (;@1;) + end + i32.const 1 + local.get 0 + local.get 3 + i32.const -1 + i32.add + call $wasi_write + drop + i32.const 1 + i32.const 2768 + i32.const 1 + call $wasi_write + drop + local.get 3) + (func $kprintf (type 5) (param i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32) + global.get $__stack_pointer + i32.const 1056 + i32.sub + local.tee 2 + global.set $__stack_pointer + block ;; label = @1 + block ;; label = @2 + local.get 0 + i32.load8_u + local.tee 3 + br_if 0 (;@2;) + i32.const 0 + local.set 4 + br 1 (;@1;) + end + i32.const 0 + local.set 4 + local.get 0 + local.set 5 + i32.const 0 + local.set 6 + loop ;; label = @2 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + local.get 3 + i32.const 255 + i32.and + i32.const 37 + i32.ne + br_if 0 (;@5;) + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + local.get 5 + i32.const 1 + i32.add + i32.load8_u + local.tee 5 + i32.const -100 + i32.add + br_table 2 (;@8;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 3 (;@7;) 1 (;@9;) 0 (;@10;) + end + local.get 5 + i32.eqz + br_if 4 (;@5;) + local.get 5 + i32.const 37 + i32.ne + br_if 2 (;@7;) + local.get 2 + i32.const 32 + i32.add + local.get 4 + i32.add + i32.const 37 + i32.store8 + local.get 4 + i32.const 1 + i32.add + local.set 4 + local.get 6 + i32.const 2 + i32.add + local.set 6 + br 6 (;@3;) + end + block ;; label = @9 + local.get 4 + i32.eqz + br_if 0 (;@9;) + i32.const 1 + local.get 2 + i32.const 32 + i32.add + local.get 4 + call $wasi_write + drop + end + i32.const 1 + i32.const 1798 + i32.const 4 + call $wasi_write + drop + i32.const 0 + local.set 4 + local.get 6 + i32.const 2 + i32.add + local.set 6 + br 5 (;@3;) + end + local.get 2 + i32.const 48 + i32.store16 + i32.const 0 + local.set 3 + loop ;; label = @8 + local.get 2 + local.get 3 + i32.add + local.set 5 + local.get 3 + i32.const 1 + i32.add + local.tee 7 + local.set 3 + local.get 5 + i32.load8_u + br_if 0 (;@8;) + end + local.get 7 + i32.const 1 + i32.eq + br_if 1 (;@6;) + local.get 4 + i32.const 1022 + i32.gt_u + br_if 1 (;@6;) + local.get 7 + i32.const -1 + i32.add + local.set 8 + local.get 2 + i32.const 32 + i32.add + local.get 4 + i32.add + local.set 9 + i32.const 0 + local.set 3 + loop ;; label = @8 + local.get 9 + local.get 3 + i32.add + local.get 2 + local.get 3 + i32.add + i32.load8_u + i32.store8 + local.get 3 + i32.const 1 + i32.add + local.tee 5 + local.get 8 + i32.ge_u + br_if 4 (;@4;) + local.get 4 + local.get 3 + i32.add + local.set 7 + local.get 5 + local.set 3 + local.get 7 + i32.const 1022 + i32.lt_u + br_if 0 (;@8;) + br 4 (;@4;) + end + end + local.get 2 + i32.const 32 + i32.add + local.get 4 + i32.add + local.tee 3 + local.get 5 + i32.store8 offset=1 + local.get 3 + i32.const 37 + i32.store8 + local.get 4 + i32.const 2 + i32.add + local.set 4 + end + local.get 6 + i32.const 2 + i32.add + local.set 6 + br 2 (;@3;) + end + local.get 2 + i32.const 32 + i32.add + local.get 4 + i32.add + local.get 3 + i32.store8 + local.get 4 + i32.const 1 + i32.add + local.set 4 + local.get 6 + i32.const 1 + i32.add + local.set 6 + br 1 (;@3;) + end + local.get 4 + local.get 5 + i32.add + local.set 4 + local.get 6 + i32.const 2 + i32.add + local.set 6 + end + block ;; label = @3 + local.get 0 + local.get 6 + i32.add + local.tee 5 + i32.load8_u + local.tee 3 + i32.eqz + br_if 0 (;@3;) + local.get 4 + i32.const 1023 + i32.lt_u + br_if 1 (;@2;) + end + end + local.get 4 + i32.const 1 + i32.lt_s + br_if 0 (;@1;) + i32.const 1 + local.get 2 + i32.const 32 + i32.add + local.get 4 + call $wasi_write + drop + end + local.get 2 + i32.const 1056 + i32.add + global.set $__stack_pointer + local.get 4) + (func $kfprintf (type 6) (param i32 i32 i32) (result i32) + (local i32 i32 i32) + i32.const 0 + local.set 3 + loop ;; label = @1 + local.get 1 + local.get 3 + i32.add + local.set 4 + local.get 3 + i32.const 1 + i32.add + local.tee 5 + local.set 3 + local.get 4 + i32.load8_u + br_if 0 (;@1;) + end + local.get 0 + local.get 1 + local.get 5 + i32.const -1 + i32.add + local.tee 3 + call $wasi_write + drop + local.get 3) + (func $wasi_write (type 6) (param i32 i32 i32) (result i32) + (local i32) + global.get $__stack_pointer + i32.const 16 + i32.sub + local.tee 3 + global.set $__stack_pointer + local.get 3 + local.get 2 + i32.store offset=12 + local.get 3 + local.get 1 + i32.store offset=8 + local.get 0 + local.get 3 + i32.const 8 + i32.add + i32.const 1 + local.get 3 + i32.const 4 + i32.add + call $__wasi_fd_write + local.set 2 + local.get 3 + i32.const 16 + i32.add + global.set $__stack_pointer + local.get 2) + (func $wasi_exit (type 2) (param i32) + local.get 0 + call $__wasi_proc_exit) + (func $execute_command (type 2) (param i32) + (local i32 i32 i32 i32 i32 i32 i32) + global.get $__stack_pointer + i32.const 32 + i32.sub + local.tee 1 + global.set $__stack_pointer + local.get 0 + local.set 2 + block ;; label = @1 + block ;; label = @2 + loop ;; label = @3 + local.get 2 + i32.load8_u + local.tee 3 + i32.eqz + br_if 1 (;@2;) + block ;; label = @4 + local.get 3 + i32.const 32 + i32.eq + br_if 0 (;@4;) + local.get 2 + i32.const 1 + i32.add + local.set 2 + br 1 (;@3;) + end + end + local.get 2 + i32.const 0 + i32.store8 + loop ;; label = @3 + local.get 2 + i32.const 1 + i32.add + local.tee 2 + i32.load8_u + i32.const 32 + i32.eq + br_if 0 (;@3;) + br 2 (;@1;) + end + end + i32.const 0 + local.set 2 + end + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + block ;; label = @11 + local.get 0 + i32.load8_u + local.tee 4 + br_if 0 (;@11;) + i32.const 0 + local.set 5 + i32.const 1061 + local.set 3 + br 1 (;@10;) + end + local.get 0 + i32.const 1 + i32.add + local.set 6 + i32.const 0 + local.set 3 + local.get 4 + local.set 5 + block ;; label = @11 + loop ;; label = @12 + local.get 5 + i32.const 255 + i32.and + local.get 3 + i32.const 1129 + i32.add + i32.load8_u + local.tee 7 + i32.ne + br_if 1 (;@11;) + local.get 6 + local.get 3 + i32.add + local.set 5 + local.get 3 + i32.const 1 + i32.add + local.tee 7 + local.set 3 + local.get 5 + i32.load8_u + local.tee 5 + br_if 0 (;@12;) + end + local.get 7 + i32.const 1129 + i32.add + i32.load8_u + local.set 7 + i32.const 0 + local.set 5 + end + local.get 5 + i32.const 255 + i32.and + local.get 7 + i32.const 255 + i32.and + i32.eq + br_if 3 (;@7;) + local.get 0 + i32.const 1 + i32.add + local.set 7 + i32.const 1123 + local.set 5 + local.get 4 + local.set 3 + block ;; label = @11 + loop ;; label = @12 + local.get 3 + i32.const 255 + i32.and + local.get 5 + i32.load8_u + i32.ne + br_if 1 (;@11;) + local.get 5 + i32.const 1 + i32.add + local.set 5 + local.get 7 + i32.load8_u + local.set 3 + local.get 7 + i32.const 1 + i32.add + local.set 7 + local.get 3 + br_if 0 (;@12;) + end + i32.const 0 + local.set 3 + end + local.get 3 + i32.const 255 + i32.and + local.get 5 + i32.load8_u + i32.eq + br_if 4 (;@6;) + local.get 0 + i32.const 1 + i32.add + local.set 6 + i32.const 0 + local.set 3 + local.get 4 + local.set 5 + block ;; label = @11 + loop ;; label = @12 + local.get 5 + i32.const 255 + i32.and + local.get 3 + i32.const 1134 + i32.add + i32.load8_u + local.tee 7 + i32.ne + br_if 1 (;@11;) + local.get 6 + local.get 3 + i32.add + local.set 5 + local.get 3 + i32.const 1 + i32.add + local.tee 7 + local.set 3 + local.get 5 + i32.load8_u + local.tee 5 + br_if 0 (;@12;) + end + local.get 7 + i32.const 1134 + i32.add + i32.load8_u + local.set 7 + i32.const 0 + local.set 5 + end + local.get 5 + i32.const 255 + i32.and + local.get 7 + i32.const 255 + i32.and + i32.eq + br_if 1 (;@9;) + local.get 0 + i32.const 1 + i32.add + local.set 7 + i32.const 1061 + local.set 3 + local.get 4 + local.set 5 + block ;; label = @11 + loop ;; label = @12 + local.get 5 + i32.const 255 + i32.and + local.get 3 + i32.load8_u + i32.ne + br_if 1 (;@11;) + local.get 3 + i32.const 1 + i32.add + local.set 3 + local.get 7 + i32.load8_u + local.set 5 + local.get 7 + i32.const 1 + i32.add + local.set 7 + local.get 5 + br_if 0 (;@12;) + end + i32.const 0 + local.set 5 + end + local.get 5 + i32.const 255 + i32.and + local.set 5 + end + local.get 5 + local.get 3 + i32.load8_u + i32.ne + br_if 1 (;@8;) + i32.const 1724 + call $kputs + drop + i32.const 1973 + call $kputs + drop + i32.const 1910 + call $kputs + drop + i32.const 1672 + call $kputs + drop + i32.const 2769 + call $kputs + drop + i32.const 1189 + call $kputs + drop + i32.const 2024 + call $kputs + drop + br 8 (;@1;) + end + block ;; label = @9 + block ;; label = @10 + local.get 2 + i32.eqz + br_if 0 (;@10;) + local.get 2 + i32.load8_u + br_if 1 (;@9;) + end + i32.const 2769 + local.set 2 + end + local.get 2 + call $kputs + drop + br 7 (;@1;) + end + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + block ;; label = @11 + local.get 4 + br_if 0 (;@11;) + i32.const 0 + local.set 3 + i32.const 1067 + local.set 2 + br 1 (;@10;) + end + local.get 0 + i32.const 1 + i32.add + local.set 6 + i32.const 0 + local.set 3 + local.get 4 + local.set 5 + block ;; label = @11 + loop ;; label = @12 + local.get 5 + i32.const 255 + i32.and + local.get 3 + i32.const 1076 + i32.add + i32.load8_u + local.tee 7 + i32.ne + br_if 1 (;@11;) + local.get 6 + local.get 3 + i32.add + local.set 5 + local.get 3 + i32.const 1 + i32.add + local.tee 7 + local.set 3 + local.get 5 + i32.load8_u + local.tee 5 + br_if 0 (;@12;) + end + local.get 7 + i32.const 1076 + i32.add + i32.load8_u + local.set 7 + i32.const 0 + local.set 5 + end + local.get 5 + i32.const 255 + i32.and + local.get 7 + i32.const 255 + i32.and + i32.eq + br_if 5 (;@5;) + local.get 0 + i32.const 1 + i32.add + local.set 7 + i32.const 1079 + local.set 5 + local.get 4 + local.set 3 + block ;; label = @11 + loop ;; label = @12 + local.get 3 + i32.const 255 + i32.and + local.get 5 + i32.load8_u + i32.ne + br_if 1 (;@11;) + local.get 5 + i32.const 1 + i32.add + local.set 5 + local.get 7 + i32.load8_u + local.set 3 + local.get 7 + i32.const 1 + i32.add + local.set 7 + local.get 3 + br_if 0 (;@12;) + end + i32.const 0 + local.set 3 + end + local.get 3 + i32.const 255 + i32.and + local.get 5 + i32.load8_u + i32.eq + br_if 6 (;@4;) + local.get 0 + i32.const 1 + i32.add + local.set 6 + i32.const 0 + local.set 3 + local.get 4 + local.set 5 + block ;; label = @11 + loop ;; label = @12 + local.get 5 + i32.const 255 + i32.and + local.get 3 + i32.const 1072 + i32.add + i32.load8_u + local.tee 7 + i32.ne + br_if 1 (;@11;) + local.get 6 + local.get 3 + i32.add + local.set 5 + local.get 3 + i32.const 1 + i32.add + local.tee 7 + local.set 3 + local.get 5 + i32.load8_u + local.tee 5 + br_if 0 (;@12;) + end + local.get 7 + i32.const 1072 + i32.add + i32.load8_u + local.set 7 + i32.const 0 + local.set 5 + end + local.get 5 + i32.const 255 + i32.and + local.get 7 + i32.const 255 + i32.and + i32.eq + br_if 1 (;@9;) + local.get 0 + i32.const 1 + i32.add + local.set 5 + i32.const 1067 + local.set 2 + local.get 4 + local.set 3 + block ;; label = @11 + loop ;; label = @12 + local.get 3 + i32.const 255 + i32.and + local.get 2 + i32.load8_u + i32.ne + br_if 1 (;@11;) + local.get 2 + i32.const 1 + i32.add + local.set 2 + local.get 5 + i32.load8_u + local.set 3 + local.get 5 + i32.const 1 + i32.add + local.set 5 + local.get 3 + br_if 0 (;@12;) + end + i32.const 0 + local.set 3 + end + local.get 3 + i32.const 255 + i32.and + local.set 3 + end + local.get 3 + local.get 2 + i32.load8_u + i32.ne + br_if 1 (;@8;) + i32.const 2331 + call $kputs + drop + i32.const 0 + call $wasi_exit + br 8 (;@1;) + end + local.get 2 + i32.eqz + br_if 5 (;@3;) + local.get 2 + i32.load8_u + local.tee 7 + i32.eqz + br_if 5 (;@3;) + i32.const 0 + local.set 3 + local.get 7 + local.set 5 + block ;; label = @9 + loop ;; label = @10 + local.get 5 + i32.const 255 + i32.and + local.get 3 + i32.const 1175 + i32.add + i32.load8_u + local.tee 6 + i32.ne + br_if 1 (;@9;) + local.get 2 + local.get 3 + i32.add + local.set 5 + local.get 3 + i32.const 1 + i32.add + local.tee 6 + local.set 3 + local.get 5 + i32.const 1 + i32.add + i32.load8_u + local.tee 5 + br_if 0 (;@10;) + end + local.get 6 + i32.const 1175 + i32.add + i32.load8_u + local.set 6 + i32.const 0 + local.set 5 + end + local.get 5 + i32.const 255 + i32.and + local.get 6 + i32.const 255 + i32.and + i32.eq + br_if 6 (;@2;) + i32.const 0 + local.set 3 + block ;; label = @9 + loop ;; label = @10 + local.get 7 + i32.const 255 + i32.and + local.get 3 + i32.const 1503 + i32.add + i32.load8_u + local.tee 5 + i32.ne + br_if 1 (;@9;) + local.get 2 + local.get 3 + i32.add + local.set 5 + local.get 3 + i32.const 1 + i32.add + local.tee 6 + local.set 3 + local.get 5 + i32.const 1 + i32.add + i32.load8_u + local.tee 7 + br_if 0 (;@10;) + end + local.get 6 + i32.const 1503 + i32.add + i32.load8_u + local.set 5 + i32.const 0 + local.set 7 + end + block ;; label = @9 + local.get 7 + i32.const 255 + i32.and + local.get 5 + i32.const 255 + i32.and + i32.ne + br_if 0 (;@9;) + i32.const 1351 + call $kputs + drop + i32.const 1381 + call $kputs + drop + br 8 (;@1;) + end + local.get 1 + local.get 2 + i32.store offset=16 + i32.const 2343 + local.get 1 + i32.const 16 + i32.add + call $kprintf + drop + br 7 (;@1;) + end + local.get 4 + i32.eqz + br_if 6 (;@1;) + local.get 1 + local.get 0 + i32.store + i32.const 2606 + local.get 1 + call $kprintf + drop + br 6 (;@1;) + end + i32.const 1876 + call $kputs + drop + i32.const 1468 + call $kputs + drop + i32.const 1320 + call $kputs + drop + i32.const 1024 + call $kputs + drop + i32.const 2243 + call $kputs + drop + i32.const 2277 + call $kputs + drop + i32.const 2202 + call $kputs + drop + i32.const 1234 + call $kputs + drop + i32.const 1139 + call $kputs + drop + br 5 (;@1;) + end + i32.const 1 + i32.const 1803 + i32.const 7 + call $wasi_write + drop + br 4 (;@1;) + end + i32.const 1811 + call $kputs + drop + i32.const 1290 + call $kputs + drop + i32.const 1261 + call $kputs + drop + br 3 (;@1;) + end + i32.const 1082 + call $kputs + drop + br 2 (;@1;) + end + i32.const 1515 + call $kputs + drop + br 1 (;@1;) + end + i32.const 1953 + call $kputs + drop + end + local.get 1 + i32.const 32 + i32.add + global.set $__stack_pointer) + (func $shell_main (type 3) + i32.const 2769 + call $kputs + drop + i32.const 2307 + call $kputs + drop + i32.const 1988 + call $kputs + drop + i32.const 2769 + call $kputs + drop + i32.const 1 + i32.const 2340 + i32.const 2 + call $wasi_write + drop) + (func $handle_command (type 2) (param i32) + (local i32 i32 i32) + i32.const 2 + local.set 1 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + loop ;; label = @4 + local.get 0 + local.get 1 + i32.add + local.tee 2 + i32.const -2 + i32.add + i32.load8_u + local.tee 3 + i32.eqz + br_if 2 (;@2;) + local.get 1 + i32.const 33560062 + i32.add + local.get 3 + i32.store8 + local.get 2 + i32.const -1 + i32.add + i32.load8_u + local.tee 3 + i32.eqz + br_if 1 (;@3;) + local.get 1 + i32.const 33560063 + i32.add + local.get 3 + i32.store8 + local.get 2 + i32.load8_u + local.tee 2 + i32.eqz + br_if 3 (;@1;) + local.get 1 + i32.const 33560064 + i32.add + local.get 2 + i32.store8 + local.get 1 + i32.const 3 + i32.add + local.tee 1 + i32.const 257 + i32.ne + br_if 0 (;@4;) + end + i32.const 255 + local.set 1 + br 2 (;@1;) + end + local.get 1 + i32.const -1 + i32.add + local.set 1 + br 1 (;@1;) + end + local.get 1 + i32.const -2 + i32.add + local.set 1 + end + local.get 1 + i32.const 33560064 + i32.add + i32.const 0 + i32.store8 + i32.const 33560064 + call $execute_command + i32.const 1 + i32.const 2340 + i32.const 2 + call $wasi_write + drop) + (table (;0;) 2 2 funcref) + (global $__stack_pointer (mut i32) (i32.const 34608912)) + (global $__tls_base (mut i32) (i32.const 0)) + (export "_start" (func $_start)) + (export "handle_command" (func $handle_command)) + (start $__wasm_init_memory) + (elem (;0;) (i32.const 1) func $console_init) + (data $.rodata " echo - Echo arguments to stdout\00about\00exit\00cat\00ps\00ls\00bin dev etc home proc tmp usr var\00clear\00help\00echo\00 about - Show system information\00/proc/version\00This is a minimal UNIX-like operating system\00 exit - Exit the shell\002 tty0 00:00:00 shell\001 tty0 00:00:00 kernel\00 clear - Clear the terminal\00root:x:0:0:root:/root:/bin/sh\00user:x:1000:1000:user:/home/user:/bin/sh\00[INIT] Kernel initialization complete\00console\00 help - Show this help message\00/etc/passwd\00cat: missing file operand\00[INIT] Memory management initialized\00[INIT] Device drivers initialized\00[INIT] Filesystem initialized\00[DEBUG] kernel_main() started\00Memory: 128MB shared\00[DEBUG] kernel_main() finished\00WebVM - POSIX-compatible WebAssembly OS\00 POSIX-compatible WebAssembly OS\00TODO\00\1b[2J\1b[H\00PID TTY TIME CMD\00=====================================\00WebVM Shell - Available commands:\00Architecture: wasm32\00 WebVM Kernel v0.1.0\00WebVM version 0.1.0\00Version: 0.1.0\00Type 'help' for available commands.\00running entirely in your web browser.\00[KERNEL] Entering main loop...\00[INIT] Starting kernel initialization...\00[KERNEL] Shell exited, halting system...\00[KERNEL] Starting shell...\00 cat - Display file contents (stub)\00 ps - List processes (stub)\00 ls - List files (stub)\00Welcome to WebVM Shell!\00Goodbye!\00$ \00cat: %s: No such file or directory\0a\00[FS] Failed to mount /dev\0a\00[INIT] Failed to initialize memory management\0a\00[INIT] Failed to initialize drivers\0a\00[DRIVER] Failed to initialize driver\0a\00[FS] Failed to mount root filesystem\0a\00[INIT] Failed to initialize filesystem\0a\00sh: %s: command not found\0a\00[DRIVER] All drivers initialized\0a\00[FS] Failed to mount /proc\0a\00[KERNEL] Initialization failed, halting.\0a\00[DRIVER] Initializing driver...\0a\00")) diff --git a/kernel/lib/stdio.c b/kernel/lib/stdio.c new file mode 100644 index 0000000..333eb54 --- /dev/null +++ b/kernel/lib/stdio.c @@ -0,0 +1,138 @@ +/* + * WebVM 標準入出力関数 + * + * WASI システムコールを使用した簡易実装 + */ + +#include "../include/kernel.h" +#include "../include/wasi_syscalls.h" + +/* 内部関数宣言 */ +static int strlen_internal(const char *str); +static void itoa_internal(int value, char *str, int base); + +/** + * 文字列の長さを取得 + */ +static int strlen_internal(const char *str) { + int len = 0; + while (str[len] != '\0') + len++; + return len; +} + +/** + * 整数を文字列に変換 + */ +static void itoa_internal(int value, char *str, int base) { + char *ptr = str; + char *ptr1 = str; + char tmp_char; + int tmp_value; + + if (base < 2 || base > 36) { + *str = '\0'; + return; + } + + do { + tmp_value = value; + value /= base; + *ptr++ = "0123456789abcdefghijklmnopqrstuvwxyz"[tmp_value - value * base]; + } while (value); + + if (tmp_value < 0) + *ptr++ = '-'; + *ptr-- = '\0'; + + while (ptr1 < ptr) { + tmp_char = *ptr; + *ptr-- = *ptr1; + *ptr1++ = tmp_char; + } +} + +/** + * 文字を出力 + */ +int kputchar(int c) { + char ch = (char)c; + return wasi_write(STDOUT_FILENO, &ch, 1); +} + +/** + * 文字列を出力 + */ +int kputs(const char *str) { + int len = strlen_internal(str); + wasi_write(STDOUT_FILENO, str, len); + wasi_write(STDOUT_FILENO, "\n", 1); + return len + 1; +} + +/** + * 書式付き出力 (簡易版) + */ +int kprintf(const char *format, ...) { + char buffer[1024]; + int buf_idx = 0; + int i = 0; + + /* 簡易的な実装 - %s, %d, %x のみサポート */ + while (format[i] && buf_idx < sizeof(buffer) - 1) { + if (format[i] == '%' && format[i + 1]) { + i++; + switch (format[i]) { + case 's': { + /* 文字列は直接出力 */ + if (buf_idx > 0) { + wasi_write(STDOUT_FILENO, buffer, buf_idx); + buf_idx = 0; + } + /* TODO: 可変引数から文字列を取得 */ + const char *str = "TODO"; + wasi_write(STDOUT_FILENO, str, strlen_internal(str)); + break; + } + case 'd': { + /* 整数は変換して追加 */ + char num_buf[32]; + /* TODO: 可変引数から整数を取得 */ + int num = 0; + itoa_internal(num, num_buf, 10); + int num_len = strlen_internal(num_buf); + for (int j = 0; j < num_len && buf_idx < sizeof(buffer) - 1; j++) { + buffer[buf_idx++] = num_buf[j]; + } + break; + } + case '%': + buffer[buf_idx++] = '%'; + break; + default: + buffer[buf_idx++] = '%'; + buffer[buf_idx++] = format[i]; + break; + } + i++; + } else { + buffer[buf_idx++] = format[i++]; + } + } + + if (buf_idx > 0) { + wasi_write(STDOUT_FILENO, buffer, buf_idx); + } + + return buf_idx; +} + +/** + * エラー出力 + */ +int kfprintf(int fd, const char *format, ...) { + /* 簡易実装 - formatをそのまま出力 */ + int len = strlen_internal(format); + wasi_write(fd, format, len); + return len; +} \ No newline at end of file diff --git a/kernel/lib/string.c b/kernel/lib/string.c new file mode 100644 index 0000000..34badb7 --- /dev/null +++ b/kernel/lib/string.c @@ -0,0 +1,93 @@ +/* + * WebVM カーネル文字列関数 + * + * wasi-libc に含まれない追加の文字列関数 + */ + +#include "../include/kernel.h" +#include + +/* 文字列関数のプロトタイプ */ +size_t strlen(const char *s); +void *memcpy(void *dest, const void *src, size_t n); + +/** + * strlen実装 + */ +size_t strlen(const char *s) { + size_t len = 0; + while (s[len]) + len++; + return len; +} + +/** + * memcpy実装 + */ +void *memcpy(void *dest, const void *src, size_t n) { + unsigned char *d = dest; + const unsigned char *s = src; + while (n--) { + *d++ = *s++; + } + return dest; +} + +/** + * 安全な文字列コピー (NUL終端を保証) + */ +size_t strlcpy(char *dst, const char *src, size_t size) { + size_t src_len = strlen(src); + + if (size > 0) { + size_t copy_len = MIN(src_len, size - 1); + memcpy(dst, src, copy_len); + dst[copy_len] = '\0'; + } + + return src_len; +} + +/** + * 安全な文字列連結 (NUL終端を保証) + */ +size_t strlcat(char *dst, const char *src, size_t size) { + size_t dst_len = strlen(dst); + size_t src_len = strlen(src); + + if (dst_len >= size) { + return size + src_len; + } + + size_t remain = size - dst_len; + size_t copy_len = MIN(src_len, remain - 1); + + memcpy(dst + dst_len, src, copy_len); + dst[dst_len + copy_len] = '\0'; + + return dst_len + src_len; +} + +/** + * 文字列を複製 (メモリ確保付き) + */ +char *kstrdup(const char *s) { + /* TODO: kmalloc実装後に有効化 */ + return NULL; +} + +/** + * 文字列内の文字を置換 + */ +char *strreplace(char *str, char old, char new) { + char *p = str; + + while (*p) { + if (*p == old) { + *p = new; + } + p++; + } + + return str; +} \ No newline at end of file diff --git a/kernel/lib/wasi_wrapper.c b/kernel/lib/wasi_wrapper.c new file mode 100644 index 0000000..5c139b2 --- /dev/null +++ b/kernel/lib/wasi_wrapper.c @@ -0,0 +1,41 @@ +/* + * WebVM WASI システムコールラッパー + */ + +#include "../include/wasi_syscalls.h" + +/** + * 文字列を指定したファイルディスクリプタに書き込む + */ +int wasi_write(int fd, const char *str, size_t len) { + wasi_iovec_t iov = {.buf = str, .buf_len = len}; + int nwritten; + return __wasi_fd_write(fd, &iov, 1, &nwritten); +} + +/** + * 指定したファイルディスクリプタから読み込む + */ +int wasi_read(int fd, char *buf, size_t len) { + wasi_iovec_t iov = {.buf = buf, .buf_len = len}; + int nread; + int ret = __wasi_fd_read(fd, &iov, 1, &nread); + if (ret == 0) { + return nread; + } + return -1; +} + +/** + * プロセスを終了 + */ +void wasi_exit(int code) { __wasi_proc_exit(code); } + +/** + * 現在時刻をナノ秒で取得 + */ +uint64_t wasi_get_time_ns(void) { + uint64_t time; + __wasi_clock_time_get(WASI_CLOCK_REALTIME, 1000000, &time); + return time; +} \ No newline at end of file diff --git a/kernel/mm/memory.c b/kernel/mm/memory.c new file mode 100644 index 0000000..3bcc8c2 --- /dev/null +++ b/kernel/mm/memory.c @@ -0,0 +1,133 @@ +/* + * WebVM メモリ管理 + * + * 簡易的なメモリアロケータ実装 + */ + +#include +#include + +#include "../include/kernel.h" +#include "../include/wasi_syscalls.h" + +/* 外部関数宣言 */ +int kprintf(const char *format, ...); +int kfprintf(int fd, const char *format, ...); + +/* メモリプールサイズ (32MB) */ +#define MEMORY_POOL_SIZE (32 * 1024 * 1024) + +/* メモリブロックヘッダ */ +struct mem_block { + size_t size; + int free; + struct mem_block *next; + struct mem_block *prev; +}; + +/* グローバル変数 */ +static uint8_t *memory_pool = NULL; +static struct mem_block *free_list = NULL; +static size_t total_memory = 0; +static size_t used_memory = 0; + +/** + * メモリ管理の初期化 + */ +int mm_init(void) { + /* メモリプールを静的に確保 */ + static uint8_t static_memory_pool[MEMORY_POOL_SIZE]; + memory_pool = static_memory_pool; + + /* 初期ブロックを作成 */ + free_list = (struct mem_block *)memory_pool; + free_list->size = MEMORY_POOL_SIZE - sizeof(struct mem_block); + free_list->free = 1; + free_list->next = NULL; + free_list->prev = NULL; + + total_memory = MEMORY_POOL_SIZE; + used_memory = sizeof(struct mem_block); + + KDEBUG("Memory manager initialized"); + + return KERNEL_SUCCESS; +} + +/** + * メモリ確保 + */ +void *kmalloc(size_t size) { + struct mem_block *block = free_list; + + /* アライメント調整 */ + size = ALIGN(size, 8); + + /* 空きブロックを探す */ + while (block) { + if (block->free && block->size >= size) { + /* ブロックが大きすぎる場合は分割 */ + if (block->size > size + sizeof(struct mem_block) + 64) { + struct mem_block *new_block = + (struct mem_block *)((uint8_t *)block + sizeof(struct mem_block) + + size); + new_block->size = block->size - size - sizeof(struct mem_block); + new_block->free = 1; + new_block->next = block->next; + new_block->prev = block; + + if (block->next) { + block->next->prev = new_block; + } + + block->size = size; + block->next = new_block; + } + + block->free = 0; + used_memory += size; + + return (uint8_t *)block + sizeof(struct mem_block); + } + block = block->next; + } + + KDEBUG("kmalloc: no memory available"); + return NULL; +} + +/** + * メモリ解放 + */ +void kfree(void *ptr) { + if (!ptr) + return; + + struct mem_block *block = + (struct mem_block *)((uint8_t *)ptr - sizeof(struct mem_block)); + block->free = 1; + used_memory -= block->size; + + /* 前後の空きブロックと結合 */ + if (block->prev && block->prev->free) { + block->prev->size += sizeof(struct mem_block) + block->size; + block->prev->next = block->next; + if (block->next) { + block->next->prev = block->prev; + } + block = block->prev; + } + + if (block->next && block->next->free) { + block->size += sizeof(struct mem_block) + block->next->size; + block->next = block->next->next; + if (block->next) { + block->next->prev = block; + } + } +} + +/** + * メモリ統計を取得 + */ +void mm_get_stats(void *stats_ptr) { /* TODO: mm_stats構造体を定義後に実装 */ } \ No newline at end of file diff --git a/kernel/shell/shell.c b/kernel/shell/shell.c new file mode 100644 index 0000000..bbcb5ef --- /dev/null +++ b/kernel/shell/shell.c @@ -0,0 +1,181 @@ +/* + * WebVM シェル実装 + * + * 基本的なコマンドラインインターフェース + */ + +#include "../include/kernel.h" +#include "../include/wasi_syscalls.h" + +/* 外部関数宣言 */ +int kprintf(const char *format, ...); +int kputs(const char *str); +int kfprintf(int fd, const char *format, ...); + +/* 文字列関数 */ +static int strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { + s1++; + s2++; + } + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +static int strncmp(const char *s1, const char *s2, size_t n) { + while (n && *s1 && (*s1 == *s2)) { + s1++; + s2++; + n--; + } + if (n == 0) + return 0; + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +static size_t strlen(const char *s) { + size_t len = 0; + while (s[len]) + len++; + return len; +} + +static char *strcpy(char *dest, const char *src) { + char *d = dest; + while ((*d++ = *src++)) + ; + return dest; +} + +/* コマンドバッファ */ +#define CMD_BUFFER_SIZE 256 +static char cmd_buffer[CMD_BUFFER_SIZE]; + +/* ビルトインコマンド */ +static void cmd_help(void) { + kputs("WebVM Shell - Available commands:"); + kputs(" help - Show this help message"); + kputs(" clear - Clear the terminal"); + kputs(" echo - Echo arguments to stdout"); + kputs(" ps - List processes (stub)"); + kputs(" ls - List files (stub)"); + kputs(" cat - Display file contents (stub)"); + kputs(" exit - Exit the shell"); + kputs(" about - Show system information"); +} + +static void cmd_clear(void) { + /* ANSI エスケープシーケンスでクリア */ + const char *clear_seq = "\033[2J\033[H"; + wasi_write(STDOUT_FILENO, clear_seq, 7); +} + +static void cmd_echo(const char *args) { + if (args && *args) { + kputs(args); + } else { + kputs(""); + } +} + +static void cmd_about(void) { + kputs("WebVM - POSIX-compatible WebAssembly OS"); + kputs("Version: 0.1.0"); + kputs("Architecture: wasm32"); + kputs("Memory: 128MB shared"); + kputs(""); + kputs("This is a minimal UNIX-like operating system"); + kputs("running entirely in your web browser."); +} + +static void cmd_ps(void) { + kputs("PID TTY TIME CMD"); + kputs("1 tty0 00:00:00 kernel"); + kputs("2 tty0 00:00:00 shell"); +} + +static void cmd_ls(void) { kputs("bin dev etc home proc tmp usr var"); } + +static void cmd_cat(const char *args) { + if (!args || !*args) { + kputs("cat: missing file operand"); + return; + } + + /* 仮の実装 */ + if (strcmp(args, "/proc/version") == 0) { + kputs("WebVM version 0.1.0"); + } else if (strcmp(args, "/etc/passwd") == 0) { + kputs("root:x:0:0:root:/root:/bin/sh"); + kputs("user:x:1000:1000:user:/home/user:/bin/sh"); + } else { + kprintf("cat: %s: No such file or directory\n", args); + } +} + +/* コマンドを解析して実行 */ +void execute_command(char *cmd) { + char *args = NULL; + + /* コマンドと引数を分離 */ + char *space = cmd; + while (*space && *space != ' ') + space++; + if (*space) { + *space = '\0'; + args = space + 1; + /* 先頭の空白をスキップ */ + while (*args == ' ') + args++; + } + + /* コマンドを実行 */ + if (strcmp(cmd, "help") == 0) { + cmd_help(); + } else if (strcmp(cmd, "clear") == 0) { + cmd_clear(); + } else if (strcmp(cmd, "echo") == 0) { + cmd_echo(args); + } else if (strcmp(cmd, "about") == 0) { + cmd_about(); + } else if (strcmp(cmd, "ps") == 0) { + cmd_ps(); + } else if (strcmp(cmd, "ls") == 0) { + cmd_ls(); + } else if (strcmp(cmd, "cat") == 0) { + cmd_cat(args); + } else if (strcmp(cmd, "exit") == 0) { + kputs("Goodbye!"); + wasi_exit(0); + } else if (*cmd) { /* 空コマンドは無視 */ + kprintf("sh: %s: command not found\n", cmd); + } +} + +/* 1行読み込み (シンプル版) */ +static int read_line(char *buffer, int size) { + int n = wasi_read(STDIN_FILENO, buffer, size - 1); + if (n > 0) { + /* 改行を削除 */ + if (buffer[n - 1] == '\n') { + buffer[n - 1] = '\0'; + return n - 1; + } + buffer[n] = '\0'; + return n; + } + return -1; +} + +/* シェルのメインループ */ +void shell_main(void) { + kputs(""); + kputs("Welcome to WebVM Shell!"); + kputs("Type 'help' for available commands."); + kputs(""); + + /* 最初のプロンプトを表示 */ + wasi_write(STDOUT_FILENO, "$ ", 2); + + /* 初期化が終わったらカーネルは終了する */ + /* JavaScriptからの入力は handleCommand 経由で処理される */ +} \ No newline at end of file diff --git a/kernel/shell/shell_api.c b/kernel/shell/shell_api.c new file mode 100644 index 0000000..d23f500 --- /dev/null +++ b/kernel/shell/shell_api.c @@ -0,0 +1,31 @@ +/* + * WebVM シェルAPI + * + * JavaScript側から呼び出せるシェル関数 + */ + +#include "../include/kernel.h" +#include "../include/wasi_syscalls.h" + +/* 外部関数 */ +extern void execute_command(char *cmd); + +/** + * コマンドハンドラ (JavaScriptから呼び出される) + */ +void handle_command(const char *command) { + static char cmd_buffer[256]; + int i; + + /* コマンドをコピー */ + for (i = 0; i < 255 && command[i]; i++) { + cmd_buffer[i] = command[i]; + } + cmd_buffer[i] = '\0'; + + /* コマンドを実行 */ + execute_command(cmd_buffer); + + /* 次のプロンプトを表示 */ + wasi_write(STDOUT_FILENO, "$ ", 2); +} \ No newline at end of file diff --git a/syslib/index.ts b/syslib/index.ts index 395c940..6611bc9 100644 --- a/syslib/index.ts +++ b/syslib/index.ts @@ -1,71 +1,39 @@ -declare global { - var wasm: { - instance: WebAssembly.Instance; - module: WebAssembly.Module; - }; -} +/** + * WebVM システムライブラリ エントリポイント + */ -// WASI I/O vector 構造体のサイズとオフセット -const IOVEC_SIZE = 8; // buf(4) + buf_len(4) +import './types.js'; +import { loadKernel, setupCommandHandler } from './kernel.js'; +/** + * WebVMを起動 + */ export async function boot() { + // カーネルの max-memory=134217728 (128MB) に合わせる + // 1ページ = 64KB なので 128MB = 2048ページ const memory = new WebAssembly.Memory({ - initial: 17, - maximum: 1024, + initial: 2048, + maximum: 2048, shared: true }); - // WASI import objects - const importObj = { - env: { - memory: memory, - }, - wasi_snapshot_preview1: { - proc_exit: (code: number) => { - console.log(`[WASI] Process exited with code: ${code}`); - // TODO: プロセス終了処理 - }, - - fd_write: (fd: number, iovs_ptr: number, iovs_len: number, nwritten_ptr: number) => { - const view = new DataView(memory.buffer); - const u8 = new Uint8Array(memory.buffer); - let totalWritten = 0; - - // 各 iovec 処理 - for (let i = 0; i < iovs_len; i++) { - const iovec_ptr = iovs_ptr + i * IOVEC_SIZE; - const buf_ptr = view.getUint32(iovec_ptr, true); // little endian - const buf_len = view.getUint32(iovec_ptr + 4, true); - - // バッファから文字列を読み取り - const bytes = u8.slice(buf_ptr, buf_ptr + buf_len); - const str = new TextDecoder().decode(bytes); - - // fd に応じてコンソール出力 - if (fd === 1) { // STDOUT - console.log("[STDOUT]", str.replace(/\n$/, "")); - } else if (fd === 2) { // STDERR - console.error("[STDERR]", str.replace(/\n$/, "")); - } - - totalWritten += buf_len; - } - - // 書き込み済みバイト数を返す - view.setUint32(nwritten_ptr, totalWritten, true); - return 0; // success - }, - }, - }; + // カーネルをロード + const { instance, module } = await loadKernel(memory); - const res = await fetch("../kernel.wasm"); - const bytes = await res.arrayBuffer(); - const wasm = await WebAssembly.instantiate(bytes, importObj); - globalThis.wasm = wasm; + // グローバル変数に保存 + globalThis.wasm = { + instance, + module, + }; - console.log("WebAssembly exports:", Object.keys(wasm.instance.exports)); + console.log("WebAssembly exports:", Object.keys(instance.exports)); - const startFunction = (wasm.instance.exports as any)._start as Function; + // カーネルを起動 + const exports = instance.exports as any; + const startFunction = exports._start as Function; startFunction(); console.log("Kernel started successfully."); -} + + // コマンドハンドラを設定 + setupCommandHandler(instance, memory); +} \ No newline at end of file diff --git a/syslib/kernel.ts b/syslib/kernel.ts new file mode 100644 index 0000000..b14da9e --- /dev/null +++ b/syslib/kernel.ts @@ -0,0 +1,49 @@ +/** + * カーネル管理モジュール + */ + +import { createWasiImports } from './wasi.js'; + +export interface KernelExports { + _start: () => void; + handle_command?: (ptr: number) => void; +} + +/** + * カーネルをロードして起動 + */ +export async function loadKernel(memory: WebAssembly.Memory): Promise { + // WASI インポートオブジェクトを作成 + const importObj = { + env: { memory }, + wasi_snapshot_preview1: createWasiImports(memory), + }; + + // カーネルWASMをフェッチ + const res = await fetch("./kernel.wasm"); + const bytes = await res.arrayBuffer(); + const wasm = await WebAssembly.instantiate(bytes, importObj); + + return wasm; +} + +/** + * コマンドハンドラを設定 + */ +export function setupCommandHandler(instance: WebAssembly.Instance, memory: WebAssembly.Memory) { + const exports = instance.exports as unknown as KernelExports; + + if (exports.handle_command) { + globalThis.handleCommand = (command: string) => { + // コマンド文字列をメモリに書き込む + const encoder = new TextEncoder(); + const cmdBytes = encoder.encode(command + '\0'); + const cmdPtr = 0x10000; // 一時的なメモリ位置 + const u8 = new Uint8Array(memory.buffer); + u8.set(cmdBytes, cmdPtr); + + // handle_commandを呼び出す + exports.handle_command!(cmdPtr); + }; + } +} \ No newline at end of file diff --git a/syslib/types.ts b/syslib/types.ts new file mode 100644 index 0000000..31b1aa9 --- /dev/null +++ b/syslib/types.ts @@ -0,0 +1,18 @@ +/** + * 型定義 + */ + +// グローバル変数の型定義 +declare global { + var wasm: { + instance: WebAssembly.Instance; + module: WebAssembly.Module; + }; + var terminalWrite: (text: string) => void; + var terminalWriteLine: (text: string) => void; + var handleCommand: (command: string) => void; + var inputBuffer: string; + var inputReady: boolean; +} + +export {}; \ No newline at end of file diff --git a/syslib/wasi.ts b/syslib/wasi.ts new file mode 100644 index 0000000..349b5e4 --- /dev/null +++ b/syslib/wasi.ts @@ -0,0 +1,94 @@ +/** + * WASI (WebAssembly System Interface) 実装 + */ + +// WASI I/O vector 構造体のサイズとオフセット +const IOVEC_SIZE = 8; // buf(4) + buf_len(4) + +/** + * WASI システムコールの実装 + */ +export function createWasiImports(memory: WebAssembly.Memory) { + return { + proc_exit: (code: number) => { + console.log(`[WASI] Process exited with code: ${code}`); + // TODO: プロセス終了処理 + }, + + fd_write: (fd: number, iovs_ptr: number, iovs_len: number, nwritten_ptr: number) => { + const view = new DataView(memory.buffer); + const u8 = new Uint8Array(memory.buffer); + let totalWritten = 0; + + // 各 iovec 処理 + for (let i = 0; i < iovs_len; i++) { + const iovec_ptr = iovs_ptr + i * IOVEC_SIZE; + const buf_ptr = view.getUint32(iovec_ptr, true); // little endian + const buf_len = view.getUint32(iovec_ptr + 4, true); + + // バッファから文字列を読み取り + const bytes = u8.slice(buf_ptr, buf_ptr + buf_len); + const str = new TextDecoder().decode(bytes); + + // fd に応じて出力先を切り替え + if (fd === 1) { // STDOUT + // ターミナルウィンドウに出力 + if (globalThis.terminalWrite) { + globalThis.terminalWrite(str); + } + // デバッグ用にコンソールにも出力 + console.log("[STDOUT]", str.replace(/\n$/, "")); + } else if (fd === 2) { // STDERR + if (globalThis.terminalWrite) { + globalThis.terminalWrite(str); + } + console.error("[STDERR]", str.replace(/\n$/, "")); + } + + totalWritten += buf_len; + } + + // 書き込み済みバイト数を返す + view.setUint32(nwritten_ptr, totalWritten, true); + return 0; // success + }, + + fd_read: (fd: number, iovs_ptr: number, iovs_len: number, nread_ptr: number) => { + const view = new DataView(memory.buffer); + const u8 = new Uint8Array(memory.buffer); + + if (fd === 0 && globalThis.inputReady && globalThis.inputBuffer) { // STDIN + // 入力バッファから読み取り + const input = globalThis.inputBuffer + '\n'; + const encoder = new TextEncoder(); + const inputBytes = encoder.encode(input); + + let totalRead = 0; + for (let i = 0; i < iovs_len && totalRead < inputBytes.length; i++) { + const iovec_ptr = iovs_ptr + i * IOVEC_SIZE; + const buf_ptr = view.getUint32(iovec_ptr, true); + const buf_len = view.getUint32(iovec_ptr + 4, true); + + const copyLen = Math.min(buf_len, inputBytes.length - totalRead); + u8.set(inputBytes.slice(totalRead, totalRead + copyLen), buf_ptr); + totalRead += copyLen; + } + + view.setUint32(nread_ptr, totalRead, true); + globalThis.inputBuffer = ''; + globalThis.inputReady = false; + return 0; + } + + view.setUint32(nread_ptr, 0, true); + return 0; + }, + + clock_time_get: (clock_id: number, precision: bigint, time_ptr: number) => { + const view = new DataView(memory.buffer); + const now = BigInt(Date.now()) * 1000000n; // ミリ秒からナノ秒へ + view.setBigUint64(time_ptr, now, true); + return 0; + }, + }; +} \ No newline at end of file