update v2

This commit is contained in:
2025-07-05 14:31:39 +09:00
parent 163ac1668d
commit 3a9e563ee0
40 changed files with 1929 additions and 2128 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
octox
redox
target
.claude
*.log

View File

@ -1,12 +0,0 @@
# Bootloader configuration
[bootloader]
# Map physical memory for accessing boot info
map_physical_memory = true
# Frame buffer settings
[bootloader.framebuffer]
# Enable framebuffer mode (VGA compatible)
enable = true
width = 80
height = 25
format = "text"

View File

@ -1,30 +1,20 @@
[package]
name = "aios"
version = "0.1.0"
[workspace]
members = [
"kernel",
"user",
"claude-service",
]
[workspace.package]
version = "2.0.0"
edition = "2021"
license = "MIT"
[dependencies]
bootloader = "0.9.23"
volatile = "0.2"
spin = "0.9"
x86_64 = "0.14"
uart_16550 = "0.2"
pic8259 = "0.10"
pc-keyboard = "0.7"
[dependencies.lazy_static]
version = "1.4"
features = ["spin_no_std"]
[[bin]]
name = "kernel"
path = "src/main.rs"
[target.'cfg(target_os = "none")'.dependencies]
linked_list_allocator = "0.10"
[workspace.dependencies]
kernel = { path = "kernel", default-features = false }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
panic = "abort"

View File

