301 lines
12 KiB
Rust
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(§ion).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(§ion).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");
|
|
}
|