update v2

This commit is contained in:
2025-07-05 14:31:39 +09:00
parent 163ac1668d
commit 152d6de44d
32 changed files with 455 additions and 2121 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
octox octox
redox
target target
.claude .claude
*.log *.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,27 +1,19 @@
[package] # AIOS v2: Octox-inspired AI Operating System for x86_64
name = "aios"
version = "0.1.0" [workspace]
members = [
"kernel",
"user",
"claude-service",
]
[workspace.package]
version = "0.2.0"
edition = "2021" edition = "2021"
license = "MIT"
[dependencies] [workspace.dependencies]
bootloader = "0.9.23" kernel = { path = "kernel", default-features = false }
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"
[profile.dev] [profile.dev]
panic = "abort" panic = "abort"

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 ## 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
```

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 = "0.2.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();
}
}

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

@ -0,0 +1,58 @@
//! AIOS v2 Kernel - Using bootloader crate
#![no_std]
#![no_main]
use core::panic::PanicInfo;
// Simple VGA buffer writer
struct VgaBuffer {
chars: [[u16; 80]; 25],
}
static mut VGA_BUFFER: *mut VgaBuffer = 0xb8000 as *mut VgaBuffer;
fn print_char(c: u8, x: usize, y: usize) {
unsafe {
let vga = &mut *VGA_BUFFER;
vga.chars[y][x] = (0x0f << 8) | (c as u16);
}
}
fn print_str(s: &str, x: usize, y: usize) {
for (i, byte) in s.bytes().enumerate() {
print_char(byte, x + i, y);
}
}
// Entry point
#[no_mangle]
pub extern "C" fn _start() -> ! {
// Clear screen
for y in 0..25 {
for x in 0..80 {
print_char(b' ', x, y);
}
}
// Print welcome message
print_str("AIOS v2 - AI Operating System", 25, 12);
print_str("Kernel started successfully!", 26, 13);
loop {
unsafe {
core::arch::asm!("hlt");
}
}
}
// Panic handler
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
print_str("KERNEL PANIC!", 33, 15);
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");
}
}
}

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 v2 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