@ -1,12 +1,92 @@
# aios
# Aios - AI Operating System
create a unix-like os from scratch
A Unix-like operating system built from scratch in Rust, designed for AI integration.
## example
## Features
- **Interactive Shell**: Full keyboard input support with command processing
- **Safe Kernel**: Pure Rust implementation with memory safety
- **Minimal Design**: Inspired by octox philosophy - simple and efficient
- **Bootloader Integration**: Uses rust-bootloader for reliable booting
## Quick Start
```sh
# archlinux
cargo bootimage
qemu-system-x86_64 -drive format=raw,file=target/x86_64-unknown-none/debug/bootimage-kernel.bin -nographic
# Build and test the OS
./scpt/bootimage-test.sh
```
## Available Commands
- `help` - Show available commands
- `version` - Display version information
- `echo` - Echo test message
- `clear` - Clear the screen
## Project Structure
```
aios/
├── kernel/ # Main kernel implementation
│ ├── src/
│ │ ├── main.rs # Kernel entry point
│ │ ├── keyboard_basic.rs # Keyboard driver
│ │ └── ...
│ └── Cargo.toml
├── docs/ # Documentation
│ ├── TROUBLESHOOTING.md # Common issues and solutions
│ └── SHELL_IMPLEMENTATION.md # Shell implementation guide
└── scpt/ # Build and test scripts
```
## Documentation
- [Troubleshooting Guide](docs/TROUBLESHOOTING.md) - Solutions for common issues
- [Shell Implementation](docs/SHELL_IMPLEMENTATION.md) - Detailed implementation guide
## Development
### Requirements
- Rust nightly toolchain
- `bootimage` tool: `cargo install bootimage`
- QEMU for testing
- x86_64 target: `rustup target add x86_64-unknown-none`
### Building
```sh
cd kernel
cargo bootimage --release
```
### Testing
```sh
# Interactive test with QEMU
./scpt/bootimage-test.sh
# The OS will boot and present an interactive shell
# Type commands and press Enter to execute
```
## Architecture
Aios follows a microkernel-inspired design:
- **Kernel Space**: Minimal kernel with basic I/O and memory management
- **User Space**: Shell and applications (planned)
- **AI Integration**: Claude service integration (planned)
## Troubleshooting
If you encounter issues:
1. **Page table panics**: See [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md#1-page-table-panic-error-page_tablers)
2. **Keyboard not working**: See [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md#2-keyboard-input-not-working)
3. **Build errors**: Check Rust nightly and required components
## License
MIT License

View File

@ -0,0 +1,8 @@
[package]
name = "claude-service"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
# Placeholder for Claude integration service

View File

@ -0,0 +1 @@
// AIOS v2 Claude Service placeholder

View File

@ -43,8 +43,6 @@ tar -zcvf aios-bootstrap.tar.gz root.x86_64/
## ref
[o8vm/octox](https://github.com/o8vm/octox)が参考になるかもしれない。
- [redox-os/redox](redox-os/redox)
- [o8vm/octox](https://github.com/o8vm/octox)
```sh
git clone https://github.com/o8vm/octox
```

View File

@ -0,0 +1,378 @@
# Aios Shell Implementation Guide
## Overview
Aiosのインタラクティブシェル実装についての詳細ガイド。
## Implementation History
### Phase 1: Initial Attempt (Failed)
**実装内容:**
```rust
// keyboard.rs - 複雑な実装
use core::convert::TryFrom;
static SCANCODE_TO_ASCII: [u8; 128] = [
// 複雑なマッピングテーブル
];
unsafe fn inb(port: u16) -> u8 {
// 危険なポートアクセス
}
```
**問題:**
- Page table panic (`page_table*.rs:105:25`)
- ポートI/O競合
- 配列サイズ不一致
### Phase 2: Safe Approach (Partially Working)
**実装内容:**
```rust
// keyboard_safe.rs
unsafe fn try_inb(port: u16) -> Option<u8> {
// 遅延付きアクセス
for _ in 0..1000 {
core::arch::asm!("nop");
}
// ... 安全なアクセス
}
```
**結果:**
- パニックは解決
- キーボード入力が機能しない
### Phase 3: Basic Implementation (Success)
**実装内容:**
```rust
// keyboard_basic.rs - シンプルなアプローチ
pub fn has_key() -> bool {
unsafe {
let status: u8;
core::arch::asm!(
"in al, 0x64",
out("al") status,
options(nomem, nostack, preserves_flags)
);
(status & 1) != 0
}
}
```
**結果:**
- パニック解決
- キーボード入力動作
- スキャンコード正常取得
## Architecture
### Keyboard Driver (`keyboard_basic.rs`)
```rust
/// PS/2キーボードの状態確認
pub fn has_key() -> bool
/// 生スキャンコードの読み取り
pub fn read_scancode() -> u8
/// スキャンコードからASCII文字への変換
pub fn scancode_to_char(scancode: u8) -> Option<char>
```
**キーマッピング:**
```rust
match scancode {
0x1E => Some('a'), // A key
0x30 => Some('b'), // B key
0x2E => Some('c'), // C key
// ...
0x39 => Some(' '), // Space
0x1C => Some('\n'), // Enter
_ => None,
}
```
### Shell Core (`main.rs`)
**State Management:**
```rust
static mut INPUT_BUFFER: [u8; 64] = [0; 64];
static mut INPUT_POS: usize = 0;
static mut CURSOR_ROW: usize = 12;
static mut CURSOR_COL: usize = 6;
```
**Input Processing:**
```rust
fn process_char(ch: char) {
match ch {
'\n' => execute_command(),
'\u{8}' => handle_backspace(),
' '..='~' => add_char_to_buffer(ch),
_ => { /* ignore */ }
}
}
```
**Command Execution:**
```rust
fn execute_command() {
let cmd = core::str::from_utf8(&INPUT_BUFFER[..INPUT_POS])?;
match cmd {
"help" => show_help(),
"version" => show_version(),
"echo" => echo_test(),
"clear" => clear_screen(),
_ => show_unknown_command(cmd),
}
}
```
## Key Features
### 1. Real-time Input
- キー押下の即座の反映
- バックスペース対応
- 文字制限64文字
### 2. Command Processing
- Enter キーでコマンド実行
- コマンド履歴(将来実装予定)
- エラーハンドリング
### 3. Available Commands
**help**
```
Aios Commands:
help - Show this help
version - Show version
echo - Echo test
clear - Clear screen
```
**version**
```
Aios 2.0 - AI Operating System
Interactive Shell with Keyboard Support
```
**echo**
```
Hello from Aios Interactive Shell!
```
**clear**
- 画面全体をクリア
- カーソル位置をリセット
### 4. Input Handling
**Scancode Processing:**
```rust
loop {
if keyboard_basic::has_key() {
let scancode = keyboard_basic::read_scancode();
// キーリリースを除外
if scancode != last_scancode && (scancode & 0x80) == 0 {
last_scancode = scancode;
if let Some(ch) = keyboard_basic::scancode_to_char(scancode) {
process_char(ch);
}
}
}
// CPU負荷軽減
for _ in 0..1000 {
unsafe { core::arch::asm!("nop"); }
}
}
```
## VGA Text Mode Interface
### Screen Layout
```
Row 0: "Aios - AI Operating System" (Center)
Row 1: "Interactive Shell Ready" (Center)
Row 2: "Type 'help' for commands" (Center)
Row 3: [Empty]
Row 4: [Empty]
Row 5: aios> [user input] (Prompt)
Row 6+: [Command output]
```
### Character Display
```rust
pub fn print_char(c: u8, x: usize, y: usize) {
if x >= 80 || y >= 25 { return; }
unsafe {
let vga = &mut *VGA_BUFFER;
vga.chars[y][x] = (0x0f << 8) | (c as u16);
// ^^^^ ^^^^^^^^^^
// Color Character
// (White/Black) (ASCII)
}
}
```
**Color Scheme:**
- `0x0f` = White text on black background
- Format: `(color << 8) | character`
## Testing Procedures
### 1. Scancode Debug Test
```rust
// 目的: キーボード入力の確認
// 表示: "Scancode: 0x1E -> 'a'"
```
### 2. Interactive Shell Test
```bash
# テストシーケンス
1. help <Enter> # ヘルプ表示確認
2. version <Enter> # バージョン情報確認
3. echo <Enter> # エコーテスト確認
4. clear <Enter> # 画面クリア確認
5. invalid <Enter> # エラーハンドリング確認
```
### 3. Input Edge Cases
```bash
# テストケース
- 空のコマンド (Enter のみ)
- 長いコマンド (64文字以上)
- バックスペースの動作
- 連続入力
```
## Performance Considerations
### CPU Usage
```rust
// 軽量な遅延ループ
for _ in 0..1000 {
unsafe { core::arch::asm!("nop"); }
}
```
### Memory Usage
- Input buffer: 64 bytes
- VGA buffer: Direct access (no buffering)
- Static variables: Minimal usage
### Polling Frequency
- 高頻度ポーリング遅延1000サイクル
- CPU halt命令は使用しない応答性重視
## Known Limitations
### 1. Character Set
- ASCII printable characters only (`' '` to `'~'`)
- 日本語等のマルチバイト文字は未対応
- 特殊キーF1-F12, Arrow keysは未対応
### 2. Input Features
- コマンド履歴なし
- Tab補完なし
- 行編集機能なし(カーソル移動等)
### 3. Display
- 80x25 text mode固定
- スクロール機能なし
- 色変更機能なし
## Future Enhancements
### 1. Advanced Input
```rust
// 計画中の機能
- Command history (/ keys)
- Tab completion
- Line editing (/ keys)
- Multi-line input
```
### 2. Extended Commands
```rust
// 追加予定のコマンド
- ls // File listing
- cat // File content
- mkdir // Directory creation
- cd // Change directory
- ps // Process list
- claude // AI integration
```
### 3. Display Improvements
```rust
// 計画中の機能
- Scrolling support
- Color themes
- Multiple windows
- Status bar
```
## Debugging Tips
### 1. Scancode Debugging
```rust
// スキャンコード確認用
print_str("Raw: 0x", x, y);
print_hex(scancode);
```
### 2. Buffer State Debugging
```rust
// バッファ状態確認
print_str("Pos: ", x, y);
print_number(INPUT_POS);
```
### 3. QEMU Console Output
```bash
# QEMUでの詳細ログ
qemu-system-x86_64 -kernel kernel.bin -d int,cpu
```
## Build Configuration
### Cargo.toml
```toml
[package]
name = "aios-kernel"
version = "0.2.0"
edition = "2021"
[dependencies]
bootloader = "0.9.23"
```
### Build Command
```bash
# 開発用
cargo bootimage --release
# テスト用
./scpt/bootimage-test.sh
```
## Success Metrics
実装成功の判定基準:
1. **キーボード入力**: 文字入力が画面に表示される
2. **コマンド実行**: `help`コマンドが正常動作
3. **バックスペース**: 文字削除が正常動作
4. **エラーハンドリング**: 不明コマンドでエラー表示
5. **画面管理**: `clear`コマンドで画面クリア
全ての機能が正常動作することを確認済み。

298
docs/TROUBLESHOOTING.md Normal file
View File

@ -0,0 +1,298 @@
# Aios Troubleshooting Guide
## Common Issues and Solutions
### 1. Page Table Panic Error (`page_table*.rs`)
#### Problem
QEMUでAiosを実行すると、以下のようなパニックが発生する
```
panicked at 'page_table*.rs:105:25'
```
#### Root Cause
この問題は主にシェル機能とキーボード入力の実装時に発生する。具体的な原因:
1. **ポートI/O競合**: PS/2キーボードのポートアクセス0x60, 0x64がbootloaderクレートと競合
2. **メモリアクセス違反**: unsafe なポートアクセスがページテーブルの整合性を破壊
3. **割り込み処理**: キーボード割り込みの不適切な処理
#### Solution Steps
**Step 1: 問題の特定**
```rust
// 危険なポートアクセス例
unsafe fn inb(port: u16) -> u8 {
let result: u8;
core::arch::asm!(
"in al, dx",
out("al") result,
in("dx") port,
);
result
}
```
**Step 2: 安全な実装への移行**
```rust
// 安全なポートアクセス(遅延付き)
unsafe fn try_inb(port: u16) -> Option<u8> {
// Add delay to avoid rapid polling
for _ in 0..1000 {
core::arch::asm!("nop");
}
let result: u8;
core::arch::asm!(
"in al, dx",
out("al") result,
in("dx") port,
options(nomem, nostack, preserves_flags)
);
Some(result)
}
```
**Step 3: 完全な対策**
最終的に、以下の基本的なアプローチで解決:
```rust
/// Check if there's data in keyboard buffer
pub fn has_key() -> bool {
unsafe {
let status: u8;
core::arch::asm!(
"in al, 0x64",
out("al") status,
options(nomem, nostack, preserves_flags)
);
(status & 1) != 0
}
}
/// Read raw scancode from keyboard
pub fn read_scancode() -> u8 {
unsafe {
let scancode: u8;
core::arch::asm!(
"in al, 0x60",
out("al") scancode,
options(nomem, nostack, preserves_flags)
);
scancode
}
}
```
#### Prevention
- `options(nomem, nostack, preserves_flags)` を必ず使用
- ポーリング頻度を制限1000サイクル毎など
- エラーハンドリングの実装
- 段階的テスト(まずデバッグ版で検証)
---
### 2. Keyboard Input Not Working
#### Problem
キーボード入力が QEMU で認識されない問題。
#### Symptoms
- QEMUは正常に起動する
- 画面表示は正常
- キーを押しても反応しない
- スキャンコードが取得できない
#### Root Causes
**Cause 1: QEMUのフォーカス問題**
- QEMUウィンドウにフォーカスがない
- マウスキャプチャが無効
**Cause 2: ポーリング頻度の問題**
```rust
// 問題のあるコード
if counter % 50000 == 0 {
// キーボードチェックの頻度が低すぎ
}
```
**Cause 3: スキャンコードマッピングの不備**
```rust
// 不完全なマッピング
static SCANCODE_TO_ASCII: [u8; 128] = [
// 配列サイズが127で不一致
];
```
#### Solutions
**Solution 1: QEMUの正しい起動**
```bash
# GTK表示でフォーカス確保
qemu-system-x86_64 \
-drive format=raw,file="$BOOT_IMAGE" \
-display gtk
# または、nographic を避ける
qemu-system-x86_64 \
-drive format=raw,file="$BOOT_IMAGE"
```
**Solution 2: 適切なポーリング頻度**
```rust
loop {
if keyboard_basic::has_key() {
let scancode = keyboard_basic::read_scancode();
// 即座に処理
}
// 軽い遅延のみ
for _ in 0..1000 {
unsafe { core::arch::asm!("nop"); }
}
}
```
**Solution 3: 段階的デバッグ**
**Phase 1: スキャンコード確認**
```rust
// デバッグ版でスキャンコードを表示
if keyboard_basic::has_key() {
let scancode = keyboard_basic::read_scancode();
print_hex(scancode); // 16進数で表示
}
```
**Phase 2: キーリリース除外**
```rust
// キープレスのみ処理bit 7 = 0
if (scancode & 0x80) == 0 {
// キープレス処理
}
```
**Phase 3: 完全なマッピング**
```rust
pub fn scancode_to_char(scancode: u8) -> Option<char> {
match scancode {
0x1E => Some('a'),
0x30 => Some('b'),
// ... 全てのキーをマッピング
0x1C => Some('\n'), // Enter
_ => None,
}
}
```
#### Verification Steps
1. **基本テスト**: スキャンコード表示版でキー入力確認
2. **マッピングテスト**: 特定キーa, enter等の動作確認
3. **シェルテスト**: コマンド入力・実行の確認
#### Working Configuration
最終的に動作した設定:
```rust
// keyboard_basic.rs
pub fn has_key() -> bool {
unsafe {
let status: u8;
core::arch::asm!("in al, 0x64", out("al") status, options(nomem, nostack, preserves_flags));
(status & 1) != 0
}
}
// メインループ
loop {
if keyboard_basic::has_key() {
let scancode = keyboard_basic::read_scancode();
if scancode != last_scancode && (scancode & 0x80) == 0 {
if let Some(ch) = keyboard_basic::scancode_to_char(scancode) {
process_char(ch);
}
}
}
// 軽い遅延
for _ in 0..1000 {
unsafe { core::arch::asm!("nop"); }
}
}
```
---
### 3. Build Errors
#### Type Mismatch in Arrays
```rust
// Error: expected array with size 128, found 127
static SCANCODE_TO_ASCII: [u8; 128] = [...]; // 要素が127個
// Solution: 正確な要素数
static SCANCODE_TO_ASCII: [u8; 128] = [
// 128個の要素を確保
0, 27, b'1', ..., 0 // 最後に0で埋める
];
```
#### Unused Imports
```rust
// Warning: unused import
use core::convert::TryFrom; // 削除
// Warning: unused import
use crate::keyboard; // 使用しない場合は削除
```
---
### 4. QEMU-specific Issues
#### Display Problems
```bash
# 問題: nographic で表示されない
qemu-system-x86_64 -kernel kernel.bin -nographic
# 解決: 標準表示を使用
qemu-system-x86_64 -kernel kernel.bin
```
#### Mouse Capture
```
# QEMUでマウスキャプチャを有効化
Ctrl+Alt+G (toggle mouse capture)
```
#### Exit QEMU
```
# QEMUの終了方法
Ctrl+A, X # または
Alt+F4 # ウィンドウを閉じる
```
---
### Best Practices
1. **段階的実装**
- 静的表示 → スキャンコード確認 → 完全シェル
2. **デバッグ情報の表示**
- スキャンコードの16進表示
- ポーリング回数のカウンタ
- キー受信回数の表示
3. **安全なポートアクセス**
- `options(nomem, nostack, preserves_flags)` 使用
- 適切な遅延の実装
- エラーハンドリング
4. **テスト環境**
- QEMUの適切な起動オプション
- フォーカスの確保
- 段階的な機能テスト

7
iso/boot/grub/grub.cfg Normal file
View File

@ -0,0 +1,7 @@
set timeout=0
set default=0
menuentry "AIOS v2" {
multiboot2 /boot/kernel.bin
boot
}

13
kernel/.cargo/config.toml Normal file
View File

@ -0,0 +1,13 @@
[build]
target = "x86_64-unknown-none"
[target.x86_64-unknown-none]
rustflags = [
"-C", "link-arg=-T/Users/syui/git/aios/kernel/kernel.ld",
"-C", "relocation-model=static",
"-C", "code-model=kernel"
]
[unstable]
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]

14
kernel/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "aios-kernel"
version = "2.0.0"
edition = "2021"
[[bin]]
name = "aios-kernel"
path = "src/main.rs"
[dependencies]
bootloader = "0.9.23"
[package.metadata.bootimage]
test-args = ["-nographic"]

22
kernel/i686-aios.json Normal file
View File

@ -0,0 +1,22 @@
{
"llvm-target": "i686-unknown-none",
"target-endian": "little",
"target-pointer-width": "32",
"target-c-int-width": "32",
"data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128",
"arch": "x86",
"os": "none",
"env": "",
"vendor": "unknown",
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,-sse2",
"executables": true,
"relocation-model": "static",
"code-model": "small",
"pre-link-args": {
"ld.lld": ["-Tkernel.ld"]
}
}

35
kernel/kernel.ld Normal file
View File

@ -0,0 +1,35 @@
/* AIOS v2 Kernel Linker Script */
ENTRY(_start)
SECTIONS
{
. = 1M;
.multiboot :
{
/* Ensure the multiboot header is at the very beginning */
KEEP(*(.multiboot))
}
.text : ALIGN(0x1000)
{
*(.text .text.*)
}
.rodata : ALIGN(0x1000)
{
*(.rodata .rodata.*)
}
.data : ALIGN(0x1000)
{
*(.data .data.*)
}
.bss : ALIGN(0x1000)
{
*(COMMON)
*(.bss .bss.*)
}
}

27
kernel/src/boot.s Normal file
View File

@ -0,0 +1,27 @@
# Multiboot header
.section .multiboot
.align 8
multiboot_header:
.long 0x1BADB002 # magic
.long 0x00 # flags
.long -(0x1BADB002 + 0x00) # checksum
# Entry point
.section .text
.global _start
.code64
_start:
# Disable interrupts
cli
# Load null descriptor into data segment registers
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov %ax, %fs
mov %ax, %gs
# Halt
hlt
1: jmp 1b

34
kernel/src/boot32.s Normal file
View File

@ -0,0 +1,34 @@
.code32
.section .multiboot, "ax"
.align 4
multiboot_header:
.long 0x1BADB002 # magic
.long 0x00 # flags
.long -(0x1BADB002 + 0x00) # checksum
.section .text
.global _start
_start:
# We start in 32-bit mode
cli
# Set up a simple stack
movl $stack_top, %esp
# Write 'OK' to VGA buffer
movl $0xb8000, %edi
movb $'O', (%edi)
movb $0x0f, 1(%edi)
movb $'K', 2(%edi)
movb $0x0f, 3(%edi)
# Halt
hang:
hlt
jmp hang
.section .bss
.align 16
stack_bottom:
.space 4096
stack_top:

130
kernel/src/console.rs Normal file
View File

@ -0,0 +1,130 @@
//! Simple VGA console for AIOS v2
//! No external crates, pure Rust implementation
use core::fmt::{self, Write};
const VGA_BUFFER: *mut u16 = 0xb8000 as *mut u16;
const VGA_WIDTH: usize = 80;
const VGA_HEIGHT: usize = 25;
static mut CONSOLE: Console = Console {
col: 0,
row: 0,
color: 0x0f, // White on black
};
struct Console {
col: usize,
row: usize,
color: u8,
}
impl Console {
fn clear(&mut self) {
for i in 0..(VGA_WIDTH * VGA_HEIGHT) {
unsafe {
VGA_BUFFER.add(i).write_volatile(0x0f00); // Clear with white on black
}
}
self.col = 0;
self.row = 0;
}
fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => {
self.col = 0;
self.row += 1;
if self.row >= VGA_HEIGHT {
self.scroll_up();
}
}
byte => {
if self.col >= VGA_WIDTH {
self.col = 0;
self.row += 1;
if self.row >= VGA_HEIGHT {
self.scroll_up();
}
}
let pos = self.row * VGA_WIDTH + self.col;
let entry = (self.color as u16) << 8 | byte as u16;
unsafe {
VGA_BUFFER.add(pos).write_volatile(entry);
}
self.col += 1;
}
}
}
fn scroll_up(&mut self) {
// Move all lines up by one
for row in 1..VGA_HEIGHT {
for col in 0..VGA_WIDTH {
let pos = row * VGA_WIDTH + col;
let prev_pos = (row - 1) * VGA_WIDTH + col;
unsafe {
let entry = VGA_BUFFER.add(pos).read_volatile();
VGA_BUFFER.add(prev_pos).write_volatile(entry);
}
}
}
// Clear last line
for col in 0..VGA_WIDTH {
let pos = (VGA_HEIGHT - 1) * VGA_WIDTH + col;
unsafe {
VGA_BUFFER.add(pos).write_volatile(0x0f00);
}
}
self.row = VGA_HEIGHT - 1;
}
}
impl Write for Console {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
self.write_byte(byte);
}
Ok(())
}
}
pub fn init() {
unsafe {
CONSOLE.clear();
}
}
pub fn print(s: &str) {
unsafe {
CONSOLE.write_str(s).unwrap();
}
}
#[macro_export]
macro_rules! kprint {
($($arg:tt)*) => {
$crate::console::_print(format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! kprintln {
() => ($crate::console::print("\n"));
($($arg:tt)*) => ({
$crate::console::_print(format_args!($($arg)*));
$crate::console::print("\n");
})
}
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
unsafe {
CONSOLE.write_fmt(args).unwrap();
}
}

67
kernel/src/keyboard.rs Normal file
View File

@ -0,0 +1,67 @@
//! PS/2 Keyboard Driver for AIOS v2
/// PS/2 keyboard data port
const KEYBOARD_DATA_PORT: u16 = 0x60;
/// PS/2 keyboard status port
const KEYBOARD_STATUS_PORT: u16 = 0x64;
/// Keyboard scan codes to ASCII mapping (US layout)
static SCANCODE_TO_ASCII: [u8; 128] = [
0, 27, b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'-', b'=', 8, // backspace
b'\t', b'q', b'w', b'e', b'r', b't', b'y', b'u', b'i', b'o', b'p', b'[', b']', b'\n', // enter
0, // left ctrl
b'a', b's', b'd', b'f', b'g', b'h', b'j', b'k', b'l', b';', b'\'', b'`',
0, // left shift
b'\\', b'z', b'x', b'c', b'v', b'b', b'n', b'm', b',', b'.', b'/',
0, // right shift
b'*',
0, // left alt
b' ', // space
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F1-F10
0, // num lock
0, // scroll lock
0, 0, 0, 0, 0, 0, 0, 0, 0, // keypad
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
/// Read a byte from a port
unsafe fn inb(port: u16) -> u8 {
let result: u8;
core::arch::asm!(
"in al, dx",
out("al") result,
in("dx") port,
options(nomem, nostack, preserves_flags)
);
result
}
/// Check if keyboard has data available
pub fn keyboard_ready() -> bool {
unsafe {
(inb(KEYBOARD_STATUS_PORT) & 0x01) != 0
}
}
/// Read a key from the keyboard (non-blocking)
pub fn read_key() -> Option<u8> {
if !keyboard_ready() {
return None;
}
unsafe {
let scancode = inb(KEYBOARD_DATA_PORT);
// Only handle key press (bit 7 = 0), ignore key release
if scancode & 0x80 == 0 {
if let Some(&ascii) = SCANCODE_TO_ASCII.get(scancode as usize) {
if ascii != 0 {
return Some(ascii);
}
}
}
}
None
}

View File

@ -0,0 +1,62 @@
//! Basic Keyboard Driver - Minimal approach
/// Check if there's data in keyboard buffer
pub fn has_key() -> bool {
unsafe {
let status: u8;
core::arch::asm!(
"in al, 0x64",
out("al") status,
options(nomem, nostack, preserves_flags)
);
(status & 1) != 0
}
}
/// Read raw scancode from keyboard
pub fn read_scancode() -> u8 {
unsafe {
let scancode: u8;
core::arch::asm!(
"in al, 0x60",
out("al") scancode,
options(nomem, nostack, preserves_flags)
);
scancode
}
}
/// Convert scancode to ASCII (basic mapping)
pub fn scancode_to_char(scancode: u8) -> Option<char> {
match scancode {
0x1E => Some('a'),
0x30 => Some('b'),
0x2E => Some('c'),
0x20 => Some('d'),
0x12 => Some('e'),
0x21 => Some('f'),
0x22 => Some('g'),
0x23 => Some('h'),
0x17 => Some('i'),
0x24 => Some('j'),
0x25 => Some('k'),
0x26 => Some('l'),
0x32 => Some('m'),
0x31 => Some('n'),
0x18 => Some('o'),
0x19 => Some('p'),
0x10 => Some('q'),
0x13 => Some('r'),
0x1F => Some('s'),
0x14 => Some('t'),
0x16 => Some('u'),
0x2F => Some('v'),
0x11 => Some('w'),
0x2D => Some('x'),
0x15 => Some('y'),
0x2C => Some('z'),
0x39 => Some(' '),
0x1C => Some('\n'),
_ => None,
}
}

116
kernel/src/keyboard_safe.rs Normal file
View File

@ -0,0 +1,116 @@
//! Safe Keyboard Driver for AIOS v2
//! Uses polling without interrupts to avoid conflicts
/// PS/2 keyboard data port
const KEYBOARD_DATA_PORT: u16 = 0x60;
/// PS/2 keyboard status port
const KEYBOARD_STATUS_PORT: u16 = 0x64;
/// Simple scan code to ASCII mapping for common keys
fn scancode_to_ascii(scancode: u8) -> Option<u8> {
match scancode {
0x02 => Some(b'1'),
0x03 => Some(b'2'),
0x04 => Some(b'3'),
0x05 => Some(b'4'),
0x06 => Some(b'5'),
0x07 => Some(b'6'),
0x08 => Some(b'7'),
0x09 => Some(b'8'),
0x0A => Some(b'9'),
0x0B => Some(b'0'),
0x10 => Some(b'q'),
0x11 => Some(b'w'),
0x12 => Some(b'e'),
0x13 => Some(b'r'),
0x14 => Some(b't'),
0x15 => Some(b'y'),
0x16 => Some(b'u'),
0x17 => Some(b'i'),
0x18 => Some(b'o'),
0x19 => Some(b'p'),
0x1E => Some(b'a'),
0x1F => Some(b's'),
0x20 => Some(b'd'),
0x21 => Some(b'f'),
0x22 => Some(b'g'),
0x23 => Some(b'h'),
0x24 => Some(b'j'),
0x25 => Some(b'k'),
0x26 => Some(b'l'),
0x2C => Some(b'z'),
0x2D => Some(b'x'),
0x2E => Some(b'c'),
0x2F => Some(b'v'),
0x30 => Some(b'b'),
0x31 => Some(b'n'),
0x32 => Some(b'm'),
0x39 => Some(b' '), // space
0x1C => Some(b'\n'), // enter
0x0E => Some(8), // backspace
_ => None,
}
}
/// Safe port read with error handling
unsafe fn try_inb(port: u16) -> Option<u8> {
// Add a simple delay to avoid rapid polling
for _ in 0..1000 {
core::arch::asm!("nop");
}
let result: u8;
core::arch::asm!(
"in al, dx",
out("al") result,
in("dx") port,
options(nomem, nostack, preserves_flags)
);
Some(result)
}
/// Check if keyboard has data available (safe version)
pub fn keyboard_ready() -> bool {
unsafe {
if let Some(status) = try_inb(KEYBOARD_STATUS_PORT) {
(status & 0x01) != 0
} else {
false
}
}
}
/// Read a key from keyboard with safety checks
pub fn read_key() -> Option<u8> {
if !keyboard_ready() {
return None;
}
unsafe {
if let Some(scancode) = try_inb(KEYBOARD_DATA_PORT) {
// Only handle key press (bit 7 = 0)
if scancode & 0x80 == 0 {
return scancode_to_ascii(scancode);
}
}
}
None
}
/// Get raw scancode for debugging
pub fn get_raw_scancode() -> Option<u8> {
if !keyboard_ready() {
return None;
}
unsafe {
try_inb(KEYBOARD_DATA_PORT)
}
}

208
kernel/src/main.rs Normal file
View File

@ -0,0 +1,208 @@
//! Aios Kernel - Interactive Shell
#![no_std]
#![no_main]
mod keyboard_basic;
use core::panic::PanicInfo;
// Simple VGA buffer writer
struct VgaBuffer {
chars: [[u16; 80]; 25],
}
static mut VGA_BUFFER: *mut VgaBuffer = 0xb8000 as *mut VgaBuffer;
static mut CURSOR_ROW: usize = 12;
static mut CURSOR_COL: usize = 6;
pub fn print_char(c: u8, x: usize, y: usize) {
if x >= 80 || y >= 25 {
return;
}
unsafe {
let vga = &mut *VGA_BUFFER;
vga.chars[y][x] = (0x0f << 8) | (c as u16);
}
}
pub fn print_str(s: &str, x: usize, y: usize) {
for (i, byte) in s.bytes().enumerate() {
if x + i >= 80 {
break;
}
print_char(byte, x + i, y);
}
}
pub fn clear_screen() {
for y in 0..25 {
for x in 0..80 {
print_char(b' ', x, y);
}
}
}
// Shell state
static mut INPUT_BUFFER: [u8; 64] = [0; 64];
static mut INPUT_POS: usize = 0;
fn print_prompt() {
unsafe {
print_str("aios> ", 0, CURSOR_ROW);
CURSOR_COL = 6;
}
}
fn new_line() {
unsafe {
CURSOR_ROW += 1;
CURSOR_COL = 0;
if CURSOR_ROW >= 24 {
CURSOR_ROW = 23;
}
}
}
fn execute_command() {
unsafe {
let cmd = core::str::from_utf8(&INPUT_BUFFER[..INPUT_POS]).unwrap_or("");
new_line();
match cmd {
"help" => {
print_str("Aios Commands:", 0, CURSOR_ROW);
new_line();
print_str(" help - Show this help", 0, CURSOR_ROW);
new_line();
print_str(" version - Show version", 0, CURSOR_ROW);
new_line();
print_str(" echo - Echo test", 0, CURSOR_ROW);
new_line();
print_str(" clear - Clear screen", 0, CURSOR_ROW);
new_line();
}
"version" => {
print_str("Aios 2.0 - AI Operating System", 0, CURSOR_ROW);
new_line();
print_str("Interactive Shell with Keyboard Support", 0, CURSOR_ROW);
new_line();
}
"echo" => {
print_str("Hello from Aios Interactive Shell!", 0, CURSOR_ROW);
new_line();
}
"clear" => {
clear_screen();
CURSOR_ROW = 2;
print_str("Aios - AI Operating System", 25, 0);
print_str("Interactive Shell Ready", 27, 1);
CURSOR_ROW = 3;
}
"" => {
// Empty command
}
_ => {
print_str("Unknown command: ", 0, CURSOR_ROW);
print_str(cmd, 17, CURSOR_ROW);
new_line();
print_str("Type 'help' for available commands", 0, CURSOR_ROW);
new_line();
}
}
// Clear input buffer
INPUT_BUFFER.fill(0);
INPUT_POS = 0;
new_line();
print_prompt();
}
}
fn process_char(ch: char) {
unsafe {
match ch {
'\n' => {
execute_command();
}
'\u{8}' => {
// Backspace
if INPUT_POS > 0 {
INPUT_POS -= 1;
INPUT_BUFFER[INPUT_POS] = 0;
CURSOR_COL -= 1;
print_char(b' ', CURSOR_COL, CURSOR_ROW);
}
}
' '..='~' => {
// Printable characters
if INPUT_POS < INPUT_BUFFER.len() - 1 {
INPUT_BUFFER[INPUT_POS] = ch as u8;
print_char(ch as u8, CURSOR_COL, CURSOR_ROW);
INPUT_POS += 1;
CURSOR_COL += 1;
}
}
_ => {
// Ignore other characters
}
}
}
}
// Entry point
#[no_mangle]
pub extern "C" fn _start() -> ! {
// Clear screen
clear_screen();
// Print welcome message
print_str("Aios - AI Operating System", 25, 0);
print_str("Interactive Shell Ready", 27, 1);
print_str("Type 'help' for commands", 27, 2);
// Initialize shell
unsafe {
CURSOR_ROW = 5;
}
print_prompt();
let mut last_scancode = 0u8;
loop {
if keyboard_basic::has_key() {
let scancode = keyboard_basic::read_scancode();
// Only process key press (not key release)
if scancode != last_scancode && (scancode & 0x80) == 0 {
last_scancode = scancode;
if let Some(ch) = keyboard_basic::scancode_to_char(scancode) {
process_char(ch);
}
}
}
// Small delay to avoid excessive polling
for _ in 0..1000 {
unsafe {
core::arch::asm!("nop");
}
}
}
}
// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
clear_screen();
print_str("!!! KERNEL PANIC !!!", 29, 12);
print_str("System halted.", 33, 13);
loop {
unsafe {
core::arch::asm!("hlt");
}
}
}

7
kernel/src/memory.rs Normal file
View File

@ -0,0 +1,7 @@
//! Simple memory management for AIOS v2
//! Minimal implementation for initial boot
pub fn init() {
// TODO: Implement proper memory management
// For now, just a placeholder to complete initialization sequence
}

27
kernel/src/panic.rs Normal file
View File

@ -0,0 +1,27 @@
//! Panic handler for AIOS v2
use core::panic::PanicInfo;
use crate::console;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
console::print("\n!!! KERNEL PANIC !!!\n");
if let Some(location) = info.location() {
console::print("Location: ");
console::print(location.file());
console::print(":");
// TODO: Convert line number to string
console::print("\n");
}
console::print("Message: ");
console::print("Kernel panic occurred\n");
console::print("System halted.\n");
loop {
unsafe {
core::arch::asm!("hlt");
}
}
}

209
kernel/src/shell.rs Normal file
View File

@ -0,0 +1,209 @@
//! Simple Shell for AIOS v2
use crate::{print_char, print_str, clear_screen};
/// Maximum command line length
const MAX_CMD_LEN: usize = 256;
/// Shell state
pub struct Shell {
input_buffer: [u8; MAX_CMD_LEN],
cursor: usize,
prompt: &'static str,
}
impl Shell {
/// Create a new shell instance
pub fn new() -> Self {
Shell {
input_buffer: [0; MAX_CMD_LEN],
cursor: 0,
prompt: "aios> ",
}
}
/// Print the shell prompt
pub fn print_prompt(&self) {
print_str(self.prompt, 0, get_current_row());
set_cursor_col(self.prompt.len());
}
/// Process a single character input
pub fn process_char(&mut self, c: u8) {
match c {
b'\n' => {
// Enter pressed - execute command
self.execute_command();
self.clear_input();
print_newline();
self.print_prompt();
}
8 => {
// Backspace
if self.cursor > 0 {
self.cursor -= 1;
self.input_buffer[self.cursor] = 0;
self.redraw_line();
}
}
32..=126 => {
// Printable characters
if self.cursor < MAX_CMD_LEN - 1 {
self.input_buffer[self.cursor] = c;
self.cursor += 1;
print_char(c, get_cursor_col(), get_current_row());
inc_cursor_col();
}
}
_ => {
// Ignore other characters
}
}
}
/// Execute the current command
fn execute_command(&mut self) {
let cmd = self.get_command_str();
print_newline();
match cmd {
"help" => self.cmd_help(),
"clear" => self.cmd_clear(),
"echo" => self.cmd_echo(),
"version" => self.cmd_version(),
"reboot" => self.cmd_reboot(),
"" => {
// Empty command, do nothing
}
_ => {
print_str("Unknown command: ", 0, get_current_row());
print_str(cmd, 18, get_current_row());
print_newline();
print_str("Type 'help' for available commands.", 0, get_current_row());
print_newline();
}
}
}
/// Get command as string slice
fn get_command_str(&self) -> &str {
let bytes = &self.input_buffer[..self.cursor];
core::str::from_utf8(bytes).unwrap_or("")
}
/// Clear input buffer
fn clear_input(&mut self) {
self.input_buffer.fill(0);
self.cursor = 0;
}
/// Redraw the current line
fn redraw_line(&self) {
let row = get_current_row();
let prompt_len = self.prompt.len();
// Clear the line after prompt
for i in prompt_len..80 {
print_char(b' ', i, row);
}
// Redraw the command
for (i, &c) in self.input_buffer[..self.cursor].iter().enumerate() {
print_char(c, prompt_len + i, row);
}
set_cursor_col(prompt_len + self.cursor);
}
// Command implementations
fn cmd_help(&self) {
print_str("AIOS v2 Shell Commands:", 0, get_current_row());
print_newline();
print_str(" help - Show this help message", 0, get_current_row());
print_newline();
print_str(" clear - Clear the screen", 0, get_current_row());
print_newline();
print_str(" echo - Echo test message", 0, get_current_row());
print_newline();
print_str(" version - Show AIOS version", 0, get_current_row());
print_newline();
print_str(" reboot - Reboot the system", 0, get_current_row());
print_newline();
}
fn cmd_clear(&self) {
clear_screen();
reset_cursor();
}
fn cmd_echo(&self) {
print_str("Hello from AIOS v2 Shell!", 0, get_current_row());
print_newline();
}
fn cmd_version(&self) {
print_str("AIOS v2.0 - AI Operating System", 0, get_current_row());
print_newline();
print_str("Built with Rust and powered by Claude", 0, get_current_row());
print_newline();
}
fn cmd_reboot(&self) {
print_str("Rebooting system...", 0, get_current_row());
print_newline();
// Simple triple fault to reboot
unsafe {
core::arch::asm!("int3");
}
}
}
// VGA buffer interface (temporary - will be moved to separate module)
static mut CURSOR_ROW: usize = 0;
static mut CURSOR_COL: usize = 0;
fn get_current_row() -> usize {
unsafe { CURSOR_ROW }
}
fn get_cursor_col() -> usize {
unsafe { CURSOR_COL }
}
fn set_cursor_col(col: usize) {
unsafe { CURSOR_COL = col; }
}
fn inc_cursor_col() {
unsafe {
CURSOR_COL += 1;
if CURSOR_COL >= 80 {
CURSOR_COL = 0;
CURSOR_ROW += 1;
if CURSOR_ROW >= 25 {
CURSOR_ROW = 24;
// TODO: Scroll screen
}
}
}
}
fn print_newline() {
unsafe {
CURSOR_ROW += 1;
CURSOR_COL = 0;
if CURSOR_ROW >= 25 {
CURSOR_ROW = 24;
// TODO: Scroll screen
}
}
}
fn reset_cursor() {
unsafe {
CURSOR_ROW = 0;
CURSOR_COL = 0;
}
}

109
kernel/src/shell_simple.rs Normal file
View File

@ -0,0 +1,109 @@
//! Simple Interactive Shell for AIOS v2
/// Shell buffer for command input
static mut INPUT_BUFFER: [u8; 64] = [0; 64];
static mut INPUT_POS: usize = 0;
static mut SHELL_ROW: usize = 12;
/// Initialize shell
pub fn init() {
crate::print_str("aios> ", 0, unsafe { SHELL_ROW });
}
/// Process a character input
pub fn process_char(c: u8) {
unsafe {
match c {
b'\n' => {
// Execute command
execute_command();
new_prompt();
}
8 => {
// Backspace
if INPUT_POS > 0 {
INPUT_POS -= 1;
INPUT_BUFFER[INPUT_POS] = 0;
redraw_line();
}
}
32..=126 => {
// Printable character
if INPUT_POS < INPUT_BUFFER.len() - 1 {
INPUT_BUFFER[INPUT_POS] = c;
INPUT_POS += 1;
crate::print_char(c, 6 + INPUT_POS - 1, SHELL_ROW);
}
}
_ => {}
}
}
}
/// Execute the current command
fn execute_command() {
unsafe {
let cmd = core::str::from_utf8(&INPUT_BUFFER[..INPUT_POS]).unwrap_or("");
SHELL_ROW += 1;
match cmd {
"help" => {
crate::print_str("Commands: help, version, echo, clear", 0, SHELL_ROW);
SHELL_ROW += 1;
}
"version" => {
crate::print_str("AIOS v2.0 - Interactive Shell", 0, SHELL_ROW);
SHELL_ROW += 1;
}
"echo" => {
crate::print_str("Hello from interactive shell!", 0, SHELL_ROW);
SHELL_ROW += 1;
}
"clear" => {
crate::clear_screen();
SHELL_ROW = 5;
crate::print_str("AIOS v2 - AI Operating System", 25, 2);
crate::print_str("Interactive Shell Active", 27, 3);
}
"" => {
// Empty command, just new line
}
_ => {
crate::print_str("Unknown command: ", 0, SHELL_ROW);
crate::print_str(cmd, 18, SHELL_ROW);
SHELL_ROW += 1;
}
}
// Clear input buffer
INPUT_BUFFER.fill(0);
INPUT_POS = 0;
}
}
/// Start a new command prompt
fn new_prompt() {
unsafe {
if SHELL_ROW >= 24 {
SHELL_ROW = 23;
}
SHELL_ROW += 1;
crate::print_str("aios> ", 0, SHELL_ROW);
}
}
/// Redraw current input line
fn redraw_line() {
unsafe {
// Clear line after prompt
for i in 6..80 {
crate::print_char(b' ', i, SHELL_ROW);
}
// Redraw input
for (i, &c) in INPUT_BUFFER[..INPUT_POS].iter().enumerate() {
crate::print_char(c, 6 + i, SHELL_ROW);
}
}
}

4
rust-toolchain.toml Normal file
View File

@ -0,0 +1,4 @@
[toolchain]
channel = "nightly"
targets = ["x86_64-unknown-none"]
components = ["rust-src", "llvm-tools-preview"]

42
scpt/bootimage-test.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/zsh
d=${0:a:h:h}
cd $d
echo "=== Aios Boot Image Test ==="
# Install bootimage tool
echo "Installing bootimage tool..."
cargo install bootimage --quiet
# Build boot image
echo "Building bootable image..."
cd kernel
cargo bootimage --release
if [ $? -eq 0 ]; then
echo "Build successful!"
# Find the boot image
echo "Looking for boot image..."
# Check the path shown in the build output
if [ -f "../target/x86_64-unknown-none/release/bootimage-aios-kernel.bin" ]; then
BOOT_IMAGE="../target/x86_64-unknown-none/release/bootimage-aios-kernel.bin"
else
# Search for it
BOOT_IMAGE=$(find .. -name "bootimage-*.bin" 2>/dev/null | head -1)
fi
if [ -n "$BOOT_IMAGE" ] && [ -f "$BOOT_IMAGE" ]; then
echo "Found boot image: $BOOT_IMAGE"
echo "Running in QEMU..."
qemu-system-x86_64 -drive format=raw,file="$BOOT_IMAGE"
else
echo "Boot image not found"
echo "Looking in parent directory..."
ls -la ../target/x86_64-unknown-none/release/ | grep bootimage
fi
else
echo "Build failed"
fi

View File

@ -1,44 +0,0 @@
use linked_list_allocator::LockedHeap;
use x86_64::{
structures::paging::{
mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB,
},
VirtAddr,
};
use crate::memory::{HEAP_START, HEAP_SIZE};
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
pub fn init_heap(
mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<(), MapToError<Size4KiB>> {
let page_range = {
let heap_start = VirtAddr::new(HEAP_START as u64);
let heap_end = heap_start + HEAP_SIZE - 1u64;
let heap_start_page = Page::containing_address(heap_start);
let heap_end_page = Page::containing_address(heap_end);
Page::range_inclusive(heap_start_page, heap_end_page)
};
for page in page_range {
let frame = frame_allocator
.allocate_frame()
.ok_or(MapToError::FrameAllocationFailed)?;
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
unsafe { mapper.map_to(page, frame, flags, frame_allocator) }?.flush();
}
unsafe {
ALLOCATOR.lock().init(HEAP_START as *mut u8, HEAP_SIZE);
}
Ok(())
}
#[alloc_error_handler]
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
panic!("allocation error: {:?}", layout)
}

View File

@ -1,289 +0,0 @@
use alloc::{collections::BTreeMap, string::String, vec::Vec, string::ToString, format};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FileType {
Regular,
Directory,
Executable,
}
#[derive(Debug, Clone)]
pub struct FileMetadata {
pub name: String,
pub size: usize,
pub file_type: FileType,
pub permissions: u32,
}
#[derive(Debug, Clone)]
pub struct File {
pub metadata: FileMetadata,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct Directory {
pub metadata: FileMetadata,
pub entries: BTreeMap<String, FileSystemEntry>,
}
#[derive(Debug, Clone)]
pub enum FileSystemEntry {
File(File),
Directory(Directory),
}
pub struct MemoryFileSystem {
root: Directory,
current_dir: String,
}
impl MemoryFileSystem {
pub fn new() -> Self {
let root_metadata = FileMetadata {
name: "/".to_string(),
size: 0,
file_type: FileType::Directory,
permissions: 0o755,
};
let mut root = Directory {
metadata: root_metadata,
entries: BTreeMap::new(),
};
// Create initial directories
root.entries.insert("bin".to_string(), FileSystemEntry::Directory(Directory {
metadata: FileMetadata {
name: "bin".to_string(),
size: 0,
file_type: FileType::Directory,
permissions: 0o755,
},
entries: BTreeMap::new(),
}));
root.entries.insert("home".to_string(), FileSystemEntry::Directory(Directory {
metadata: FileMetadata {
name: "home".to_string(),
size: 0,
file_type: FileType::Directory,
permissions: 0o755,
},
entries: BTreeMap::new(),
}));
root.entries.insert("tmp".to_string(), FileSystemEntry::Directory(Directory {
metadata: FileMetadata {
name: "tmp".to_string(),
size: 0,
file_type: FileType::Directory,
permissions: 0o777,
},
entries: BTreeMap::new(),
}));
Self {
root,
current_dir: "/".to_string(),
}
}
pub fn create_file(&mut self, path: &str, content: &[u8]) -> Result<(), &'static str> {
let (dir_path, filename) = self.split_path(path);
let directory = self.get_directory_mut(&dir_path)?;
let file = File {
metadata: FileMetadata {
name: filename.clone(),
size: content.len(),
file_type: FileType::Regular,
permissions: 0o644,
},
data: content.to_vec(),
};
directory.entries.insert(filename, FileSystemEntry::File(file));
Ok(())
}
pub fn create_executable(&mut self, path: &str, content: &[u8]) -> Result<(), &'static str> {
let (dir_path, filename) = self.split_path(path);
let directory = self.get_directory_mut(&dir_path)?;
let file = File {
metadata: FileMetadata {
name: filename.clone(),
size: content.len(),
file_type: FileType::Executable,
permissions: 0o755,
},
data: content.to_vec(),
};
directory.entries.insert(filename, FileSystemEntry::File(file));
Ok(())
}
pub fn read_file(&self, path: &str) -> Result<&Vec<u8>, &'static str> {
let (dir_path, filename) = self.split_path(path);
let directory = self.get_directory(&dir_path)?;
match directory.entries.get(&filename) {
Some(FileSystemEntry::File(file)) => Ok(&file.data),
Some(FileSystemEntry::Directory(_)) => Err("Path is a directory"),
None => Err("File not found"),
}
}
pub fn create_directory(&mut self, path: &str) -> Result<(), &'static str> {
let (parent_path, dirname) = self.split_path(path);
let parent = self.get_directory_mut(&parent_path)?;
let directory = Directory {
metadata: FileMetadata {
name: dirname.clone(),
size: 0,
file_type: FileType::Directory,
permissions: 0o755,
},
entries: BTreeMap::new(),
};
parent.entries.insert(dirname, FileSystemEntry::Directory(directory));
Ok(())
}
pub fn list_directory(&self, path: &str) -> Result<Vec<&FileMetadata>, &'static str> {
let directory = self.get_directory(path)?;
let mut entries = Vec::new();
for entry in directory.entries.values() {
match entry {
FileSystemEntry::File(file) => entries.push(&file.metadata),
FileSystemEntry::Directory(dir) => entries.push(&dir.metadata),
}
}
Ok(entries)
}
pub fn remove_file(&mut self, path: &str) -> Result<(), &'static str> {
let (dir_path, filename) = self.split_path(path);
let directory = self.get_directory_mut(&dir_path)?;
match directory.entries.remove(&filename) {
Some(FileSystemEntry::File(_)) => Ok(()),
Some(FileSystemEntry::Directory(_)) => {
directory.entries.insert(filename.clone(),
FileSystemEntry::Directory(Directory {
metadata: FileMetadata {
name: filename.clone(),
size: 0,
file_type: FileType::Directory,
permissions: 0o755,
},
entries: BTreeMap::new(),
}));
Err("Cannot remove directory with rm")
},
None => Err("File not found"),
}
}
pub fn change_directory(&mut self, path: &str) -> Result<(), &'static str> {
let absolute_path = if path.starts_with('/') {
path.to_string()
} else {
if self.current_dir == "/" {
format!("/{}", path)
} else {
format!("{}/{}", self.current_dir, path)
}
};
// Check if directory exists
self.get_directory(&absolute_path)?;
self.current_dir = absolute_path;
Ok(())
}
pub fn get_current_directory(&self) -> &str {
&self.current_dir
}
fn split_path(&self, path: &str) -> (String, String) {
if path == "/" {
return ("/".to_string(), "".to_string());
}
let full_path = if path.starts_with('/') {
path.to_string()
} else {
if self.current_dir == "/" {
format!("/{}", path)
} else {
format!("{}/{}", self.current_dir, path)
}
};
if let Some(last_slash) = full_path.rfind('/') {
if last_slash == 0 {
("/".to_string(), full_path[1..].to_string())
} else {
(full_path[..last_slash].to_string(), full_path[last_slash + 1..].to_string())
}
} else {
(self.current_dir.clone(), path.to_string())
}
}
fn get_directory(&self, path: &str) -> Result<&Directory, &'static str> {
if path == "/" {
return Ok(&self.root);
}
let mut current = &self.root;
let parts: Vec<&str> = path.trim_start_matches('/').split('/').filter(|s| !s.is_empty()).collect();
for part in parts {
match current.entries.get(part) {
Some(FileSystemEntry::Directory(dir)) => current = dir,
Some(FileSystemEntry::File(_)) => return Err("Path component is a file"),
None => return Err("Directory not found"),
}
}
Ok(current)
}
fn get_directory_mut(&mut self, path: &str) -> Result<&mut Directory, &'static str> {
if path == "/" {
return Ok(&mut self.root);
}
let mut current = &mut self.root;
let parts: Vec<&str> = path.trim_start_matches('/').split('/').filter(|s| !s.is_empty()).collect();
for part in parts {
match current.entries.get_mut(part) {
Some(FileSystemEntry::Directory(dir)) => current = dir,
Some(FileSystemEntry::File(_)) => return Err("Path component is a file"),
None => return Err("Directory not found"),
}
}
Ok(current)
}
}
impl fmt::Display for FileType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FileType::Regular => write!(f, "-"),
FileType::Directory => write!(f, "d"),
FileType::Executable => write!(f, "x"),
}
}
}

