diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2026-07-17 19:16:37 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2026-07-17 19:16:37 +0800 |
| commit | b4d9a57f193c64bca188a76e4f711cc168da9737 (patch) | |
| tree | 81258da175f58bac49bb8992240c50bf8b154e11 | |
init
with limine as bootloader, changing soon
| -rw-r--r-- | .cargo/config.toml | 16 | ||||
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 23 | ||||
| -rw-r--r-- | Cargo.toml | 32 | ||||
| -rw-r--r-- | OVMF_VARS.4m.fd | bin | 0 -> 540672 bytes | |||
| -rw-r--r-- | README.md | 24 | ||||
| m--------- | limine | 0 | ||||
| -rw-r--r-- | limine.conf | 6 | ||||
| -rw-r--r-- | linker.ld | 47 | ||||
| -rw-r--r-- | qemu.log | 5 | ||||
| -rw-r--r-- | qemu_output.txt | 4 | ||||
| -rw-r--r-- | readelf.out | 23 | ||||
| -rw-r--r-- | report.typ | 13 | ||||
| -rw-r--r-- | src/io.rs | 1 | ||||
| -rw-r--r-- | src/io/render.rs | 1 | ||||
| -rw-r--r-- | src/io/render/fb.rs | 2 | ||||
| -rw-r--r-- | src/io/render/fb/graphic.rs | 0 | ||||
| -rw-r--r-- | src/io/render/fb/text.rs | 1 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/main.rs | 76 | ||||
| -rw-r--r-- | target.json | 19 |
21 files changed, 297 insertions, 0 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..12f8cca --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,16 @@ +[build] +target = "target.json" + +[unstable] +build-std = ["core", "compiler_builtins", "alloc"] +build-std-features = ["compiler-builtins-mem"] + +[target.target] +# Limage compatibility with `cargo run` +runner = "limage runner" +# Required for Cargo to pass the correct flags to the linker before running `limage runner` +rustflags = ["-C", "relocation-model=static", "-C", "link-arg=linker.ld", "-C", "code-model=kernel"] + +# [target.target] +# rustflags = ["-C", "link-arg=-Tlinker.ld", "-C", "link-arg=-zmax-page-size=0x1000"] +# runner = "./run.sh " diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c3ebf11 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "font8x8" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875488b8711a968268c7cf5d139578713097ca4635a76044e8fe8eedf831d07e" + +[[package]] +name = "limine" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29363c0f37e66e18575fadf7141c56ee7ea04ae5fecbeb25eff303f77af203a9" + +[[package]] +name = "rust-os" +version = "0.1.0" +dependencies = [ + "font8x8", + "limine", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4f49da8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "rust-os" +version = "0.1.0" +edition = "2024" + +[[bin]] +name = "rust-os" +test = false +bench = false + +[lib] +test = false + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[dependencies] +font8x8 = { version = "0.3.1", default-features = false, features = ["unicode"] } +limine = "0.6.5" + +[package.metadata.limage] +run-command = [ + "qemu-system-x86_64", + "-drive", "if=pflash,format=raw,readonly=on,file=/usr/share/edk2-ovmf/x64/OVMF_CODE.4m.fd", + "-drive", "if=pflash,format=raw,file=OVMF_VARS.4m.fd", + "-no-reboot", + "-d", "int", + "-cdrom", "{}" +] diff --git a/OVMF_VARS.4m.fd b/OVMF_VARS.4m.fd Binary files differnew file mode 100644 index 0000000..c363987 --- /dev/null +++ b/OVMF_VARS.4m.fd diff --git a/README.md b/README.md new file mode 100644 index 0000000..bb7be93 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Rust OS + +A bare-metal Rust OS kernel utilizing the Limine bootloader protocol v8.x. + +This repository demonstrates how to set up a minimal `x86_64` rust kernel that uses `limine` `0.6.5` to retrieve a framebuffer, draw a built-in bitmap font (`font8x8`) to print "Hello World!" on the screen, and output "Hello World!" to the serial port as a fallback. + +## Building and Running + +1. **Build and Run** + ```bash + cargo run + ``` + The `runner.sh` script will automatically: + - Build the Rust kernel via Cargo + - Clone the Limine binary release + - Build a bootable ISO with `xorriso` and deploy `limine` + - Boot the ISO in QEMU + +## Design Notes + +- The Limine crate version `0.6.5` relies on raw protocol structures such as `BaseRevision`, `RequestsStartMarker`, `RequestsEndMarker`, and the standard `FramebufferRequest`. +- Built-in terminal requests were dropped from Limine 5.x+ and therefore they do not exist in the `limine` `0.6.5` crate. +- We utilize `font8x8` to iterate over character glyphs and plot the pixels directly to the `Framebuffer` slice obtained via Limine. +- Serial port output is written to `0x3F8` using inline `outb` assembly. diff --git a/limine b/limine new file mode 160000 +Subproject aad3edd370955449717a334f0289dee10e2c5f0 diff --git a/limine.conf b/limine.conf new file mode 100644 index 0000000..be16e24 --- /dev/null +++ b/limine.conf @@ -0,0 +1,6 @@ +timeout: 0 +serial: yes + +/Rust OS + protocol: limine + kernel_path: boot():/boot/kernel/kernel diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..f71177b --- /dev/null +++ b/linker.ld @@ -0,0 +1,47 @@ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) + +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ + rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ + data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ + dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic */ +} + +SECTIONS +{ + . = 0xffffffff80000000; + + .text : { + *(.text .text.*) + } :text + + . += CONSTANT(MAXPAGESIZE); + + .rodata : { + *(.rodata .rodata.*) + } :rodata + + . += CONSTANT(MAXPAGESIZE); + + .data : { + *(.data .data.*) + + KEEP(*(.requests_start_marker)) + KEEP(*(.requests)) + KEEP(*(.requests_end_marker)) + } :data + + .bss : { + *(COMMON) + *(.bss .bss.*) + } :data + + /DISCARD/ : { + *(.eh_frame) + *(.note .note.*) + } +} diff --git a/qemu.log b/qemu.log new file mode 100644 index 0000000..02feb78 --- /dev/null +++ b/qemu.log @@ -0,0 +1,5 @@ +limine: Loading executable `boot():/boot/rust-os`...
+Entered _start! +BASE_REVISION not supported! +Hello World! +qemu-system-x86_64: terminating on signal 15 from pid 773461 (timeout) diff --git a/qemu_output.txt b/qemu_output.txt new file mode 100644 index 0000000..3441336 --- /dev/null +++ b/qemu_output.txt @@ -0,0 +1,4 @@ +limine: Loading executable `boot():/boot/rust-os`...
+Entered _start! +BASE_REVISION not supported! +qemu-system-x86_64: terminating on signal 15 from pid 799698 (timeout) diff --git a/readelf.out b/readelf.out new file mode 100644 index 0000000..f47369f --- /dev/null +++ b/readelf.out @@ -0,0 +1,23 @@ + +Elf file type is EXEC (Executable file) +Entry point 0xffffffff80000400 +There are 4 program headers, starting at offset 64 + +Program Headers: + Type Offset VirtAddr PhysAddr + FileSiz MemSiz Flags Align + LOAD 0x0000000000001000 0xffffffff80000000 0xffffffff80000000 + 0x000000000000d8dc 0x000000000000d8dc R E 0x1000 + LOAD 0x000000000000e8e0 0xffffffff8000e8e0 0xffffffff8000e8e0 + 0x0000000000003dbd 0x0000000000003dbd R 0x1000 + LOAD 0x00000000000126a0 0xffffffff800136a0 0xffffffff800136a0 + 0x0000000000000078 0x0000000000000078 RW 0x1000 + DYNAMIC 0x0000000000000000 0x0000000000000000 0x0000000000000000 + 0x0000000000000000 0x0000000000000000 RW 0x0 + + Section to Segment mapping: + Segment Sections... + 00 .text + 01 .rodata + 02 .data + 03 diff --git a/report.typ b/report.typ new file mode 100644 index 0000000..eb02d77 --- /dev/null +++ b/report.typ @@ -0,0 +1,13 @@ +i got inspiration from this #link("https://os.phil-opp.com")[blog] + +booatloader selection: +i could just use qemu skipping to kernel but i wanted to explore about bootloaders too since im making a os, but i dont wanna spend all my time just doing repetitive stuff and dealing with legacy stuff +two realistic options only: +#link("https://crates.io/crates/bootloader_api")[bootloader_api] which is what the aforementioned blog uses +rejected because: +- don't wanna deal with manually setting up SMP +- hands me a poor memory base my mmu off of +- more niche(thereforee less mature/popular and therefore getting less support) and only gain traction due to the aforementioned blog post + - due to that it's tightly coupled with it +#link("https://crates.io/crates/limine")[limine] +- cons: no documentation diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..cf25e6c --- /dev/null +++ b/src/io.rs @@ -0,0 +1 @@ +pub mod render; diff --git a/src/io/render.rs b/src/io/render.rs new file mode 100644 index 0000000..402daf7 --- /dev/null +++ b/src/io/render.rs @@ -0,0 +1 @@ +pub mod fb; diff --git a/src/io/render/fb.rs b/src/io/render/fb.rs new file mode 100644 index 0000000..2924838 --- /dev/null +++ b/src/io/render/fb.rs @@ -0,0 +1,2 @@ +pub mod graphic; +pub mod text; diff --git a/src/io/render/fb/graphic.rs b/src/io/render/fb/graphic.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/io/render/fb/graphic.rs diff --git a/src/io/render/fb/text.rs b/src/io/render/fb/text.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/io/render/fb/text.rs @@ -0,0 +1 @@ + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f0111d3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +pub mod io; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bf338df --- /dev/null +++ b/src/main.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] +#![feature(used_with_arg)] + +use core::panic::PanicInfo; +use font8x8::UnicodeFonts; +use limine::{BaseRevision, request::FramebufferRequest}; + +#[used(linker)] +#[unsafe(link_section = ".requests")] +static BASE_REVISION: BaseRevision = BaseRevision::new(); + +#[used(linker)] +#[unsafe(link_section = ".requests")] +static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); + +#[unsafe(no_mangle)] +pub extern "C" fn _start() -> ! { + if !BASE_REVISION.is_supported() { + panic!("Limine Base Revision not supported"); + } + + if let Some(response) = FRAMEBUFFER_REQUEST.response() { + if let Some(fb) = response + .framebuffers() + .first() + { + let pitch = fb.pitch as usize; + let bpp = fb.bpp as usize; + + if bpp == 32 { + let slice = unsafe { fb.as_slice_mut() }; + let msg = "Hello World!"; + let mut cursor_x = 10; + let cursor_y = 10; + + for c in msg.chars() { + if let Some(glyph) = font8x8::BASIC_FONTS.get(c) { + for (y, row) in glyph + .iter() + .enumerate() + { + for x in 0..8 { + if (*row & (1 << x)) != 0 { + let pixel_offset = (cursor_y + y) * pitch + (cursor_x + x) * 4; + if pixel_offset + 3 < slice.len() { + slice[pixel_offset] = 0xFF; // B + slice[pixel_offset + 1] = 0xFF; // G + slice[pixel_offset + 2] = 0xFF; // R + } + } + } + } + } + cursor_x += 8; + } + } + } + } + + hcf(); +} + +fn hcf() -> ! { + unsafe { + core::arch::asm!("cli"); + loop { + core::arch::asm!("hlt"); + } + } +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + hcf() +} diff --git a/target.json b/target.json new file mode 100644 index 0000000..7914b12 --- /dev/null +++ b/target.json @@ -0,0 +1,19 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": 64, + "target-c-int-width": 32, + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float", + "rustc-abi": "x86-softfloat", + "relocation-model": "static", + "position-independent-executables": false, + "code-model": "kernel" +} |
