mirror of
https://github.com/XunilGroup/XunilOS.git
synced 2026-04-25 11:49:03 +02:00
Build upon the limine Rust template and a working pipeline, display an orange background on load.
This commit is contained in:
2
kernel/.gitignore
vendored
Normal file
2
kernel/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/kernel
|
||||
/target
|
||||
25
kernel/Cargo.lock
generated
Normal file
25
kernel/Cargo.lock
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "limine"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af6d2ee42712e7bd2c787365cd1dab06ef59a61becbf87bec7b32b970bd2594b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "limine-rust-template"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"limine",
|
||||
]
|
||||
7
kernel/Cargo.toml
Normal file
7
kernel/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "limine-rust-template"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
limine = "0.5"
|
||||
44
kernel/GNUmakefile
Normal file
44
kernel/GNUmakefile
Normal file
@@ -0,0 +1,44 @@
|
||||
# Nuke built-in rules and variables.
|
||||
MAKEFLAGS += -rR
|
||||
.SUFFIXES:
|
||||
|
||||
# This is the name that our final executable will have.
|
||||
# Change as needed.
|
||||
override OUTPUT := kernel
|
||||
|
||||
# Convenience macro to reliably declare user overridable variables.
|
||||
override USER_VARIABLE = $(if $(filter $(origin $(1)),default undefined),$(eval override $(1) := $(2)))
|
||||
|
||||
# Target architecture to build for. Default to x86_64.
|
||||
$(call USER_VARIABLE,KARCH,x86_64)
|
||||
|
||||
ifeq ($(RUST_TARGET),)
|
||||
override RUST_TARGET := $(KARCH)-unknown-none
|
||||
ifeq ($(KARCH),riscv64)
|
||||
override RUST_TARGET := riscv64gc-unknown-none-elf
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(RUST_PROFILE),)
|
||||
override RUST_PROFILE := dev
|
||||
endif
|
||||
|
||||
override RUST_PROFILE_SUBDIR := $(RUST_PROFILE)
|
||||
ifeq ($(RUST_PROFILE),dev)
|
||||
override RUST_PROFILE_SUBDIR := debug
|
||||
endif
|
||||
|
||||
# Default target.
|
||||
.PHONY: all
|
||||
all:
|
||||
RUSTFLAGS="-C relocation-model=static" cargo build --target $(RUST_TARGET) --profile $(RUST_PROFILE)
|
||||
cp target/$(RUST_TARGET)/$(RUST_PROFILE_SUBDIR)/$$(cd target/$(RUST_TARGET)/$(RUST_PROFILE_SUBDIR) && find -maxdepth 1 -perm -111 -type f) kernel
|
||||
|
||||
# Remove object files and the final executable.
|
||||
.PHONY: clean
|
||||
clean:
|
||||
cargo clean
|
||||
rm -rf kernel
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
7
kernel/build.rs
Normal file
7
kernel/build.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
// Tell cargo to pass the linker script to the linker..
|
||||
println!("cargo:rustc-link-arg=-Tlinker-{arch}.ld");
|
||||
// ..and to re-run if it changes.
|
||||
println!("cargo:rerun-if-changed=linker-{arch}.ld");
|
||||
}
|
||||
63
kernel/linker-aarch64.ld
Normal file
63
kernel/linker-aarch64.ld
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Tell the linker that we want an aarch64 ELF64 output file */
|
||||
OUTPUT_FORMAT(elf64-littleaarch64)
|
||||
|
||||
/* We want the symbol kmain to be our entry point */
|
||||
ENTRY(kmain)
|
||||
|
||||
/* Define the program headers we want so the bootloader gives us the right */
|
||||
/* MMU permissions; this also allows us to exert more control over the linking */
|
||||
/* process. */
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* We want to be placed in the topmost 2GiB of the address space, for optimisations */
|
||||
/* and because that is what the Limine spec mandates. */
|
||||
/* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
|
||||
/* that is the beginning of the region. */
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
/* Move to the next memory page for .rodata */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
/* Move to the next memory page for .data */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
|
||||
/* Place the sections that contain the Limine requests as part of the .data */
|
||||
/* output section. */
|
||||
KEEP(*(.requests_start_marker))
|
||||
KEEP(*(.requests))
|
||||
KEEP(*(.requests_end_marker))
|
||||
} :data
|
||||
|
||||
/* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
|
||||
/* unnecessary zeros will be written to the binary. */
|
||||
/* If you need, for example, .init_array and .fini_array, those should be placed */
|
||||
/* above this. */
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
/* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame*)
|
||||
*(.note .note.*)
|
||||
}
|
||||
}
|
||||
63
kernel/linker-loongarch64.ld
Normal file
63
kernel/linker-loongarch64.ld
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Tell the linker that we want a loongarch64 ELF64 output file */
|
||||
OUTPUT_FORMAT(elf64-loongarch)
|
||||
|
||||
/* We want the symbol kmain to be our entry point */
|
||||
ENTRY(kmain)
|
||||
|
||||
/* Define the program headers we want so the bootloader gives us the right */
|
||||
/* MMU permissions; this also allows us to exert more control over the linking */
|
||||
/* process. */
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* We want to be placed in the topmost 2GiB of the address space, for optimisations */
|
||||
/* and because that is what the Limine spec mandates. */
|
||||
/* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
|
||||
/* that is the beginning of the region. */
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
/* Move to the next memory page for .rodata */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
/* Move to the next memory page for .data */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
|
||||
/* Place the sections that contain the Limine requests as part of the .data */
|
||||
/* output section. */
|
||||
KEEP(*(.requests_start_marker))
|
||||
KEEP(*(.requests))
|
||||
KEEP(*(.requests_end_marker))
|
||||
} :data
|
||||
|
||||
/* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
|
||||
/* unnecessary zeros will be written to the binary. */
|
||||
/* If you need, for example, .init_array and .fini_array, those should be placed */
|
||||
/* above this. */
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
/* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame*)
|
||||
*(.note .note.*)
|
||||
}
|
||||
}
|
||||
66
kernel/linker-riscv64.ld
Normal file
66
kernel/linker-riscv64.ld
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Tell the linker that we want a riscv64 ELF64 output file */
|
||||
OUTPUT_FORMAT(elf64-littleriscv)
|
||||
|
||||
/* We want the symbol kmain to be our entry point */
|
||||
ENTRY(kmain)
|
||||
|
||||
/* Define the program headers we want so the bootloader gives us the right */
|
||||
/* MMU permissions; this also allows us to exert more control over the linking */
|
||||
/* process. */
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* We want to be placed in the topmost 2GiB of the address space, for optimisations */
|
||||
/* and because that is what the Limine spec mandates. */
|
||||
/* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
|
||||
/* that is the beginning of the region. */
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
/* Move to the next memory page for .rodata */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
/* Move to the next memory page for .data */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
|
||||
/* Place the sections that contain the Limine requests as part of the .data */
|
||||
/* output section. */
|
||||
KEEP(*(.requests_start_marker))
|
||||
KEEP(*(.requests))
|
||||
KEEP(*(.requests_end_marker))
|
||||
|
||||
*(.sdata .sdata.*)
|
||||
} :data
|
||||
|
||||
/* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
|
||||
/* unnecessary zeros will be written to the binary. */
|
||||
/* If you need, for example, .init_array and .fini_array, those should be placed */
|
||||
/* above this. */
|
||||
.bss : {
|
||||
*(.sbss .sbss.*)
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
/* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame*)
|
||||
*(.note .note.*)
|
||||
}
|
||||
}
|
||||
63
kernel/linker-x86_64.ld
Normal file
63
kernel/linker-x86_64.ld
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Tell the linker that we want an x86_64 ELF64 output file */
|
||||
OUTPUT_FORMAT(elf64-x86-64)
|
||||
|
||||
/* We want the symbol kmain to be our entry point */
|
||||
ENTRY(kmain)
|
||||
|
||||
/* Define the program headers we want so the bootloader gives us the right */
|
||||
/* MMU permissions; this also allows us to exert more control over the linking */
|
||||
/* process. */
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* We want to be placed in the topmost 2GiB of the address space, for optimisations */
|
||||
/* and because that is what the Limine spec mandates. */
|
||||
/* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
|
||||
/* that is the beginning of the region. */
|
||||
. = 0xffffffff80000000;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
/* Move to the next memory page for .rodata */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
/* Move to the next memory page for .data */
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
|
||||
/* Place the sections that contain the Limine requests as part of the .data */
|
||||
/* output section. */
|
||||
KEEP(*(.requests_start_marker))
|
||||
KEEP(*(.requests))
|
||||
KEEP(*(.requests_end_marker))
|
||||
} :data
|
||||
|
||||
/* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
|
||||
/* unnecessary zeros will be written to the binary. */
|
||||
/* If you need, for example, .init_array and .fini_array, those should be placed */
|
||||
/* above this. */
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
/* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame*)
|
||||
*(.note .note.*)
|
||||
}
|
||||
}
|
||||
8
kernel/rust-toolchain.toml
Normal file
8
kernel/rust-toolchain.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
targets = [
|
||||
"x86_64-unknown-none",
|
||||
# "aarch64-unknown-none",
|
||||
# "riscv64gc-unknown-none-elf",
|
||||
# "loongarch64-unknown-none",
|
||||
]
|
||||
25
kernel/src/graphics.rs
Normal file
25
kernel/src/graphics.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use limine::framebuffer::Framebuffer;
|
||||
|
||||
pub fn rgb(r: u8, g: u8, b: u8) -> u32 {
|
||||
((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
|
||||
}
|
||||
|
||||
pub fn create_rect(framebuffer: &Framebuffer, x: u64, y: u64, width: u64, height: u64, color: u32) {
|
||||
for fb_x in x..x+width {
|
||||
for fb_y in y..y+height {
|
||||
// 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 bytes_per_pixel = framebuffer.bpp() as u64 / 8;
|
||||
let pixel_offset = fb_y * framebuffer.pitch() + fb_x * bytes_per_pixel;
|
||||
|
||||
// Write 0xFFFFFFFF to the provided pixel offset to fill it white.
|
||||
unsafe {
|
||||
framebuffer
|
||||
.addr()
|
||||
.add(pixel_offset as usize)
|
||||
.cast::<u32>()
|
||||
.write(color)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
64
kernel/src/main.rs
Normal file
64
kernel/src/main.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
use limine::BaseRevision;
|
||||
use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker};
|
||||
|
||||
mod graphics;
|
||||
use crate::graphics::*;
|
||||
|
||||
|
||||
/// 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.
|
||||
#[unsafe(link_section = ".requests")]
|
||||
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
||||
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests")]
|
||||
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
||||
|
||||
/// Define the stand and end markers for Limine requests.
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests_start_marker")]
|
||||
static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests_end_marker")]
|
||||
static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
||||
|
||||
#[unsafe(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() {
|
||||
graphics::create_rect(&framebuffer, 0, 0, framebuffer.width(), framebuffer.height(), graphics::rgb(253, 129, 0));
|
||||
}
|
||||
}
|
||||
|
||||
hcf();
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
hcf();
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user