View File

@ -1,88 +0,0 @@
use crate::{print, println};
use lazy_static::lazy_static;
use pic8259::ChainedPics;
use spin;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
pub static PICS: spin::Mutex<ChainedPics> =
spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum InterruptIndex {
Timer = PIC_1_OFFSET,
Keyboard,
}
impl InterruptIndex {
fn as_u8(self) -> u8 {
self as u8
}
fn as_usize(self) -> usize {
usize::from(self.as_u8())
}
}
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt[InterruptIndex::Timer.as_usize()]
.set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.as_usize()]
.set_handler_fn(keyboard_interrupt_handler);
idt
};
}
pub fn init_idt() {
IDT.load();
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
}
}
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Mutex;
use x86_64::instructions::port::Port;
lazy_static! {
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> =
Mutex::new(Keyboard::new(ScancodeSet1::new(), layouts::Us104Key,
HandleControl::Ignore)
);
}
let mut keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => {
crate::shell::handle_keyboard_input(character);
}
DecodedKey::RawKey(key) => print!("{:?}", key),
}
}
}
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
}
}

View File

@ -1,100 +0,0 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![feature(alloc_error_handler)]
#![feature(abi_x86_interrupt)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
extern crate alloc;
use core::panic::PanicInfo;
use bootloader::{BootInfo, entry_point};
use linked_list_allocator::LockedHeap;
// Global allocatorは削除allocator.rsで定義済み
mod vga_buffer;
mod serial;
mod memory;
mod allocator;
mod interrupts;
mod shell;
mod systemd;
mod filesystem;
mod process;
mod package;
entry_point!(kernel_main);
fn kernel_main(boot_info: &'static BootInfo) -> ! {
use memory::BootInfoFrameAllocator;
use x86_64::VirtAddr;
println!("AIOS Kernel Starting...");
// メモリ管理の初期化bootloader 0.9系では物理メモリオフセットが固定)
let phys_mem_offset = VirtAddr::new(0);
let mut mapper = unsafe { memory::init(phys_mem_offset) };
let mut frame_allocator = unsafe {
BootInfoFrameAllocator::init(&boot_info.memory_map)
};
// ヒープの初期化
allocator::init_heap(&mut mapper, &mut frame_allocator)
.expect("heap initialization failed");
// 割り込み処理の初期化
interrupts::init_idt();
println!("AIOS initialized successfully!");
// シェルの初期化
shell::init();
#[cfg(test)]
test_main();
loop {
x86_64::instructions::hlt();
}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("{}", info);
serial_println!("PANIC: {}", info);
loop {}
}
#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
serial_println!("Running {} tests", tests.len());
for test in tests {
test();
}
exit_qemu(QemuExitCode::Success);
}
#[cfg(test)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Success = 0x10,
Failed = 0x11,
}
#[cfg(test)]
pub fn exit_qemu(exit_code: QemuExitCode) {
use x86_64::instructions::port::Port;
unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
}
#[test_case]
fn trivial_assertion() {
assert_eq!(1, 1);
}

