···11-/limine
22-/ovmf
33-*.iso
44-*.hdd
11+# Created by https://www.toptal.com/developers/gitignore/api/rust
22+# Edit at https://www.toptal.com/developers/gitignore?templates=rust
33+44+### Rust ###
55+# Generated by Cargo
66+# will have compiled files and executables
77+debug/
88+target/
99+1010+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
1111+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
1212+Cargo.lock
1313+1414+# These are backup files generated by rustfmt
1515+**/*.rs.bk
1616+1717+# MSVC Windows builds of rustc generate these, which store debugging information
1818+*.pdb
1919+2020+# End of https://www.toptal.com/developers/gitignore/api/rust
···11-Copyright (C) 2023-2024 mintsuki and contributors.
22-33-Permission to use, copy, modify, and/or distribute this software for any
44-purpose with or without fee is hereby granted.
55-66-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
77-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
88-FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
99-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1010-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
1111-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1212-PERFORMANCE OF THIS SOFTWARE.
-33
README.md
···11-# Limine Rust Template
22-33-This repository will demonstrate how to set up a basic kernel in Rust using Limine.
44-55-## How to use this?
66-77-### Dependencies
88-99-Any `make` command depends on GNU make (`gmake`) and is expected to be run using it. This usually means using `make` on most GNU/Linux distros, or `gmake` on other non-GNU systems.
1010-1111-All `make all*` targets depend on Rust.
1212-1313-Additionally, building an ISO with `make all` requires `xorriso`, and building a HDD/USB image with `make all-hdd` requires `sgdisk` (usually from `gdisk` or `gptfdisk` packages) and `mtools`.
1414-1515-### Architectural targets
1616-1717-The `KARCH` make variable determines the target architecture to build the kernel and image for.
1818-1919-The default `KARCH` is `x86_64`. Other options include: `aarch64`, `riscv64`, and `loongarch64`.
2020-2121-Other architectures will need to be enabled in kernel/rust-toolchain.toml
2222-2323-### Makefile targets
2424-2525-Running `make all` will compile the kernel (from the `kernel/` directory) and then generate a bootable ISO image.
2626-2727-Running `make all-hdd` will compile the kernel and then generate a raw image suitable to be flashed onto a USB stick or hard drive/SSD.
2828-2929-Running `make run` will build the kernel and a bootable ISO (equivalent to make all) and then run it using `qemu` (if installed).
3030-3131-Running `make run-hdd` will build the kernel and a raw HDD image (equivalent to make all-hdd) and then run it using `qemu` (if installed).
3232-3333-The `run-uefi` and `run-hdd-uefi` targets are equivalent to their non `-uefi` counterparts except that they boot `qemu` using a UEFI-compatible firmware.
···11-# Nuke built-in rules and variables.
22-MAKEFLAGS += -rR
33-.SUFFIXES:
44-55-# This is the name that our final executable will have.
66-# Change as needed.
77-override OUTPUT := kernel
88-99-# Convenience macro to reliably declare user overridable variables.
1010-override USER_VARIABLE = $(if $(filter $(origin $(1)),default undefined),$(eval override $(1) := $(2)))
1111-1212-# Target architecture to build for. Default to x86_64.
1313-$(call USER_VARIABLE,KARCH,x86_64)
1414-1515-ifeq ($(RUST_TARGET),)
1616- override RUST_TARGET := $(KARCH)-unknown-none
1717- ifeq ($(KARCH),riscv64)
1818- override RUST_TARGET := riscv64gc-unknown-none-elf
1919- endif
2020-endif
2121-2222-ifeq ($(RUST_PROFILE),)
2323- override RUST_PROFILE := dev
2424-endif
2525-2626-override RUST_PROFILE_SUBDIR := $(RUST_PROFILE)
2727-ifeq ($(RUST_PROFILE),dev)
2828- override RUST_PROFILE_SUBDIR := debug
2929-endif
3030-3131-# Default target.
3232-.PHONY: all
3333-all:
3434- RUSTFLAGS="-C relocation-model=static" cargo build --target $(RUST_TARGET) --profile $(RUST_PROFILE)
3535- cp target/$(RUST_TARGET)/$(RUST_PROFILE_SUBDIR)/$$(cd target/$(RUST_TARGET)/$(RUST_PROFILE_SUBDIR) && find -maxdepth 1 -perm -111 -type f) kernel
3636-3737-# Remove object files and the final executable.
3838-.PHONY: clean
3939-clean:
4040- cargo clean
4141- rm -rf kernel
4242-4343-.PHONY: distclean
4444-distclean: clean
-7
kernel/build.rs
···11-fn main() {
22- let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
33- // Tell cargo to pass the linker script to the linker..
44- println!("cargo:rustc-link-arg=-Tlinker-{arch}.ld");
55- // ..and to re-run if it changes.
66- println!("cargo:rerun-if-changed=linker-{arch}.ld");
77-}
-63
kernel/linker-aarch64.ld
···11-/* Tell the linker that we want an aarch64 ELF64 output file */
22-OUTPUT_FORMAT(elf64-littleaarch64)
33-44-/* We want the symbol kmain to be our entry point */
55-ENTRY(kmain)
66-77-/* Define the program headers we want so the bootloader gives us the right */
88-/* MMU permissions; this also allows us to exert more control over the linking */
99-/* process. */
1010-PHDRS
1111-{
1212- text PT_LOAD;
1313- rodata PT_LOAD;
1414- data PT_LOAD;
1515-}
1616-1717-SECTIONS
1818-{
1919- /* We want to be placed in the topmost 2GiB of the address space, for optimisations */
2020- /* and because that is what the Limine spec mandates. */
2121- /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
2222- /* that is the beginning of the region. */
2323- . = 0xffffffff80000000;
2424-2525- .text : {
2626- *(.text .text.*)
2727- } :text
2828-2929- /* Move to the next memory page for .rodata */
3030- . = ALIGN(CONSTANT(MAXPAGESIZE));
3131-3232- .rodata : {
3333- *(.rodata .rodata.*)
3434- } :rodata
3535-3636- /* Move to the next memory page for .data */
3737- . = ALIGN(CONSTANT(MAXPAGESIZE));
3838-3939- .data : {
4040- *(.data .data.*)
4141-4242- /* Place the sections that contain the Limine requests as part of the .data */
4343- /* output section. */
4444- KEEP(*(.requests_start_marker))
4545- KEEP(*(.requests))
4646- KEEP(*(.requests_end_marker))
4747- } :data
4848-4949- /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
5050- /* unnecessary zeros will be written to the binary. */
5151- /* If you need, for example, .init_array and .fini_array, those should be placed */
5252- /* above this. */
5353- .bss : {
5454- *(.bss .bss.*)
5555- *(COMMON)
5656- } :data
5757-5858- /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
5959- /DISCARD/ : {
6060- *(.eh_frame*)
6161- *(.note .note.*)
6262- }
6363-}
-63
kernel/linker-loongarch64.ld
···11-/* Tell the linker that we want a loongarch64 ELF64 output file */
22-OUTPUT_FORMAT(elf64-loongarch)
33-44-/* We want the symbol kmain to be our entry point */
55-ENTRY(kmain)
66-77-/* Define the program headers we want so the bootloader gives us the right */
88-/* MMU permissions; this also allows us to exert more control over the linking */
99-/* process. */
1010-PHDRS
1111-{
1212- text PT_LOAD;
1313- rodata PT_LOAD;
1414- data PT_LOAD;
1515-}
1616-1717-SECTIONS
1818-{
1919- /* We want to be placed in the topmost 2GiB of the address space, for optimisations */
2020- /* and because that is what the Limine spec mandates. */
2121- /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
2222- /* that is the beginning of the region. */
2323- . = 0xffffffff80000000;
2424-2525- .text : {
2626- *(.text .text.*)
2727- } :text
2828-2929- /* Move to the next memory page for .rodata */
3030- . = ALIGN(CONSTANT(MAXPAGESIZE));
3131-3232- .rodata : {
3333- *(.rodata .rodata.*)
3434- } :rodata
3535-3636- /* Move to the next memory page for .data */
3737- . = ALIGN(CONSTANT(MAXPAGESIZE));
3838-3939- .data : {
4040- *(.data .data.*)
4141-4242- /* Place the sections that contain the Limine requests as part of the .data */
4343- /* output section. */
4444- KEEP(*(.requests_start_marker))
4545- KEEP(*(.requests))
4646- KEEP(*(.requests_end_marker))
4747- } :data
4848-4949- /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
5050- /* unnecessary zeros will be written to the binary. */
5151- /* If you need, for example, .init_array and .fini_array, those should be placed */
5252- /* above this. */
5353- .bss : {
5454- *(.bss .bss.*)
5555- *(COMMON)
5656- } :data
5757-5858- /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
5959- /DISCARD/ : {
6060- *(.eh_frame*)
6161- *(.note .note.*)
6262- }
6363-}
-66
kernel/linker-riscv64.ld
···11-/* Tell the linker that we want a riscv64 ELF64 output file */
22-OUTPUT_FORMAT(elf64-littleriscv)
33-44-/* We want the symbol kmain to be our entry point */
55-ENTRY(kmain)
66-77-/* Define the program headers we want so the bootloader gives us the right */
88-/* MMU permissions; this also allows us to exert more control over the linking */
99-/* process. */
1010-PHDRS
1111-{
1212- text PT_LOAD;
1313- rodata PT_LOAD;
1414- data PT_LOAD;
1515-}
1616-1717-SECTIONS
1818-{
1919- /* We want to be placed in the topmost 2GiB of the address space, for optimisations */
2020- /* and because that is what the Limine spec mandates. */
2121- /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
2222- /* that is the beginning of the region. */
2323- . = 0xffffffff80000000;
2424-2525- .text : {
2626- *(.text .text.*)
2727- } :text
2828-2929- /* Move to the next memory page for .rodata */
3030- . = ALIGN(CONSTANT(MAXPAGESIZE));
3131-3232- .rodata : {
3333- *(.rodata .rodata.*)
3434- } :rodata
3535-3636- /* Move to the next memory page for .data */
3737- . = ALIGN(CONSTANT(MAXPAGESIZE));
3838-3939- .data : {
4040- *(.data .data.*)
4141-4242- /* Place the sections that contain the Limine requests as part of the .data */
4343- /* output section. */
4444- KEEP(*(.requests_start_marker))
4545- KEEP(*(.requests))
4646- KEEP(*(.requests_end_marker))
4747-4848- *(.sdata .sdata.*)
4949- } :data
5050-5151- /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
5252- /* unnecessary zeros will be written to the binary. */
5353- /* If you need, for example, .init_array and .fini_array, those should be placed */
5454- /* above this. */
5555- .bss : {
5656- *(.sbss .sbss.*)
5757- *(.bss .bss.*)
5858- *(COMMON)
5959- } :data
6060-6161- /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
6262- /DISCARD/ : {
6363- *(.eh_frame*)
6464- *(.note .note.*)
6565- }
6666-}
-63
kernel/linker-x86_64.ld
···11-/* Tell the linker that we want an x86_64 ELF64 output file */
22-OUTPUT_FORMAT(elf64-x86-64)
33-44-/* We want the symbol kmain to be our entry point */
55-ENTRY(kmain)
66-77-/* Define the program headers we want so the bootloader gives us the right */
88-/* MMU permissions; this also allows us to exert more control over the linking */
99-/* process. */
1010-PHDRS
1111-{
1212- text PT_LOAD;
1313- rodata PT_LOAD;
1414- data PT_LOAD;
1515-}
1616-1717-SECTIONS
1818-{
1919- /* We want to be placed in the topmost 2GiB of the address space, for optimisations */
2020- /* and because that is what the Limine spec mandates. */
2121- /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
2222- /* that is the beginning of the region. */
2323- . = 0xffffffff80000000;
2424-2525- .text : {
2626- *(.text .text.*)
2727- } :text
2828-2929- /* Move to the next memory page for .rodata */
3030- . = ALIGN(CONSTANT(MAXPAGESIZE));
3131-3232- .rodata : {
3333- *(.rodata .rodata.*)
3434- } :rodata
3535-3636- /* Move to the next memory page for .data */
3737- . = ALIGN(CONSTANT(MAXPAGESIZE));
3838-3939- .data : {
4040- *(.data .data.*)
4141-4242- /* Place the sections that contain the Limine requests as part of the .data */
4343- /* output section. */
4444- KEEP(*(.requests_start_marker))
4545- KEEP(*(.requests))
4646- KEEP(*(.requests_end_marker))
4747- } :data
4848-4949- /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
5050- /* unnecessary zeros will be written to the binary. */
5151- /* If you need, for example, .init_array and .fini_array, those should be placed */
5252- /* above this. */
5353- .bss : {
5454- *(.bss .bss.*)
5555- *(COMMON)
5656- } :data
5757-5858- /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
5959- /DISCARD/ : {
6060- *(.eh_frame*)
6161- *(.note .note.*)
6262- }
6363-}
···11-#![no_std]
22-#![no_main]
33-44-use core::arch::asm;
55-66-use limine::BaseRevision;
77-use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker};
88-99-/// Sets the base revision to the latest revision supported by the crate.
1010-/// See specification for further info.
1111-/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
1212-#[used]
1313-// The .requests section allows limine to find the requests faster and more safely.
1414-#[unsafe(link_section = ".requests")]
1515-static BASE_REVISION: BaseRevision = BaseRevision::new();
1616-1717-#[used]
1818-#[unsafe(link_section = ".requests")]
1919-static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
2020-2121-/// Define the stand and end markers for Limine requests.
2222-#[used]
2323-#[unsafe(link_section = ".requests_start_marker")]
2424-static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
2525-#[used]
2626-#[unsafe(link_section = ".requests_end_marker")]
2727-static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
2828-2929-#[unsafe(no_mangle)]
3030-unsafe extern "C" fn kmain() -> ! {
3131- // All limine requests must also be referenced in a called function, otherwise they may be
3232- // removed by the linker.
3333- assert!(BASE_REVISION.is_supported());
3434-3535- if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() {
3636- if let Some(framebuffer) = framebuffer_response.framebuffers().next() {
3737- for i in 0..100_u64 {
3838- // Calculate the pixel offset using the framebuffer information we obtained above.
3939- // We skip `i` scanlines (pitch is provided in bytes) and add `i * 4` to skip `i` pixels forward.
4040- let pixel_offset = i * framebuffer.pitch() + i * 4;
4141-4242- // Write 0xFFFFFFFF to the provided pixel offset to fill it white.
4343- unsafe {
4444- framebuffer
4545- .addr()
4646- .add(pixel_offset as usize)
4747- .cast::<u32>()
4848- .write(0xFFFFFFFF)
4949- };
5050- }
5151- }
5252- }
5353-5454- hcf();
5555-}
5656-5757-#[panic_handler]
5858-fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
5959- hcf();
6060-}
6161-6262-fn hcf() -> ! {
6363- loop {
6464- unsafe {
6565- #[cfg(target_arch = "x86_64")]
6666- asm!("hlt");
6767- #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
6868- asm!("wfi");
6969- #[cfg(target_arch = "loongarch64")]
7070- asm!("idle 0");
7171- }
7272- }
7373-}
-10
limine.conf
···11-# Timeout in seconds that Limine will use before automatically booting.
22-timeout: 3
33-44-# The entry name that will be displayed in the boot menu.
55-/Limine Template
66- # We use the Limine boot protocol.
77- protocol: limine
88-99- # Path to the kernel to boot. boot():/ represents the partition on which limine.conf is located.
1010- kernel_path: boot():/boot/kernel