diff --git a/.cargo/config.toml b/.cargo/config.toml index 973fadb..cd125e0 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,8 +3,11 @@ target = "x86_64-kernel" target-dir = "build/target" [unstable] -build-std = ["core", "compiler_builtins"] +build-std = ["core", "compiler_builtins", "alloc"] build-std-features = ["compiler-builtins-mem"] [env] RUST_TARGET_PATH = { value = "kernel", relative = true } + +[target.x86_64-kernel] +runner = "scripts/run.sh" \ No newline at end of file diff --git a/.gitea/workflows/rust.yml b/.gitea/workflows/rust.yml new file mode 100644 index 0000000..f1b3660 --- /dev/null +++ b/.gitea/workflows/rust.yml @@ -0,0 +1,22 @@ +on: + push: + branches: [ main ] + pull-request: + branches: [ main ] + +name: Continuous integration + +jobs: + build: + name: build + runs-on: ubuntu-latest + steps: + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + target: x86_64-unknown-none + components: rust-src, llvm-tools-preview + toolchain: nightly + - uses: actions/checkout@v4 + with: + submodules: true + - run: cargo build \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d74f31e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,15 @@ +[submodule "lib/lib_application"] + path = lib/lib_application + url = https://git.zxq5.dev/OsDev/FoundryOS-lib_application.git +[submodule "lib/lib_ascii"] + path = lib/lib_ascii + url = https://git.zxq5.dev/OsDev/FoundryOS-lib_ascii.git +[submodule "lib/lib_framebuffer"] + path = lib/lib_framebuffer + url = https://git.zxq5.dev/OsDev/FoundryOS-lib_framebuffer.git +[submodule "lib/lib_serial"] + path = lib/lib_serial + url = https://git.zxq5.dev/OsDev/FoundryOS-lib_serial.git +[submodule "lib/lib_alloc"] + path = lib/lib_alloc + url = https://git.zxq5.dev/OsDev/FoundryOS-lib_alloc.git diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/FoundryOS.iml b/.idea/FoundryOS.iml new file mode 100644 index 0000000..158aa3e --- /dev/null +++ b/.idea/FoundryOS.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e6c1e1c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..9bf7a27 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c7cc8ca --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "rust-analyzer.cargo.allTargets": false, + "rust-analyzer.cargo.target": "x86_64-kernel", + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true + } +} + diff --git a/Cargo.lock b/Cargo.lock index c5c8083..fdff085 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "2.8.0" @@ -9,10 +21,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] -name = "kernel" +name = "cc" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +dependencies = [ + "shlex", +] + +[[package]] +name = "foundry_os" +version = "0.1.0" +dependencies = [ + "cc", + "lib_alloc", + "lib_ascii", + "lib_framebuffer", + "lib_serial", + "limine", + "pc-keyboard", + "pic8259", + "spin", + "x86_64", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lib_alloc" +version = "0.1.0" +dependencies = [ + "linked_list_allocator", + "x86_64", +] + +[[package]] +name = "lib_application" +version = "0.1.0" +dependencies = [ + "lib_ascii", +] + +[[package]] +name = "lib_ascii" +version = "0.1.0" +dependencies = [ + "lazy_static", + "lib_framebuffer", + "spin", + "x86_64", +] + +[[package]] +name = "lib_framebuffer" version = "0.1.0" dependencies = [ "limine", + "spin", +] + +[[package]] +name = "lib_serial" +version = "0.1.0" +dependencies = [ + "lazy_static", + "spin", + "x86_64", ] [[package]] @@ -23,3 +104,91 @@ checksum = "9ca87cab008b8efeebdbe037cd4d1438037d48c5cb6fed939ffa5aa06315a321" dependencies = [ "bitflags", ] + +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "pc-keyboard" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ca629cbb3f0d5b699c338f0129ff78c9bfd7ea8b1258ad529bff490dc8ed5a" + +[[package]] +name = "pic8259" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d9a86c292b165f757e47e7fd66855def189b2564609bc4203727b27c33db22" +dependencies = [ + "x86_64", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + +[[package]] +name = "volatile" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" + +[[package]] +name = "x86_64" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" +dependencies = [ + "bit_field", + "bitflags", + "rustversion", + "volatile", +] diff --git a/Cargo.toml b/Cargo.toml index 3141e70..d9f1b96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,11 @@ [workspace] members = [ - "kernel" + "lib/lib_framebuffer", + "lib/lib_serial", + "lib/lib_ascii", + "kernel", + "lib/lib_application", + "lib/lib_alloc", ] resolver = "2" @@ -18,11 +23,10 @@ incremental = false codegen-units = 1 [profile.release] -opt-level = "z" +opt-level = 3 debug = false debug-assertions = false overflow-checks = false lto = true incremental = false codegen-units = 1 - diff --git a/README.md b/README.md index 505c9e2..e013c0a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ # FoundryOS +## Cloning and building + +Here are some simple steps to get started: + +```sh +# If you have not yet cloned the repo: +git clone --recurse-submodules https://git.zxq5.dev/OsDev/FoundryOS.git +# If you already cloned the repo: +git submodule update --init --recursive + +cargo build +# This will build the binaries if required - no need to call cargo build. +cargo run +``` + +### Build dependencies + +* jq: checks whether the app is to be run in debugging mode. +* libisoburn: creates ISO images to be booted from. +* qemu: to run the kernel. diff --git a/docs/.obsidian/app.json b/docs/.obsidian/app.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/docs/.obsidian/app.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/docs/.obsidian/appearance.json b/docs/.obsidian/appearance.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/docs/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/docs/.obsidian/core-plugins.json b/docs/.obsidian/core-plugins.json new file mode 100644 index 0000000..b977c25 --- /dev/null +++ b/docs/.obsidian/core-plugins.json @@ -0,0 +1,31 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "webviewer": false +} \ No newline at end of file diff --git a/docs/.obsidian/graph.json b/docs/.obsidian/graph.json new file mode 100644 index 0000000..42a46ec --- /dev/null +++ b/docs/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 1, + "close": true +} \ No newline at end of file diff --git a/docs/.obsidian/workspace.json b/docs/.obsidian/workspace.json new file mode 100644 index 0000000..730b8e1 --- /dev/null +++ b/docs/.obsidian/workspace.json @@ -0,0 +1,198 @@ +{ + "main": { + "id": "20da7b1c0adc4114", + "type": "split", + "children": [ + { + "id": "a20540b7c1ddbfca", + "type": "tabs", + "children": [ + { + "id": "e1fb15cab546d0b6", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "Usage/Building The Kernel.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "Building The Kernel" + } + } + ] + }, + { + "id": "6bf1f87bf81a3031", + "type": "tabs", + "children": [ + { + "id": "add883d295e04659", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "Usage/Building The Kernel.md", + "mode": "preview", + "source": false + }, + "icon": "lucide-file", + "title": "Building The Kernel" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "8c92fa79437d4f31", + "type": "split", + "children": [ + { + "id": "e4a9f8318da249b0", + "type": "tabs", + "children": [ + { + "id": "da6fd2aac65f47f1", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "Files" + } + }, + { + "id": "3f86b26f003a0d1a", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "tag:#usage", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Search" + } + }, + { + "id": "39755dd9db653e17", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Bookmarks" + } + } + ] + } + ], + "direction": "horizontal", + "width": 200, + "collapsed": true + }, + "right": { + "id": "baa2a8e61b737fa6", + "type": "split", + "children": [ + { + "id": "b628e7116722c59a", + "type": "tabs", + "children": [ + { + "id": "eb1781be59af8c30", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "Usage/Building The Kernel.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Backlinks for Building The Kernel" + } + }, + { + "id": "18e4602ee5825e11", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "Usage/Building The Kernel.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Outgoing links from Building The Kernel" + } + }, + { + "id": "df5aadad9f62dff1", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "9bc68cb074d39ab5", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "Usage/Building The Kernel.md", + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "Outline of Building The Kernel" + } + } + ], + "currentTab": 2 + } + ], + "direction": "horizontal", + "width": 300 + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Open quick switcher": false, + "graph:Open graph view": false, + "canvas:Create new canvas": false, + "daily-notes:Open today's daily note": false, + "templates:Insert template": false, + "command-palette:Open command palette": false + } + }, + "active": "e1fb15cab546d0b6", + "lastOpenFiles": [ + "Usage", + "Welcome.md", + "Usage/Building The Kernel.md" + ] +} \ No newline at end of file diff --git a/docs/Usage/Building The Kernel.md b/docs/Usage/Building The Kernel.md new file mode 100644 index 0000000..154f9f4 --- /dev/null +++ b/docs/Usage/Building The Kernel.md @@ -0,0 +1,11 @@ +#usage +### Requirements +- Latest rust nightly release +> rustup update +> rustup override set nightly +- Specific rustup components that can be installed with the following commands: +> rustup component add rust-src +> rustup component add llvm-tools-preview +### Building +run the following command (in the root directory of the project) +> ./run.sh diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 2e813c9..9958b56 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -1,14 +1,25 @@ [package] -name = "kernel" +name = "foundry_os" version = "0.1.0" edition = "2021" [dependencies] limine = "0.3.1" +lib_framebuffer = { path = "../lib/lib_framebuffer" } +lib_serial = { path = "../lib/lib_serial" } +lib_ascii = { path = "../lib/lib_ascii" } +lib_alloc = { path = "../lib/lib_alloc" } +x86_64 = "0.15.2" +spin = "0.9.8" +pic8259 = "0.11.0" +pc-keyboard = "0.8.0" + +[build-dependencies] +cc = "1.2.14" [features] default = [] [[bin]] name = "kernel" -path = "src/main.rs" \ No newline at end of file +path = "src/main.rs" diff --git a/kernel/build.rs b/kernel/build.rs index 4d43936..9553c96 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -1,3 +1,4 @@ +use cc; use std::process::Command; use std::{env, path::Path}; diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs new file mode 100644 index 0000000..2a99bf5 --- /dev/null +++ b/kernel/src/arch/mod.rs @@ -0,0 +1 @@ +pub mod x86_64; diff --git a/kernel/src/arch/x86_64/gdt.rs b/kernel/src/arch/x86_64/gdt.rs new file mode 100644 index 0000000..8d9bad4 --- /dev/null +++ b/kernel/src/arch/x86_64/gdt.rs @@ -0,0 +1,69 @@ +use x86_64::{ + instructions::tables::load_tss, + registers::segmentation::{Segment, CS, DS, ES, SS}, + structures::{ + gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}, + tss::TaskStateSegment, + }, + VirtAddr, +}; + +use spin::Lazy; + +pub const DOUBLE_FAULT_1ST_INDEX: u16 = 0; + +static TSS: Lazy = Lazy::new(|| { + let mut tss = TaskStateSegment::new(); + tss.interrupt_stack_table[DOUBLE_FAULT_1ST_INDEX as usize] = { + const STACK_SIZE: usize = 4096 * 8; + static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; + + let stack_start = VirtAddr::from_ptr(&raw const STACK); + let stack_end = stack_start + STACK_SIZE.try_into().unwrap(); + stack_end + }; + tss +}); + +static GDT: Lazy<(GlobalDescriptorTable, Selectors)> = Lazy::new(|| { + let mut gdt = GlobalDescriptorTable::new(); + let code_selector = gdt.append(Descriptor::kernel_code_segment()); + let data_selector = gdt.append(Descriptor::kernel_data_segment()); + + let user_code_selector = gdt.append(Descriptor::user_code_segment()); + let user_data_selector = gdt.append(Descriptor::user_data_segment()); + + let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); + + ( + gdt, + Selectors { + code_selector, + data_selector, + user_code_selector, + user_data_selector, + tss_selector, + }, + ) +}); + +struct Selectors { + code_selector: SegmentSelector, + data_selector: SegmentSelector, + user_code_selector: SegmentSelector, + user_data_selector: SegmentSelector, + tss_selector: SegmentSelector, +} + +pub fn init() { + GDT.0.load(); + + unsafe { + CS::set_reg(GDT.1.code_selector); + load_tss(GDT.1.tss_selector); + + DS::set_reg(self::GDT.1.data_selector); + ES::set_reg(self::GDT.1.data_selector); + SS::set_reg(self::GDT.1.data_selector); + } +} diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs new file mode 100644 index 0000000..0ce4b30 --- /dev/null +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -0,0 +1,132 @@ +use lib_ascii::{print, println_log}; +use lib_serial::serial_println; +use x86_64::instructions::port::Port; +use x86_64::registers::control::Cr2; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; + +use pic8259::ChainedPics; +use spin::{Lazy, Mutex}; + +use super::gdt; + +static IDT: Lazy = Lazy::new(|| { + let mut idt = InterruptDescriptorTable::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + + unsafe { + idt.double_fault + .set_handler_fn(double_fault_handler) + .set_stack_index(gdt::DOUBLE_FAULT_1ST_INDEX); + } + + idt.general_protection_fault + .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 +}); + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +pub static PICS: Mutex = + 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()) + } +} + +pub fn init_idt() { + IDT.load(); + unsafe { + PICS.lock().initialize(); + PICS.lock().write_masks(0xfc, 0xff); + } +} + +extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) { + serial_println!("Exception: Breakpoint\n{:#?}", stack_frame); + println_log!("Exception: Breakpoint\n{:#?}", stack_frame); +} + +extern "x86-interrupt" fn general_protection_fault_handler( + stack_frame: InterruptStackFrame, + _error_code: u64, +) { + serial_println!("Exception: General Protection Fault\n{:#?}", stack_frame); + panic!("Exception: General Protection Fault\n{:#?}", stack_frame); +} + +extern "x86-interrupt" fn double_fault_handler( + stack_frame: InterruptStackFrame, + _error_code: u64, +) -> ! { + serial_println!("Exception: Double Fault\n{:#?}", stack_frame); + panic!("Exception: Double Fault\n{:#?}", stack_frame); +} + +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; + + static KEYBOARD: Lazy>> = Lazy::new(|| { + 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) => print!("{}", character), + DecodedKey::RawKey(key) => print!("{:?}", key), + } + } + } + + unsafe { + PICS.lock() + .notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8()); + } +} + +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 page_fault_handler( + stack_frame: InterruptStackFrame, + error_code: PageFaultErrorCode, +) { + serial_println!("Exception: Page Fault"); + serial_println!("Accessed Address: {:?}", Cr2::read()); + serial_println!("Error Code: {:?}", error_code); + serial_println!("{:#?}", stack_frame); + + crate::hcf(); +} diff --git a/kernel/src/arch/x86_64/memmap.rs b/kernel/src/arch/x86_64/memmap.rs new file mode 100644 index 0000000..1b0cbbb --- /dev/null +++ b/kernel/src/arch/x86_64/memmap.rs @@ -0,0 +1,56 @@ +//! Sets up a memory map using Limine. + +use limine::{ + request::{HhdmRequest, KernelAddressRequest, MemoryMapRequest}, + response::MemoryMapResponse, +}; +use spin::Lazy; + +#[used] +#[link_section = ".requests"] +static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new(); + +#[used] +#[link_section = ".requests"] +static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new(); + +/// ```rs +/// let virt_addr = phys_addr + offset; +/// let phys_addr = virt_addr - offset; // (given VA is in the HHDM). Do not use for executable code. +/// ``` +pub static PHYSICAL_MEMORY_OFFSET: Lazy = Lazy::new(|| { + HIGHER_HALF_DIRECT_MAP_REQUEST + .get_response() + .unwrap() + .offset() +}); + +#[used] +#[link_section = ".requests"] +static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new(); + +/// Converts virtual addresses in the kernel to a physical address like this: +/// ```rs +/// let phys_addr = virt_addr - virtual_base + physical_base; +/// ``` +/// +/// Returns (virtual_base, physical_base) +pub static KERNEL_PHYSICAL_MEMORY_OFFSET: Lazy<(u64, u64)> = Lazy::new(|| { + let resp = KERNEL_ADDRESS_REQUEST.get_response().unwrap(); + + // These are base addresses, using Limine's built in page table. + (resp.virtual_base(), resp.physical_base()) +}); + +/// Fetches the memory map from Limine. +/// +/// # Panics +/// +/// Panics if the memory map was not found in MEMORY_MAP_REQUEST. +pub fn get_memory_map() -> &'static MemoryMapResponse { + if let Some(memory_map) = MEMORY_MAP_REQUEST.get_response() { + return memory_map; + } else { + unreachable!("Could not fetch memory map from Limine.") + } +} diff --git a/kernel/src/arch/x86_64/memory.rs b/kernel/src/arch/x86_64/memory.rs new file mode 100644 index 0000000..b7dcb90 --- /dev/null +++ b/kernel/src/arch/x86_64/memory.rs @@ -0,0 +1,134 @@ +use lib_alloc::allocator::FoundryAllocator; +use limine::{memory_map::EntryType, response::MemoryMapResponse}; +use x86_64::{ + addr, + registers::control::Cr3, + structures::paging::{ + page_table::FrameError, FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, + }, + PhysAddr, VirtAddr, +}; + +/// Returns a mutable reference to the current level 4 page table. +/// +/// # Safety +/// +/// The caller must ensure that the level 4 page table is not modified +/// simultaneously. The caller must also ensure that the physical memory offset +/// is correct, to ensure that the correct virtual address is constructed. +unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable { + let (level_4_frame, _) = Cr3::read(); + + let phys_addr = level_4_frame.start_address(); + let virt = phys_addr.as_u64() + physical_memory_offset.as_u64(); + &mut *(virt as *mut PageTable) +} + +/// Initializes the `OffsetPageTable` for the current CPU architecture. +/// +/// # Safety +/// +/// This function must be called only once and should be called before any +/// memory operations are performed that rely on virtual memory management. +/// The provided `physical_memory_offset` must be accurate to ensure correct +/// translation of physical addresses. +/// +/// # Parameters +/// +/// - `physical_memory_offset`: The offset to convert physical addresses to +/// virtual addresses in the higher-half direct map. +/// +/// # Returns +/// +/// Returns an `OffsetPageTable` that allows for manipulation of the page +/// tables for the current CPU architecture. +pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> { + let l4_table = active_l4_table(physical_memory_offset); + OffsetPageTable::new(l4_table, physical_memory_offset) +} + +pub(crate) struct FoundryOSFrameAllocator { + memory_map: &'static MemoryMapResponse, + next: usize, +} + +impl FoundryOSFrameAllocator { + /// Creates a new `FoundryOSFrameAllocator` from a memory map. + /// + /// This function takes a reference to a `MemoryMapResponse` and initializes a + /// `FoundryOSFrameAllocator` with it. The `next` field is set to 0, indicating that + /// the first frame to be allocated is the first frame in the memory map. + pub unsafe fn init(memory_map: &'static MemoryMapResponse) -> FoundryOSFrameAllocator { + FoundryOSFrameAllocator { + memory_map, + next: 0, + } + } + + /// An iterator over all usable frames in the memory map. + /// + /// Yields one `PhysFrame` for each available 4KiB frame in the memory map. + /// + /// This function is used to allocate frames for the pagemap. + fn usable_frames(&self) -> impl Iterator { + let regions = self.memory_map.entries().iter(); + let usable_regions = regions.filter(|region| region.entry_type == EntryType::USABLE); + + let addr_ranges = usable_regions.map(|region| region.base..region.base + region.length); + + let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); + + frame_addresses.map(|addr| PhysFrame::from_start_address(PhysAddr::new(addr)).unwrap()) + } +} + +unsafe impl FrameAllocator for FoundryOSFrameAllocator { + /// Allocates a frame from the list of usable frames. + /// + /// This function returns the next available `PhysFrame` from the memory map, + /// if one exists. Once a frame is allocated, the internal counter is incremented + /// to point to the next frame for future allocations. + /// + /// # Returns + /// + /// - `Some(PhysFrame)`: If a usable frame is available. + /// - `None`: If there are no more usable frames to allocate. + + fn allocate_frame(&mut self) -> Option { + let frame = self.usable_frames().nth(self.next); + self.next += 1; + frame + } +} + +// pub unsafe fn translate_addr(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option { +// translate_addr_inner(addr, physical_memory_offset) +// } + +// fn translate_addr_inner(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option { +// let (l4_table_frame, _) = Cr3::read(); + +// let table_indexes = [ +// addr.p4_index(), +// addr.p3_index(), +// addr.p2_index(), +// addr.p1_index(), +// ]; +// let mut frame = l4_table_frame; + +// for &i in &table_indexes { +// let virt = physical_memory_offset + frame.start_address().as_u64(); +// let table_ptr: *const PageTable = virt.as_ptr(); +// let table = unsafe { &*table_ptr }; + +// let entry = &table[i]; + +// frame = match entry.frame() { +// Ok(frame) => frame, +// Err(FrameError::FrameNotPresent) => return None, +// Err(FrameError::HugeFrame) => panic!("huge frames are not supported!"), +// }; +// } + +// Some(frame.start_address() + u64::from(addr.page_offset())) +// } diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs new file mode 100644 index 0000000..c8adf09 --- /dev/null +++ b/kernel/src/arch/x86_64/mod.rs @@ -0,0 +1,7 @@ +pub mod gdt; + +pub mod interrupts; + +pub mod memory; + +pub(crate) mod memmap; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs new file mode 100644 index 0000000..43e0ab4 --- /dev/null +++ b/kernel/src/lib.rs @@ -0,0 +1,95 @@ +#![no_std] +#![feature(abi_x86_interrupt)] + +extern crate alloc; + +use core::arch::asm; +use lib_alloc::allocator::init_heap; +use limine::request::{RequestsEndMarker, RequestsStartMarker}; +use limine::BaseRevision; + +pub use lib_ascii::{print, print_log, println, println_log, WRITER}; +pub use lib_serial::{serial_print, serial_println, serial_read}; +use x86_64::structures::paging::Translate; +use x86_64::{PhysAddr, VirtAddr}; + +mod arch; + +/// Sets the base revision to the latest revision supported by the crate. +/// See specification for further info. +/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler. +#[used] +// The .requests section allows limine to find the requests faster and more safely. +#[link_section = ".requests"] +static BASE_REVISION: BaseRevision = BaseRevision::new(); + +/// Define the stand and end markers for Limine requests. +#[used] +#[link_section = ".requests_start_marker"] +static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new(); +#[used] +#[link_section = ".requests_end_marker"] +static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new(); + +#[panic_handler] +fn rust_panic(_info: &core::panic::PanicInfo) -> ! { + println!("Kernel panic: {}", _info); + serial_println!("Kernel panic: {}", _info); + hcf(); +} + +pub fn hcf() -> ! { + loop { + unsafe { + #[cfg(target_arch = "x86_64")] + asm!("hlt"); + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + asm!("wfi"); + #[cfg(target_arch = "loongarch64")] + asm!("idle 0"); + } + } +} + +pub fn boot() -> Result<(), &'static str> { + if !BASE_REVISION.is_supported() { + return Err("base revision not supported"); + } + + use arch::x86_64::{gdt, interrupts, memmap, memory}; + + let memory_map = memmap::get_memory_map(); + + print_log!(" Initialising Serial... "); + lib_serial::init()?; + println_log!("[Success]"); + + print_log!(" Setting Up Global Descriptor Table... "); + gdt::init(); + println_log!("[Success]"); + + print_log!(" Setting Up Interrupt Descriptor Table... "); + interrupts::init_idt(); + println_log!("[Success]"); + + print_log!(" Setting Up Page Table... "); + let mut frame_allocator = unsafe { memory::FoundryOSFrameAllocator::init(memory_map) }; + println_log!("[Success]"); + + print_log!(" Initialising Memory Subsystem... "); + let physical_memory_offset = VirtAddr::new(*memmap::PHYSICAL_MEMORY_OFFSET); + let mut l4_table = unsafe { memory::init(physical_memory_offset) }; + println_log!("[Success]"); + + print_log!(" Initialising Heap... "); + if let Err(e) = init_heap(&mut l4_table, &mut frame_allocator) { + return Err("Failed to initialise heap"); + } + println_log!("[Success]"); + + print_log!(" Enabling Interrupts... "); + x86_64::instructions::interrupts::enable(); + println_log!("[Success]"); + + Ok(()) +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index a257fb9..20b95e3 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,68 +1,50 @@ #![no_std] #![no_main] -use core::arch::asm; +extern crate alloc; -use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker}; -use limine::BaseRevision; +use alloc::vec::Vec; -/// Sets the base revision to the latest revision supported by the crate. -/// See specification for further info. -/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler. -#[used] -// The .requests section allows limine to find the requests faster and more safely. -#[link_section = ".requests"] -static BASE_REVISION: BaseRevision = BaseRevision::new(); - -#[used] -#[link_section = ".requests"] -static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); - -/// Define the stand and end markers for Limine requests. -#[used] -#[link_section = ".requests_start_marker"] -static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new(); -#[used] -#[link_section = ".requests_end_marker"] -static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new(); +use foundry_os::{println, println_log}; #[no_mangle] unsafe extern "C" fn kmain() -> ! { // All limine requests must also be referenced in a called function, otherwise they may be // removed by the linker. - assert!(BASE_REVISION.is_supported()); - if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() { - if let Some(framebuffer) = framebuffer_response.framebuffers().next() { - for i in 0..100_u64 { - // Calculate the pixel offset using the framebuffer information we obtained above. - // We skip `i` scanlines (pitch is provided in bytes) and add `i * 4` to skip `i` pixels forward. - let pixel_offset = i * framebuffer.pitch() + i * 4; - - // Write 0xFFFFFFFF to the provided pixel offset to fill it white. - *(framebuffer.addr().add(pixel_offset as usize) as *mut u32) = 0xFFFFFFFF; - } - } + println_log!(" [ Initialising Kernel Systems ] "); + if let Err(err) = foundry_os::boot() { + panic!("{}", err); } - hcf(); -} + println_log!("[ Kernel Initialised Successfully ] "); -#[panic_handler] -fn rust_panic(_info: &core::panic::PanicInfo) -> ! { - hcf(); - // loop {} -} + let dimensions = lib_ascii::screensize_chars(); + let dimensions2 = lib_framebuffer::screensize_px(); -fn hcf() -> ! { - loop { - unsafe { - #[cfg(target_arch = "x86_64")] - asm!("hlt"); - #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] - asm!("wfi"); - #[cfg(target_arch = "loongarch64")] - asm!("idle 0"); - } - } -} \ No newline at end of file + println!("Dimensions: {}x{} (px)", dimensions2.0, dimensions2.1); + println!("Dimensions: {}x{} (chars)", dimensions.0, dimensions.1); + + println!( + " + $$$$$$$$\\ $$\\ + $$ _____| $$ | + $$ | $$$$$$\\ $$\\ $$\\ $$$$$$$\\ $$$$$$$ | $$$$$$\\ $$\\ $$\\ + $$$$$\\ $$ __$$\\ $$ | $$ |$$ __$$\\ $$ __$$ |$$ __$$\\ $$ | $$ | + $$ __|$$ / $$ |$$ | $$ |$$ | $$ |$$ / $$ |$$ | \\__|$$ | $$ | + $$ | $$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ | $$ | + $$ | \\$$$$$$ |\\$$$$$$ |$$ | $$ |\\$$$$$$$ |$$ | \\$$$$$$$ | + \\__| \\______/ \\______/ \\__| \\__| \\_______|\\__| \\____$$ | + $$$$$$\\ $$$$$$\\ $$\\ $$\\ $$\\ $$\\ $$ | + $$ __$$\\ $$ __$$\\ $$ | $$ |$$$$ | \\$$$$$$ | + $$ / $$ |$$ / \\__| $$ | $$ |\\_$$ | \\______/ + $$ | $$ |\\$$$$$$\\ \\$$\\ $$ | $$ | + $$ | $$ | \\____$$\\ \\$$\\$$ / $$ | + $$ | $$ |$$\\ $$ | \\$$$ / $$ | + $$$$$$ |\\$$$$$$ | \\$ / $$$$$$\\ + \\______/ \\______/ \\_/ \\______| + " + ); + + loop {} +} diff --git a/kernel/x86_64-kernel.json b/kernel/x86_64-kernel.json index 8f7231a..7b3dc4e 100644 --- a/kernel/x86_64-kernel.json +++ b/kernel/x86_64-kernel.json @@ -24,7 +24,8 @@ "--no-pie", "--gc-sections", "--build-id=none", - "-z", "max-page-size=0x1000" + "-z", + "max-page-size=0x1000" ] } } diff --git a/lib/lib_alloc b/lib/lib_alloc new file mode 160000 index 0000000..af814bf --- /dev/null +++ b/lib/lib_alloc @@ -0,0 +1 @@ +Subproject commit af814bf2ab9db4077f96ad63e3d34afcc56da942 diff --git a/lib/lib_application b/lib/lib_application new file mode 160000 index 0000000..4b1c606 --- /dev/null +++ b/lib/lib_application @@ -0,0 +1 @@ +Subproject commit 4b1c60676a2f67c2f95139ef2820238cdcbbf19e diff --git a/lib/lib_ascii b/lib/lib_ascii new file mode 160000 index 0000000..70d528b --- /dev/null +++ b/lib/lib_ascii @@ -0,0 +1 @@ +Subproject commit 70d528b2e8dc4d5b522955c248f47da74ce28678 diff --git a/lib/lib_framebuffer b/lib/lib_framebuffer new file mode 160000 index 0000000..5355327 --- /dev/null +++ b/lib/lib_framebuffer @@ -0,0 +1 @@ +Subproject commit 53553278d11e798f8dc770b9f5efe50d9b737c68 diff --git a/lib/lib_serial b/lib/lib_serial new file mode 160000 index 0000000..ed2fa6b --- /dev/null +++ b/lib/lib_serial @@ -0,0 +1 @@ +Subproject commit ed2fa6b50102607d33973c1851a0ec8c895ed4fa diff --git a/scripts/run.sh b/scripts/run.sh index 16a53c4..79359bf 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -22,7 +22,7 @@ info() { } compiling() { - echo -e "${GREEN}${BOLD}Compiling${NC} $1" + echo -e "${GREEN}${BOLD}Compiling${NC}: $1" } warning() { @@ -30,7 +30,11 @@ warning() { } building() { - echo -e "${GREEN}${BOLD}Building${NC} $1" + echo -e "${GREEN}${BOLD}Building${NC}: $1" +} + +copying() { + echo -e "${GREEN}${BOLD} Copying${NC}: $1 to $2" } error() { @@ -38,6 +42,11 @@ error() { exit 1 } +copy_file() { + copying $1 $2 + cp "$1" "$2" || error $3 +} + build_dir="$project_root/build" iso_root="$build_dir/iso_root" @@ -49,7 +58,7 @@ if [[ $1 == *"deps"* ]]; then else # Build the kernel normally cd "$project_root" - cargo build + # cargo build kernel_path="$build_dir/target/x86_64-kernel/debug/kernel" fi @@ -76,19 +85,20 @@ mkdir -p "$iso_root/EFI/BOOT" if [ ! -d "$build_dir/limine" ]; then compiling "limine bootloader" cd "$build_dir" - git clone https://github.com/limine-bootloader/limine.git --branch=v8.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine" + git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine" make -C "$build_dir/limine" || error "failed to build limine" cd "$project_root" fi # Copy files info "Copying files to ISO root" -cp -v "$kernel_path" "$iso_root/boot/kernel" || error "failed to copy kernel" -cp -v "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" || error "failed to copy limine config" -cp -v "$build_dir/limine/limine-bios.sys" "$build_dir/limine/limine-bios-cd.bin" \ - "$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" || error "failed to copy limine files" -cp -v "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" || error "failed to copy BOOTX64.EFI" -cp -v "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" || error "failed to copy BOOTIA32.EFI" +copy_file "$kernel_path" "$iso_root/boot/kernel" "failed to copy kernel" +copy_file "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" "failed to copy limine config" +copy_file "$build_dir/limine/limine-bios-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-bios-cd.bin" +copy_file "$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-uefi-cd.bin" +copy_file "$build_dir/limine/limine-bios.sys" "$iso_root/boot/limine/" "failed to copy limine-bios.sys" +copy_file "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTX64.EFI" +copy_file "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTIA32.EFI" # Create ISO building "bootable ISO image"