View File

@ -1,76 +0,0 @@
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
use x86_64::{
structures::paging::{
mapper::MapToError, FrameAllocator, Mapper, Page, PageTable, PhysFrame, Size4KiB,
},
PhysAddr, VirtAddr,
};
pub struct BootInfoFrameAllocator {
memory_map: &'static MemoryMap,
next: usize,
}
impl BootInfoFrameAllocator {
pub unsafe fn init(memory_map: &'static MemoryMap) -> Self {
BootInfoFrameAllocator {
memory_map,
next: 0,
}
}
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
let regions = self.memory_map.iter();
let usable_regions = regions
.filter(|r| r.region_type == MemoryRegionType::Usable);
let addr_ranges = usable_regions
.map(|r| r.range.start_addr()..r.range.end_addr());
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
}
}
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame> {
let frame = self.usable_frames().nth(self.next);
self.next += 1;
frame
}
}
pub fn create_example_mapping(
page: Page,
mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<(), MapToError<Size4KiB>> {
use x86_64::structures::paging::PageTableFlags as Flags;
let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
let flags = Flags::PRESENT | Flags::WRITABLE;
let map_to_result = unsafe { mapper.map_to(page, frame, flags, frame_allocator) };
map_to_result?.flush();
Ok(())
}
pub unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {
use x86_64::registers::control::Cr3;
let (level_4_table_frame, _) = Cr3::read();
let phys = level_4_table_frame.start_address();
let virt = physical_memory_offset + phys.as_u64();
let page_table_ptr: *mut PageTable = virt.as_mut_ptr();
&mut *page_table_ptr
}
pub const HEAP_START: usize = 0x_4444_4444_0000;
pub const HEAP_SIZE: usize = 16 * 1024; // 16 KiB - より小さなサイズで安全に
pub unsafe fn init(physical_memory_offset: VirtAddr) -> impl Mapper<Size4KiB> {
let level_4_table = active_level_4_table(physical_memory_offset);
x86_64::structures::paging::OffsetPageTable::new(level_4_table, physical_memory_offset)
}

View File

@ -1,413 +0,0 @@
use alloc::{collections::BTreeMap, string::String, vec::Vec, string::ToString, format, vec};
use core::fmt;
use crate::filesystem::MemoryFileSystem;
use crate::systemd::SystemManager;
#[derive(Debug, Clone)]
pub struct PackageMetadata {
pub name: String,
pub version: String,
pub description: String,
pub arch: String,
pub size: usize,
pub dependencies: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct PackageFile {
pub path: String,
pub content: Vec<u8>,
pub executable: bool,
}
#[derive(Debug, Clone)]
pub struct ServiceConfig {
pub name: String,
pub command: String,
pub container: bool,
pub auto_start: bool,
}
#[derive(Debug, Clone)]
pub struct Package {
pub metadata: PackageMetadata,
pub files: Vec<PackageFile>,
pub services: Vec<ServiceConfig>,
}
#[derive(Debug)]
pub struct Repository {
pub name: String,
pub url: String,
pub packages: BTreeMap<String, PackageMetadata>,
}
pub struct PackageManager {
installed_packages: BTreeMap<String, Package>,
repositories: Vec<Repository>,
package_db_path: String,
}
impl PackageManager {
pub fn new() -> Self {
let mut pm = Self {
installed_packages: BTreeMap::new(),
repositories: Vec::new(),
package_db_path: "/var/pkg".to_string(),
};
// Add default repository
pm.add_repository("aios-main".to_string(), "https://packages.aios.dev".to_string());
// Create some built-in packages
pm.create_builtin_packages();
pm
}
fn create_builtin_packages(&mut self) {
// Claude Code package
let claude_package = Package {
metadata: PackageMetadata {
name: "claude-code".to_string(),
version: "1.0.0".to_string(),
description: "AI Development Environment".to_string(),
arch: "x86_64".to_string(),
size: 1024 * 1024, // 1MB
dependencies: vec!["nodejs".to_string(), "python".to_string()],
},
files: vec![
PackageFile {
path: "/bin/claude-code".to_string(),
content: b"AIOS\x00\x10\x00\x00claude-code-ai-assistant".to_vec(),
executable: true,
},
PackageFile {
path: "/lib/claude/runtime.so".to_string(),
content: b"Claude Runtime Library".to_vec(),
executable: false,
},
],
services: vec![
ServiceConfig {
name: "claude-code".to_string(),
command: "/bin/claude-code --daemon".to_string(),
container: true,
auto_start: false,
},
],
};
// Node.js package
let nodejs_package = Package {
metadata: PackageMetadata {
name: "nodejs".to_string(),
version: "18.0.0".to_string(),
description: "JavaScript Runtime".to_string(),
arch: "x86_64".to_string(),
size: 2 * 1024 * 1024, // 2MB
dependencies: vec![],
},
files: vec![
PackageFile {
path: "/bin/node".to_string(),
content: b"AIOS\x00\x10\x00\x00nodejs-runtime".to_vec(),
executable: true,
},
PackageFile {
path: "/bin/npm".to_string(),
content: b"AIOS\x00\x10\x00\x00npm-package-manager".to_vec(),
executable: true,
},
],
services: vec![],
};
// Python package
let python_package = Package {
metadata: PackageMetadata {
name: "python".to_string(),
version: "3.9.0".to_string(),
description: "Python Interpreter".to_string(),
arch: "x86_64".to_string(),
size: 3 * 1024 * 1024, // 3MB
dependencies: vec![],
},
files: vec![
PackageFile {
path: "/bin/python".to_string(),
content: b"AIOS\x00\x10\x00\x00python-interpreter".to_vec(),
executable: true,
},
PackageFile {
path: "/bin/pip".to_string(),
content: b"AIOS\x00\x10\x00\x00pip-package-installer".to_vec(),
executable: true,
},
],
services: vec![],
};
// Store as available packages (not installed)
self.repositories[0].packages.insert("claude-code".to_string(), claude_package.metadata.clone());
self.repositories[0].packages.insert("nodejs".to_string(), nodejs_package.metadata.clone());
self.repositories[0].packages.insert("python".to_string(), python_package.metadata.clone());
}
pub fn add_repository(&mut self, name: String, url: String) {
let repo = Repository {
name,
url,
packages: BTreeMap::new(),
};
self.repositories.push(repo);
}
pub fn install_package(&mut self, package_name: &str, filesystem: &mut MemoryFileSystem, system_manager: &mut SystemManager) -> Result<(), &'static str> {
// Check if already installed
if self.installed_packages.contains_key(package_name) {
return Err("Package already installed");
}
// Find package in repositories
let package = self.find_package(package_name)?;
// Check dependencies
for dep in &package.metadata.dependencies {
if !self.is_package_installed(dep) {
crate::println!("Installing dependency: {}", dep);
self.install_package(dep, filesystem, system_manager)?;
}
}
// Install files
for file in &package.files {
if file.executable {
filesystem.create_executable(&file.path, &file.content)?;
} else {
filesystem.create_file(&file.path, &file.content)?;
}
}
// Register services
for service in &package.services {
use crate::systemd::{ServiceUnit, ServiceState};
let service_unit = ServiceUnit {
name: service.name.clone(),
description: format!("Service for {}", package.metadata.name),
exec_start: service.command.clone(),
working_directory: Some("/".to_string()),
environment: vec![],
container: if service.container { Some("default".to_string()) } else { None },
state: ServiceState::Inactive,
};
system_manager.create_service(service_unit)?;
}
// Mark as installed
self.installed_packages.insert(package_name.to_string(), package);
crate::println!("Successfully installed package: {}", package_name);
Ok(())
}
pub fn remove_package(&mut self, package_name: &str, filesystem: &mut MemoryFileSystem) -> Result<(), &'static str> {
let package = self.installed_packages.get(package_name)
.ok_or("Package not installed")?
.clone();
// Remove files
for file in &package.files {
let _ = filesystem.remove_file(&file.path); // Ignore errors for now
}
// Remove from installed list
self.installed_packages.remove(package_name);
crate::println!("Successfully removed package: {}", package_name);
Ok(())
}
pub fn list_installed(&self) {
crate::println!("Installed packages:");
crate::println!("{:<20} {:<10} {:<30}", "NAME", "VERSION", "DESCRIPTION");
crate::println!("{}", "-".repeat(60));
for package in self.installed_packages.values() {
crate::println!("{:<20} {:<10} {:<30}",
package.metadata.name,
package.metadata.version,
package.metadata.description
);
}
}
pub fn list_available(&self) {
crate::println!("Available packages:");
crate::println!("{:<20} {:<10} {:<30}", "NAME", "VERSION", "DESCRIPTION");
crate::println!("{}", "-".repeat(60));
for repo in &self.repositories {
for package in repo.packages.values() {
let status = if self.installed_packages.contains_key(&package.name) {
"[installed]"
} else {
""
};
crate::println!("{:<20} {:<10} {:<20} {}",
package.name,
package.version,
package.description,
status
);
}
}
}
pub fn search_package(&self, query: &str) -> Vec<&PackageMetadata> {
let mut results = Vec::new();
for repo in &self.repositories {
for package in repo.packages.values() {
if package.name.contains(query) || package.description.contains(query) {
results.push(package);
}
}
}
results
}
pub fn show_package_info(&self, package_name: &str) -> Result<(), &'static str> {
if let Some(package) = self.installed_packages.get(package_name) {
crate::println!("Package: {}", package.metadata.name);
crate::println!("Version: {}", package.metadata.version);
crate::println!("Description: {}", package.metadata.description);
crate::println!("Architecture: {}", package.metadata.arch);
crate::println!("Size: {} bytes", package.metadata.size);
crate::println!("Status: Installed");
if !package.metadata.dependencies.is_empty() {
crate::println!("Dependencies:");
for dep in &package.metadata.dependencies {
crate::println!(" - {}", dep);
}
}
if !package.files.is_empty() {
crate::println!("Files:");
for file in &package.files {
crate::println!(" {}", file.path);
}
}
Ok(())
} else {
// Check in repositories
for repo in &self.repositories {
if let Some(package) = repo.packages.get(package_name) {
crate::println!("Package: {}", package.name);
crate::println!("Version: {}", package.version);
crate::println!("Description: {}", package.description);
crate::println!("Architecture: {}", package.arch);
crate::println!("Size: {} bytes", package.size);
crate::println!("Status: Available");
if !package.dependencies.is_empty() {
crate::println!("Dependencies:");
for dep in &package.dependencies {
crate::println!(" - {}", dep);
}
}
return Ok(());
}
}
Err("Package not found")
}
}
fn find_package(&self, package_name: &str) -> Result<Package, &'static str> {
// For now, return built-in packages
match package_name {
"claude-code" => {
Ok(Package {
metadata: PackageMetadata {
name: "claude-code".to_string(),
version: "1.0.0".to_string(),
description: "AI Development Environment".to_string(),
arch: "x86_64".to_string(),
size: 1024 * 1024,
dependencies: vec!["nodejs".to_string()],
},
files: vec![
PackageFile {
path: "/bin/claude-code".to_string(),
content: b"AIOS\x00\x10\x00\x00claude-code-ai-assistant".to_vec(),
executable: true,
},
],
services: vec![
ServiceConfig {
name: "claude-code".to_string(),
command: "/bin/claude-code --daemon".to_string(),
container: true,
auto_start: false,
},
],
})
}
"nodejs" => {
Ok(Package {
metadata: PackageMetadata {
name: "nodejs".to_string(),
version: "18.0.0".to_string(),
description: "JavaScript Runtime".to_string(),
arch: "x86_64".to_string(),
size: 2 * 1024 * 1024,
dependencies: vec![],
},
files: vec![
PackageFile {
path: "/bin/node".to_string(),
content: b"AIOS\x00\x10\x00\x00nodejs-runtime".to_vec(),
executable: true,
},
],
services: vec![],
})
}
"python" => {
Ok(Package {
metadata: PackageMetadata {
name: "python".to_string(),
version: "3.9.0".to_string(),
description: "Python Interpreter".to_string(),
arch: "x86_64".to_string(),
size: 3 * 1024 * 1024,
dependencies: vec![],
},
files: vec![
PackageFile {
path: "/bin/python".to_string(),
content: b"AIOS\x00\x10\x00\x00python-interpreter".to_vec(),
executable: true,
},
],
services: vec![],
})
}
_ => Err("Package not found")
}
}
fn is_package_installed(&self, package_name: &str) -> bool {
self.installed_packages.contains_key(package_name)
}
pub fn update_repositories(&mut self) -> Result<(), &'static str> {
crate::println!("Updating package repositories...");
// In a real implementation, this would fetch from remote repositories
crate::println!("Repository update completed");
Ok(())
}
}

