diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..b2570e105ae3efc2a2616570deb567a155b69425 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +#[build] +#target = "x86_64-unknown-none" + +[target.x86_64-unknown-none] +runner = "./.cargo/runner.sh" diff --git a/.cargo/runner.sh b/.cargo/runner.sh new file mode 100755 index 0000000000000000000000000000000000000000..5914a40ca832eeb293638ee09e78f146e92fb401 --- /dev/null +++ b/.cargo/runner.sh @@ -0,0 +1,41 @@ +#!/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 diff --git a/Cargo.lock b/Cargo.lock index 5124ebb6e1567de5e586afb5258d91c95f10ea46..1131368621289df3d2ff0b22ed4c8eebeb90faca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,14 +8,35 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "hyperion" version = "0.1.0" dependencies = [ + "limine", "spin", + "uart_16550", "volatile", + "x86_64", ] +[[package]] +name = "limine" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c847ac148a0c53ba3755dfa9830722b1043179584009869e6afc2b413e13f105" + [[package]] name = "lock_api" version = "0.4.9" @@ -26,6 +47,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + [[package]] name = "scopeguard" version = "1.1.0" @@ -41,8 +68,31 @@ dependencies = [ "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]] name = "volatile" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" 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", +] diff --git a/Cargo.toml b/Cargo.toml index 793abe36bf13afd0a95af116816056a459e3d702..ecdebf34a2c9dd3706f5d3d73f63471946ad64b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,10 +5,21 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -crate-type = ["staticlib"] +[features] +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] spin = "0.9.4" 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 } diff --git a/Makefile b/Makefile index aaef11cce026a1233240c3befe9a61c621a33e81..74ff7dca26ef91ddf3a66c04186498783a2bd05b 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ ARCH ?= x86_64 #ARCH ?= x86 PROFILE ?= debug #PROFILE ?= release +GDB ?= false # binary config NASM ?= nasm @@ -36,23 +37,23 @@ ${KERNEL_LIB} : ${KERNEL_SRC} Makefile Cargo.toml Cargo.lock ${CARGO} build ${CARGO_FLAGS} # hyperion boot code -BOOT_SRC := ${ARCH_DIR}/start.asm -BOOT_OBJ := ${HYPER_DIR}/start.o -NASM_F_x86_64 := elf64 -NASM_F_x86 := elf32 -NASM_FLAGS ?= -NASM_FLAGS += ${BOOT_SRC} -NASM_FLAGS += -o ${BOOT_OBJ} -NASM_FLAGS += -f ${NASM_F_${ARCH}} -${BOOT_OBJ} : ${BOOT_SRC} Makefile - @echo "\n\033[32m--[[ building Hyperion boot ]]--\033[0m" - mkdir -p ${HYPER_DIR} - ${NASM} ${NASM_FLAGS} +# BOOT_SRC := ${ARCH_DIR}/start.asm +# BOOT_OBJ := ${HYPER_DIR}/start.o +# NASM_F_x86_64 := elf64 +# NASM_F_x86 := elf32 +# NASM_FLAGS ?= +# NASM_FLAGS += ${BOOT_SRC} +# NASM_FLAGS += -o ${BOOT_OBJ} +# NASM_FLAGS += -f ${NASM_F_${ARCH}} +# ${BOOT_OBJ} : ${BOOT_SRC} Makefile +# @echo "\n\033[32m--[[ building Hyperion boot ]]--\033[0m" +# mkdir -p ${HYPER_DIR} +# ${NASM} ${NASM_FLAGS} # hyperion kernel elf LD_SCRIPT := ${ARCH_DIR}/link.ld 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 := elf_i386 LD_FLAGS ?= @@ -70,18 +71,52 @@ ${KERNEL_ELF} : ${KERNEL_DEPS} ${LD_SCRIPT} Makefile # the entry format has to be x86 not x86_64 ${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 : ${KERNEL_ELF} -# qemu alias +# qemu direct kernel boot alias QEMU_x86_64 ?= qemu-system-x86_64 -QEMU_x86 ?= qemu-system-x86 +QEMU_x86 ?= qemu-system-i386 QEMU_FLAGS ?= +QEMU_FLAGS += -serial stdio +QEMU_FLAGS += -s +ifeq (${GDB},true) +QEMU_FLAGS += -S +endif QEMU_FLAGS += -enable-kvm 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_${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 : ${KERNEL_ELF} diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..b53fae90545914afdc65e1e5228ea312a921c36b --- /dev/null +++ b/build.rs @@ -0,0 +1,39 @@ +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(()) +} diff --git a/cfg/grub.cfg b/cfg/grub.cfg new file mode 100644 index 0000000000000000000000000000000000000000..eda56d4e2f277501d7ac4eb6cb34a9c518e36391 --- /dev/null +++ b/cfg/grub.cfg @@ -0,0 +1,14 @@ +# 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 +} diff --git a/cfg/limine.cfg b/cfg/limine.cfg new file mode 100644 index 0000000000000000000000000000000000000000..1777d21e9ef695df6a3a46ba4f16d936389bd62f --- /dev/null +++ b/cfg/limine.cfg @@ -0,0 +1,6 @@ +# boot instantly +TIMEOUT=0 + +:Hyperion + PROTOCOL=limine + KERNEL_PATH=boot:///hyperion diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000000000000000000000000000000000..5d56faf9ae08cb604e06df9aa6281e2b51ce5809 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/arch/x86_64/bootboot/mod.rs b/src/arch/x86_64/bootboot/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..95bb6858abe5ffdef58ac9cf88b0a3d17c72d85a --- /dev/null +++ b/src/arch/x86_64/bootboot/mod.rs @@ -0,0 +1,126 @@ +#[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() +} diff --git a/src/arch/x86_64/limine/link.ld b/src/arch/x86_64/limine/link.ld new file mode 100644 index 0000000000000000000000000000000000000000..1f53501c84b51393f55bce31c21e67568a6f4eef --- /dev/null +++ b/src/arch/x86_64/limine/link.ld @@ -0,0 +1,74 @@ +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) } +} diff --git a/src/arch/x86_64/limine/mod.rs b/src/arch/x86_64/limine/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..ef9274aceb65802760e45470b8bafa801a6a9c00 --- /dev/null +++ b/src/arch/x86_64/limine/mod.rs @@ -0,0 +1,52 @@ +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) + } +} diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index f3dcc9932f43b006432368a4ca24923103ec4405..42f8285e42045437e0c818ec14c0073133b4e7b8 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,12 +1,15 @@ -// both cannot coexist (AFAIK.) and QEMU -// cannot boot multiboot2 kernels directly -// -// so multiboot1 it is .. temporarily +#[cfg(feature = "multiboot1")] +#[path = "multiboot1/mod.rs"] +pub mod boot; -// multiboot1 header and glue code -#[cfg(all())] -mod multiboot1; +#[cfg(feature = "multiboot2")] +#[path = "multiboot2/mod.rs"] +pub mod boot; -// multiboot2 header and glue code -#[cfg(any())] -mod multiboot2; +#[cfg(feature = "bootboot")] +#[path = "bootboot/mod.rs"] +pub mod boot; + +#[cfg(feature = "limine")] +#[path = "limine/mod.rs"] +pub mod boot; diff --git a/src/arch/x86_64/multiboot1.rs b/src/arch/x86_64/multiboot1.rs deleted file mode 100644 index 3a61044346ae6ccf6ea4a6270223d9bfbeb605c3..0000000000000000000000000000000000000000 --- a/src/arch/x86_64/multiboot1.rs +++ /dev/null @@ -1,82 +0,0 @@ -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(); -} diff --git a/src/arch/x86_64/link.ld b/src/arch/x86_64/multiboot1/link.ld similarity index 67% rename from src/arch/x86_64/link.ld rename to src/arch/x86_64/multiboot1/link.ld index 874eb9bed9f802b5e4d5e33e116831be639ed98d..345eb98bf182cedddc926f0b434cf2bb8ea49189 100644 --- a/src/arch/x86_64/link.ld +++ b/src/arch/x86_64/multiboot1/link.ld @@ -1,15 +1,10 @@ -ENTRY(start) +ENTRY(_start) SECTIONS { . = 1M; - .multiboot : ALIGN(4k) { - KEEP(*(.multiboot)) - KEEP(*(.multiboot_rust)) - } - .boot : ALIGN(4k) { - *(.boot) + KEEP(*(.boot)) } .text : ALIGN(4k) { diff --git a/src/arch/x86_64/multiboot1/mod.rs b/src/arch/x86_64/multiboot1/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..8f3db54e1751d19013847927b69167fadc426212 --- /dev/null +++ b/src/arch/x86_64/multiboot1/mod.rs @@ -0,0 +1,232 @@ +//! 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:?}"); +} diff --git a/src/arch/x86_64/start.asm b/src/arch/x86_64/multiboot1/start.asm similarity index 93% rename from src/arch/x86_64/start.asm rename to src/arch/x86_64/multiboot1/start.asm index 4bcb16a1cf4400c5567236c7066abe21b6555037..cefcd854f62503524e5c940e55f350041785803a 100644 --- a/src/arch/x86_64/start.asm +++ b/src/arch/x86_64/multiboot1/start.asm @@ -1,5 +1,5 @@ global start - extern kernel_main + extern _start_rust ;; ---------- ;; Boot entry @@ -9,7 +9,7 @@ global start bits 32 -start: +_start: cli cld @@ -139,22 +139,22 @@ setup_page_tables: ret enable_paging: - ; pass page table location to the cpu + ;; pass page table location to the cpu mov eax, page_table_l4 mov cr3, eax - ; enable PAE + ;; enable Physical Address Extension mov eax, cr4 or eax, 1 << 5 mov cr4, eax - ; enable long mode + ;; enable long mode mov ecx, 0xC0000080 rdmsr or eax, 1 << 8 wrmsr - ; enable paging + ;; enable paging mov eax, cr0 or eax, 1 << 31 mov cr0, eax @@ -175,12 +175,9 @@ long_mode_start: mov fs, ax mov gs, ax - ; print 'OK' - mov dword [0xb8000], 0x2F4B2F4F - ;; take the multiboot info struct pointer pop rdi - call kernel_main + call _start_rust .halt: hlt jmp halt diff --git a/src/arch/x86_64/multiboot2/link.ld b/src/arch/x86_64/multiboot2/link.ld new file mode 100644 index 0000000000000000000000000000000000000000..345eb98bf182cedddc926f0b434cf2bb8ea49189 --- /dev/null +++ b/src/arch/x86_64/multiboot2/link.ld @@ -0,0 +1,26 @@ +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) + } +} diff --git a/src/arch/x86_64/multiboot2.rs b/src/arch/x86_64/multiboot2/mod.rs similarity index 80% rename from src/arch/x86_64/multiboot2.rs rename to src/arch/x86_64/multiboot2/mod.rs index 892aac2015781eb8740eab28c1353312f3cfa986..bb2a49a58961fe3a20581623c86eb5e84f9b4c58 100644 --- a/src/arch/x86_64/multiboot2.rs +++ b/src/arch/x86_64/multiboot2/mod.rs @@ -16,7 +16,7 @@ const CHECKSUM: u32 = (0x100000000 - (MAGIC + ARCH + LEN) as u64) as u32; #[used] #[no_mangle] -#[link_section = ".multiboot"] +#[link_section = ".boot"] pub static MULTIBOOT2_HEADER: Multiboot2Header = Multiboot2Header { magic: MAGIC, architecture: ARCH, @@ -27,6 +27,9 @@ pub static MULTIBOOT2_HEADER: Multiboot2Header = Multiboot2Header { }; #[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(); } + +compile_error!("TODO: Multiboot2"); diff --git a/src/arch/x86_64/multiboot2/start.asm b/src/arch/x86_64/multiboot2/start.asm new file mode 100644 index 0000000000000000000000000000000000000000..cefcd854f62503524e5c940e55f350041785803a --- /dev/null +++ b/src/arch/x86_64/multiboot2/start.asm @@ -0,0 +1,210 @@ + 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 diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 82848fbdcbeb0a92a347c671f894b03a3db9f0e1..0000000000000000000000000000000000000000 --- a/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![no_std] -#![no_main] - -#[path = "arch/x86_64/mod.rs"] -pub mod arch; - -pub mod vga; - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo) -> ! { - loop {} -} - -fn kernel_main() -> ! { - // null byte clears the VGA buffer - // print!("\0"); - - // println!("Hello from Hyperion, pointer = {pointer:#x}, fb = {fb:#x}"); - - loop { - unsafe { - core::arch::asm!("hlt"); - } - } -} diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000000000000000000000000000000000000..b8e024dcbb05b35200b265a395b8583574400978 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,47 @@ +use core::fmt::Arguments; +use spin::Lazy; + +// + +#[macro_export] +macro_rules! print { + ($($t:tt)*) => { $crate::log::_print(format_args!($($t)*)) }; +} + +#[macro_export] +macro_rules! println { + () => { $crate::log::_print(format_args!("\n")); }; + ($($t:tt)*) => { $crate::log::_print(format_args_nl!($($t)*)); }; +} + +// + +static LOGGER: Lazy<Logger> = Lazy::new(Logger::init); + +struct Logger { + term: bool, + qemu: bool, +} + +impl Logger { + fn init() -> Self { + Logger { + term: true, + qemu: true, + } + } + + fn print(&self, args: Arguments) { + if self.term { + crate::arch::boot::_print(args); + } + if self.qemu { + crate::qemu::_print(args); + } + } +} + +#[doc(hidden)] +pub fn _print(args: Arguments) { + LOGGER.print(args) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..77827b89551ee834ce728191804b522f9f3f33f8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] +#![feature(format_args_nl)] +#![feature(abi_x86_interrupt)] + +use spin::Mutex; + +#[path = "arch/x86_64/mod.rs"] +pub mod arch; +pub mod log; +pub mod qemu; +// pub mod vga; + +static BOOTLOADER: Mutex<&'static str> = Mutex::new("Hyperion"); + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} + +fn kernel_main() -> ! { + println!("Hello from Hyperion"); + println!(" - Hyperion was booted with {}", BOOTLOADER.lock()); + + loop { + unsafe { + core::arch::asm!("hlt"); + } + } +} diff --git a/src/qemu.rs b/src/qemu.rs new file mode 100644 index 0000000000000000000000000000000000000000..d2c85f9e6540c4431149f7690b99aeaa30505fb6 --- /dev/null +++ b/src/qemu.rs @@ -0,0 +1,19 @@ +use core::fmt::{Arguments, Write}; +use spin::{Lazy, Mutex}; +use uart_16550::SerialPort; + +// + +static COM1: Lazy<Mutex<SerialPort>> = Lazy::new(|| { + let mut port = unsafe { SerialPort::new(0x3f8) }; + port.init(); + Mutex::new(port) +}); + +// + +#[doc(hidden)] +pub fn _print(args: Arguments) { + let mut writer = COM1.lock(); + writer.write_fmt(args).unwrap(); +} diff --git a/src/vga.rs b/src/vga.rs index 690d6383e6c927b8f37521bb43717e9ca125feaa..1c404904a5e17c622c1f5fd2ff643f6e3c476080 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -7,30 +7,6 @@ use volatile::Volatile; // -#[macro_export] -macro_rules! println { - () => { - println!(""); - }; - - ($($arg:tt)*) => { - $crate::vga::_println(format_args!($($arg)*)) - } -} - -#[macro_export] -macro_rules! print { - () => { - print!(""); - }; - - ($($arg:tt)*) => { - $crate::vga::_print(format_args!($($arg)*)) - }; -} - -// - pub struct Writer { cursor: [usize; 2], color: ColorCode,