kernel/src/main.rs

301 lines
12 KiB
Rust

#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(alloc_error_handler)]
#![feature(allocator_api)]
#![feature(naked_functions)]
#![feature(stmt_expr_attributes)]
#![feature(exposed_provenance)]
// #![feature(strict_provenance)]
#![deny(
unsafe_op_in_unsafe_fn,
reason = "Unsafe blocks make it clear what parts of the code is unsafe and should not be skipped in unsafe functions, which are only unsafe to call."
)]
#![deny(
unfulfilled_lint_expectations,
reason = "If the expected lint goes away, this most likely means the expect attribute should be removed"
)]
// #![deny(fuzzy_provenance_casts)]
#![allow(clippy::duplicated_attributes, reason = "Identical reasons trigger this falsely")]
#![deny(
clippy::unwrap_used,
reason = "Unwraps must be used only when they will provably not panic, and the justification must be provided"
)]
#![deny(
clippy::expect_used,
reason = "Expects will cause a kernel panic. Kernel panics must be justified, as the kernel crashing is irrecoverable and brings the whole system down."
)]
#![deny(
clippy::panic,
reason = "Kernel panics must be justified, as the kernel crashing is irrecoverable and brings the whole system down."
)]
#![deny(
clippy::expect_used,
reason = "Expects will cause a kernel panic. Kernel panics must be justified, as the kernel crashing is irrecoverable and brings the whole system down."
)]
#![deny(
clippy::allow_attributes,
reason = "Expect attributes warn when the expected lint is not present, which is preferrable"
)]
#![deny(clippy::allow_attributes_without_reason, reason = "Allowing lints needs a justification")]
#![deny(clippy::arithmetic_side_effects)]
#![deny(clippy::indexing_slicing)]
#![warn(clippy::unimplemented)]
#![warn(clippy::todo)]
#![warn(clippy::pedantic)]
#![allow(clippy::cast_lossless, reason = "Covered by as_conversions")]
#![allow(clippy::cast_possible_truncation, reason = "Covered by as_conversions")]
#![allow(clippy::cast_possible_wrap, reason = "Covered by as_conversions")]
#![allow(clippy::cast_precision_loss, reason = "Covered by as_conversions")]
#![allow(clippy::cast_sign_loss, reason = "Covered by as_conversions")]
#![allow(
clippy::match_same_arms,
reason = "Repeated match arms usually exist for readability or when the ordering is important"
)]
#![allow(clippy::missing_panics_doc, reason = "Don't care")]
#![allow(clippy::missing_errors_doc, reason = "Don't care")]
#![allow(clippy::similar_names, reason = "Don't care")]
#![allow(clippy::too_many_lines, reason = "Don't care")]
#![warn(clippy::nursery)]
#![allow(clippy::suspicious_operation_groupings, reason = "Too easy for false positives")]
#![allow(
clippy::option_if_let_else,
reason = "if-let is for imperative code, map_or for functional. Not the same unlike what this lint says."
)]
#![allow(clippy::non_send_fields_in_send_ty, reason = "Too easy for false positives")]
#![allow(clippy::missing_const_for_fn, reason = "Most triggers don't actually make sense as const")]
#![allow(
clippy::while_float,
reason = "Lint checks for a construct you'd have to be really stupid to write and has easy false positives"
)]
#![deny(clippy::as_conversions)]
extern crate alloc;
mod bootinfo;
mod gdt;
mod interrupts;
mod kernel_heap;
mod panic_handler;
mod physical_memory;
mod pit;
mod qemu_exit;
mod serial;
mod start;
mod tasking;
mod virtual_memory;
use core::{ptr, slice};
use alloc::ffi::CString;
use bootinfo::BOOTINFO;
use cast::usize;
use elf::{
abi::{
PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_GNU_STACK, PT_LOAD, PT_NULL, PT_PHDR,
R_X86_64_RELATIVE, SHT_REL, SHT_RELA,
},
endian::AnyEndian,
ElfBytes,
};
use physical_memory::PHYSICAL_MEMORY;
use serial::SECOND_PORT;
use spin::lazy::Lazy;
use tar_no_std::TarArchiveRef;
use tasking::TASKING;
use virtual_memory::{ACTIVE_SPACE, KERNEL_SPACE};
use x86_64::{
registers::rflags::{self, RFlags},
structures::paging::{Page, PageTableFlags},
VirtAddr,
};
use crate::virtual_memory::AddressSpace;
// pub static INITRD: &[u8] = include_bytes!("../initrd.tar");
#[expect(
clippy::expect_used,
reason = "Nothing to do here but panic on errors, this is the top level"
)]
pub fn main() {
let mut rflags_data = rflags::read();
rflags_data |= RFlags::IOPL_HIGH | RFlags::IOPL_LOW;
unsafe {
rflags::write(rflags_data);
}
gdt::init();
Lazy::force(&PHYSICAL_MEMORY);
Lazy::force(&KERNEL_SPACE);
Lazy::force(&ACTIVE_SPACE);
interrupts::init();
pit::init(100);
let initrd = unsafe {
let ramdisk_start = BOOTINFO.ramdisk_addr.into_option().expect("initrd no present");
let ramdisk_len = BOOTINFO.ramdisk_len;
slice::from_raw_parts(
ptr::with_exposed_provenance(usize(ramdisk_start)),
usize(ramdisk_len),
)
};
let initrd = TarArchiveRef::new(initrd).expect("initrd not valid TAR archive");
let init_data = initrd
.entries()
.find(|x| x.filename().as_str() == Ok("bin/init"))
.expect("Could not find init in initrd")
.data();
let init = ElfBytes::<AnyEndian>::minimal_parse(init_data).expect("init not valid ELF file");
let mut init_addr_space = AddressSpace::new().expect("failed to create address space for init");
let pheaders = init.segments().expect("init has no program headers (not an executable?)");
for mut pheader in pheaders {
match pheader.p_type {
PT_NULL => (),
PT_LOAD => {
if pheader.p_vaddr < 0x1000 {
if pheader.p_memsz < 0x1000 {
continue;
}
#[expect(
clippy::arithmetic_side_effects,
reason = "p_vaddr has been cheched to be below 0x1000, thus this cannot underflow"
)]
{
pheader.p_offset += 0x1000 - pheader.p_vaddr;
pheader.p_memsz -= 0x1000 - pheader.p_vaddr;
pheader.p_filesz -= 0x1000 - pheader.p_vaddr;
}
pheader.p_vaddr = 0x1000;
}
let start_page = Page::containing_address(VirtAddr::new(pheader.p_vaddr));
let num_pages = if pheader.p_vaddr.trailing_zeros() >= 12 {
usize(pheader.p_memsz.div_ceil(4096))
} else {
#[expect(
clippy::arithmetic_side_effects,
reason = "The RHS is always < 4096, thus this cannot underflow"
)]
let page_part_sz = 4096 - (pheader.p_vaddr & 0xFFF);
if pheader.p_memsz < page_part_sz {
1
} else {
#[expect(
clippy::arithmetic_side_effects,
reason = "Sub: pheader.p_memsz >= page_part_sz, thus this cannot underflow. Add: usize::MAX.div_ceil(4096) < usize::MAX, so this cannot overflow."
)]
let res = 1 + usize((pheader.p_memsz - page_part_sz).div_ceil(4096));
res
}
};
init_addr_space
.map_only_unused(start_page, num_pages, PageTableFlags::USER_ACCESSIBLE)
.expect("Unable to map region");
init_addr_space.run(|| unsafe {
let dst = slice::from_raw_parts_mut(
ptr::with_exposed_provenance_mut(usize(pheader.p_vaddr)),
usize(pheader.p_memsz),
);
dst.get_mut(0..usize(pheader.p_filesz))
.expect("Pheader filesize greater tham memsize")
.copy_from_slice(
init_data
.get(
usize(pheader.p_offset)
..usize(
pheader
.p_offset
.checked_add(pheader.p_filesz)
.expect("End of segment in file wraps around"),
),
)
.expect(
"Program header references data beyond the end of the file",
),
);
dst.get_mut(usize(pheader.p_filesz)..usize(pheader.p_memsz))
.expect("Pheader filesize greater than memsize")
.fill(0);
});
}
PT_GNU_RELRO => (),
PT_GNU_EH_FRAME => (),
PT_GNU_STACK => (),
PT_DYNAMIC => (),
PT_PHDR => (),
_ => println!("Warning: Unimplemented ELF program header type {:#x}", pheader.p_type),
}
}
if let Some(section_headers) = init.section_headers() {
for section in section_headers.iter() {
if section.sh_type == SHT_REL {
#[expect(
clippy::never_loop,
reason = "This loop exists as a template to add relocations when they are implemented"
)]
#[expect(
clippy::unwrap_used,
reason = "section_data_as_rels requires the section type to be SHT_REL, which we already checked"
)]
for rel in init.section_data_as_rels(&section).unwrap() {
#[expect(
clippy::match_single_binding,
reason = "This match exists as a template to add relocations when they are implemented"
)]
match rel.r_type {
_ => unimplemented!("ELF relocation type {}", rel.r_type),
}
}
}
if section.sh_type == SHT_RELA {
#[expect(
clippy::unwrap_used,
reason = "section_data_as_relas requires the section type to be SHT_RELA, which we already checked"
)]
for rela in init.section_data_as_relas(&section).unwrap() {
match rela.r_type {
R_X86_64_RELATIVE => {
init_addr_space.run(|| unsafe {
let ptr =
ptr::with_exposed_provenance_mut::<u64>(usize(rela.r_offset));
ptr.write(
rela.r_addend
.try_into()
.expect("Invalid addend for relocation"),
);
});
}
_ => unimplemented!("ELF relocation type {}", rela.r_type),
}
}
}
}
}
// Before starting init, write the pcapng section header + interface description to the second serial port
SECOND_PORT.write_u32s(&[
0x0A0D_0D0A, // SHB type
7 * 4, // Total block length
0x1A2B_3C4D, // Byte order magic
0x0000_0001, // Version (1.0)
0xFFFF_FFFF, // Length upper (-1) across both
0xFFFF_FFFF, // Length lower
7 * 4, // Total block length
0x1, // IDB type
5 * 4, // Total block length
147, // Link type
4096 + 8, // Packet length limit,
5 * 4, // Total block length
]);
#[expect(
clippy::unwrap_used,
reason = "Argument does not contain a null byte, thus this cannot panic"
)]
TASKING
.new_process(
ptr::with_exposed_provenance(usize(init.ehdr.e_entry)),
init_addr_space,
&[&CString::new(b"init").unwrap()],
)
.expect("Failed to create init process");
}