View File

@ -1,203 +0,0 @@
use alloc::{collections::BTreeMap, string::String, vec::Vec, string::ToString, format};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ProcessState {
Ready,
Running,
Waiting,
Terminated,
}
#[derive(Debug, Clone, Copy)]
pub struct ProcessId(pub usize);
#[derive(Debug)]
pub struct Process {
pub pid: ProcessId,
pub name: String,
pub state: ProcessState,
pub entry_point: usize,
pub stack_pointer: usize,
pub memory: Vec<u8>,
pub exit_code: Option<i32>,
}
pub struct ProcessManager {
processes: BTreeMap<usize, Process>,
next_pid: usize,
current_process: Option<usize>,
}
impl ProcessManager {
pub fn new() -> Self {
Self {
processes: BTreeMap::new(),
next_pid: 1,
current_process: None,
}
}
pub fn create_process(&mut self, name: String, program: &[u8]) -> Result<ProcessId, &'static str> {
let pid = ProcessId(self.next_pid);
self.next_pid += 1;
// Simple ELF-like parsing (simplified for demo)
let entry_point = self.parse_program(program)?;
let process = Process {
pid,
name,
state: ProcessState::Ready,
entry_point,
stack_pointer: 0x8000, // Simple stack setup
memory: program.to_vec(),
exit_code: None,
};
self.processes.insert(pid.0, process);
Ok(pid)
}
pub fn start_process(&mut self, pid: ProcessId) -> Result<(), &'static str> {
if let Some(process) = self.processes.get_mut(&pid.0) {
if process.state == ProcessState::Ready {
process.state = ProcessState::Running;
self.current_process = Some(pid.0);
crate::println!("Starting process: {} (PID: {})", process.name, pid.0);
// In a real OS, this would switch to the process context
// For now, we'll simulate execution
self.simulate_execution(pid)?;
Ok(())
} else {
Err("Process not in ready state")
}
} else {
Err("Process not found")
}
}
pub fn terminate_process(&mut self, pid: ProcessId, exit_code: i32) -> Result<(), &'static str> {
if let Some(process) = self.processes.get_mut(&pid.0) {
process.state = ProcessState::Terminated;
process.exit_code = Some(exit_code);
if self.current_process == Some(pid.0) {
self.current_process = None;
}
crate::println!("Process {} terminated with exit code {}", process.name, exit_code);
Ok(())
} else {
Err("Process not found")
}
}
pub fn list_processes(&self) -> Vec<&Process> {
self.processes.values().collect()
}
pub fn get_process(&self, pid: ProcessId) -> Option<&Process> {
self.processes.get(&pid.0)
}
pub fn kill_process(&mut self, pid: ProcessId) -> Result<(), &'static str> {
self.terminate_process(pid, -1)
}
// Simple program parser - in reality this would be a full ELF parser
fn parse_program(&self, program: &[u8]) -> Result<usize, &'static str> {
if program.len() < 4 {
return Err("Invalid program format");
}
// Check for simple executable magic
if &program[0..4] == b"AIOS" {
// AIOS native executable format
if program.len() < 8 {
return Err("Invalid AIOS executable");
}
// Entry point stored at offset 4-8
let entry_point = u32::from_le_bytes([program[4], program[5], program[6], program[7]]) as usize;
Ok(entry_point)
} else if &program[0..4] == [0x7f, b'E', b'L', b'F'] {
// Basic ELF detection
crate::println!("ELF executable detected (simplified parsing)");
Ok(0x1000) // Default entry point for ELF
} else {
// Treat as raw machine code
Ok(0x0)
}
}
// Simulate process execution (in a real OS this would involve context switching)
fn simulate_execution(&mut self, pid: ProcessId) -> Result<(), &'static str> {
if let Some(process) = self.processes.get(&pid.0) {
crate::println!("Executing process: {}", process.name);
// Check if it's a built-in command or script
if process.name == "hello" {
crate::println!("Hello from AIOS process!");
self.terminate_process(pid, 0)?;
} else if process.name == "claude-code" {
crate::println!("Claude Code AI Assistant starting...");
crate::println!("AI development environment initialized");
crate::println!("Type 'ai help' for available commands");
self.terminate_process(pid, 0)?;
} else if process.name.starts_with("ai-") {
crate::println!("AI service: {}", process.name);
crate::println!("Connecting to Claude API...");
crate::println!("AI service ready");
self.terminate_process(pid, 0)?;
} else {
// Unknown program
crate::println!("Unknown program format, terminating");
self.terminate_process(pid, 1)?;
}
Ok(())
} else {
Err("Process not found")
}
}
pub fn execute_command(&mut self, command: &str, args: &[&str]) -> Result<ProcessId, &'static str> {
// Create a simple program based on command
let program_name = if command == "claude-code" {
"claude-code".to_string()
} else if command.starts_with("ai") {
format!("ai-{}", args.join("-"))
} else {
command.to_string()
};
// Create a simple executable
let mut program = Vec::new();
program.extend_from_slice(b"AIOS"); // Magic
program.extend_from_slice(&(0x1000u32).to_le_bytes()); // Entry point
program.extend_from_slice(command.as_bytes()); // Command data
let pid = self.create_process(program_name, &program)?;
self.start_process(pid)?;
Ok(pid)
}
}
impl fmt::Display for ProcessState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ProcessState::Ready => write!(f, "Ready"),
ProcessState::Running => write!(f, "Running"),
ProcessState::Waiting => write!(f, "Waiting"),
ProcessState::Terminated => write!(f, "Terminated"),
}
}
}
impl fmt::Display for ProcessId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}

