6 Commits

Author SHA1 Message Date
092004bab0 Add qemu serial output to stdio
Signed-off-by: Noah Knegt <git@noahknegt.com>
2025-08-02 19:12:56 +02:00
022ec27bd5 Update LICENSE information
Signed-off-by: Noah Knegt <git@noahknegt.com>
2025-08-02 19:12:30 +02:00
bf3923acd0 chore: Pin the nightly version (#5)
Signed-off-by: Noah Knegt <git@noahknegt.com>
Co-authored-by: Noah Knegt <git@noahkengt.com>
Reviewed-on: #5
2025-07-30 23:02:43 +02:00
4bd1e3bc6a feat: Add serial logging (#3)
Reviewed-on: #3
2025-07-22 19:17:20 +02:00
d8159a373e feat(kernel): Add logging to the kernel (#2)
This PR will allow the kernel to have a simple logging using a FrameBuffer created by the bootloader crate.

Reviewed-on: #2
2025-07-21 22:58:31 +02:00
f0ae99f652 feat(OS): Create basic kernel that can be run through both QEMU and on hardware (#1)
Reviewed-on: #1
2025-07-20 21:15:05 +02:00
14 changed files with 1229 additions and 12 deletions

4
.cargo/config.toml Normal file
View File

@@ -0,0 +1,4 @@
[unstable]
# enable the unstable artifact-dependencies feature, see
# https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies
bindeps = true

757
Cargo.lock generated Normal file
View File

@@ -0,0 +1,757 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "FerrOS"
version = "0.1.0"
dependencies = [
"bootloader",
"kernel",
"ovmf-prebuilt",
]
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "bootloader"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718ebb60a92a2e37f9c6c6ab26f0adfd117aa6f1a8cd08d07bbd5a7762ff0812"
dependencies = [
"anyhow",
"bootloader-boot-config",
"fatfs",
"gpt",
"llvm-tools",
"mbrman",
"serde_json",
"tempfile",
]
[[package]]
name = "bootloader-boot-config"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a4ff46e7fd938aa06b17d44dce17f605105da3df01b1ae8a7f02d248c9b12f6"
dependencies = [
"serde",
]
[[package]]
name = "bootloader_api"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77fadafc2a2fa690f4f14a2e2a41c65d9e810b7fe959245bdf6d3b9c6d7e4bf1"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "conquer-once"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d008a441c0f269f36ca13712528069a86a3e60dffee1d98b976eb3b0b2160b4"
dependencies = [
"conquer-util",
]
[[package]]
name = "conquer-util"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582"
[[package]]
name = "crc"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "errno"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.60.2",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fatfs"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05669f8e7e2d7badc545c513710f0eba09c2fbef683eb859fd79c46c355048e0"
dependencies = [
"bitflags 1.3.2",
"byteorder",
"log",
]
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "gpt"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69"
dependencies = [
"bitflags 2.9.1",
"crc",
"log",
"uuid",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "kernel"
version = "0.1.0"
dependencies = [
"bootloader_api",
"conquer-once",
"log",
"noto-sans-mono-bitmap",
"spinning_top",
"uart_16550",
"x86_64",
]
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "llvm-tools"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf"
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "mbrman"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1fc3bff63c208d4a14301c6cb807af2d1a0760052584ce3f9a737b55fb85498"
dependencies = [
"bincode",
"bitvec",
"serde",
"serde-big-array",
"thiserror",
]
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "noto-sans-mono-bitmap"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1064d564ae026ae123bf5d607318b42a5b31d70a3c48e6ea7ee44cce4cdb095e"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "ovmf-prebuilt"
version = "0.1.0-alpha.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa50141d081512ab30fd9e7e7692476866df5098b028536ad6680212e717fa8d"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "raw-cpuid"
version = "10.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "rustix"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
dependencies = [
"bitflags 2.9.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.60.2",
]
[[package]]
name = "rustversion"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-big-array"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f"
dependencies = [
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "spinning_top"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
dependencies = [
"lock_api",
]
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tempfile"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "uart_16550"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e492212ac378a5e00da953718dafb1340d9fbaf4f27d6f3c5cab03d931d1c049"
dependencies = [
"bitflags 2.9.1",
"rustversion",
"x86",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "uuid"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "volatile"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.2",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "x86"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385"
dependencies = [
"bit_field",
"bitflags 1.3.2",
"raw-cpuid",
]
[[package]]
name = "x86_64"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae"
dependencies = [
"bit_field",
"bitflags 2.9.1",
"rustversion",
"volatile",
]

View File

@@ -1,6 +1,34 @@
[package] [workspace]
name = "FerrOS" resolver = "3"
members = [
"kernel"
]
[workspace.package]
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
license-file = "LICENSE"
authors = [
"Noah Knegt <personal@noahknegt.com>"
]
readme = "README.md"
publish = false
[workspace.dependencies]
[package]
name = "FerrOS"
version.workspace = true
authors.workspace = true
edition.workspace = true
license-file.workspace = true
readme.workspace = true
publish.workspace = true
[build-dependencies]
bootloader = { version = "0.11.10" }
kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none" }
[dependencies] [dependencies]
ovmf-prebuilt = "0.1.0-alpha.1"

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2025 noah.knegt Copyright (c) 2025 Noah Knegt <personal@noahknegt.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including associated documentation files (the "Software"), to deal in the Software without restriction, including

View File

@@ -2,3 +2,25 @@
A custom OS written in Rust A custom OS written in Rust
## Running the OS
You can start running the OS by executing:
```bash
cargo run
```
This assumes that you have a rust environment setup and cargo is available, the easiest way to do this is with [rustup](https://rustup.rs/).
### Dependencies
You should have the command `qemu-system-x86_64` available on your system to run the OS through an emulator, although it is also possible to flash it on an USB-drive and boot it directly.
For debian users you can install the [`qemu-system-x86`](https://packages.debian.org/sid/qemu-system-x86) package and that will make sure all dependencies for the emulator are correctly installed.
### Install to USB-drive
To install it to an USB-drive you can use the following command if you are on a linux system.
```bash
sudo dd if=<path-to-project>/target/<config>/FerrOS of=/dev/sdX bs=1M status=progress
```

21
build.rs Normal file
View File

@@ -0,0 +1,21 @@
use std::path::PathBuf;
fn main() {
// set by cargo, build scripts should use this directory for output files
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
// set by cargo's artifact dependency feature, see
// https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies
let kernel = PathBuf::from(std::env::var_os("CARGO_BIN_FILE_KERNEL_kernel").unwrap());
// create an UEFI disk image (optional)
let uefi_path = out_dir.join("uefi.img");
bootloader::UefiBoot::new(&kernel).create_disk_image(&uefi_path).unwrap();
// create a BIOS disk image
let bios_path = out_dir.join("bios.img");
bootloader::BiosBoot::new(&kernel).create_disk_image(&bios_path).unwrap();
// pass the disk image paths as env variables to the `main.rs`
println!("cargo:rustc-env=UEFI_PATH={}", uefi_path.display());
println!("cargo:rustc-env=BIOS_PATH={}", bios_path.display());
}

28
kernel/Cargo.toml Normal file
View File

@@ -0,0 +1,28 @@
[package]
name = "kernel"
version = "0.1.0"
edition.workspace = true
license-file.workspace = true
authors.workspace = true
readme.workspace = true
publish.workspace = true
[dependencies]
bootloader_api = { version = "0.11.10" }
conquer-once = { version = "0.4.0", default-features = false }
log = "0.4.17"
spinning_top = "0.3.0"
uart_16550 = "0.3.2"
x86_64 = "0.15.2"
[dependencies.noto-sans-mono-bitmap]
version = "0.3.1"
default-features = false
features = [
"regular",
"size_16",
"unicode-basic-latin",
# required for the fallback char '<27>'
"unicode-specials",
]

View File

@@ -0,0 +1,154 @@
use bootloader_api::info::{FrameBufferInfo, PixelFormat};
use core::{fmt, ptr};
use font_constants::BACKUP_CHAR;
use noto_sans_mono_bitmap::{
get_raster, get_raster_width, FontWeight, RasterHeight, RasterizedChar,
};
/// Additional vertical space between lines
const LINE_SPACING: usize = 2;
/// Additional horizontal space between characters.
const LETTER_SPACING: usize = 0;
/// Padding from the border. Prevent that font is too close to border.
const BORDER_PADDING: usize = 1;
/// Constants for the usage of the [`noto_sans_mono_bitmap`] crate.
mod font_constants {
use super::*;
/// Height of each char raster. The font size is ~0.84% of this. Thus, this is the line height that
/// enables multiple characters to be side-by-side and appear optically in one line in a natural way.
pub const CHAR_RASTER_HEIGHT: RasterHeight = RasterHeight::Size16;
/// The width of each single symbol of the mono space font.
pub const CHAR_RASTER_WIDTH: usize = get_raster_width(FontWeight::Regular, CHAR_RASTER_HEIGHT);
/// Backup character if a desired symbol is not available by the font.
/// The '<27>' character requires the feature "unicode-specials".
pub const BACKUP_CHAR: char = '<27>';
pub const FONT_WEIGHT: FontWeight = FontWeight::Regular;
}
/// Returns the raster of the given char or the raster of [`font_constants::BACKUP_CHAR`].
fn get_char_raster(c: char) -> RasterizedChar {
fn get(c: char) -> Option<RasterizedChar> {
get_raster(
c,
font_constants::FONT_WEIGHT,
font_constants::CHAR_RASTER_HEIGHT,
)
}
get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("Should get raster of backup char."))
}
/// Allows logging text to a pixel-based framebuffer.
pub struct FrameBufferWriter {
framebuffer: &'static mut [u8],
info: FrameBufferInfo,
x_pos: usize,
y_pos: usize,
}
impl FrameBufferWriter {
/// Creates a new logger that uses the given framebuffer.
pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self {
let mut logger = Self {
framebuffer,
info,
x_pos: 0,
y_pos: 0,
};
logger.clear();
logger
}
fn newline(&mut self) {
self.y_pos += font_constants::CHAR_RASTER_HEIGHT.val() + LINE_SPACING;
self.carriage_return()
}
fn carriage_return(&mut self) {
self.x_pos = BORDER_PADDING;
}
/// Erases all text on the screen. Resets `self.x_pos` and `self.y_pos`.
pub fn clear(&mut self) {
self.x_pos = BORDER_PADDING;
self.y_pos = BORDER_PADDING;
self.framebuffer.fill(0);
}
fn width(&self) -> usize {
self.info.width
}
fn height(&self) -> usize {
self.info.height
}
/// Writes a single char to the framebuffer. Takes care of special control characters, such as
/// newlines and carriage returns.
fn write_char(&mut self, c: char) {
match c {
'\n' => self.newline(),
'\r' => self.carriage_return(),
c => {
let new_xpos = self.x_pos + font_constants::CHAR_RASTER_WIDTH;
if new_xpos >= self.width() {
self.newline();
}
let new_ypos =
self.y_pos + font_constants::CHAR_RASTER_HEIGHT.val() + BORDER_PADDING;
if new_ypos >= self.height() {
self.clear();
}
self.write_rendered_char(get_char_raster(c));
}
}
}
/// Prints a rendered char into the framebuffer.
/// Updates `self.x_pos`.
fn write_rendered_char(&mut self, rendered_char: RasterizedChar) {
for (y, row) in rendered_char.raster().iter().enumerate() {
for (x, byte) in row.iter().enumerate() {
self.write_pixel(self.x_pos + x, self.y_pos + y, *byte);
}
}
self.x_pos += rendered_char.width() + LETTER_SPACING;
}
fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) {
let pixel_offset = y * self.info.stride + x;
let color = match self.info.pixel_format {
PixelFormat::Rgb => [intensity, intensity, intensity / 2, 0],
PixelFormat::Bgr => [intensity / 2, intensity, intensity, 0],
PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0],
other => {
// set a supported (but invalid) pixel format before panicking to avoid a double
// panic; it might not be readable though
self.info.pixel_format = PixelFormat::Rgb;
panic!("pixel format {:?} not supported in logger", other)
}
};
let bytes_per_pixel = self.info.bytes_per_pixel;
let byte_offset = pixel_offset * bytes_per_pixel;
self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)]
.copy_from_slice(&color[..bytes_per_pixel]);
let _ = unsafe { ptr::read_volatile(&self.framebuffer[byte_offset]) };
}
}
unsafe impl Send for FrameBufferWriter {}
unsafe impl Sync for FrameBufferWriter {}
impl fmt::Write for FrameBufferWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
self.write_char(c);
}
Ok(())
}
}

View File

@@ -0,0 +1,95 @@
use bootloader_api::info::FrameBufferInfo;
use conquer_once::spin::OnceCell;
use core::fmt::Write;
use spinning_top::Spinlock;
use super::*;
/// The global logger instance used for the `log` crate.
static LOGGER: OnceCell<LockedLogger> = OnceCell::uninit();
/// A logger instance protected by a spinlock.
struct LockedLogger {
framebuffer: Option<Spinlock<framebuffer::FrameBufferWriter>>,
serial: Option<Spinlock<serial::SerialPort>>
}
impl LockedLogger {
/// Create a new instance that logs to the given framebuffer.
pub fn new(
framebuffer: &'static mut [u8],
info: FrameBufferInfo,
) -> Self {
let framebuffer = Some(Spinlock::new(framebuffer::FrameBufferWriter::new(framebuffer, info)));
let serial = Some(Spinlock::new(unsafe { serial::SerialPort::init() }));
LockedLogger {
framebuffer,
serial
}
}
/// Force-unlocks the logger to prevent a deadlock.
///
/// ## Safety
/// This method is not memory safe and should be only used when absolutely necessary.
pub unsafe fn force_unlock(&self) {
if let Some(framebuffer) = &self.framebuffer {
unsafe { framebuffer.force_unlock() };
}
if let Some(serial) = &self.serial {
unsafe { serial.force_unlock() };
}
}
}
impl log::Log for LockedLogger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
if let Some(framebuffer) = &self.framebuffer {
let mut framebuffer = framebuffer.lock();
writeln!(framebuffer, "{:5}: {}", record.level(), record.args()).unwrap();
}
if let Some(serial) = &self.serial {
let mut serial = serial.lock();
writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap();
}
}
fn flush(&self) {}
}
/// Initialize a text-based logger using the given pixel-based framebuffer as output.
pub fn init_logger(
framebuffer: &'static mut [u8],
info: FrameBufferInfo,
) {
let logger = LOGGER.get_or_init(move || {
LockedLogger::new(
framebuffer,
info,
)
});
log::set_logger(logger).expect("logger already set");
#[cfg(debug_assertions)]
log::set_max_level(log::LevelFilter::Debug);
#[cfg(not(debug_assertions))]
log::set_max_level(log::LevelFilter::Info);
log::debug!("Framebuffer info: {:?}", info);
}
pub fn force_unlock() {
unsafe {
LOGGER
.get()
.map(|l| l.force_unlock())
};
}

5
kernel/src/logger/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod logger;
mod framebuffer;
mod serial;
pub use logger::*;

View File

@@ -0,0 +1,28 @@
use core::fmt;
pub struct SerialPort {
port: uart_16550::SerialPort,
}
impl SerialPort {
/// # Safety
///
/// unsafe because this function must only be called once
pub unsafe fn init() -> Self {
let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) };
port.init();
Self { port }
}
}
impl fmt::Write for SerialPort {
fn write_str(&mut self, s: &str) -> fmt::Result {
for char in s.bytes() {
match char {
b'\n' => self.port.write_str("\r\n").unwrap(),
byte => self.port.send(byte),
}
}
Ok(())
}
}

37
kernel/src/main.rs Normal file
View File

@@ -0,0 +1,37 @@
#![no_std]
#![no_main]
use bootloader_api::{
config::{BootloaderConfig, Mapping},
entry_point, BootInfo
};
mod logger;
pub static BOOTLOADER_CONFIG: BootloaderConfig = {
let mut config = BootloaderConfig::new_default();
config.mappings.physical_memory = Some(Mapping::Dynamic);
config
};
entry_point!(kernel_main);
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
let info = boot_info.framebuffer.as_ref().unwrap().info();
let buffer = boot_info.framebuffer.as_mut().unwrap().buffer_mut();
logger::init_logger(buffer, info);
log::info!("Hello World from KERNEL");
// Endless loop as the kernel must stay running
loop {}
}
#[panic_handler]
#[cfg(not(test))]
fn panic(info: &core::panic::PanicInfo) -> ! {
logger::force_unlock();
log::error!("{}", info);
loop {}
}

13
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,13 @@
[toolchain]
# The nightly version that was build on 24-07-2025 is the last known working version,
# the c-int-width definition for rust targets has changed from a string to a u16
channel = "nightly-2025-07-24"
targets = [
"x86_64-unknown-none"
]
components = [
"clippy",
"rustfmt",
"rust-src",
"llvm-tools-preview"
]

25
src/main.rs Normal file
View File

@@ -0,0 +1,25 @@
// Helper amin that will allow the kernel of FerrOS to be run through QEMU with just the "cargo run" command
fn main() {
// read env variables that were set in build script
let uefi_path = env!("UEFI_PATH");
let bios_path = env!("BIOS_PATH");
// choose whether to start the UEFI or BIOS image
let uefi = true;
let mut cmd = std::process::Command::new("qemu-system-x86_64");
if uefi {
cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
cmd.arg("-drive")
.arg(format!("format=raw,file={uefi_path}"));
} else {
cmd.arg("-drive")
.arg(format!("format=raw,file={bios_path}"));
}
cmd.arg("-serial").arg("stdio");
let mut child = cmd.spawn().unwrap();
child.wait().unwrap();
}