diff --git a/.cargo/config.toml b/.cargo/config.toml index cc031044..62e08301 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,11 +5,11 @@ build-std-features = ["compiler-builtins-mem"] [build] target = "x86_64-eduos.json" -[target.aarch64-eduos] +[target.i686-eduos] rustflags = [ - "-C", "link-arg=-Tlink_aarch64.ld" + "-C", "link-arg=-Tsrc/arch/x86/link_i686.ld", "-C", "relocation-model=static" ] -runner = "qemu-system-aarch64 -display none -serial stdio -machine virt,gic-version=3 -cpu cortex-a72 -semihosting -smp 1 -m 256M --kernel" +runner = "qemu-system-x86_64 -display none -serial stdio -smp 1 -m 256M -device isa-debug-exit,iobase=0xf4,iosize=0x04 -kernel" [target.x86_64-eduos] runner = "bootimage runner" diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index 64947116..c5207495 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -24,7 +24,7 @@ jobs: if: ${{ matrix.os == 'ubuntu-latest' }} run: | sudo apt-get update - sudo apt-get install qemu-system-x86 qemu-system-aarch64 + sudo apt-get install qemu-system-x86 - name: Install QEMU (macos) if: ${{ matrix.os == 'macos-latest' }} run: | @@ -46,9 +46,9 @@ jobs: - name: Build (x86_64) run: cargo build - - name: Build (aarch64) + - name: Build (i686) run: - cargo build --target=aarch64-eduos.json + 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 8cc5342a..08f66c5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,12 +26,6 @@ version = "0.9.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "365861702868e2a37b4247aaecc7bd8f4389baec8d025497ad8ba7ff37ee9440" -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "cfg-if" version = "1.0.0" @@ -43,22 +37,11 @@ name = "eduos-rs" version = "0.1.0" dependencies = [ "bootloader", - "semihosting", + "cfg-if", "spinning_top", - "wasm-bindgen", - "web-sys", "x86", ] -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "lock_api" version = "0.4.12" @@ -69,36 +52,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "proc-macro2" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - [[package]] name = "raw-cpuid" version = "10.7.0" @@ -114,12 +67,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "semihosting" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5260e8474680a4617e36cee7f4602655c89146bde012a5b6ba7cc080ec49dcd" - [[package]] name = "spinning_top" version = "0.3.0" @@ -129,87 +76,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "syn" -version = "2.0.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "x86" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 7f7849a6..ae0c3881 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,16 +15,18 @@ run-command = ["qemu-system-x86_64", "-display", "none", "-smp", "1", "-m", "256 # Applies to `bootimage run` and `bootimage runner` run-args = [] +[features] +default = ["qemu_exit"] +vga = [] +qemu_exit = [] + [dependencies] spinning_top = "0.3.0" - -[target.'cfg(target_arch = "aarch64")'.dependencies] -semihosting = "0.1.9" +cfg-if = "1.0" [target.'cfg(target_arch = "x86_64")'.dependencies] bootloader = "0.9.29" x86 = { version = "0.52", default-features = false } -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { version = "0.2.92" } -web-sys = { version = "0.3.4", features = ['Document','Element','HtmlElement','Node','Window',] } \ No newline at end of file +[target.'cfg(target_arch = "x86")'.dependencies] +x86 = { version = "0.52", default-features = false } \ No newline at end of file diff --git a/i686-eduos.json b/i686-eduos.json new file mode 100644 index 00000000..1895a79c --- /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/link_aarch64.ld b/link_aarch64.ld deleted file mode 100644 index 30939f94..00000000 --- a/link_aarch64.ld +++ /dev/null @@ -1,40 +0,0 @@ -ENTRY(_start) - -SECTIONS { - /* Load the kernel into ram of qemu device. */ - . = 0x40000000; - - kernel_start = .; - - .boot : - { - /* This goes first. */ - KEEP(*(.kheader)) - } - - .text : AT(ADDR(.text)) - { - *(.text) - *(.text.*) - } - - .rodata : AT(ADDR(.rodata)) - { - *(.rodata) - *(.rodata.*) - } - - .data ALIGN(4096) : AT(ADDR(.data)) - { - *(.data) - *(.data.*) - } - - .bss ALIGN(4096) : AT(ADDR(.bss)) - { - __bss_start = .; - *(.bss) - *(.bss.*) - } - kernel_end = .; -} \ No newline at end of file diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 3ba9a0e9..36321880 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -1,15 +1,13 @@ // Export our platform-specific modules. -#[cfg(target_arch = "x86_64")] -pub use self::x86_64::{processor, serial}; +#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] +pub use self::x86::{init, processor}; -// Implementations for x86_64. -#[cfg(target_arch = "x86_64")] -pub mod x86_64; +#[cfg(all(target_arch = "x86", feature = "vga"))] +pub use self::x86::vga; -// Export our platform-specific modules. -#[cfg(target_arch = "x86")] -pub use self::x86_64::{processor, serial}; +#[cfg(not(all(target_arch = "x86", feature = "vga")))] +pub use self::x86::serial; -// Implementations for x86_64. -#[cfg(target_arch = "x86")] -pub mod x86_64; +// Implementations for x86. +#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] +pub mod x86; diff --git a/src/arch/x86/entry.s b/src/arch/x86/entry.s new file mode 100644 index 00000000..97397767 --- /dev/null +++ b/src/arch/x86/entry.s @@ -0,0 +1,55 @@ +# 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 +.global mboot +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/link_i686.ld b/src/arch/x86/link_i686.ld new file mode 100644 index 00000000..05504fb1 --- /dev/null +++ b/src/arch/x86/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/mod.rs b/src/arch/x86/mod.rs index 96f6460c..783f1f1e 100644 --- a/src/arch/x86/mod.rs +++ b/src/arch/x86/mod.rs @@ -1,3 +1,17 @@ pub mod processor; +#[cfg(not(all(target_arch = "x86", feature = "vga")))] pub mod serial; +#[cfg(target_arch = "x86_64")] pub mod start; +#[cfg(all(target_arch = "x86", feature = "vga"))] +pub mod vga; + +#[cfg(target_arch = "x86")] +core::arch::global_asm!(include_str!("entry.s")); + +pub fn init() { + processor::cpu_init(); + + #[cfg(all(target_arch = "x86", feature = "vga"))] + vga::init(); +} diff --git a/src/arch/x86/processor.rs b/src/arch/x86/processor.rs index d4223f2f..ab3fcb07 100644 --- a/src/arch/x86/processor.rs +++ b/src/arch/x86/processor.rs @@ -1,12 +1,14 @@ use x86::controlregs::*; +#[cfg(feature = "qemu_exit")] use x86::io; -pub fn halt() { +pub(crate) fn halt() { unsafe { x86::halt(); } } +#[cfg(feature = "qemu_exit")] fn qemu_exit(success: bool) { let code = if success { 3 >> 1 } else { 0 }; unsafe { @@ -14,15 +16,17 @@ fn qemu_exit(success: bool) { } } +#[allow(unused_variables)] #[no_mangle] pub extern "C" fn shutdown(error_code: i32) -> ! { + #[cfg(feature = "qemu_exit")] qemu_exit(error_code == 0); loop { halt(); } } -pub fn cpu_init() { +pub(crate) fn cpu_init() { let mut cr0 = unsafe { cr0() }; // be sure that AM, NE and MP is enabled diff --git a/src/arch/x86/serial.rs b/src/arch/x86/serial.rs index 48401601..22429dc9 100644 --- a/src/arch/x86/serial.rs +++ b/src/arch/x86/serial.rs @@ -3,7 +3,7 @@ use spinning_top::Spinlock; use x86::io::*; /// A COM serial port. -pub struct ComPort { +pub(crate) struct ComPort { /// COM ports are identified by the base address of their associated /// I/O registers. base_addr: u16, @@ -32,4 +32,4 @@ impl fmt::Write for ComPort { } /// Our primary serial port. -pub static COM1: Spinlock = Spinlock::new(ComPort::new(0x3F8)); +pub(crate) static COM1: Spinlock = Spinlock::new(ComPort::new(0x3F8)); diff --git a/src/arch/x86/start.rs b/src/arch/x86/start.rs index 1612629a..5166990e 100644 --- a/src/arch/x86/start.rs +++ b/src/arch/x86/start.rs @@ -1,4 +1,4 @@ -use crate::arch::x86_64::processor::shutdown; +use crate::arch::x86::processor::shutdown; extern "C" { fn main() -> i32; diff --git a/src/arch/x86/vga.rs b/src/arch/x86/vga.rs new file mode 100644 index 00000000..88ff222f --- /dev/null +++ b/src/arch/x86/vga.rs @@ -0,0 +1,129 @@ +use core::fmt; +use spinning_top::Spinlock; +use x86::io::*; + +const CRT_CONTROLLER_ADDRESS_PORT: u16 = 0x3D4; +const CRT_CONTROLLER_DATA_PORT: u16 = 0x3D5; +const CURSOR_START_REGISTER: u8 = 0x0A; +const CURSOR_DISABLE: u8 = 0x20; + +const ATTRIBUTE_BLACK: u8 = 0x00; +const ATTRIBUTE_LIGHTGREY: u8 = 0x07; +const COLS: usize = 80; +const ROWS: usize = 25; +const VGA_BUFFER_ADDRESS: u64 = 0xB8000; + +pub(crate) static VGA_SCREEN: Spinlock = Spinlock::new(VgaScreen::new()); + +#[derive(Clone, Copy)] +#[repr(C, packed)] +pub(crate) struct VgaCharacter { + character: u8, + attribute: u8, +} + +impl VgaCharacter { + const fn new(character: u8, attribute: u8) -> Self { + Self { + character, + attribute, + } + } +} + +pub(crate) struct VgaScreen { + buffer: *mut [[VgaCharacter; COLS]; ROWS], + current_col: usize, + current_row: usize, + is_initialized: bool, +} + +impl VgaScreen { + const fn new() -> Self { + Self { + buffer: VGA_BUFFER_ADDRESS as *mut _, + current_col: 0, + current_row: 0, + is_initialized: false, + } + } + + fn init(&mut self) { + // Disable the cursor. + unsafe { + outb(CRT_CONTROLLER_ADDRESS_PORT, CURSOR_START_REGISTER); + outb(CRT_CONTROLLER_DATA_PORT, CURSOR_DISABLE); + } + + // Clear the screen. + for r in 0..ROWS { + self.clear_row(r); + } + + // Initialization done! + self.is_initialized = true; + } + + #[inline] + fn clear_row(&mut self, row: usize) { + // Overwrite this row by a bogus character in black. + for c in 0..COLS { + unsafe { + (*self.buffer)[row][c] = VgaCharacter::new(0, ATTRIBUTE_BLACK); + } + } + } + + fn write_byte(&mut self, byte: u8) { + if !self.is_initialized { + return; + } + + // Move to the next row if we have a newline character or hit the end of a column. + if byte == b'\n' || self.current_col == COLS { + self.current_col = 0; + self.current_row += 1; + } + + // Check if we have hit the end of the screen rows. + if self.current_row == ROWS { + // Shift all rows up by one line, removing the oldest visible screen row. + for r in 1..ROWS { + for c in 0..COLS { + unsafe { + (*self.buffer)[r - 1][c] = (*self.buffer)[r][c]; + } + } + } + + // Clear the last screen row and write to it next time. + self.clear_row(ROWS - 1); + self.current_row = ROWS - 1; + } + + if byte != b'\n' { + // Put our character into the VGA screen buffer and advance the column counter. + unsafe { + (*self.buffer)[self.current_row][self.current_col] = + VgaCharacter::new(byte, ATTRIBUTE_LIGHTGREY); + } + self.current_col += 1; + } + } +} + +unsafe impl Send for VgaScreen {} + +impl fmt::Write for VgaScreen { + fn write_str(&mut self, s: &str) -> fmt::Result { + for &b in s.as_bytes() { + self.write_byte(b); + } + + Ok(()) + } +} + +pub(crate) fn init() { + VGA_SCREEN.lock().init(); +} diff --git a/src/console.rs b/src/console.rs index a743e4df..cbcf6eea 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,6 +1,9 @@ //! A wrapper around our serial console. +#[cfg(not(all(target_arch = "x86", feature = "vga")))] use crate::arch::serial; +#[cfg(all(target_arch = "x86", feature = "vga"))] +use crate::arch::vga; use core::fmt; pub struct Console; @@ -8,6 +11,12 @@ pub struct Console; impl fmt::Write for Console { /// Output a string to each of our console outputs. fn write_str(&mut self, s: &str) -> fmt::Result { - serial::COM1.lock().write_str(s) + cfg_if::cfg_if! { + if #[cfg(all(target_arch = "x86", feature = "vga"))] { + vga::VGA_SCREEN.lock().write_str(s) + } else { + serial::COM1.lock().write_str(s) + } + } } } diff --git a/src/lib.rs b/src/lib.rs index f2638425..1acdbb3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ #![no_main] // These need to be visible to the linker, so we need to export them. -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] pub use arch::processor::*; pub use logging::*; diff --git a/src/main.rs b/src/main.rs index d12d7970..dfd352ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,11 +12,12 @@ use core::panic::PanicInfo; /// This function is the entry point of the kernel #[cfg(not(test))] #[no_mangle] // don't mangle the name of this function -pub extern "C" fn main() -> ! { +pub extern "C" fn main() -> i32 { + eduos_rs::arch::init(); + println!("Hello world!"); - // shutdown system - eduos_rs::shutdown(0); + 0 } /// This function is called on panic.