Skip to content
Snippets Groups Projects
Commit d53fd6d7 authored by Eemeli Lehtonen's avatar Eemeli Lehtonen
Browse files

Limine & BOOTBOOT

parent d6707183
No related branches found
No related tags found
No related merge requests found
Showing
with 969 additions and 130 deletions
#[build]
#target = "x86_64-unknown-none"
[target.x86_64-unknown-none]
runner = "./.cargo/runner.sh"
#!/usr/bin/env bash
#
# Hyperion x86_64 is runnable
set -xe
LIMINE_GIT_URL="https://github.com/limine-bootloader/limine.git"
ISO_DIR=target/hyperion/x86_64/iso
KERNEL=$1
# Clone the `limine` repository if we don't have it yet.
if [ ! -d target/limine ]; then
git clone $LIMINE_GIT_URL --depth=1 --branch v3.0-branch-binary target/limine
fi
# Make sure we have an up-to-date version of the bootloader.
cd target/limine
git fetch
make
cd -
# Copy the needed files into an ISO image.
mkdir -p $ISO_DIR
cp $KERNEL cfg/limine.cfg target/limine/limine{.sys,-cd.bin,-cd-efi.bin} $ISO_DIR
xorriso -as mkisofs \
-b limine-cd.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
--efi-boot limine-cd-efi.bin \
-efi-boot-part --efi-boot-image --protective-msdos-label \
$ISO_DIR -o $KERNEL.iso
# For the image to be bootable on BIOS systems, we must run `limine-deploy` on it.
target/limine/limine-deploy $KERNEL.iso
# Run the created image with QEMU.
qemu-system-x86_64 \
-machine q35 -cpu qemu64 -M smm=off \
-D target/log.txt -d int,guest_errors -no-reboot -no-shutdown \
-serial stdio \
$KERNEL.iso
...@@ -8,14 +8,35 @@ version = "1.1.0" ...@@ -8,14 +8,35 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bit_field"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "hyperion" name = "hyperion"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"limine",
"spin", "spin",
"uart_16550",
"volatile", "volatile",
"x86_64",
] ]
[[package]]
name = "limine"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c847ac148a0c53ba3755dfa9830722b1043179584009869e6afc2b413e13f105"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.9" version = "0.4.9"
...@@ -26,6 +47,12 @@ dependencies = [ ...@@ -26,6 +47,12 @@ dependencies = [
"scopeguard", "scopeguard",
] ]
[[package]]
name = "rustversion"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
...@@ -41,8 +68,31 @@ dependencies = [ ...@@ -41,8 +68,31 @@ dependencies = [
"lock_api", "lock_api",
] ]
[[package]]
name = "uart_16550"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b074eb9300ad949edd74c529c0e8d451625af71bb948e6b65fe69f72dc1363d9"
dependencies = [
"bitflags",
"rustversion",
"x86_64",
]
[[package]] [[package]]
name = "volatile" name = "volatile"
version = "0.4.5" version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c" checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c"
[[package]]
name = "x86_64"
version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69"
dependencies = [
"bit_field",
"bitflags",
"rustversion",
"volatile",
]
...@@ -5,10 +5,21 @@ edition = "2021" ...@@ -5,10 +5,21 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib] [features]
crate-type = ["staticlib"] default = ["limine"]
limine = ["dep:limine"]
bootboot = []
multiboot1 = []
multiboot2 = []
# Pick limine OR bootboot OR multiboot1 OR multiboot2, they conflict with eachother
# [lib]
# crate-type = ["staticlib"]
[dependencies] [dependencies]
spin = "0.9.4" spin = "0.9.4"
volatile = "0.4.5" volatile = "0.4.5"
x86_64 = "0.14.10"
uart_16550 = "0.2.18"
limine = { version = "0.1.9", optional = true }
#tracing = { version = "0.1.37", default-features = false } #tracing = { version = "0.1.37", default-features = false }
...@@ -8,6 +8,7 @@ ARCH ?= x86_64 ...@@ -8,6 +8,7 @@ ARCH ?= x86_64
#ARCH ?= x86 #ARCH ?= x86
PROFILE ?= debug PROFILE ?= debug
#PROFILE ?= release #PROFILE ?= release
GDB ?= false
# binary config # binary config
NASM ?= nasm NASM ?= nasm
...@@ -36,23 +37,23 @@ ${KERNEL_LIB} : ${KERNEL_SRC} Makefile Cargo.toml Cargo.lock ...@@ -36,23 +37,23 @@ ${KERNEL_LIB} : ${KERNEL_SRC} Makefile Cargo.toml Cargo.lock
${CARGO} build ${CARGO_FLAGS} ${CARGO} build ${CARGO_FLAGS}
# hyperion boot code # hyperion boot code
BOOT_SRC := ${ARCH_DIR}/start.asm # BOOT_SRC := ${ARCH_DIR}/start.asm
BOOT_OBJ := ${HYPER_DIR}/start.o # BOOT_OBJ := ${HYPER_DIR}/start.o
NASM_F_x86_64 := elf64 # NASM_F_x86_64 := elf64
NASM_F_x86 := elf32 # NASM_F_x86 := elf32
NASM_FLAGS ?= # NASM_FLAGS ?=
NASM_FLAGS += ${BOOT_SRC} # NASM_FLAGS += ${BOOT_SRC}
NASM_FLAGS += -o ${BOOT_OBJ} # NASM_FLAGS += -o ${BOOT_OBJ}
NASM_FLAGS += -f ${NASM_F_${ARCH}} # NASM_FLAGS += -f ${NASM_F_${ARCH}}
${BOOT_OBJ} : ${BOOT_SRC} Makefile # ${BOOT_OBJ} : ${BOOT_SRC} Makefile
@echo "\n\033[32m--[[ building Hyperion boot ]]--\033[0m" # @echo "\n\033[32m--[[ building Hyperion boot ]]--\033[0m"
mkdir -p ${HYPER_DIR} # mkdir -p ${HYPER_DIR}
${NASM} ${NASM_FLAGS} # ${NASM} ${NASM_FLAGS}
# hyperion kernel elf # hyperion kernel elf
LD_SCRIPT := ${ARCH_DIR}/link.ld LD_SCRIPT := ${ARCH_DIR}/link.ld
KERNEL_ELF := ${HYPER_DIR}/hyperion KERNEL_ELF := ${HYPER_DIR}/hyperion
KERNEL_DEPS := ${BOOT_OBJ} ${KERNEL_LIB} KERNEL_DEPS := ${KERNEL_LIB} #${BOOT_OBJ}
LD_M_x86_64 := elf_x86_64 LD_M_x86_64 := elf_x86_64
LD_M_x86 := elf_i386 LD_M_x86 := elf_i386
LD_FLAGS ?= LD_FLAGS ?=
...@@ -70,18 +71,52 @@ ${KERNEL_ELF} : ${KERNEL_DEPS} ${LD_SCRIPT} Makefile ...@@ -70,18 +71,52 @@ ${KERNEL_ELF} : ${KERNEL_DEPS} ${LD_SCRIPT} Makefile
# the entry format has to be x86 not x86_64 # the entry format has to be x86 not x86_64
${OBJCOPY} -O elf32-i386 ${KERNEL_ELF} ${OBJCOPY} -O elf32-i386 ${KERNEL_ELF}
# hyperion iso
HYPERION := ${HYPER_DIR}/hyperion.iso
ISO_DIR := ${HYPER_DIR}/iso
BOOT_DIR := ${ISO_DIR}/boot
GRUB_DIR := ${BOOT_DIR}/grub
${HYPERION} : ${KERNEL_ELF} cfg/grub.cfg Makefile
@echo "\n\033[32m--[[ building Hyperion iso ]]--\033[0m"
mkdir -p ${GRUB_DIR}
cp cfg/grub.cfg ${GRUB_DIR}
cp ${KERNEL_ELF} ${BOOT_DIR}/
grub-mkrescue /usr/lib/grub/i386-pc -o $@ ${ISO_DIR}
# build alias # build alias
build : ${KERNEL_ELF} build : ${KERNEL_ELF}
# qemu alias # qemu direct kernel boot alias
QEMU_x86_64 ?= qemu-system-x86_64 QEMU_x86_64 ?= qemu-system-x86_64
QEMU_x86 ?= qemu-system-x86 QEMU_x86 ?= qemu-system-i386
QEMU_FLAGS ?= QEMU_FLAGS ?=
QEMU_FLAGS += -serial stdio
QEMU_FLAGS += -s
ifeq (${GDB},true)
QEMU_FLAGS += -S
endif
QEMU_FLAGS += -enable-kvm QEMU_FLAGS += -enable-kvm
QEMU_FLAGS += -d cpu_reset,guest_errors QEMU_FLAGS += -d cpu_reset,guest_errors
QEMU_FLAGS += -kernel ${KERNEL_ELF} #QEMU_FLAGS += -M pc-i440fx-7.2
#QEMU_FLAGS += -device VGA,vgamem_mb=64
QEMU_FLAGS += -vga std
QEMU_KERNEL := -kernel ${KERNEL_ELF} -append qemu
qemu : ${KERNEL_ELF} qemu : ${KERNEL_ELF}
${QEMU_${ARCH}} ${QEMU_FLAGS} ${QEMU_${ARCH}} ${QEMU_FLAGS} ${QEMU_KERNEL}
# qemu iso boot alias
#QEMU_FLAGS += -bios ${QEMU_OVMF}
QEMU_OVMF ?= /usr/share/ovmf/x64/OVMF.fd
QEMU_ISO := -drive format=raw,file=${HYPERION}
qemu_iso : ${HYPERION}
${QEMU_${ARCH}} ${QEMU_FLAGS} ${QEMU_ISO}
# connect gdb to qemu
GDB_FLAGS ?=
GDB_FLAGS += --eval-command="target remote localhost:1234"
GDB_FLAGS += --eval-command="symbol-file ${KERNEL_ELF}"
gdb:
gdb ${GDB_FLAGS}
# objdump # objdump
objdump : ${KERNEL_ELF} objdump : ${KERNEL_ELF}
......
build.rs 0 → 100644
use std::{env::var, error::Error};
//
fn main() -> Result<(), Box<dyn Error>> {
let kernel = var("CARGO_PKG_NAME")?;
println!("cargo:rerun-if-env-changed=CARGO_PKG_NAME");
let arch = var("CARGO_CFG_TARGET_ARCH")?;
println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_ARCH");
let mut bootloader: Option<&'static str> = None;
let mut set = |s| {
if let Some(already) = bootloader {
println!("cargo:warning=Bootloaders {s} and {already} are mutually exclusive");
panic!();
} else {
bootloader = Some(s);
}
};
#[cfg(feature = "limine")]
set("limine");
#[cfg(feature = "bootboot")]
set("bootboot");
#[cfg(feature = "multiboot1")]
set("multiboot1");
#[cfg(feature = "multiboot2")]
set("multiboot2");
if let Some(bootloader) = bootloader {
let script = format!("src/arch/{arch}/{bootloader}/link.ld");
println!("cargo:rustc-link-arg-bin={kernel}=--script={script}");
println!("cargo:rerun-if-changed={script}");
} else {
println!("cargo:warning=No bootloaders given");
panic!();
};
Ok(())
}
# boot instantly
set timeout=0
# default to Hyperion multiboot1
set default=0
menuentry "Hyperion multiboot1" {
multiboot /boot/hyperion
boot
}
menuentry "Hyperion multiboot2" {
multiboot2 /boot/hyperion
boot
}
# boot instantly
TIMEOUT=0
:Hyperion
PROTOCOL=limine
KERNEL_PATH=boot:///hyperion
[toolchain]
channel = "nightly"
#[allow(unused)]
const BOOTBOOT_MAGIC: &'static [u8; 5usize] = b"BOOT\0";
#[allow(unused)]
const BOOTBOOT_MMIO: u64 = 0xfffffffff8000000; /* memory mapped IO virtual address */
#[allow(unused)]
const BOOTBOOT_FB: u64 = 0xfffffffffc000000; /* frame buffer virtual address */
#[allow(unused)]
const BOOTBOOT_INFO: u64 = 0xffffffffffe00000; /* bootboot struct virtual address */
#[allow(unused)]
const BOOTBOOT_ENV: u64 = 0xffffffffffe01000; /* environment string virtual address */
#[allow(unused)]
const BOOTBOOT_CORE: u64 = 0xffffffffffe02000; /* core loadable segment start */
#[allow(unused)]
const PROTOCOL_MINIMAL: u32 = 0;
#[allow(unused)]
const PROTOCOL_STATIC: u32 = 1;
#[allow(unused)]
const PROTOCOL_DYNAMIC: u32 = 2;
#[allow(unused)]
const PROTOCOL_BIGENDIAN: u32 = 0x80;
#[allow(unused)]
const LOADER_BIOS: u32 = 0 << 2;
#[allow(unused)]
const LOADER_UEFI: u32 = 1 << 2;
#[allow(unused)]
const LOADER_RPI: u32 = 2 << 2;
#[allow(unused)]
const LOADER_COREBOOT: u32 = 3 << 2;
#[allow(unused)]
const FB_ARGB: u32 = 0;
#[allow(unused)]
const FB_RGBA: u32 = 1;
#[allow(unused)]
const FB_ABGR: u32 = 2;
#[allow(unused)]
const FB_BGRA: u32 = 3;
#[allow(unused)]
const MMAP_USED: u32 = 0; /* don't use. Reserved or unknown regions */
#[allow(unused)]
const MMAP_FREE: u32 = 1; /* usable memory */
#[allow(unused)]
const MMAP_ACPI: u32 = 2; /* acpi memory, volatile and non-volatile as well */
#[allow(unused)]
const MMAP_MMIO: u32 = 3; /* memory mapped IO region */
#[allow(unused)]
const INITRD_MAXSIZE: u32 = 16; /* Mb */
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
struct MMapEnt {
ptr: u64,
size: u64,
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
struct BootBoot {
/* first 64 bytes is platform independent */
magic: [u8; 4usize], /* 'BOOT' magic */
size: u32, /* length of bootboot structure, minimum 128 */
protocol: u8, /* 1, static addresses, see PROTOCOL_* and LOADER_* above */
fb_type: u8, /* framebuffer type, see FB_* above */
numcores: u16, /* number of processor cores */
bspid: u16, /* Bootsrap processor ID (Local APIC Id on x86_64) */
timezone: i16, /* in minutes -1440..1440 */
datetime: [u8; 8usize], /* in BCD yyyymmddhhiiss UTC (independent to timezone) */
initrd_ptr: u64, /* ramdisk image position and size */
initrd_size: u64,
fb_ptr: *mut u8, /* framebuffer pointer and dimensions */
fb_size: u32,
fb_width: u32,
fb_height: u32,
fb_scanline: u32,
arch: Arch,
mmap: MMapEnt,
}
#[derive(Clone, Copy)]
#[repr(C)]
union Arch {
x86_64: ArchX86,
aarch64: ArchAarch64,
_bindgen_union_align: [u64; 8usize],
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct ArchX86 {
acpi_ptr: u64,
smbi_ptr: u64,
efi_ptr: u64,
mp_ptr: u64,
unused0: u64,
unused1: u64,
unused2: u64,
unused3: u64,
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct ArchAarch64 {
acpi_ptr: u64,
mmio_ptr: u64,
efi_ptr: u64,
unused0: u64,
unused1: u64,
unused2: u64,
unused3: u64,
unused4: u64,
}
#[no_mangle]
extern "C" fn _start() -> ! {
*crate::BOOTLOADER.lock() = "BOOTBOOT";
crate::kernel_main()
}
ENTRY(_start)
OUTPUT_ARCH(i386:x86-64)
OUTPUT_FORMAT(elf64-x86-64)
KERNEL_BASE = 0xffffffff80000000;
SECTIONS {
. = KERNEL_BASE + SIZEOF_HEADERS;
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.rela : { *(.rela*) }
.rodata : { *(.rodata .rodata.*) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.eh_frame_hdr : {
PROVIDE(__eh_frame_hdr = .);
KEEP(*(.eh_frame_hdr))
PROVIDE(__eh_frame_hdr_end = .);
}
.eh_frame : {
PROVIDE(__eh_frame = .);
KEEP(*(.eh_frame))
PROVIDE(__eh_frame_end = .);
}
.gcc_except_table : { KEEP(*(.gcc_except_table .gcc_except_table.*)) }
. += CONSTANT(MAXPAGESIZE);
.plt : { *(.plt .plt.*) }
.text : { *(.text .text.*) }
. += CONSTANT(MAXPAGESIZE);
.tdata : { *(.tdata .tdata.*) }
.tbss : { *(.tbss .tbss.*) }
.data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) }
.dynamic : { *(.dynamic) }
. = DATA_SEGMENT_RELRO_END(0, .);
.got : { *(.got .got.*) }
.got.plt : { *(.got.plt .got.plt.*) }
.data : { *(.data .data.*) }
.bss : { *(.bss .bss.*) *(COMMON) }
. = DATA_SEGMENT_END(.);
.comment 0 : { *(.comment) }
.debug 0 : { *(.debug) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_aranges 0 : { *(.debug_aranges) }
.debug_frame 0 : { *(.debug_frame) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_line 0 : { *(.debug_line) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
.debug_sfnames 0 : { *(.debug_sfnames) }
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_str 0 : { *(.debug_str) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
.debug_weaknames 0 : { *(.debug_weaknames) }
.line 0 : { *(.line) }
.shstrtab 0 : { *(.shstrtab) }
.strtab 0 : { *(.strtab) }
.symtab 0 : { *(.symtab) }
}
use core::fmt::{self, Arguments, Write};
use limine::{LimineTerminalRequest, LimineTerminalResponse};
use spin::{Lazy, Mutex, MutexGuard, Once};
//
#[no_mangle]
pub extern "C" fn _start() -> ! {
*crate::BOOTLOADER.lock() = "Limine";
crate::kernel_main()
}
//
struct Writer(pub &'static LimineTerminalResponse);
unsafe impl Send for Writer {}
impl Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
let mut write = self.0.write().ok_or(fmt::Error)?;
for term in self.0.terminals() {
write(term, s);
}
Ok(())
}
}
static TERMINALS: LimineTerminalRequest = LimineTerminalRequest::new(0);
static WRITER: Once<Mutex<Writer>> = Once::new();
fn get() -> Result<MutexGuard<'static, Writer>, fmt::Error> {
WRITER.try_call_once(|| {
Ok(Mutex::new(Writer(
TERMINALS.get_response().get().ok_or(fmt::Error)?,
)))
})?;
WRITER.get().ok_or(fmt::Error).map(|mutex| mutex.lock())
}
fn print(args: Arguments) -> Option<()> {
Some(())
}
#[doc(hidden)]
pub fn _print(args: Arguments) {
if let Ok(mut writer) = get() {
_ = writer.write_fmt(args)
}
}
// both cannot coexist (AFAIK.) and QEMU #[cfg(feature = "multiboot1")]
// cannot boot multiboot2 kernels directly #[path = "multiboot1/mod.rs"]
// pub mod boot;
// so multiboot1 it is .. temporarily
// multiboot1 header and glue code #[cfg(feature = "multiboot2")]
#[cfg(all())] #[path = "multiboot2/mod.rs"]
mod multiboot1; pub mod boot;
// multiboot2 header and glue code #[cfg(feature = "bootboot")]
#[cfg(any())] #[path = "bootboot/mod.rs"]
mod multiboot2; pub mod boot;
#[cfg(feature = "limine")]
#[path = "limine/mod.rs"]
pub mod boot;
use core::ffi::CStr;
#[allow(unused)]
#[repr(C)]
struct Multiboot1Header {
magic: u32,
flags: u32,
checksum: u32,
_unused: [u32; 5], // header_addr, load_addr, load_end_addr, bss_end_addr, entry_addr
mode_type: u32,
width: u32,
height: u32,
depth: u32,
}
const MAGIC: u32 = 0x1BADB002;
const ALIGN: u32 = 1 << 0;
const MEMINFO: u32 = 1 << 1;
const VIDEO: u32 = 1 << 2;
const FLAGS: u32 = ALIGN | MEMINFO | VIDEO;
#[used]
#[no_mangle]
#[link_section = ".multiboot"]
static MULTIBOOT1_HEADER: Multiboot1Header = Multiboot1Header {
magic: MAGIC,
flags: FLAGS,
checksum: (0x100000000 - (MAGIC + FLAGS) as u64) as u32,
_unused: [0; 5],
mode_type: 0, // 0 = linear graphics
width: 0, // 0 = no preference
height: 0, // 0 = no preference
depth: 0, // 0 = no preference
};
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct Multiboot1Information {
flags: u32,
optional: [u8; 112],
}
impl Multiboot1Information {
fn bootloader_name(&self) -> Option<&str> {
if (self.flags & 1 << 9) != 0 {
let ptr = u32::from_le_bytes((self.optional[60..=64]).try_into().ok()?) as _;
let s = unsafe { CStr::from_ptr(ptr) };
let s = s.to_str().ok()?;
Some(s)
} else {
None
}
}
fn framebuffer(&self) -> Option<&[u8]> {
if (self.flags & 1 << 12) != 0 {
Some(&self.optional[84..])
} else {
None
}
}
}
#[no_mangle]
extern "C" fn kernel_main(magic_num: u64) {
let mb1_info_pointer = magic_num & u32::MAX as u64;
let mb1_info = unsafe { *(mb1_info_pointer as *const Multiboot1Information) };
crate::println!(
"\0{:?}\n{:#b}\n{:?}",
mb1_info.bootloader_name(),
mb1_info.flags,
mb1_info.framebuffer(),
);
crate::kernel_main();
}
ENTRY(start) ENTRY(_start)
SECTIONS { SECTIONS {
. = 1M; . = 1M;
.multiboot : ALIGN(4k) {
KEEP(*(.multiboot))
KEEP(*(.multiboot_rust))
}
.boot : ALIGN(4k) { .boot : ALIGN(4k) {
*(.boot) KEEP(*(.boot))
} }
.text : ALIGN(4k) { .text : ALIGN(4k) {
......
//! https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
use crate::println;
use core::{ffi::CStr, mem::transmute, slice};
use spin::Lazy;
use uart_16550::SerialPort;
use volatile::Volatile;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
//
#[allow(unused)]
#[repr(packed)]
struct Multiboot1Header {
magic: u32,
flags: u32,
checksum: u32,
_unused: [u32; 5], // header_addr, load_addr, load_end_addr, bss_end_addr, entry_addr
mode_type: u32,
width: u32,
height: u32,
depth: u32,
}
const MAGIC: u32 = 0x1BADB002;
const ALIGN: u32 = 1 << 0;
const MEMINFO: u32 = 1 << 1;
const VIDEO: u32 = 1 << 2;
const FLAGS: u32 = ALIGN | MEMINFO | VIDEO;
#[used]
#[no_mangle]
#[link_section = ".boot"]
static MULTIBOOT1_HEADER: Multiboot1Header = Multiboot1Header {
magic: MAGIC,
flags: FLAGS,
checksum: (0x100000000 - (MAGIC + FLAGS) as u64) as u32,
_unused: [0; 5],
mode_type: 0, // 0 = linear graphics
width: 1280, // 0 = no preference
height: 720, // 0 = no preference
depth: 32, // 0 = no preference
};
#[derive(Debug, Clone, Copy)]
#[repr(packed)]
struct Multiboot1Information {
flags: u32,
mem_lower: u32,
mem_upper: u32,
boot_device: u32,
cmdline: u32,
mods_count: u32,
mods_addr: u32,
syms: [u32; 4],
mmap_len: u32,
mmap_addr: u32,
drives_len: u32,
drives_addr: u32,
config_table: u32,
boot_loader_name: u32,
apm_table: u32,
// VESA Bios Extensions table
vbe_control_info: u32,
vbe_mode_info: u32,
vbe_mode: u16,
vbe_interface_seg: u16,
vbe_interface_off: u16,
vbe_interface_len: u16,
// Framebuffer table
framebuffer_addr: u64,
framebuffer_pitch: u32,
framebuffer_width: u32,
framebuffer_height: u32,
framebuffer_bpp: u8,
framebuffer_type: u8,
color_info: [u8; 6],
}
#[allow(unused)]
const fn test() {
unsafe {
// at compile time: make sure that Multiboot1Information is exactly 116 bytes
transmute::<[u8; 116], Multiboot1Information>([0; 116]);
}
}
#[derive(Debug)]
pub struct BootInfo {
pub cmdline: Option<&'static str>,
pub bootloader: Option<&'static str>,
pub framebuffer: Option<Framebuffer>,
}
pub struct Framebuffer {
pub buffer: &'static mut [u8],
pub stride: u32, // bytes per row
pub width: u32,
pub height: u32,
}
impl core::fmt::Debug for Framebuffer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Framebuffer")
.field("buffer", &self.buffer.as_ptr_range())
.field("stride", &self.stride)
.field("width", &self.width)
.field("height", &self.height)
.finish()
}
}
impl Multiboot1Information {
fn build(&self) -> BootInfo {
BootInfo {
cmdline: self.cmdline(),
bootloader: self.bootloader(),
framebuffer: self.framebuffer(),
}
}
fn cmdline(&self) -> Option<&'static str> {
if self.get_bit(2) {
// SAFETY: if flags[2] is set, this pointer is valid
let s = unsafe { CStr::from_ptr(self.cmdline as _) };
let s = s.to_str().ok()?;
Some(s)
} else {
None
}
}
fn bootloader(&self) -> Option<&'static str> {
if self.get_bit(9) {
// SAFETY: if flags[9] is set, this pointer is valid
let s = unsafe { CStr::from_ptr(self.boot_loader_name as _) };
let s = s.to_str().ok()?;
Some(s)
} else {
None
}
}
fn vbe(&self) -> Option<impl core::fmt::Debug> {
if self.get_bit(11) {
let vbe = (
self.vbe_control_info,
self.vbe_mode_info,
self.vbe_mode,
self.vbe_interface_seg,
self.vbe_interface_off,
self.vbe_interface_len,
);
crate::println!("{vbe:?}");
Some(vbe)
} else {
None
}
}
fn framebuffer(&self) -> Option<Framebuffer> {
if self.get_bit(12) {
let size = self.framebuffer_pitch as usize * self.framebuffer_height as usize;
let buffer =
unsafe { slice::from_raw_parts_mut(self.framebuffer_addr as *mut _, size) };
Some(Framebuffer {
buffer,
stride: self.framebuffer_pitch,
width: self.framebuffer_width,
height: self.framebuffer_height,
})
} else {
None
}
}
fn get_bit(&self, n: u8) -> bool {
(self.flags & 1 << n) != 0
}
}
static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint);
idt
});
#[no_mangle]
extern "C" fn _start_rust(magic_num: u64) -> ! {
*crate::BOOTLOADER.lock() = "Multiboot1";
crate::print!("\0");
// let mb1_info_pointer = magic_num & u32::MAX as u64;
// let mb1_info = unsafe { *(mb1_info_pointer as *const Multiboot1Information) };
// let mut boot_info = mb1_info.build();
// crate::println!("{boot_info:#?} {mb1_info:#?}");
crate::println!("test");
// IDT.load();
// x86_64::instructions::interrupts::int3();
crate::println!("comp");
// if let Some(fb) = &mut boot_info.framebuffer {
// // fb.buffer[1000] = 255;
// // fb.buffer.fill(255);
// }
crate::kernel_main();
}
extern "x86-interrupt" fn breakpoint(stack: InterruptStackFrame) {
// println!("{stack:?}");
}
global start global start
extern kernel_main extern _start_rust
;; ---------- ;; ----------
;; Boot entry ;; Boot entry
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
global start global start
bits 32 bits 32
start: _start:
cli cli
cld cld
...@@ -139,22 +139,22 @@ setup_page_tables: ...@@ -139,22 +139,22 @@ setup_page_tables:
ret ret
enable_paging: enable_paging:
; pass page table location to the cpu ;; pass page table location to the cpu
mov eax, page_table_l4 mov eax, page_table_l4
mov cr3, eax mov cr3, eax
; enable PAE ;; enable Physical Address Extension
mov eax, cr4 mov eax, cr4
or eax, 1 << 5 or eax, 1 << 5
mov cr4, eax mov cr4, eax
; enable long mode ;; enable long mode
mov ecx, 0xC0000080 mov ecx, 0xC0000080
rdmsr rdmsr
or eax, 1 << 8 or eax, 1 << 8
wrmsr wrmsr
; enable paging ;; enable paging
mov eax, cr0 mov eax, cr0
or eax, 1 << 31 or eax, 1 << 31
mov cr0, eax mov cr0, eax
...@@ -175,12 +175,9 @@ long_mode_start: ...@@ -175,12 +175,9 @@ long_mode_start:
mov fs, ax mov fs, ax
mov gs, ax mov gs, ax
; print 'OK'
mov dword [0xb8000], 0x2F4B2F4F
;; take the multiboot info struct pointer ;; take the multiboot info struct pointer
pop rdi pop rdi
call kernel_main call _start_rust
.halt: .halt:
hlt hlt
jmp halt jmp halt
......
ENTRY(_start)
SECTIONS {
. = 1M;
.boot : ALIGN(4k) {
KEEP(*(.boot))
}
.text : ALIGN(4k) {
*(.text)
}
.rodata : ALIGN(4k) {
*(.rodata)
}
.data : ALIGN(4k) {
*(.data)
}
.bss : ALIGN(4k) {
*(COMMON)
*(.bss)
}
}
...@@ -16,7 +16,7 @@ const CHECKSUM: u32 = (0x100000000 - (MAGIC + ARCH + LEN) as u64) as u32; ...@@ -16,7 +16,7 @@ const CHECKSUM: u32 = (0x100000000 - (MAGIC + ARCH + LEN) as u64) as u32;
#[used] #[used]
#[no_mangle] #[no_mangle]
#[link_section = ".multiboot"] #[link_section = ".boot"]
pub static MULTIBOOT2_HEADER: Multiboot2Header = Multiboot2Header { pub static MULTIBOOT2_HEADER: Multiboot2Header = Multiboot2Header {
magic: MAGIC, magic: MAGIC,
architecture: ARCH, architecture: ARCH,
...@@ -27,6 +27,9 @@ pub static MULTIBOOT2_HEADER: Multiboot2Header = Multiboot2Header { ...@@ -27,6 +27,9 @@ pub static MULTIBOOT2_HEADER: Multiboot2Header = Multiboot2Header {
}; };
#[no_mangle] #[no_mangle]
pub extern "C" fn kernel_main(_magic_num: u64) { extern "C" fn _start_rust(_magic_num: u64) -> ! {
*crate::BOOTLOADER.lock() = "Multiboot2";
crate::kernel_main(); crate::kernel_main();
} }
compile_error!("TODO: Multiboot2");
global start
extern _start_rust
;; ----------
;; Boot entry
;; ----------
section .boot
global start
bits 32
_start:
cli
cld
;; init stack
mov esp, stack_top
;; support checks
call check_multiboot1
push ebx
push ebx
call check_cpuid
call check_long_mode
;; setup
call setup_page_tables
call enable_paging
;; enter long mode
lgdt [gdt64.pointer]
jmp gdt64.code_segment: long_mode_start
jmp halt
error:
;; print 'ERR: <err>'
mov dword [0xb8000], 0x4F524F45
mov dword [0xb8004], 0x4F3A4F52
mov dword [0xb8008], 0x4F204F20
mov byte [0xb800a], al
jmp halt
halt:
;; print ZZZ
mov word [0xb8f00], 0x0F5A
mov word [0xb8f02], 0x0F5A
mov word [0xb8f04], 0x0F5A
hlt
jmp halt
;; ------
;; Checks
;; ------
section .boot
bits 32
check_multiboot2:
cmp eax, 0x36D76289
jne .no_multiboot2
ret
.no_multiboot2:
mov al, 'M'
jmp error
check_multiboot1:
cmp eax, 0x2BADB002
jne .no_multiboot1
ret
.no_multiboot1:
mov al, 'M'
jmp error
check_cpuid:
pushfd
pop eax
mov ecx, eax
xor eax, 1 << 21
push eax
popfd
pushfd
pop eax
push ecx
popfd
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, 'C'
jmp error
check_long_mode:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .no_long_mode
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jz .no_long_mode
ret
.no_long_mode:
mov al, 'L'
jmp error
;; ----------
;; Page setup
;; ----------
setup_page_tables:
mov eax, page_table_l3
or eax, 0b11 ; present, writeable
mov [page_table_l4], eax
mov eax, page_table_l2
or eax, 0b11 ; present, writeable
mov [page_table_l3], eax
mov ecx, 0 ; counter
.loop:
mov eax, 0x200000 ; 2MiB
mul ecx,
or eax, 0b10000011 ; present, writeable, huge page
mov [page_table_l2 + ecx * 8], eax
inc ecx ; inc counter
cmp ecx, 512 ; check if the whole table is mapped
jne .loop ; if not: continue
ret
enable_paging:
;; pass page table location to the cpu
mov eax, page_table_l4
mov cr3, eax
;; enable Physical Address Extension
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
;; enable long mode
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
;; enable paging
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
ret
;; ---------
;; Long mode
;; ---------
section .text
bits 64
long_mode_start:
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
;; take the multiboot info struct pointer
pop rdi
call _start_rust
.halt:
hlt
jmp halt
;; ------
;; Memory
;; ------
section .bss
page_table_l4:
resb 4096
page_table_l3:
resb 4096
page_table_l2:
resb 4096
stack_bottom:
;; 16 KiB
resb 4096 * 4
stack_top:
section .rodata
gdt64:
dq 0 ; zero entry
.code_segment: equ $ - gdt64
dq (1 << 43) | (1 << 44) | (1 << 47) | (1 << 53)
.pointer:
dw $ - gdt64 - 1
dq gdt64
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment