From 152d6de44dc029aab89d1d77d647045eeb36f690 Mon Sep 17 00:00:00 2001 From: syui Date: Sat, 5 Jul 2025 14:31:39 +0900 Subject: [PATCH] update v2 --- .gitignore | 1 + Bootloader.toml | 12 - Cargo.toml | 36 ++- claude-service/Cargo.toml | 8 + claude-service/src/lib.rs | 1 + claude.md | 6 +- iso/boot/grub/grub.cfg | 7 + kernel/.cargo/config.toml | 13 + kernel/Cargo.toml | 14 ++ kernel/i686-aios.json | 22 ++ kernel/kernel.ld | 35 +++ kernel/src/boot.s | 27 +++ kernel/src/boot32.s | 34 +++ kernel/src/console.rs | 130 ++++++++++ kernel/src/main.rs | 58 +++++ kernel/src/memory.rs | 7 + kernel/src/panic.rs | 27 +++ rust-toolchain.toml | 4 + scpt/bootimage-test.sh | 42 ++++ src/allocator.rs | 44 ---- src/filesystem.rs | 289 ---------------------- src/interrupts.rs | 88 ------- src/main.rs | 100 -------- src/memory.rs | 76 ------ src/package.rs | 413 ------------------------------- src/process.rs | 203 ---------------- src/serial.rs | 32 --- src/shell.rs | 494 -------------------------------------- src/systemd.rs | 180 -------------- src/vga_buffer.rs | 164 ------------- user/Cargo.toml | 8 + user/src/lib.rs | 1 + 32 files changed, 455 insertions(+), 2121 deletions(-) delete mode 100644 Bootloader.toml create mode 100644 claude-service/Cargo.toml create mode 100644 claude-service/src/lib.rs create mode 100644 iso/boot/grub/grub.cfg create mode 100644 kernel/.cargo/config.toml create mode 100644 kernel/Cargo.toml create mode 100644 kernel/i686-aios.json create mode 100644 kernel/kernel.ld create mode 100644 kernel/src/boot.s create mode 100644 kernel/src/boot32.s create mode 100644 kernel/src/console.rs create mode 100644 kernel/src/main.rs create mode 100644 kernel/src/memory.rs create mode 100644 kernel/src/panic.rs create mode 100644 rust-toolchain.toml create mode 100755 scpt/bootimage-test.sh delete mode 100644 src/allocator.rs delete mode 100644 src/filesystem.rs delete mode 100644 src/interrupts.rs delete mode 100644 src/main.rs delete mode 100644 src/memory.rs delete mode 100644 src/package.rs delete mode 100644 src/process.rs delete mode 100644 src/serial.rs delete mode 100644 src/shell.rs delete mode 100644 src/systemd.rs delete mode 100644 src/vga_buffer.rs create mode 100644 user/Cargo.toml create mode 100644 user/src/lib.rs diff --git a/.gitignore b/.gitignore index a8b3dcf..3720dbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ octox +redox target .claude *.log diff --git a/Bootloader.toml b/Bootloader.toml deleted file mode 100644 index 39ad0c9..0000000 --- a/Bootloader.toml +++ /dev/null @@ -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" \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index f894710..5e89758 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,19 @@ -[package] -name = "aios" -version = "0.1.0" +# AIOS v2: Octox-inspired AI Operating System for x86_64 + +[workspace] +members = [ + "kernel", + "user", + "claude-service", +] + +[workspace.package] +version = "0.2.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" diff --git a/claude-service/Cargo.toml b/claude-service/Cargo.toml new file mode 100644 index 0000000..46ad6b0 --- /dev/null +++ b/claude-service/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "claude-service" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +# Placeholder for Claude integration service \ No newline at end of file diff --git a/claude-service/src/lib.rs b/claude-service/src/lib.rs new file mode 100644 index 0000000..d5e803c --- /dev/null +++ b/claude-service/src/lib.rs @@ -0,0 +1 @@ +// AIOS v2 Claude Service placeholder \ No newline at end of file diff --git a/claude.md b/claude.md index 95d2c88..08c89cd 100644 --- a/claude.md +++ b/claude.md @@ -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 -``` diff --git a/iso/boot/grub/grub.cfg b/iso/boot/grub/grub.cfg new file mode 100644 index 0000000..cc6a933 --- /dev/null +++ b/iso/boot/grub/grub.cfg @@ -0,0 +1,7 @@ +set timeout=0 +set default=0 + +menuentry "AIOS v2" { + multiboot2 /boot/kernel.bin + boot +} diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml new file mode 100644 index 0000000..ce73288 --- /dev/null +++ b/kernel/.cargo/config.toml @@ -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"] \ No newline at end of file diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml new file mode 100644 index 0000000..413301f --- /dev/null +++ b/kernel/Cargo.toml @@ -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"] \ No newline at end of file diff --git a/kernel/i686-aios.json b/kernel/i686-aios.json new file mode 100644 index 0000000..620482e --- /dev/null +++ b/kernel/i686-aios.json @@ -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"] + } +} \ No newline at end of file diff --git a/kernel/kernel.ld b/kernel/kernel.ld new file mode 100644 index 0000000..4a96c16 --- /dev/null +++ b/kernel/kernel.ld @@ -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.*) + } +} \ No newline at end of file diff --git a/kernel/src/boot.s b/kernel/src/boot.s new file mode 100644 index 0000000..e710607 --- /dev/null +++ b/kernel/src/boot.s @@ -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 \ No newline at end of file diff --git a/kernel/src/boot32.s b/kernel/src/boot32.s new file mode 100644 index 0000000..1ce01a3 --- /dev/null +++ b/kernel/src/boot32.s @@ -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: \ No newline at end of file diff --git a/kernel/src/console.rs b/kernel/src/console.rs new file mode 100644 index 0000000..80fd3ce --- /dev/null +++ b/kernel/src/console.rs @@ -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(); + } +} \ No newline at end of file diff --git a/kernel/src/main.rs b/kernel/src/main.rs new file mode 100644 index 0000000..6d25483 --- /dev/null +++ b/kernel/src/main.rs @@ -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"); + } + } +} \ No newline at end of file diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs new file mode 100644 index 0000000..a83c54a --- /dev/null +++ b/kernel/src/memory.rs @@ -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 +} \ No newline at end of file diff --git a/kernel/src/panic.rs b/kernel/src/panic.rs new file mode 100644 index 0000000..cb278a1 --- /dev/null +++ b/kernel/src/panic.rs @@ -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"); + } + } +} \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..a240598 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly" +targets = ["x86_64-unknown-none"] +components = ["rust-src", "llvm-tools-preview"] \ No newline at end of file diff --git a/scpt/bootimage-test.sh b/scpt/bootimage-test.sh new file mode 100755 index 0000000..ce38329 --- /dev/null +++ b/scpt/bootimage-test.sh @@ -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 diff --git a/src/allocator.rs b/src/allocator.rs deleted file mode 100644 index 3666303..0000000 --- a/src/allocator.rs +++ /dev/null @@ -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, - frame_allocator: &mut impl FrameAllocator, -) -> Result<(), MapToError> { - 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) -} \ No newline at end of file diff --git a/src/filesystem.rs b/src/filesystem.rs deleted file mode 100644 index ac49d5b..0000000 --- a/src/filesystem.rs +++ /dev/null @@ -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, -} - -#[derive(Debug, Clone)] -pub struct Directory { - pub metadata: FileMetadata, - pub entries: BTreeMap, -} - -#[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, &'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, &'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"), - } - } -} \ No newline at end of file diff --git a/src/interrupts.rs b/src/interrupts.rs deleted file mode 100644 index 209cc3f..0000000 --- a/src/interrupts.rs +++ /dev/null @@ -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 = - 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> = - 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()); - } -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 6276076..0000000 --- a/src/main.rs +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/src/memory.rs b/src/memory.rs deleted file mode 100644 index 3e9b2d4..0000000 --- a/src/memory.rs +++ /dev/null @@ -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 { - 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 for BootInfoFrameAllocator { - fn allocate_frame(&mut self) -> Option { - let frame = self.usable_frames().nth(self.next); - self.next += 1; - frame - } -} - -pub fn create_example_mapping( - page: Page, - mapper: &mut impl Mapper, - frame_allocator: &mut impl FrameAllocator, -) -> Result<(), MapToError> { - 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 { - let level_4_table = active_level_4_table(physical_memory_offset); - x86_64::structures::paging::OffsetPageTable::new(level_4_table, physical_memory_offset) -} \ No newline at end of file diff --git a/src/package.rs b/src/package.rs deleted file mode 100644 index e7121b6..0000000 --- a/src/package.rs +++ /dev/null @@ -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, -} - -#[derive(Debug, Clone)] -pub struct PackageFile { - pub path: String, - pub content: Vec, - 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, - pub services: Vec, -} - -#[derive(Debug)] -pub struct Repository { - pub name: String, - pub url: String, - pub packages: BTreeMap, -} - -pub struct PackageManager { - installed_packages: BTreeMap, - repositories: Vec, - 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 { - // 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(()) - } -} \ No newline at end of file diff --git a/src/process.rs b/src/process.rs deleted file mode 100644 index 5ec76d4..0000000 --- a/src/process.rs +++ /dev/null @@ -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, - pub exit_code: Option, -} - -pub struct ProcessManager { - processes: BTreeMap, - next_pid: usize, - current_process: Option, -} - -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 { - 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 { - 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 { - // 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) - } -} \ No newline at end of file diff --git a/src/serial.rs b/src/serial.rs deleted file mode 100644 index c9df926..0000000 --- a/src/serial.rs +++ /dev/null @@ -1,32 +0,0 @@ -use uart_16550::SerialPort; -use spin::Mutex; -use lazy_static::lazy_static; - -lazy_static! { - pub static ref SERIAL1: Mutex = { - 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)*)); -} \ No newline at end of file diff --git a/src/shell.rs b/src/shell.rs deleted file mode 100644 index 095eabb..0000000 --- a/src/shell.rs +++ /dev/null @@ -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 = Mutex::new(Shell::new()); -} - -pub struct Shell { - input_buffer: String, - history: Vec, - 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 "); - 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 "); - 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 - Print text to output"); - println!(" history - Show command history"); - println!(""); - println!("File System:"); - println!(" ls [path] - List directory contents"); - println!(" cd - Change directory"); - println!(" pwd - Print working directory"); - println!(" mkdir - Create directory"); - println!(" cat - Display file contents"); - println!(" touch - Create empty file"); - println!(" rm - Remove file"); - println!(""); - println!("Process Management:"); - println!(" ps - List running processes"); - println!(" exec - Execute program"); - println!(" kill - Kill process by PID"); - println!(""); - println!("Package Management:"); - println!(" pkg install - Install package"); - println!(" pkg remove - Remove package"); - println!(" pkg list - List installed packages"); - println!(" pkg search - Search packages"); - println!(" pkg info - Show package info"); - println!(" pkg update - Update repositories"); - println!(""); - println!("System:"); - println!(" systemctl - Control system services"); - println!(" claude - Claude Code integration"); - println!(" ai - 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::() { - 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 [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(); -} \ No newline at end of file diff --git a/src/systemd.rs b/src/systemd.rs deleted file mode 100644 index c0f9d77..0000000 --- a/src/systemd.rs +++ /dev/null @@ -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, - pub environment: Vec<(String, String)>, - pub container: Option, - 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, -} - -pub struct SystemManager { - services: BTreeMap, - containers: BTreeMap, -} - -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 "); - } - - 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= "); - } - - 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"), - } - } -} \ No newline at end of file diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs deleted file mode 100644 index b2ce1e2..0000000 --- a/src/vga_buffer.rs +++ /dev/null @@ -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; 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 = 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); - } -} \ No newline at end of file diff --git a/user/Cargo.toml b/user/Cargo.toml new file mode 100644 index 0000000..4c8c369 --- /dev/null +++ b/user/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "aios-user" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +# Placeholder for user programs \ No newline at end of file diff --git a/user/src/lib.rs b/user/src/lib.rs new file mode 100644 index 0000000..6179650 --- /dev/null +++ b/user/src/lib.rs @@ -0,0 +1 @@ +// AIOS v2 User programs placeholder \ No newline at end of file