330 lines
No EOL
14 KiB
Rust
330 lines
No EOL
14 KiB
Rust
//! The entrypoint to the kernel; placed before all other code.
|
|
#![cfg(any(target_arch = "x86"))]
|
|
#![no_std]
|
|
#![no_main]
|
|
#![warn(missing_docs)]
|
|
#![allow(unexpected_cfgs)]
|
|
#![allow(static_mut_refs)]
|
|
#![feature(ptr_metadata)]
|
|
#![feature(cfg_match)]
|
|
#![feature(formatting_options)]
|
|
|
|
use core::{arch::asm, ffi::CStr, panic::PanicInfo};
|
|
use core::fmt::Debug;
|
|
|
|
use aphrodite::boot::{BootInfo, MemoryMapping};
|
|
use aphrodite::multiboot2::{FramebufferInfo, MemoryMap, MemorySection, RawMemoryMap, RootTag, Tag};
|
|
use aphrodite::arch::output::*;
|
|
use aphrodite::arch::egatext as egatext;
|
|
use aphrodite::output::*;
|
|
use aphrodite::display::COLOR_DEFAULT;
|
|
|
|
#[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))]
|
|
#[unsafe(link_section = ".multiboot2")]
|
|
#[unsafe(no_mangle)]
|
|
static MULTIBOOT2_HEADER: [u8; 24] = [
|
|
0xd6, 0x50, 0x52, 0xe8, // Magic number
|
|
0x00, 0x00, 0x00, 0x00, // Architecture
|
|
0x18, 0x00, 0x00, 0x00, // Size
|
|
0x12, 0xaf, 0xad, 0x17, // Checksum
|
|
|
|
0x00, 0x00, // End tag
|
|
0x00, 0x00, // Flags
|
|
0x08, 0x00, 0x00, 0x00, // Size
|
|
];
|
|
|
|
// The root tag, provided directly from the multiboot2 bootloader.
|
|
static mut RT: *const RootTag = core::ptr::null();
|
|
|
|
// The raw pointer to bootloader-specific data.
|
|
static mut O: *const u8 = core::ptr::null();
|
|
|
|
static mut MM: MemoryMap = MemoryMap {
|
|
entry_size: 0,
|
|
version: 0,
|
|
sections: &[],
|
|
};
|
|
|
|
static mut FBI: aphrodite::arch::egatext::FramebufferInfo = aphrodite::arch::egatext::FramebufferInfo {
|
|
address: 0,
|
|
pitch: 0,
|
|
width: 0,
|
|
height: 0,
|
|
bpp: 0,
|
|
change_cursor: false,
|
|
};
|
|
|
|
// The magic number in eax. 0x36D76289 for multiboot2.
|
|
static mut MAGIC: u32 = 0xFFFFFFFF;
|
|
|
|
#[unsafe(link_section = ".start")]
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn _start() -> ! {
|
|
unsafe { // Copy values provided by the bootloader out
|
|
// Aphrodite bootloaders pass values in eax and ebx, however rust doesn't know that it can't overwrite those.
|
|
// we force using ebx and eax as the output of an empty assembly block to let it know.
|
|
asm!(
|
|
"", out("ebx") O, // Bootloader-specific data(ebx)
|
|
out("eax") MAGIC, // Magic number(eax)
|
|
options(nomem, nostack, preserves_flags, pure)
|
|
);
|
|
}
|
|
#[allow(non_snake_case)]
|
|
let mut BI: BootInfo<'static> = BootInfo {
|
|
cmdline: None,
|
|
memory_map: None,
|
|
bootloader_name: None,
|
|
output: None,
|
|
};
|
|
unsafe {
|
|
match MAGIC {
|
|
#[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))]
|
|
0x36D76289 => { // Multiboot2
|
|
RT = O as *const RootTag; // This is unsafe rust! We can do whatever we want! *manical laughter*
|
|
|
|
sdebugs("Total boot info length is ");
|
|
sdebugbnp(&aphrodite::u32_as_u8_slice((*RT).total_len));
|
|
sdebugunp(b'\n');
|
|
|
|
sdebugs("Root tag address is ");
|
|
sdebugbnp(&aphrodite::usize_as_u8_slice(O as usize));
|
|
sdebugunp(b'\n');
|
|
|
|
if (*RT).total_len<16 { // Size of root tag+size of terminating tag. Something's up.
|
|
panic!("total length < 16")
|
|
}
|
|
|
|
let end_addr = O as usize+(*RT).total_len as usize;
|
|
|
|
sdebugunp(b'\n');
|
|
|
|
let mut ptr = O as usize;
|
|
ptr += size_of::<RootTag>();
|
|
|
|
let mut current_tag = core::ptr::read_volatile(ptr as *const Tag);
|
|
|
|
loop {
|
|
sdebugs("Tag address is ");
|
|
sdebugbnpln(&aphrodite::usize_as_u8_slice(ptr));
|
|
|
|
sdebugs("Tag type is ");
|
|
sdebugbnpln(&aphrodite::u32_as_u8_slice(current_tag.tag_type));
|
|
|
|
sdebugs("Tag length is ");
|
|
sdebugbnpln(&aphrodite::u32_as_u8_slice(current_tag.tag_len));
|
|
|
|
match current_tag.tag_type {
|
|
0 => { // Ending tag
|
|
if current_tag.tag_len != 8 { // Unexpected size, something is probably up
|
|
panic!("size of ending tag != 8");
|
|
}
|
|
break
|
|
},
|
|
4 => { // Basic memory information
|
|
if current_tag.tag_len != 16 { // Unexpected size, something is probably up
|
|
panic!("size of basic memory information tag != 16");
|
|
}
|
|
},
|
|
5 => { // BIOS boot device, ignore
|
|
if current_tag.tag_len != 20 { // Unexpected size, something is probably up
|
|
panic!("size of bios boot device tag != 20");
|
|
}
|
|
},
|
|
1 => { // Command line
|
|
if current_tag.tag_len < 8 { // Unexpected size, something is probably up
|
|
panic!("size of command line tag < 8");
|
|
}
|
|
let cstring = CStr::from_ptr((ptr + 8) as *const i8);
|
|
// creates a &core::ffi::CStr from the start of the command line...
|
|
|
|
BI.cmdline = Some(cstring.to_str().unwrap());
|
|
// ...before the BootInfo's commandline is set.
|
|
},
|
|
6 => { // Memory map tag
|
|
if current_tag.tag_len < 16 { // Unexpected size, something is probably up
|
|
panic!("size of memory map tag < 16");
|
|
}
|
|
let rawmemorymap: *mut RawMemoryMap = core::ptr::from_raw_parts_mut(
|
|
ptr as *mut u8, (current_tag.tag_len / *((ptr + 8usize) as *const u32)) as usize
|
|
);
|
|
// The end result of the above is creating a *const RawMemoryMap that has the same address as current_tag
|
|
// and has all of the [aphrodite::multiboot2::MemorySection]s for the memory map
|
|
|
|
let memorysections: &'static mut [aphrodite::multiboot2::MemorySection] = &mut *core::ptr::from_raw_parts_mut((&mut (*rawmemorymap).sections[0]) as &mut MemorySection, (*rawmemorymap).sections.len());
|
|
// Above is a bit hard to understand, but what it does is transmute rawmemorymap's sections into a pointer to those sections.
|
|
|
|
for ele in &mut *memorysections {
|
|
(*ele) = core::mem::transmute(Into::<MemoryMapping>::into(*ele))
|
|
}
|
|
|
|
MM = MemoryMap {
|
|
version: (*rawmemorymap).entry_version,
|
|
entry_size: (*rawmemorymap).entry_size,
|
|
sections: core::mem::transmute(memorysections),
|
|
};
|
|
let mm2 = aphrodite::boot::MemoryMap {
|
|
len: MM.sections.len() as u64,
|
|
size_pages: 1,
|
|
page_size: MM.mem_size(),
|
|
sections: MM.sections,
|
|
idx: 0
|
|
};
|
|
BI.memory_map = Some(mm2);
|
|
},
|
|
2 => { // Bootloader name
|
|
if current_tag.tag_len < 8 { // Unexpected size, something is probably up
|
|
panic!("size of command line tag < 8");
|
|
}
|
|
let cstring = CStr::from_ptr((ptr + 8) as *const i8);
|
|
// creates a &core::ffi::CStr from the start of the bootloader name...
|
|
|
|
BI.bootloader_name = Some(cstring.to_str().unwrap());
|
|
// ...before the BootInfo's bootloader_name is set.
|
|
},
|
|
8 => { // Framebuffer info
|
|
if current_tag.tag_len < 32 { // Unexpected size, something is probably up
|
|
panic!("size of framebuffer info tag < 32");
|
|
}
|
|
let framebufferinfo: *const FramebufferInfo = (ptr as usize + size_of::<Tag>()) as *const FramebufferInfo;
|
|
match (*framebufferinfo).fb_type {
|
|
0 => { // Indexed
|
|
panic!("Indexed color is unimplemented");
|
|
},
|
|
1 => { // RGB
|
|
panic!("RGB color is unimplemented");
|
|
},
|
|
2 => { // EGA Text
|
|
},
|
|
_ => {
|
|
panic!("unknown color info type")
|
|
}
|
|
}
|
|
let framebuffer_info = (*framebufferinfo).clone();
|
|
|
|
FBI = egatext::FramebufferInfo {
|
|
address: framebuffer_info.address,
|
|
pitch: framebuffer_info.pitch,
|
|
width: framebuffer_info.width,
|
|
height: framebuffer_info.height,
|
|
bpp: framebuffer_info.bpp,
|
|
change_cursor: false,
|
|
};
|
|
BI.output = Some(&FBI)
|
|
},
|
|
_ => { // Unknown/unimplemented tag type, ignore
|
|
swarnings("Unknown tag type ");
|
|
swarningbnpln(&aphrodite::u32_as_u8_slice(current_tag.tag_type));
|
|
}
|
|
}
|
|
sinfounp(b'\n');
|
|
ptr = (ptr + current_tag.tag_len as usize + 7) & !7;
|
|
if ptr>end_addr {
|
|
cfg_match! {
|
|
all(CONFIG_PREUSER_ERROR_ON_INVALID_LENGTH = "true", CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH = "false") => {
|
|
serrorsln("Current tag length would put pointer out-of-bounds; CONFIG_PREUSER_ERROR_ON_INVALID_LENGTH is set, continuing");
|
|
}
|
|
all(CONFIG_PREUSER_WARN_ON_INVALID_LENGTH = "true", CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH = "false") => {
|
|
swarningsln("Current tag length would put pointer out-of-bounds; CONFIG_PREUSER_WARN_ON_INVALID_LENGTH is set, continuing");
|
|
}
|
|
}
|
|
cfg_match! {
|
|
not(CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH = "false") => {
|
|
panic!("current tag length would put pointer out-of-bounds")
|
|
}
|
|
CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH = "true" => {
|
|
sinfos("Exiting loop as current tag length would put pointer out-of-bounds ");
|
|
sinfosnpln("and CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH is set");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
current_tag = core::ptr::read_volatile(ptr as *const Tag);
|
|
}
|
|
},
|
|
_ => { // Unknown bootloader, panic
|
|
panic!("unknown bootloader");
|
|
}
|
|
}
|
|
}
|
|
sdebugsln("Bootloader information has been successfully loaded");
|
|
sdebugunp(b'\n');
|
|
unsafe {
|
|
if BI.output.clone().is_some() {
|
|
let framebuffer_info = FBI;
|
|
|
|
sdebugs("Framebuffer width: ");
|
|
sdebugbnpln(&aphrodite::u32_as_u8_slice(framebuffer_info.width));
|
|
sdebugs("Framebuffer height: ");
|
|
sdebugbnpln(&aphrodite::u32_as_u8_slice(framebuffer_info.height));
|
|
sdebugs("Framebuffer pitch: ");
|
|
sdebugbnpln(&aphrodite::u32_as_u8_slice(framebuffer_info.pitch));
|
|
sdebugs("Framebuffer address: ");
|
|
sdebugbnpln(&aphrodite::usize_as_u8_slice(framebuffer_info.address as usize));
|
|
sdebugs("Framebuffer bpp: ");
|
|
sdebugbnpln(&aphrodite::u8_as_u8_slice(framebuffer_info.bpp));
|
|
|
|
sdebugsln("Beginning test output to screen...");
|
|
|
|
let ega: &dyn aphrodite::display::TextDisplay = &framebuffer_info;
|
|
framebuffer_info.disable_cursor();
|
|
ega.clear_screen(COLOR_DEFAULT);
|
|
toutputsln("Testing EGA Text framebuffer...", ega).unwrap();
|
|
toutputsln("Testing EGA Text framebuffer...", ega).unwrap();
|
|
toutputsln("Testing EGA Text framebuffer...", ega).unwrap();
|
|
|
|
aphrodite::indep_boot_entry::IndepBootEntry(Some(ega), &BI);
|
|
}
|
|
}
|
|
|
|
aphrodite::indep_boot_entry::IndepBootEntry(None, &BI);
|
|
}
|
|
|
|
#[unsafe(link_section = ".panic")]
|
|
#[panic_handler]
|
|
#[cfg(not(CONFIG_HALT_ON_PANIC = "false"))]
|
|
fn halt_on_panic(info: &PanicInfo) -> ! {
|
|
use core::fmt::FormattingOptions;
|
|
|
|
if info.location().is_some() {
|
|
sfatals("Panic at ");
|
|
sfatalsnp(info.location().unwrap().file());
|
|
sfatalsnp(":");
|
|
sfatalbnp(&aphrodite::u32_as_u8_slice(info.location().unwrap().line()));
|
|
sfatalsnp(":");
|
|
sfatalbnp(&aphrodite::u32_as_u8_slice(info.location().unwrap().column()));
|
|
sfatalsnp(": ");
|
|
} else {
|
|
sfatals("Panic: ");
|
|
}
|
|
let mut formatter = FormattingOptions::new().create_formatter(unsafe { &mut FBI });
|
|
let _ = info.message().fmt(&mut formatter);
|
|
aphrodite::arch::interrupts::disable_interrupts();
|
|
unsafe {
|
|
asm!("hlt", options(noreturn));
|
|
}
|
|
}
|
|
|
|
#[unsafe(link_section = ".panic")]
|
|
#[panic_handler]
|
|
#[cfg(all(CONFIG_SPIN_ON_PANIC = "true", CONFIG_PREUSER_HALT_ON_PANIC = "false"))]
|
|
fn spin_on_panic(info: &PanicInfo) -> ! {
|
|
if info.location().is_some() {
|
|
sfatals("Panic at ");
|
|
sfatalsnp(info.location().unwrap().file());
|
|
sfatalsnp(":");
|
|
sfatalbnp(&aphrodite::u32_as_u8_slice(info.location().unwrap().line()));
|
|
sfatalsnp(":");
|
|
sfatalbnp(&aphrodite::u32_as_u8_slice(info.location().unwrap().column()));
|
|
sfatalsnp(": ");
|
|
} else {
|
|
sfatals("Panic: ");
|
|
}
|
|
let message = info.message().as_str().unwrap_or("");
|
|
if message != "" {
|
|
sfatalsnpln(message);
|
|
} else {
|
|
sfatalsnp("\n");
|
|
}
|
|
aphrodite::arch::interrupts::disable_interrupts();
|
|
loop {}
|
|
} |