diff --git a/.cargo/config.toml b/.cargo/config.toml index 62e0830..2f831a6 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,7 +7,7 @@ target = "x86_64-eduos.json" [target.i686-eduos] rustflags = [ - "-C", "link-arg=-Tsrc/arch/x86/link_i686.ld", "-C", "relocation-model=static" + "-C", "link-arg=-Tsrc/arch/x86/kernel/link_i686.ld", "-C", "relocation-model=static" ] runner = "qemu-system-x86_64 -display none -serial stdio -smp 1 -m 256M -device isa-debug-exit,iobase=0xf4,iosize=0x04 -kernel" diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index c63acad..1bf67af 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -45,9 +45,12 @@ jobs: run: cargo --version - name: Install bootimage run: cargo install bootimage - - name: Build + - name: Build (x86_64) run: cargo build + - name: Build (i686) + run: + cargo build --target=i686-eduos.json - name: run (ubuntu) if: ${{ matrix.os == 'ubuntu-latest' }} run: ./test.sh diff --git a/Cargo.lock b/Cargo.lock index 171cb03..f30796c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,11 +26,18 @@ version = "0.9.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "365861702868e2a37b4247aaecc7bd8f4389baec8d025497ad8ba7ff37ee9440" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "eduos-rs" version = "0.1.0" dependencies = [ "bootloader", + "cfg-if", "qemu-exit", "simple-chunk-allocator", "spin", diff --git a/Cargo.toml b/Cargo.toml index d47c608..4a182eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,8 @@ default = ["qemu-exit"] spin = "0.9" simple-chunk-allocator = "0.1.5" qemu-exit = { version = "3.0", optional = true } -bootloader = "0.9.29" - -[target.'cfg(target_arch = "x86_64")'.dependencies.x86] -version = "0.52" -default-features = false +x86 = { version = "0.52", default-features = false } +cfg-if = "1.0" +[target.'cfg(target_arch = "x86_64")'.dependencies] +bootloader = "0.9.29" \ No newline at end of file diff --git a/i686-eduos.json b/i686-eduos.json new file mode 100644 index 0000000..1895a79 --- /dev/null +++ b/i686-eduos.json @@ -0,0 +1,14 @@ +{ + "llvm-target": "i686-unknown-none", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "os": "none", + "arch": "x86", + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", + "features": "-mmx,-sse,+soft-float", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort" +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 725d101..0caa607 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -1,4 +1,8 @@ -// Implementations for x86_64. +/// Currently, eduOS supports only x86_64 (64 bit) +/// and x86 (32 bit) code. Both architecture are similar +/// and share the code in the directory x86 + +// Implementations for x86_64 and x86. #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] pub mod x86; diff --git a/src/arch/x86/kernel/entry32.s b/src/arch/x86/kernel/entry32.s new file mode 100644 index 0000000..cfe6cd6 --- /dev/null +++ b/src/arch/x86/kernel/entry32.s @@ -0,0 +1,54 @@ +# This is the kernel's entry point. We could either call main here, +# or we can use this to setup the stack or other nice stuff, like +# perhaps setting up the GDT and segments. Please note that interrupts +# are disabled at this point: More on interrupts later! + +.code32 + +.set BOOT_STACK_SIZE, 4096 + +# We use a special name to map this section at the begin of our kernel +# => Multiboot expects its magic number at the beginning of the kernel. +.section .mboot, "a" + +# This part MUST be 4 byte aligned, so we solve that issue using '.align 4'. +.align 4 +mboot: + # Multiboot macros to make a few lines more readable later + .set MULTIBOOT_PAGE_ALIGN, (1 << 0) + .set MULTIBOOT_MEMORY_INFO, (1 << 1) + .set MULTIBOOT_HEADER_MAGIC, 0x1BADB002 + .set MULTIBOOT_HEADER_FLAGS, MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO + .set MULTIBOOT_CHECKSUM, -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) + + # This is the GRUB Multiboot header. A boot signature + .4byte MULTIBOOT_HEADER_MAGIC + .4byte MULTIBOOT_HEADER_FLAGS + .4byte MULTIBOOT_CHECKSUM + .4byte 0, 0, 0, 0, 0 # address fields + +.section .text +.align 4 +.extern main +.extern shutdown +.global _start +_start: + cli # avoid any interrupt + + # Initialize stack pointer + mov esp, OFFSET BOOT_STACK + add esp, BOOT_STACK_SIZE - 16 + + call main + # eax has the return value of main + push eax + call shutdown +L0: + hlt + jmp L0 + +.section .data +.align 4096 +.global BOOT_STACK +BOOT_STACK: + .fill BOOT_STACK_SIZE, 1, 0xcd \ No newline at end of file diff --git a/src/arch/x86/kernel/link_i686.ld b/src/arch/x86/kernel/link_i686.ld new file mode 100644 index 0000000..05504fb --- /dev/null +++ b/src/arch/x86/kernel/link_i686.ld @@ -0,0 +1,27 @@ +ENTRY(_start) +phys = 0x000000100000; + +SECTIONS +{ + kernel_start = phys; + .mboot phys : AT(ADDR(.mboot)) { + KEEP(*(.mboot)) + KEEP(*(.mboot.*)) + } + .text ALIGN(4096) : AT(ADDR(.text)) { + *(.text) + *(.text.*) + } + .rodata ALIGN(4096) : AT(ADDR(.rodata)) { + *(.rodata) + *(.rodata.*) + } + .data ALIGN(4096) : AT(ADDR(.data)) { + *(.data) + *(.data.*) + } + .bss ALIGN(4096) : AT(ADDR(.bss)) { + *(.bss) + *(.bss.*) + } +} \ No newline at end of file diff --git a/src/arch/x86/kernel/mod.rs b/src/arch/x86/kernel/mod.rs index 44a13d1..90e5dcb 100644 --- a/src/arch/x86/kernel/mod.rs +++ b/src/arch/x86/kernel/mod.rs @@ -1,9 +1,14 @@ pub mod processor; pub mod serial; +#[cfg(target_arch = "x86_64")] mod start; -pub mod switch; -pub mod task; +pub(crate) mod switch; +pub(crate) mod task; +#[cfg(target_arch = "x86_64")] use bootloader::BootInfo; - +#[cfg(target_arch = "x86_64")] pub(crate) static mut BOOT_INFO: Option<&'static BootInfo> = None; + +#[cfg(target_arch = "x86")] +core::arch::global_asm!(include_str!("entry32.s")); diff --git a/src/arch/x86/kernel/switch.rs b/src/arch/x86/kernel/switch.rs index e43e942..ab365bb 100755 --- a/src/arch/x86/kernel/switch.rs +++ b/src/arch/x86/kernel/switch.rs @@ -1,5 +1,6 @@ use core::arch::asm; +#[cfg(target_arch = "x86_64")] macro_rules! save_context { () => { concat!( @@ -26,6 +27,7 @@ macro_rules! save_context { }; } +#[cfg(target_arch = "x86_64")] macro_rules! restore_context { () => { concat!( @@ -53,6 +55,7 @@ macro_rules! restore_context { }; } +#[cfg(target_arch = "x86_64")] #[naked] pub unsafe extern "C" fn switch(_old_stack: *mut usize, _new_stack: usize) { // rdi = old_stack => the address to store the old rsp @@ -68,3 +71,21 @@ pub unsafe extern "C" fn switch(_old_stack: *mut usize, _new_stack: usize) { options(noreturn) ); } + +#[cfg(target_arch = "x86")] +pub unsafe extern "C" fn switch(_old_stack: *mut usize, _new_stack: usize) { + asm!( + // store all registers + "pushfd", + "pushad", + // switch stack + "mov edi, [esp+10*4]", + "mov [edi], esp", + "mov esp, [esp+11*4]", + // restore registers + "popad", + "popfd", + "ret", + options(noreturn) + ); +} diff --git a/src/arch/x86/kernel/task.rs b/src/arch/x86/kernel/task.rs index 96ea691..9fa2a8b 100644 --- a/src/arch/x86/kernel/task.rs +++ b/src/arch/x86/kernel/task.rs @@ -8,6 +8,7 @@ use crate::scheduler::{do_exit, get_current_taskid}; use core::mem::size_of; use core::ptr::write_bytes; +#[cfg(target_arch = "x86_64")] #[repr(C, packed)] struct State { /// R15 register @@ -48,6 +49,31 @@ struct State { rip: u64, } +#[cfg(target_arch = "x86")] +#[repr(C, packed)] +struct State { + /// EDI register + edi: u32, + /// ESI register + esi: u32, + /// EBP register + ebp: u32, + /// (pseudo) ESP register + esp: u32, + /// EBX register + ebx: u32, + /// EDX register + edx: u32, + /// ECX register + ecx: u32, + /// EAX register + eax: u32, + /// status flags + eflags: u32, + /// instruction pointer + eip: u32, +} + extern "C" fn leave_task() -> ! { debug!("finish task {}", get_current_taskid()); @@ -59,6 +85,7 @@ extern "C" fn leave_task() -> ! { } impl TaskFrame for Task { + #[cfg(target_arch = "x86_64")] fn create_stack_frame(&mut self, func: extern "C" fn()) { unsafe { let mut stack: *mut u64 = ((*self.stack).top()).as_mut_ptr(); @@ -90,4 +117,37 @@ impl TaskFrame for Task { self.last_stack_pointer = stack as usize; } } + + #[cfg(target_arch = "x86")] + fn create_stack_frame(&mut self, func: extern "C" fn()) { + unsafe { + let mut stack: *mut u32 = ((*self.stack).top()).as_mut_ptr(); + + write_bytes((*self.stack).bottom().as_mut_ptr::(), 0xCD, STACK_SIZE); + + /* Only marker for debugging purposes, ... */ + *stack = 0xDEADBEEFu32; + stack = (stack as usize - size_of::()) as *mut u32; + + /* the first-function-to-be-called's arguments, ... */ + //TODO: add arguments + + /* and the "caller" we shall return to. + * This procedure cleans the task after exit. */ + *stack = (leave_task as *const ()) as u32; + stack = (stack as usize - size_of::()) as *mut u32; + + let state: *mut State = stack as *mut State; + write_bytes(state, 0x00, 1); + + (*state).esp = (stack as usize + size_of::()) as u32; + (*state).ebp = (*state).esp + size_of::() as u32; + + (*state).eip = (func as *const ()) as u32; + (*state).eflags = 0x1002u32; + + /* Set the task's stack pointer entry to the stack we have crafted right now. */ + self.last_stack_pointer = stack as usize; + } + } } diff --git a/src/arch/x86/mm/mod.rs b/src/arch/x86/mm/mod.rs index de50f1f..15a7927 100644 --- a/src/arch/x86/mm/mod.rs +++ b/src/arch/x86/mm/mod.rs @@ -1,8 +1,8 @@ -use crate::arch::x86::kernel::BOOT_INFO; use crate::scheduler::task::Stack; -use bootloader::bootinfo::MemoryRegionType; -use core::ops::Deref; +#[cfg(target_arch = "x86")] +pub use x86::bits32::paging::VAddr as VirtAddr; +#[cfg(target_arch = "x86_64")] pub use x86::bits64::paging::VAddr as VirtAddr; #[derive(Copy, Clone)] @@ -19,7 +19,13 @@ impl BootStack { impl Stack for BootStack { fn top(&self) -> VirtAddr { - self.end - 16u64 + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86")] { + self.end - 16u32 + } else { + self.end - 16u64 + } + } } fn bottom(&self) -> VirtAddr { @@ -27,7 +33,12 @@ impl Stack for BootStack { } } -pub fn get_boot_stack() -> BootStack { +#[cfg(target_arch = "x86_64")] +pub(crate) fn get_boot_stack() -> BootStack { + use crate::arch::x86::kernel::BOOT_INFO; + use bootloader::bootinfo::MemoryRegionType; + use core::ops::Deref; + unsafe { let regions = BOOT_INFO.unwrap().memory_map.deref(); @@ -43,3 +54,16 @@ pub fn get_boot_stack() -> BootStack { panic!("Unable to determine the kernel stack"); } } + +#[cfg(target_arch = "x86")] +extern "C" { + static BOOT_STACK: usize; +} + +#[cfg(target_arch = "x86")] +pub(crate) fn get_boot_stack() -> BootStack { + BootStack::new( + unsafe { VirtAddr(BOOT_STACK.try_into().unwrap()) }, + unsafe { VirtAddr((BOOT_STACK + 0x1000).try_into().unwrap()) }, + ) +} diff --git a/src/lib.rs b/src/lib.rs index d7cc940..d70381e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,9 +10,8 @@ extern crate spin; extern crate x86; // These need to be visible to the linker, so we need to export them. +use crate::arch::processor::shutdown; use crate::consts::HEAP_SIZE; -#[cfg(target_arch = "x86_64")] -use arch::processor::*; use core::panic::PanicInfo; pub use logging::*; use simple_chunk_allocator::{heap, heap_bitmap, GlobalChunkAllocator, PageAligned}; diff --git a/src/scheduler/task.rs b/src/scheduler/task.rs index 79fa017..9729339 100755 --- a/src/scheduler/task.rs +++ b/src/scheduler/task.rs @@ -61,11 +61,11 @@ impl TaskStack { impl Stack for TaskStack { fn top(&self) -> VirtAddr { - VirtAddr::from((&(self.buffer[STACK_SIZE - 16]) as *const _) as u64) + VirtAddr::from((&(self.buffer[STACK_SIZE - 16]) as *const _) as usize) } fn bottom(&self) -> VirtAddr { - VirtAddr::from((&(self.buffer[0]) as *const _) as u64) + VirtAddr::from((&(self.buffer[0]) as *const _) as usize) } }