diff --git a/README.md b/README.md index 23f7e26..fe03e6b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ git clone https://git.zxq5.dev/OsDev/FoundryOS.git * latest rust nightly release * all necessary rust components installed * xorriso: creates ISO images to be booted from. -* (Optional / Recommended) Qemu: to run the kernel. (this may be packaged as qemu-desktop) +* (Optional / Recommended) qemu: to run the kernel. (this may be packaged as qemu-desktop) * (Optional) GDB: for debugging the kernel. ```sh @@ -20,12 +20,29 @@ rustup component add rust-src rustup component add llvm-tools-preview ``` -## Building & Running in Qemu: +## Building & Running in qemu ```sh cargo run ``` -## Running in GDB: +## Running in GDB ```sh USE_GDB=1 cargo run ``` + +## Build errors + +If you see a qemu error like this: + +``` +qemu-system-x86_64: -drive if=pflash,format=raw,readonly=on,file=<...>/OVMF_CODE.fd: Could not open '<...>/OVMF_CODE.fd': No such file or directory +``` + +Simply delete the ./build directory and try to rebuild the program. Using the runner script or `cargo run` will download the required files for you, this is because the script only checks for the presence of one file and not the VARS file. + +Alternatively, you may disable using a UEFI firmware with qemu like so: +```sh +USE_LEGACY_BIOS=1 cargo run +``` + +If you have any other issues, feel free to create an issue or a PR. \ No newline at end of file diff --git a/config/limine.conf b/config/limine.conf index 0988692..33e4a36 100644 --- a/config/limine.conf +++ b/config/limine.conf @@ -1,5 +1,5 @@ # Timeout in seconds that Limine will use before automatically booting. -timeout: 1 +timeout: 0 # The entry name that will be displayed in the boot menu. /foundry-os diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 8035918..2d9dd92 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,6 +1,6 @@ use libk::drivers::apic::enable_apic; -use libk::drivers::pic::ChainedPics; use libk::prelude::*; +use pic8259::ChainedPics; use x86_64::registers::control::Cr2; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; @@ -22,8 +22,8 @@ static IDT: Lazy = Lazy::new(|| { .set_handler_fn(general_protection_fault_handler); idt.page_fault.set_handler_fn(page_fault_handler); - // idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); - // idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); + idt[InterruptIndex::Timer.as_u8()].set_handler_fn(timer_interrupt_handler); + idt[InterruptIndex::Keyboard.as_u8()].set_handler_fn(keyboard_interrupt_handler); idt }); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index d81265e..1eb77f5 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -5,8 +5,8 @@ extern crate alloc; use libk::{ drivers::{ + async_io::task::{Executor, Task}, io, - scheduling::task::{Executor, Task}, }, prelude::*, util::shell::shell, diff --git a/libk/src/drivers/apic/mod.rs b/libk/src/drivers/apic/mod.rs index 1c59e9e..7e47e8f 100644 --- a/libk/src/drivers/apic/mod.rs +++ b/libk/src/drivers/apic/mod.rs @@ -43,3 +43,7 @@ pub fn enable_apic() { write_apic_register(0xF0, read_apic_register(0xF0) | 0x100); } + +pub struct Apic {} + +pub enum ApicVector {} diff --git a/libk/src/drivers/scheduling/mod.rs b/libk/src/drivers/async_io/mod.rs similarity index 100% rename from libk/src/drivers/scheduling/mod.rs rename to libk/src/drivers/async_io/mod.rs diff --git a/libk/src/drivers/scheduling/task.rs b/libk/src/drivers/async_io/task.rs similarity index 95% rename from libk/src/drivers/scheduling/task.rs rename to libk/src/drivers/async_io/task.rs index 392638b..6076917 100644 --- a/libk/src/drivers/scheduling/task.rs +++ b/libk/src/drivers/async_io/task.rs @@ -1,3 +1,8 @@ +//! Allows creation of asynchronous IO bound tasks. +//! +//! Written by @zxq5 for the most part with code from +//! [here](https://github.com/phil-opp/blog_os/). +//! use crate::prelude::*; use alloc::collections::BTreeMap; use alloc::sync::Arc; diff --git a/libk/src/drivers/mod.rs b/libk/src/drivers/mod.rs index 5ed6e5e..0222bb5 100644 --- a/libk/src/drivers/mod.rs +++ b/libk/src/drivers/mod.rs @@ -1,7 +1,7 @@ pub mod io; pub mod kalloc; -pub mod scheduling; pub mod apic; +pub mod async_io; pub mod cpu; pub mod pic; diff --git a/libk/src/lib.rs b/libk/src/lib.rs index 1846161..57f60cb 100644 --- a/libk/src/lib.rs +++ b/libk/src/lib.rs @@ -1,6 +1,4 @@ #![no_std] -#![allow(async_fn_in_trait)] -#![warn(tail_expr_drop_order)] #![warn( clippy::correctness, clippy::nursery, @@ -9,18 +7,15 @@ clippy::suspicious, clippy::perf, rustdoc::missing_errors_doc, - rustdoc::missing_panics_doc + rustdoc::missing_panics_doc, + tail_expr_drop_order )] -// alloc -// io : serial, framebuffer, ascii(?), keyboard -// ????? -// scheduling / tasks : async - extern crate alloc; pub mod drivers; pub mod resources; +pub mod threads; pub mod util; #[allow(unused)] // We aren't using much of this right now. diff --git a/libk/src/std/application.rs b/libk/src/std/application.rs index 44c2651..59ce535 100644 --- a/libk/src/std/application.rs +++ b/libk/src/std/application.rs @@ -7,7 +7,10 @@ mod window; pub trait Application { type Output; - async fn run(&mut self, args: Vec) -> Result; + fn run( + &mut self, + args: Vec, + ) -> impl core::future::Future> + Send; } #[derive(Debug)] diff --git a/libk/src/std/io/io.rs b/libk/src/std/io.rs similarity index 100% rename from libk/src/std/io/io.rs rename to libk/src/std/io.rs diff --git a/libk/src/std/io/mod.rs b/libk/src/std/io/mod.rs deleted file mode 100644 index 608d4e1..0000000 --- a/libk/src/std/io/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod io; -pub use io::*; diff --git a/libk/src/threads.rs b/libk/src/threads.rs new file mode 100644 index 0000000..76c2c13 --- /dev/null +++ b/libk/src/threads.rs @@ -0,0 +1,113 @@ +use core::arch::asm; + +#[repr(C)] +pub struct Thread { + id: usize, + /// This shall be default before the program is interrupted, otherwise it will store + /// CPU registers etc to be restored on context switch. + ctx: ThreadContext, +} + +/// CPU state to be saved on context switches. +#[repr(C)] +#[derive(Default)] +pub struct ThreadContext { + /// Accumulator register. + rax: u64, + /// Base register. + rbx: u64, + /// Counter register. + rcx: u64, + /// Data register. + rdx: u64, + /// Source index register. + rsi: u64, + /// Destination index register. + rdi: u64, + /// Base pointer register. + rbp: u64, + /// Stack pointer register. + rsp: u64, + /// An extended register. + r8: u64, + /// An extended register. + r9: u64, + /// An extended register. + r10: u64, + /// An extended register. + r11: u64, + /// An extended register. + r12: u64, + /// An extended register. + r13: u64, + /// An extended register. + r14: u64, + /// An extended register. + r15: u64, + /// The instruction pointer. + rip: u64, + /// RFLAGS register. + rflags: u64, +} + +impl ThreadContext { + /// Saves the current registers of the CPU before a context switch + /// to be restored later. + /// + /// # Notes + /// + /// This function should ONLY be called in interrupt handlers such + /// as that of the timer. This will then save registers as required + /// + /// + /// # Safety + /// + /// This function is unsafe because of the usage of inline ASM. + #[inline(always)] + pub unsafe fn save_registers() -> Self { + let mut context = Self::default(); + unsafe { + asm!( + "mov {0}, rax", + "mov {1}, rbx", + "mov {2}, rcx", + "mov {3}, rdx", + "mov {4}, rsi", + "mov {5}, rdi", + "mov {6}, rbp", + "mov {7}, rsp", + "mov {8}, r8", + "mov {9}, r9", + "mov {10}, r10", + "mov {11}, r11", + "mov {12}, r12", + "mov {13}, r13", + "mov {14}, r14", + "mov {15}, r15", + "lea {16}, [rip]", + "pushf", + "pop {17}", + out(reg) context.rax, + out(reg) context.rbx, + out(reg) context.rcx, + out(reg) context.rdx, + out(reg) context.rsi, + out(reg) context.rdi, + out(reg) context.rbp, + out(reg) context.rsp, + out(reg) context.r8, + out(reg) context.r9, + out(reg) context.r10, + out(reg) context.r11, + out(reg) context.r12, + out(reg) context.r13, + out(reg) context.r14, + out(reg) context.r15, + out(reg) context.rip, + out(reg) context.rflags, + ); + } + + context + } +} diff --git a/libk/src/util/shell.rs b/libk/src/util/shell.rs index fceb8ee..cf851de 100644 --- a/libk/src/util/shell.rs +++ b/libk/src/util/shell.rs @@ -1,4 +1,4 @@ -use x86_64::registers::rflags::read; +// use x86_64::registers::rflags::read; use crate::{drivers::io::ascii::clear_screen, prelude::stdin::read_line, print, println}; diff --git a/scripts/hardware.sh b/scripts/hardware.sh index 5070e47..cd03a7c 100755 --- a/scripts/hardware.sh +++ b/scripts/hardware.sh @@ -1,2 +1,24 @@ #!/bin/bash -sudo dd if=./build/image.iso of="$1" + +# Colors +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +info() { + echo -e "${BLUE}${BOLD}info${NC}: $1" +} + +warning() { + echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2 +} + +warning "This script will OVERWRITE whatever media you throw at it\nwith the built ISO." +info "sudo ./hardware.sh /dev/yourdisk" + +if echo "$1" | grep -q "/dev"; then + dd if=./build/image.iso of="$1" +fi diff --git a/scripts/run_debug.sh b/scripts/run_debug.sh index b538ce7..91bcdb6 100755 --- a/scripts/run_debug.sh +++ b/scripts/run_debug.sh @@ -135,6 +135,23 @@ else debug_flags="" fi +if [ $USE_LEGACY_BIOS ]; then + boot_flags="" +else +# Check for the presence of the OVMF firmware. + if [ ! -f $build_dir/RELEASEX64_OVMF_CODE.fd ]; then + info "Downloading OVMF UEFI firmware for QEMU" + info "To disable this, set USE_LEGACY_BIOS=1." + pushd $build_dir + curl https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_CODE.fd -LO || error "failed to download OVMF firmware for UEFI" + curl https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_VARS.fd -LO || error "failed to download OVMF firmware for UEFI" + popd + fi + + boot_flags="-drive if=pflash,format=raw,readonly=on,file=$build_dir/RELEASEX64_OVMF_CODE.fd \ + -drive if=pflash,format=raw,file=$build_dir/RELEASEX64_OVMF_VARS.fd" +fi + # Set up test-specific flags if [ $is_test -eq 1 ]; then test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none" @@ -189,5 +206,6 @@ qemu-system-x86_64 -M q35 \ -no-reboot \ ${test_flags} \ ${debug_flags} \ + ${boot_flags} \ ${QEMU_FLAGS:-} diff --git a/scripts/run_release.sh b/scripts/run_release.sh index 97f6134..c410362 100755 --- a/scripts/run_release.sh +++ b/scripts/run_release.sh @@ -131,6 +131,23 @@ fi # I'm lazy but I just remove GDB flags when running this script. debug_flags="" +if [ $USE_LEGACY_BIOS ]; then + boot_flags="" +else +# Check for the presence of the OVMF firmware. + if [ ! -f $build_dir/RELEASEX64_OVMF_CODE.fd ]; then + info "Downloading OVMF UEFI firmware for QEMU" + info "To disable this, set USE_LEGACY_BIOS=1." + pushd $build_dir + curl https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_CODE.fd -LO || error "failed to download OVMF firmware for UEFI" + curl https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_VARS.fd -LO || error "failed to download OVMF firmware for UEFI" + popd + fi + + boot_flags="-drive if=pflash,format=raw,readonly=on,file=$build_dir/RELEASEX64_OVMF_CODE.fd \ + -drive if=pflash,format=raw,file=$build_dir/RELEASEX64_OVMF_VARS.fd" +fi + # Set up test-specific flags if [ $is_test -eq 1 ]; then test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none"