View File

@ -1,32 +0,0 @@
use uart_16550::SerialPort;
use spin::Mutex;
use lazy_static::lazy_static;
lazy_static! {
pub static ref SERIAL1: Mutex<SerialPort> = {
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
serial_port.init();
Mutex::new(serial_port)
};
}
#[doc(hidden)]
pub fn _print(args: ::core::fmt::Arguments) {
use core::fmt::Write;
SERIAL1.lock().write_fmt(args).expect("Printing to serial failed");
}
#[macro_export]
macro_rules! serial_print {
($($arg:tt)*) => {
$crate::serial::_print(format_args!($($arg)*));
};
}
#[macro_export]
macro_rules! serial_println {
() => ($crate::serial_print!("\n"));
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(
concat!($fmt, "\n"), $($arg)*));
}

View File

@ -1,494 +0,0 @@
use alloc::{string::String, vec::Vec, vec, string::ToString};
use crate::{print, println};
use crate::systemd::SystemManager;
use crate::filesystem::MemoryFileSystem;
use crate::process::ProcessManager;
use crate::package::PackageManager;
use lazy_static::lazy_static;
use spin::Mutex;
lazy_static! {
static ref SHELL: Mutex<Shell> = Mutex::new(Shell::new());
}
pub struct Shell {
input_buffer: String,
history: Vec<String>,
prompt: &'static str,
system_manager: SystemManager,
filesystem: MemoryFileSystem,
process_manager: ProcessManager,
package_manager: PackageManager,
}
impl Shell {
fn new() -> Self {
// 一時的にメモリを使う部分を最小限に
let filesystem = MemoryFileSystem::new();
let process_manager = ProcessManager::new();
let system_manager = SystemManager::new();
let package_manager = PackageManager::new();
Self {
input_buffer: String::new(),
history: Vec::new(),
prompt: "aios$ ",
system_manager,
filesystem,
process_manager,
package_manager,
}
}
fn execute_command(&mut self, command: &str) {
let parts: Vec<&str> = command.trim().split_whitespace().collect();
if parts.is_empty() {
return;
}
match parts[0] {
"help" => self.show_help(),
"clear" => self.clear_screen(),
"echo" => {
for arg in &parts[1..] {
print!("{} ", arg);
}
println!();
}
"history" => self.show_history(),
"systemctl" => {
if let Err(e) = self.system_manager.systemctl(&parts[1..]) {
println!("systemctl: {}", e);
}
}
"nspawn" => {
if let Err(e) = self.system_manager.nspawn(&parts[1..]) {
println!("systemd-nspawn: {}", e);
}
}
"claude" => self.execute_claude_command(&parts[1..]),
"ai" => self.execute_ai_command(&parts[1..]),
// File system commands
"ls" => self.list_directory(&parts[1..]),
"cd" => self.change_directory(&parts[1..]),
"pwd" => println!("{}", self.filesystem.get_current_directory()),
"mkdir" => self.make_directory(&parts[1..]),
"cat" => self.cat_file(&parts[1..]),
"touch" => self.create_file(&parts[1..]),
"rm" => self.remove_file(&parts[1..]),
// Process commands
"ps" => self.list_processes(),
"kill" => self.kill_process(&parts[1..]),
"exec" => self.execute_program(&parts[1..]),
// Package manager commands
"pkg" => self.handle_package_command(&parts[1..]),
"exit" => {
println!("Goodbye!");
// TODO: 適切なシャットダウン処理
}
_ => {
// Try to execute as a program
if let Err(e) = self.process_manager.execute_command(parts[0], &parts[1..]) {
println!("Unknown command: {}. Type 'help' for available commands.", parts[0]);
println!("Error: {}", e);
}
}
}
}
fn execute_claude_command(&mut self, args: &[&str]) {
if args.is_empty() {
println!("Claude Code integration - AI-powered development environment");
println!("Usage: claude <subcommand>");
println!(" start - Start Claude Code server");
println!(" stop - Stop Claude Code server");
println!(" status - Show Claude Code status");
return;
}
match args[0] {
"start" => {
println!("Starting Claude Code in container...");
// TODO: Claude Codeをコンテナ内で起動
if let Err(e) = self.start_claude_service() {
println!("Failed to start Claude Code: {}", e);
}
}
"stop" => {
println!("Stopping Claude Code...");
if let Err(e) = self.system_manager.stop_service("claude-code") {
println!("Failed to stop Claude Code: {}", e);
}
}
"status" => {
if let Err(e) = self.system_manager.systemctl(&["status", "claude-code"]) {
println!("Claude Code status: {}", e);
}
}
_ => println!("Unknown claude subcommand: {}", args[0]),
}
}
fn execute_ai_command(&mut self, args: &[&str]) {
if args.is_empty() {
println!("AI assistant integration");
println!("Usage: ai <query>");
return;
}
let query = args.join(" ");
println!("AI Query: {}", query);
println!("Response: This would integrate with Claude API for AI assistance");
// TODO: 実際のAI統合
}
fn start_claude_service(&mut self) -> Result<(), &'static str> {
use crate::systemd::{ServiceUnit, ServiceState};
// Claude Codeサービスを作成
let service = ServiceUnit {
name: "claude-code".to_string(),
description: "Claude Code AI Development Environment".to_string(),
exec_start: "/usr/local/bin/claude-code".to_string(),
working_directory: Some("/workspace".to_string()),
environment: vec![
("ANTHROPIC_API_KEY".to_string(), "your-api-key".to_string()),
("PORT".to_string(), "3000".to_string()),
],
container: Some("claude-container".to_string()),
state: ServiceState::Inactive,
};
// コンテナを作成
self.system_manager.create_container(
"claude-container".to_string(),
"/containers/claude".to_string(),
)?;
// サービスを登録・起動
self.system_manager.create_service(service)?;
self.system_manager.start_service("claude-code")
}
fn show_help(&self) {
println!("AIOS Shell - Available Commands:");
println!(" help - Show this help message");
println!(" clear - Clear the screen");
println!(" echo <text> - Print text to output");
println!(" history - Show command history");
println!("");
println!("File System:");
println!(" ls [path] - List directory contents");
println!(" cd <path> - Change directory");
println!(" pwd - Print working directory");
println!(" mkdir <dir> - Create directory");
println!(" cat <file> - Display file contents");
println!(" touch <file>- Create empty file");
println!(" rm <file> - Remove file");
println!("");
println!("Process Management:");
println!(" ps - List running processes");
println!(" exec <prog> - Execute program");
println!(" kill <pid> - Kill process by PID");
println!("");
println!("Package Management:");
println!(" pkg install <name> - Install package");
println!(" pkg remove <name> - Remove package");
println!(" pkg list - List installed packages");
println!(" pkg search <query> - Search packages");
println!(" pkg info <name> - Show package info");
println!(" pkg update - Update repositories");
println!("");
println!("System:");
println!(" systemctl - Control system services");
println!(" claude - Claude Code integration");
println!(" ai <query> - AI assistant");
println!(" exit - Exit shell");
}
// File system command implementations
fn list_directory(&mut self, args: &[&str]) {
let path = if args.is_empty() {
self.filesystem.get_current_directory()
} else {
args[0]
};
match self.filesystem.list_directory(path) {
Ok(entries) => {
for entry in entries {
println!("{} {:>8} {}", entry.file_type, entry.size, entry.name);
}
}
Err(e) => println!("ls: {}", e),
}
}
fn change_directory(&mut self, args: &[&str]) {
if args.is_empty() {
if let Err(e) = self.filesystem.change_directory("/") {
println!("cd: {}", e);
}
} else {
if let Err(e) = self.filesystem.change_directory(args[0]) {
println!("cd: {}", e);
}
}
}
fn make_directory(&mut self, args: &[&str]) {
if args.is_empty() {
println!("mkdir: missing directory name");
return;
}
if let Err(e) = self.filesystem.create_directory(args[0]) {
println!("mkdir: {}", e);
}
}
fn cat_file(&mut self, args: &[&str]) {
if args.is_empty() {
println!("cat: missing file name");
return;
}
match self.filesystem.read_file(args[0]) {
Ok(data) => {
if let Ok(text) = core::str::from_utf8(data) {
print!("{}", text);
} else {
println!("cat: binary file");
}
}
Err(e) => println!("cat: {}", e),
}
}
fn create_file(&mut self, args: &[&str]) {
if args.is_empty() {
println!("touch: missing file name");
return;
}
if let Err(e) = self.filesystem.create_file(args[0], b"") {
println!("touch: {}", e);
}
}
fn remove_file(&mut self, args: &[&str]) {
if args.is_empty() {
println!("rm: missing file name");
return;
}
if let Err(e) = self.filesystem.remove_file(args[0]) {
println!("rm: {}", e);
}
}
// Process command implementations
fn list_processes(&mut self) {
let processes = self.process_manager.list_processes();
println!("PID NAME STATE");
for process in processes {
println!("{:<6} {:<14} {}", process.pid, process.name, process.state);
}
}
fn kill_process(&mut self, args: &[&str]) {
if args.is_empty() {
println!("kill: missing process ID");
return;
}
if let Ok(pid) = args[0].parse::<usize>() {
if let Err(e) = self.process_manager.kill_process(crate::process::ProcessId(pid)) {
println!("kill: {}", e);
}
} else {
println!("kill: invalid process ID");
}
}
fn execute_program(&mut self, args: &[&str]) {
if args.is_empty() {
println!("exec: missing program name");
return;
}
if let Err(e) = self.process_manager.execute_command(args[0], &args[1..]) {
println!("exec: {}", e);
}
}
// Package manager command implementations
fn handle_package_command(&mut self, args: &[&str]) {
if args.is_empty() {
println!("pkg: missing command");
println!("Usage: pkg <command> [args...]");
println!("Commands: install, remove, list, search, info, update");
return;
}
match args[0] {
"install" => {
if args.len() < 2 {
println!("pkg install: missing package name");
return;
}
self.install_package(args[1]);
}
"remove" => {
if args.len() < 2 {
println!("pkg remove: missing package name");
return;
}
self.remove_package(args[1]);
}
"list" => {
if args.len() > 1 && args[1] == "available" {
self.package_manager.list_available();
} else {
self.package_manager.list_installed();
}
}
"search" => {
if args.len() < 2 {
println!("pkg search: missing search query");
return;
}
self.search_packages(args[1]);
}
"info" => {
if args.len() < 2 {
println!("pkg info: missing package name");
return;
}
if let Err(e) = self.package_manager.show_package_info(args[1]) {
println!("pkg info: {}", e);
}
}
"update" => {
if let Err(e) = self.package_manager.update_repositories() {
println!("pkg update: {}", e);
}
}
_ => {
println!("pkg: unknown command '{}'", args[0]);
println!("Available commands: install, remove, list, search, info, update");
}
}
}
fn install_package(&mut self, package_name: &str) {
println!("Installing package: {}", package_name);
match self.package_manager.install_package(package_name, &mut self.filesystem, &mut self.system_manager) {
Ok(()) => {
println!("Package '{}' installed successfully", package_name);
// Special handling for Claude Code
if package_name == "claude-code" {
println!("Claude Code is now available!");
println!("You can start it with: claude start");
println!("Or run it directly: /bin/claude-code");
}
}
Err(e) => println!("Failed to install package '{}': {}", package_name, e),
}
}
fn remove_package(&mut self, package_name: &str) {
println!("Removing package: {}", package_name);
match self.package_manager.remove_package(package_name, &mut self.filesystem) {
Ok(()) => println!("Package '{}' removed successfully", package_name),
Err(e) => println!("Failed to remove package '{}': {}", package_name, e),
}
}
fn search_packages(&mut self, query: &str) {
let results = self.package_manager.search_package(query);
if results.is_empty() {
println!("No packages found matching '{}'", query);
} else {
println!("Packages matching '{}':", query);
println!("{:<20} {:<10} {:<30}", "NAME", "VERSION", "DESCRIPTION");
println!("{}", "-".repeat(60));
for package in results {
println!("{:<20} {:<10} {:<30}", package.name, package.version, package.description);
}
}
}
fn clear_screen(&self) {
// VGAバッファをクリア
crate::vga_buffer::WRITER.lock().clear_screen();
}
fn show_history(&self) {
println!("Command History:");
for (i, cmd) in self.history.iter().enumerate() {
println!(" {}: {}", i + 1, cmd);
}
}
fn show_prompt(&self) {
print!("{}", self.prompt);
}
fn add_to_history(&mut self, command: String) {
if !command.trim().is_empty() {
self.history.push(command);
}
}
}
// VGAバッファにclear_screen機能を追加
impl crate::vga_buffer::Writer {
pub fn clear_screen(&mut self) {
use crate::vga_buffer::{BUFFER_HEIGHT, BUFFER_WIDTH, ColorCode, Color, ScreenChar};
let blank = ScreenChar {
ascii_character: b' ',
color_code: ColorCode::new(Color::Yellow, Color::Black),
};
for row in 0..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
self.buffer.chars[row][col].write(blank);
}
}
self.column_position = 0;
}
}
pub fn handle_keyboard_input(character: char) {
let mut shell = SHELL.lock();
match character {
'\n' => {
println!();
let command = shell.input_buffer.clone();
shell.add_to_history(command.clone());
shell.execute_command(&command);
shell.input_buffer.clear();
shell.show_prompt();
}
'\x08' => { // Backspace
if !shell.input_buffer.is_empty() {
shell.input_buffer.pop();
print!("\x08 \x08"); // Backspace, space, backspace
}
}
c if c.is_ascii() && !c.is_control() => {
shell.input_buffer.push(c);
print!("{}", c);
}
_ => {} // 他の文字は無視
}
}
pub fn init() {
println!("AIOS Shell v0.1.0 - AI-Integrated Operating System");
println!("Type 'help' for available commands.");
SHELL.lock().show_prompt();
}

View File

@ -1,180 +0,0 @@
use alloc::{collections::BTreeMap, string::String, vec::Vec};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ServiceState {
Inactive,
Active,
Failed,
Activating,
Deactivating,
}
#[derive(Debug, Clone)]
pub struct ServiceUnit {
pub name: String,
pub description: String,
pub exec_start: String,
pub working_directory: Option<String>,
pub environment: Vec<(String, String)>,
pub container: Option<String>,
pub state: ServiceState,
}
#[derive(Debug, Clone)]
pub struct Container {
pub name: String,
pub root_directory: String,
pub private_network: bool,
pub bind_mounts: Vec<(String, String)>,
pub capability_bounding_set: Vec<String>,
}
pub struct SystemManager {
services: BTreeMap<String, ServiceUnit>,
containers: BTreeMap<String, Container>,
}
impl SystemManager {
pub fn new() -> Self {
Self {
services: BTreeMap::new(),
containers: BTreeMap::new(),
}
}
pub fn create_container(&mut self, name: String, root_dir: String) -> Result<(), &'static str> {
let container = Container {
name: name.clone(),
root_directory: root_dir,
private_network: true,
bind_mounts: Vec::new(),
capability_bounding_set: Vec::new(),
};
self.containers.insert(name, container);
Ok(())
}
pub fn create_service(&mut self, service: ServiceUnit) -> Result<(), &'static str> {
self.services.insert(service.name.clone(), service);
Ok(())
}
pub fn start_service(&mut self, name: &str) -> Result<(), &'static str> {
if let Some(service) = self.services.get_mut(name) {
service.state = ServiceState::Activating;
// コンテナ内で実行する場合
if let Some(container_name) = &service.container {
if let Some(_container) = self.containers.get(container_name) {
// TODO: nspawn風のコンテナ内でプロセス実行
crate::println!("Starting service {} in container {}", name, container_name);
} else {
return Err("Container not found");
}
} else {
// ホスト上で直接実行
crate::println!("Starting service {} on host", name);
}
service.state = ServiceState::Active;
Ok(())
} else {
Err("Service not found")
}
}
pub fn stop_service(&mut self, name: &str) -> Result<(), &'static str> {
if let Some(service) = self.services.get_mut(name) {
service.state = ServiceState::Deactivating;
crate::println!("Stopping service {}", name);
service.state = ServiceState::Inactive;
Ok(())
} else {
Err("Service not found")
}
}
pub fn list_services(&self) {
crate::println!("UNIT LOAD ACTIVE SUB DESCRIPTION");
for (name, service) in &self.services {
crate::println!("{:<24} loaded {:?} running {}",
name, service.state, service.description);
}
}
pub fn list_containers(&self) {
crate::println!("MACHINE CLASS SERVICE");
for (name, _container) in &self.containers {
crate::println!("{:<8} container -", name);
}
}
// systemctl風のコマンド処理
pub fn systemctl(&mut self, args: &[&str]) -> Result<(), &'static str> {
if args.len() < 2 {
return Err("Usage: systemctl <command> <service>");
}
match args[0] {
"start" => self.start_service(args[1]),
"stop" => self.stop_service(args[1]),
"status" => {
if let Some(service) = self.services.get(args[1]) {
crate::println!("{}", service.name);
crate::println!(" Loaded: loaded");
crate::println!(" Active: {:?}", service.state);
crate::println!(" Description: {}", service.description);
Ok(())
} else {
Err("Service not found")
}
}
"list-units" => {
self.list_services();
Ok(())
}
_ => Err("Unknown systemctl command"),
}
}
// systemd-nspawn風のコマンド
pub fn nspawn(&mut self, args: &[&str]) -> Result<(), &'static str> {
if args.len() < 2 {
return Err("Usage: nspawn --directory=<dir> <command>");
}
let mut directory = None;
let mut command = None;
for arg in args {
if arg.starts_with("--directory=") {
directory = Some(&arg[12..]);
} else if !arg.starts_with("--") {
command = Some(*arg);
break;
}
}
if let (Some(dir), Some(cmd)) = (directory, command) {
crate::println!("Starting container in {} executing {}", dir, cmd);
// TODO: 実際のコンテナ実行
Ok(())
} else {
Err("Invalid nspawn arguments")
}
}
}
impl fmt::Display for ServiceState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ServiceState::Inactive => write!(f, "inactive"),
ServiceState::Active => write!(f, "active"),
ServiceState::Failed => write!(f, "failed"),
ServiceState::Activating => write!(f, "activating"),
ServiceState::Deactivating => write!(f, "deactivating"),
}
}
}

View File

@ -1,164 +0,0 @@
use volatile::Volatile;
use core::fmt;
use lazy_static::lazy_static;
use spin::Mutex;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct ColorCode(u8);
impl ColorCode {
pub fn new(foreground: Color, background: Color) -> ColorCode {
ColorCode((background as u8) << 4 | (foreground as u8))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct ScreenChar {
pub ascii_character: u8,
pub color_code: ColorCode,
}
pub const BUFFER_HEIGHT: usize = 25;
pub const BUFFER_WIDTH: usize = 80;
#[repr(transparent)]
pub struct Buffer {
pub chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
pub struct Writer {
pub column_position: usize,
color_code: ColorCode,
pub buffer: &'static mut Buffer,
}
impl Writer {
pub fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => self.new_line(),
byte => {
if self.column_position >= BUFFER_WIDTH {
self.new_line();
}
let row = BUFFER_HEIGHT - 1;
let col = self.column_position;
let color_code = self.color_code;
self.buffer.chars[row][col].write(ScreenChar {
ascii_character: byte,
color_code,
});
self.column_position += 1;
}
}
}
pub fn write_string(&mut self, s: &str) {
for byte in s.bytes() {
match byte {
0x20..=0x7e | b'\n' => self.write_byte(byte),
_ => self.write_byte(0xfe),
}
}
}
fn new_line(&mut self) {
for row in 1..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
let character = self.buffer.chars[row][col].read();
self.buffer.chars[row - 1][col].write(character);
}
}
self.clear_row(BUFFER_HEIGHT - 1);
self.column_position = 0;
}
fn clear_row(&mut self, row: usize) {
let blank = ScreenChar {
ascii_character: b' ',
color_code: self.color_code,
};
for col in 0..BUFFER_WIDTH {
self.buffer.chars[row][col].write(blank);
}
}
}
impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_string(s);
Ok(())
}
}
lazy_static! {
pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
column_position: 0,
color_code: ColorCode::new(Color::Yellow, Color::Black),
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
});
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
WRITER.lock().write_fmt(args).unwrap();
}
#[test_case]
fn test_println_simple() {
println!("test_println_simple output");
}
#[test_case]
fn test_println_many() {
for i in 0..200 {
println!("test_println_many output {}", i);
}
}
#[test_case]
fn test_println_output() {
let s = "Some test string that fits on a single line";
println!("{}", s);
for (i, c) in s.chars().enumerate() {
let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
}

8
user/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "aios-user"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
# Placeholder for user programs

1
user/src/lib.rs Normal file
View File

@ -0,0 +1 @@
// AIOS v2 User programs placeholder