uhhhhhhh,

This commit is contained in:
janis 2026-07-26 04:32:21 +02:00
parent 76506e919d
commit 57ea368511
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
14 changed files with 2279 additions and 38 deletions

View file

@ -4,7 +4,7 @@ codegen-backend = "llvm"
[unstable]
json-target-spec = true # lets us specify a custom target specification file
build-std-features = ["compiler-builtins-mem"]
build-std = ["core", "compiler_builtins"]
build-std = ["core", "alloc", "compiler_builtins"]
[build]
target = "x86_64-unknown-kernel.json"

View file

@ -71,6 +71,7 @@ set +e
qemu-system-x86_64 \
-drive file="$image",format=raw \
-machine q35,accel=kvm -enable-kvm \
-m 2G \
-drive if=pflash,format=raw,readonly=on,file="$OVMF_PATH/FV/OVMF_CODE.fd" \
-chardev stdio,id=serial0,logfile=qemu.log,signal=on \
-serial chardev:serial0 \

122
kernel/src/boot.rs Normal file
View file

@ -0,0 +1,122 @@
use crate::{limine, sync::OnceLock};
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum MemoryRegionType {
Usable = 0,
Reserved = 1,
AcpiReclaimable = 2,
AcpiNvs = 3,
BadMemory = 4,
Framebuffer = 5,
FirmwareReserved = 6,
FirmwareReclaimable = 7,
KernelAndModules = 8,
}
impl From<limine::MemMapEntryKind> for MemoryRegionType {
fn from(kind: limine::MemMapEntryKind) -> Self {
match kind {
limine::MemMapEntryKind::Usable => Self::Usable,
limine::MemMapEntryKind::Reserved => Self::Reserved,
limine::MemMapEntryKind::AcpiReclaimable => Self::AcpiReclaimable,
limine::MemMapEntryKind::AcpiNvs => Self::AcpiNvs,
limine::MemMapEntryKind::BadMemory => Self::BadMemory,
limine::MemMapEntryKind::BootloaderReclaimable => Self::FirmwareReclaimable,
limine::MemMapEntryKind::KernelAndModules => Self::KernelAndModules,
limine::MemMapEntryKind::Framebuffer => Self::Framebuffer,
limine::MemMapEntryKind::ReservedMapped => Self::FirmwareReserved,
limine::MemMapEntryKind::Unknown => Self::Reserved,
}
}
}
impl MemoryRegionType {
pub fn should_map(&self) -> bool {
!matches!(self, Self::Reserved | Self::BadMemory)
}
pub fn is_usable(&self) -> bool {
matches!(self, Self::Usable | Self::AcpiReclaimable)
}
}
#[derive(Debug, Clone, Copy)]
pub struct MemoryRegion {
pub start: u64,
pub length: u64,
pub region_type: MemoryRegionType,
}
impl From<limine::MemMapEntry> for MemoryRegion {
fn from(entry: limine::MemMapEntry) -> Self {
Self {
start: entry.base,
length: entry.length,
region_type: entry.kind().into(),
}
}
}
impl MemoryRegion {
pub fn iter_pages(&self, page_size: usize) -> MemoryRegionIter<'_> {
MemoryRegionIter {
region: self,
cursor: 0,
page_size,
}
}
}
pub struct MemoryRegionIter<'a> {
region: &'a MemoryRegion,
cursor: u64,
page_size: usize,
}
impl Iterator for MemoryRegionIter<'_> {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.region.length {
return None;
}
let addr = self.region.start + self.cursor;
self.cursor += self.page_size as u64;
Some(addr)
}
}
const impl Default for MemoryRegion {
fn default() -> Self {
Self {
start: 0,
length: 0,
region_type: MemoryRegionType::Reserved,
}
}
}
pub struct BootInfo {
pub hhdm_base: u64,
pub memory_map: &'static [MemoryRegion],
}
pub static BOOT_INFO: OnceLock<BootInfo> = OnceLock::new();
static mut MEMORY_MAP: [MemoryRegion; 128] = [MemoryRegion::default(); 128];
pub fn init_boot_info<I: Iterator<Item = MemoryRegion>>(hhdm_base: u64, memory_map: I) {
BOOT_INFO.initialize(|| {
for (i, region) in memory_map.enumerate() {
assert!(i < 128, "Memory map has more than 128 entries");
unsafe {
MEMORY_MAP[i] = region;
}
}
Ok::<_, !>(BootInfo {
hhdm_base,
memory_map: unsafe { (&raw const MEMORY_MAP).as_ref_unchecked() },
})
});
}

View file

@ -1,16 +1,30 @@
#![no_std]
#![feature(const_trait_impl, const_default, const_range, debug_closure_helpers)]
#![feature(
const_trait_impl,
const_default,
const_range,
debug_closure_helpers,
allocator_api,
ptr_cast_slice,
likely_unlikely,
never_type
)]
#![cfg_attr(test, feature(custom_test_frameworks))]
#![cfg_attr(test, test_runner(crate::tests::test_runner))]
#![cfg_attr(test, no_main)]
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
extern crate alloc;
pub mod bits;
pub mod limine;
pub mod serial;
pub mod sync;
pub mod x86_64;
pub mod boot;
pub mod memory;
pub mod testing;
#[cfg(test)]
mod tests;
@ -38,7 +52,7 @@ macro_rules! drop_guard {
fn forget(self) {
let mut this = ::core::mem::ManuallyDrop::new(self);
unsafe {
ManuallyDrop::drop(&mut this.0);
::core::mem::ManuallyDrop::drop(&mut this.0);
}
}
}

View file

@ -1,4 +1,4 @@
use core::{cell::UnsafeCell, ptr::NonNull};
use core::{cell::UnsafeCell, fmt::Debug, ptr::NonNull};
#[repr(C)]
pub struct BaseRevision(UnsafeCell<[u64; 3]>);
@ -44,7 +44,7 @@ pub struct Request<T, U = ()> {
request: U,
}
unsafe impl<T: Sync, U: Sync> Sync for Request<T, U>{}
unsafe impl<T: Sync, U: Sync> Sync for Request<T, U> {}
#[repr(C)]
pub struct Response<T> {
@ -165,3 +165,110 @@ impl FramebufferRequest {
.unwrap_or(&[])
}
}
#[repr(C)]
pub struct MemMapResponse {
count: u64,
entries: *const *const MemMapEntry,
}
unsafe impl Send for MemMapResponse {}
unsafe impl Sync for MemMapResponse {}
pub type MemMapRequest = Request<MemMapResponse>;
impl MemMapRequest {
const ID: [u64; 2] = [0x67cf3d9d378a806f, 0xe304acdfc50c3c62];
pub const fn new() -> Self {
Self::new_raw(Self::ID, 0, ())
}
pub fn entries<'a>(&self) -> &'a [&'a MemMapEntry] {
// SAFETY: limine responses are guaranteed to be valid for the lifetime of the memory mapping.
self.response()
.map(|response| unsafe {
core::slice::from_raw_parts(
response.response.entries.cast::<&'a MemMapEntry>(),
response.response.count as usize,
)
})
.unwrap_or(&[])
}
}
#[repr(u64)]
#[derive(Debug)]
pub enum MemMapEntryKind {
Usable = 0,
Reserved = 1,
AcpiReclaimable = 2,
AcpiNvs = 3,
BadMemory = 4,
BootloaderReclaimable = 5,
KernelAndModules = 6,
Framebuffer = 7,
ReservedMapped = 8,
Unknown = u64::MAX,
}
impl MemMapEntryKind {
pub fn from_u64(value: u64) -> Self {
match value {
0 => Self::Usable,
1 => Self::Reserved,
2 => Self::AcpiReclaimable,
3 => Self::AcpiNvs,
4 => Self::BadMemory,
5 => Self::BootloaderReclaimable,
6 => Self::KernelAndModules,
7 => Self::Framebuffer,
8 => Self::ReservedMapped,
_ => Self::Unknown,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct MemMapEntry {
pub base: u64,
pub length: u64,
pub kind: u64,
}
impl MemMapEntry {
pub fn kind(&self) -> MemMapEntryKind {
MemMapEntryKind::from_u64(self.kind)
}
}
impl Debug for MemMapEntry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MemMapEntry")
.field("base", &format_args!("{:#x}", self.base))
.field("length", &format_args!("{:#x}", self.length))
.field("kind", &self.kind())
.finish()
}
}
#[repr(C)]
#[derive(Debug)]
pub struct HhdmResponse {
pub offset: u64,
}
pub type HhdmRequest = Request<HhdmResponse>;
impl HhdmRequest {
const ID: [u64; 2] = [0x48dcf1cb8ad2b852, 0x63984e959a98244b];
pub const fn new() -> Self {
Self::new_raw(Self::ID, 0, ())
}
pub fn offset(&self) -> Option<u64> {
self.response().map(|response| response.response.offset)
}
}

View file

@ -1,18 +1,75 @@
#![no_std]
#![no_main]
use kernel::{
memory::VirtAddr,
sync::LazyLock,
x86_64::{gdt::GlobalDescriptorTable, idt::InterruptDescriptorTable},
};
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
kernel::serial_println!("[PANIC] {}", _info);
kernel::x86_64::halt_loop()
}
static IDT: LazyLock<InterruptDescriptorTable> =
LazyLock::new(InterruptDescriptorTable::new_default);
static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptorTable::new);
/// Entry point for the kernel
#[unsafe(no_mangle)]
extern "C" fn _start() -> ! {
kernel::serial_println!("Hello, world!");
assert!(limine_requests::LIMINE_BASE_REVISION.is_supported());
_ = kernel::memory::HHDM_BASE
.try_insert(
limine_requests::HHDM_REQUEST
.offset()
.expect("HHDM offset not provided by bootloader"),
)
.expect("HHDM offset already set");
kernel::boot::init_boot_info(
limine_requests::HHDM_REQUEST
.offset()
.expect("HHDM offset not provided by bootloader"),
limine_requests::MEMMAP_REQUEST
.entries()
.iter()
.map(|&&e| e.into()),
);
kernel::serial_println!(
"HHDM offset: 0x{:#x}",
kernel::memory::HHDM_BASE.get().unwrap()
);
kernel::serial_println!(
"Memory map: {:#?}",
limine_requests::MEMMAP_REQUEST.entries()
);
GDT.load();
IDT.load();
kernel::serial_println!("entry point: 0x{:x}", _start as *const () as usize);
kernel::serial_println!(
"entry point phy: {:?}",
kernel::x86_64::paging::get_physical_addr(VirtAddr(_start as *const () as u64))
);
let pmm = kernel::memory::PhysicalMemoryAllocator::from_memory_map(
kernel::boot::BOOT_INFO
.get()
.expect("Boot info not initialized")
.memory_map,
);
kernel::serial_println!("PMM: {pmm:#?}");
let fb = limine_requests::FRAMEBUFFER_REQUEST
.framebuffers()
.first()
@ -52,6 +109,14 @@ mod limine_requests {
#[unsafe(link_section = ".limine_requests")]
pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static HHDM_REQUEST: kernel::limine::HhdmRequest = kernel::limine::HhdmRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests")]
pub static MEMMAP_REQUEST: kernel::limine::MemMapRequest = kernel::limine::MemMapRequest::new();
#[used]
#[unsafe(link_section = ".limine_requests_end")]
static LIMINE_REQUESTS_END: RequestsEndMarker = REQUESTS_END_MARKER;

1066
kernel/src/memory.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,18 @@
#![cfg(target_arch = "x86_64")]
pub mod cpuid;
pub mod gdt;
pub mod idt;
pub mod instructions;
pub mod paging;
pub mod registers;
use core::{arch::asm, fmt::Debug};
pub use instructions::hlt;
use crate::memory::VirtAddr;
pub const PAGE_SIZE: usize = 4096;
pub fn halt_loop() -> ! {
@ -82,3 +87,128 @@ bitflags::bitflags! {
const ID_FLAG = 1 << 21;
}
}
pub trait VirtAddrExt {
const LEVEL5: u8 = 4;
const LEVEL4: u8 = 3;
const LEVEL3: u8 = 2;
const LEVEL2: u8 = 1;
const LEVEL1: u8 = 0;
const PT: u8 = Self::LEVEL1;
const PD: u8 = Self::LEVEL2;
const PDPT: u8 = Self::LEVEL3;
const PML4: u8 = Self::LEVEL4;
const PML5: u8 = Self::LEVEL5;
fn is_canonical(&self) -> bool;
fn page_table_index<const LEVEL: u8>(&self) -> u16;
fn offset_4k(&self) -> u16;
fn offset_2m(&self) -> u32;
fn offset_1g(&self) -> u32;
fn into_parts<E: sealed::PageSize>(self) -> (E::Entries, E::Offset);
}
pub struct L4Entries4K;
pub struct L4Entries2M;
pub struct L4Entries1G;
pub struct U12(pub u16);
pub struct U21(pub u32);
pub struct U30(pub u32);
pub(crate) mod sealed {
use super::{L4Entries1G, L4Entries2M, L4Entries4K, U12, U21, U30};
pub trait PageSize {
type Entries;
type Offset;
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset);
}
impl PageSize for L4Entries4K {
type Entries = [u16; 4];
type Offset = U12;
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) {
let entries = [
((addr_bits >> 39) & 0x1FF) as u16,
((addr_bits >> 30) & 0x1FF) as u16,
((addr_bits >> 21) & 0x1FF) as u16,
((addr_bits >> 12) & 0x1FF) as u16,
];
let offset = U12((addr_bits & 0xFFF) as u16);
(entries, offset)
}
}
impl PageSize for L4Entries2M {
type Entries = [u16; 3];
type Offset = U21;
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) {
let entries = [
((addr_bits >> 39) & 0x1FF) as u16,
((addr_bits >> 30) & 0x1FF) as u16,
((addr_bits >> 21) & 0x1FF) as u16,
];
let offset = U21((addr_bits & 0x1FFFFF) as u32);
(entries, offset)
}
}
impl PageSize for L4Entries1G {
type Entries = [u16; 2];
type Offset = U30;
fn decompose(addr_bits: u64) -> (Self::Entries, Self::Offset) {
let entries = [
((addr_bits >> 39) & 0x1FF) as u16,
((addr_bits >> 30) & 0x1FF) as u16,
];
let offset = U30((addr_bits & 0x3FFFFFFF) as u32);
(entries, offset)
}
}
}
impl VirtAddrExt for VirtAddr {
fn is_canonical(&self) -> bool {
// sign bits are bits 48-63, and they must all be the same as bit 47
//
// shift right 48 bits puts the 47th bit into the carry flag.
// sign-bits + CF should be 0
let canonical: u8;
unsafe {
asm!(
"shr {bits}, 48",
"adc {bits}, 0",
"setz {canonical}",
bits = inout(reg) self.0 => _,
canonical = out(reg_byte) canonical,
);
}
canonical == 1
}
fn page_table_index<const LEVEL: u8>(&self) -> u16 {
assert!(LEVEL <= 4, "LEVEL must be in the range 0..=4");
let shift = 12 + (LEVEL * 9);
((self.0 >> shift) & 0x1FF) as u16
}
fn offset_4k(&self) -> u16 {
(self.0 & 0xFFF) as u16
}
fn offset_2m(&self) -> u32 {
(self.0 & 0x1FFFFF) as u32
}
fn offset_1g(&self) -> u32 {
(self.0 & 0x3FFFFFFF) as u32
}
fn into_parts<E: sealed::PageSize>(self) -> (E::Entries, E::Offset) {
E::decompose(self.0)
}
}

425
kernel/src/x86_64/cpuid.rs Normal file
View file

@ -0,0 +1,425 @@
#![allow(clippy::identity_op)]
use bit_field::BitField;
use bitflags::bitflags;
pub struct CpuidResult {
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
}
pub fn cpuid(eax: u32, ecx: u32) -> CpuidResult {
let result = core::arch::x86_64::__cpuid_count(eax, ecx);
CpuidResult {
eax: result.eax,
ebx: result.ebx,
ecx: result.ecx,
edx: result.edx,
}
}
pub struct CpuId {
max_eax: u32,
vendor_id: [u8; 12],
}
pub struct Leaf1(CpuidResult);
impl Leaf1 {
pub fn get() -> Self {
let result = cpuid(1, 0);
Self(result)
}
pub fn brand_index(&self) -> u8 {
self.0.ebx.get_bits(0..8) as u8
}
pub fn clflush_line_size(&self) -> u8 {
self.0.ebx.get_bits(8..16) as u8
}
pub fn max_logical_processors(&self) -> u8 {
self.0.ebx.get_bits(16..24) as u8
}
pub fn initial_apic_id(&self) -> u8 {
self.0.ebx.get_bits(24..32) as u8
}
pub fn family_id(&self) -> u8 {
self.0.eax.get_bits(8..12) as u8 | ((self.0.eax.get_bits(20..28) as u8) << 4)
}
pub fn model_id(&self) -> u8 {
self.0.eax.get_bits(4..8) as u8 | ((self.0.eax.get_bits(16..20) as u8) << 4)
}
pub fn stepping_id(&self) -> u8 {
self.0.eax.get_bits(0..4) as u8
}
pub fn processor_type(&self) -> u8 {
self.0.eax.get_bits(12..14) as u8
}
pub fn flags(&self) -> Leaf1Flags {
Leaf1Flags::from_bits_truncate(self.0.ecx as u64 | ((self.0.edx as u64) << 32))
}
}
bitflags! {
pub struct Leaf1Flags: u64 {
const SSE3 = 1 << 0;
const PCLMULQDQ = 1 << 1;
const DTES64 = 1 << 2;
const MONITOR = 1 << 3;
const DS_CPL = 1 << 4;
const VMX = 1 << 5;
const SMX = 1 << 6;
const EIST = 1 << 7;
const TM2 = 1 << 8;
const SSSE3 = 1 << 9;
const CNXT_ID = 1 << 10;
const SDBG = 1 << 11;
const FMA = 1 << 12;
const CMPXCHG16B = 1 << 13;
const XTPR_UPDATE_CONTROL = 1 << 14;
const PDCM = 1 << 15;
const PCID = 1 << 17;
const DCA = 1 << 18;
const SSE4_1 = 1 << 19;
const SSE4_2 = 1 << 20;
const X2APIC = 1 << 21;
const MOVBE = 1 << 22;
const POPCNT = 1 << 23;
const TSC_DEADLINE_TIMER = 1 << 24;
const AESNI = 1 << 25;
const XSAVE = 1 << 26;
const OSXSAVE = 1 << 27;
const AVX = 1 <<28 ;
const F16C = 1 << 29;
const RDRAND = 1 << 30;
const FPU = 1 << (0 + u32::BITS);
const VME = 1 << (1 + u32::BITS);
const DE = 1 << (2 + u32::BITS);
const PSE = 1 << (3 + u32::BITS);
const TSC = 1 << (4 + u32::BITS);
const MSR = 1 << (5 + u32::BITS);
const PAE = 1 << (6 + u32::BITS);
const MCE = 1 << (7 + u32::BITS);
const CX8 = 1 << (8 + u32::BITS);
const APIC = 1 << (9 + u32::BITS);
const SEP = 1 << (11 + u32::BITS);
const MTRR = 1 << (12 + u32::BITS);
const PGE = 1 << (13 + u32::BITS);
const MCA = 1 << (14 + u32::BITS);
const CMOV = 1 << (15 + u32::BITS);
const PAT = 1 << (16 + u32::BITS);
const PSE36 = 1 << (17 + u32::BITS);
const PSN = 1 << (18 + u32::BITS);
const CLFSH = 1 << (19 + u32::BITS);
const DS = 1 << (21 + u32::BITS);
const ACPI = 1 << (22 + u32::BITS);
const MMX = 1 << (23 + u32::BITS);
const FXSR = 1 << (24 + u32::BITS);
const SSE = 1 << (25 + u32::BITS);
const SSE2 = 1 << (26 + u32::BITS);
const SS = 1 << (27 + u32::BITS);
const HTT = 1 << (28 + u32::BITS);
const TM = 1 << (29 + u32::BITS);
const PBE = 1 << (31 + u32::BITS);
}
}
pub struct Leaf7(CpuidResult);
impl Leaf7 {
pub fn get() -> Self {
let result = cpuid(7, 0);
Self(result)
}
pub fn subleaf1(&self) -> Option<Leaf7Subleaf1> {
if self.max_subleaf() >= 1 {
let result = cpuid(7, 1);
Some(Leaf7Subleaf1(result))
} else {
None
}
}
pub fn max_subleaf(&self) -> u32 {
self.0.eax
}
pub fn flags(&self) -> Leaf7Flags {
Leaf7Flags::from_bits_truncate(unsafe {
core::mem::transmute::<[[u8; 4]; 4], u128>(
[self.0.ebx, self.0.ecx, self.0.edx, 0].map(u32::to_ne_bytes),
)
})
}
pub fn mawau(&self) -> u8 {
self.0.ecx.get_bits(17..22) as u8
}
}
bitflags! {
pub struct Leaf7Flags: u128{
const FSGSBASE = 1 << 0;
const IA32_TSC_ADJUST_MSR = 1 << 1;
const SGX = 1 << 2;
const BMI1 = 1 << 3;
const HLE = 1 << 4;
const AVX2 = 1 << 5;
const SMEP = 1 << 7;
const BMI2 = 1 << 8;
const ERMS = 1 << 9;
const INVPCID = 1 << 10;
const RTM = 1 << 11;
const PQM = 1 << 12;
const FPU_CS_DS_DEPRECATION = 1 << 13;
const MPX = 1 << 14;
const PQE = 1 << 15;
const AVX512F = 1 << 16;
const AVX512DQ = 1 << 17;
const RDSEED = 1 << 18;
const ADX = 1 << 19;
const SMAP = 1 << 20;
const AVX512IFMA = 1 << 21;
const PCOMMIT = 1 << 22;
const CLFLUSHOPT = 1 << 23;
const CLWB = 1 <<24 ;
const INTEL_PT = 1 << 25;
const AVX512PF = 1 << 26;
const AVX512ER = 1 << 27;
const AVX512CD = 1 << 28;
const SHA = 1 << 29;
const AVX512BW = 1 << 30;
const AVX512VL = 1 << 31;
const PREFETCHWT1 = 1 << (0 + u32::BITS);
const AVX512VBMI = 1 << (1 + u32::BITS);
const UMIP = 1 << (2 + u32::BITS);
const PKU = 1 << (3 + u32::BITS);
const OSPKE = 1 << (4 + u32::BITS);
const RDPID = 1 << (22 + u32::BITS);
const SGX_LC = 1 << (30 + u32::BITS);
const SGX_KEYS = 1 << (1 + 2 * u32::BITS);
const AVX512_4VNNIW = 1 << (2 + 2 * u32::BITS);
const AVX512_4FMAPS = 1 << (3 + 2 * u32::BITS);
const FSRM = 1 << (4 + 2 * u32::BITS);
const UINTR = 1 << (5 + 2 * u32::BITS);
}
}
pub struct Leaf7Subleaf1(CpuidResult);
impl Leaf7Subleaf1 {
pub fn flags(&self) -> Leaf7Subleaf1Flags {
Leaf7Subleaf1Flags::from_bits_truncate(unsafe {
core::mem::transmute::<[u32; 4], u128>([self.0.eax, self.0.ebx, self.0.ecx, self.0.edx])
})
}
}
// in eax:ebx:ecx:edx order
bitflags! {
pub struct Leaf7Subleaf1Flags: u128 {
const SHA512 = 1 << 0;
const SM3 = 1 << 1;
const SM4 = 1 << 2;
const RAO_INT = 1 << 3;
const AVX_VNNI = 1 << 4;
const AVX512_BF16 = 1 << 5;
const LASS = 1 << 6;
const CMPCCXADD = 1 << 7;
const ARCHPERFMONTEXT = 1 << 8;
const FZRM = 1 << 10;
const FSRS = 1 << 11;
const RSRCS = 1 << 12;
const FRED = 1 << 17;
const LKGS = 1 << 18;
const WRMSRNS = 1 << 19;
const NMI_SRC = 1 << 20;
const AMX_FP16 = 1 << 21;
const HRESET = 1 << 22;
const AVX_IFMA = 1 << 23;
const LAM = 1 << 26;
const MSRLIST = 1 << 27;
const INVD_DISABLE_POST_BIOS_DONE = 1 << 30;
const MOVRS = 1 << 31;
const PPINSTR = 1 << (0 + u32::BITS);
const PBNDKB = 1 << (1 + u32::BITS);
const CPUIDMAXVAL_LIM_RMV = 1 << (2 + u32::BITS);
const RDT_M_ASYM = 1 << (0 + 2 * u32::BITS);
const RDT_A_ASYM = 1 << (1 + 2 * u32::BITS);
const MSR_IMM = 1 << (2 + 2 * u32::BITS);
const ACE = 1 << (3 + 2 * u32::BITS);
const AVX_VNNI_INT8 = 1 << (4 + 3 * u32::BITS);
const AVX_NE_CONVERT = 1 << (5 + 3 * u32::BITS);
const AMX_COMPLEX = 1 << (8 + 3 * u32::BITS);
const AVX_VNNI_INT16 = 1 << (10 + 3 * u32::BITS);
const UTMR = 1 << (13 + 3 * u32::BITS);
const PREFETCHI = 1 << (14 + 3 * u32::BITS);
const USER_MRS = 1 << (15 + 3 * u32::BITS);
const UIRET_UIF_FROM_RFLAGS = 1 << (17 + 3 * u32::BITS);
const CET_SSS = 1 << (18 + 3 * u32::BITS);
const AVX10 = 1 << (19 + 3 * u32::BITS);
const APX_F = 1 << (21 + 3 * u32::BITS);
const SEC_TEE_ATTESTATION = 1 << (22 + 3 * u32::BITS);
const MWAIT = 1 << (23 + 3 * u32::BITS);
const SLSM = 1 << (24 + 3 * u32::BITS);
}
}
bitflags! {
pub struct Leaf8000001Flags: u64 {
const FPU = 1 << 0;
const VME = 1 << 1;
const DE = 1 << 2;
const PSE = 1 << 3;
const TSC = 1 << 4;
const MSR = 1 << 5;
const PAE = 1 << 6;
const MCE = 1 << 7;
const CX8 = 1 << 8;
const APIC = 1 << 9;
const SYSCALL_K9 = 1 << 10;
const SYSCALL = 1 << 11;
const MTRR = 1 << 12;
const PGE = 1 << 13;
const MCA = 1 << 14;
const CMOV = 1 << 15;
const PAT = 1 << 16;
const PSE36 = 1 << 17;
const ECC_K9 = 1 << 18;
const ECC = 1 << 19;
const NX = 1 << 20;
const MMXEXT = 1 << 22;
const MMX = 1 << 23;
const FXSR_OPT = 1 << 24;
const PDPE1GB = 1 << 26;
const RDTSCP = 1 << 27;
const REX32 = 1 << 28;
const LM = 1 << 29;
const _3DNOWEXT = 1 << 30;
const _3DNOW = 1 << 31;
const LAHF_LM = 1 << (0 + u32::BITS);
const CMP_LEGACY = 1 << (1 + u32::BITS);
const SVM = 1 << (2 + u32::BITS);
const EXTAPIC = 1 << (3 + u32::BITS);
const CR8_LEGACY = 1 << (4 + u32::BITS);
const LZCNT = 1 << (5 + u32::BITS);
const SSE4A = 1 << (6 + u32::BITS);
const MISALIGNSSE = 1 << (7 + u32::BITS);
const _3DNOWPREFETCH = 1 << (8 + u32::BITS);
const OSVW = 1 << (9 + u32::BITS);
const IBS = 1 << (10 + u32::BITS);
const XOP = 1 << (11 + u32::BITS);
const SKINIT = 1 << (12 + u32::BITS);
const WDT = 1 << (13 + u32::BITS);
const LWP = 1 << (15 + u32::BITS);
const FMA4 = 1 << (16 + u32::BITS);
const TCE = 1 << (17 + u32::BITS);
const NODEID_MSR = 1 << (19 + u32::BITS);
const TBM = 1 << (21 + u32::BITS);
const TOPOEXT = 1 << (22 + u32::BITS);
const PERFCTR_CORE = 1 << (23 + u32::BITS);
const PERFCTR_NB = 1 << (24 + u32::BITS);
const DBX = 1 << (26 + u32::BITS);
const PERFTSC = 1 << (27 + u32::BITS);
const PCX_L2I = 1 << (28 + u32::BITS);
const MONITORX = 1 << (29 + u32::BITS);
const ADDR_MASK_EXT = 1 << (30 + u32::BITS);
}
}
pub struct Leaf8000008(CpuidResult);
impl Leaf8000008 {
pub fn physical_address_bits(&self) -> u8 {
self.0.eax.get_bits(0..8) as u8
}
pub fn num_linear_address_bits(&self) -> u8 {
self.0.eax.get_bits(8..16) as u8
}
pub fn gest_physical_address_bits(&self) -> u8 {
self.0.eax.get_bits(16..24) as u8
}
pub fn num_physical_threads(&self) -> u8 {
self.0.ecx.get_bits(0..8) as u8 - 1
}
pub fn apic_id_size(&self) -> u8 {
self.0.ecx.get_bits(12..16) as u8
}
pub fn performance_timestamp_counter_size(&self) -> u8 {
self.0.ecx.get_bits(16..18) as u8
}
pub fn max_invlpgb_page_count(&self) -> u16 {
self.0.edx.get_bits(0..16) as u16
}
pub fn max_rdpru_ecx(&self) -> u16 {
self.0.edx.get_bits(16..32) as u16
}
pub fn flags(&self) -> Leaf8000008Flags {
Leaf8000008Flags::from_bits_truncate(self.0.ebx)
}
}
bitflags! {
pub struct Leaf8000008Flags: u32 {
const CLZERO = 1 << 0;
const RETIRED_INSTR = 1 << 1;
const XRSTOR_FP_ERR = 1 << 2;
const INVLPGB = 1 << 3;
const RDPRU = 1 << 4;
const XOTEXT = 1 << 5;
const MBE = 1 << 6;
const MCOMMIT = 1 << 8;
const WBNOINVD = 1 << 9;
const LBR_EXT_V1 = 1 << 10;
const IBPB = 1 << 12;
const WBINVD_INT = 1 << 13;
const IBRS = 1 << 14;
const STIBP = 1 << 15;
const IBRS_ALWAYS = 1 << 16;
const STIBP_ALWAYS = 1 << 17;
const IBRS_PREFERRED = 1 << 18;
const IBRS_SAME_MODE_PROT = 1 << 19;
const NO_EFER_LMSLE = 1 << 20;
const INVLPGB_NESTED = 1 << 21;
const LBR_TSX = 1 << 22;
const PPIN = 1 << 23;
const SSBD = 1 << 24;
const SSBD_LEGACY = 1 << 25;
const SSBD_NO = 1 << 26;
const CPPC = 1 << 27;
const PSFD = 1 << 28;
const BTC_NO = 1 << 29;
const IBPB_RET = 1 << 30;
const BRANCH_SAPLING = 1 << 31;
}
}
impl CpuId {
pub fn get() -> Self {
let result = cpuid(0, 0);
let vendor_id = unsafe {
core::mem::transmute::<[u32; 3], [u8; 12]>([result.ebx, result.edx, result.ecx])
};
Self {
max_eax: result.eax,
vendor_id,
}
}
pub fn vendor_id(&self) -> &[u8; 12] {
&self.vendor_id
}
}

View file

@ -514,5 +514,3 @@ pub static TSS: LazyLock<TaskStateSegment> = LazyLock::new(|| {
tss
});
pub static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptorTable::new);

View file

@ -4,7 +4,7 @@ use bit_field::BitField;
use crate::{
serial_println,
x86_64::{RFlags, Registers},
x86_64::{RFlags, Registers, halt_loop},
};
#[derive(Clone, Copy)]
@ -507,13 +507,24 @@ extern "C" fn global_interrupt_handler(
error_code: u64,
registers: &Registers,
) {
crate::serial_println!(
serial_println!(
"Interrupt {} occurred! Error code: {:#x}, Frame: {:#?}, Registers: {:#?}",
index,
error_code,
frame,
registers
);
match index {
ExceptionVector::BREAKPOINT => {
serial_println!("Breakpoint interrupt handled successfully.");
}
ExceptionVector::PAGE_FAULT => {
serial_println!("Page fault occurred! Error code: {:#x}", error_code);
halt_loop()
}
_ => {}
}
}
#[cfg(test)]

View file

@ -1,35 +1,241 @@
use core::{
borrow::Borrow,
fmt::Debug,
hint::unlikely,
ops::{Deref, Index},
};
use bit_field::BitField;
use crate::{
memory::{PhyAddr, VirtAddr, VirtAddrTranslationExt},
serial_println,
x86_64::{VirtAddrExt, registers::Cr4},
};
#[repr(C, align(4096))]
pub struct PageTable<Entry> {
entries: [Entry; 512],
pub struct PageTable {
entries: [PageTableEntry; 512],
}
impl PageTable {
pub fn get(&self, index: u16) -> Option<PageTableEntry> {
if index < 512 {
let entry = self.entries[index as usize];
if entry.present() { Some(entry) } else { None }
} else {
None
}
}
pub fn get_unchecked(&self, index: u16) -> PageTableEntry {
assert!(index < 512, "Page table index out of bounds");
self.entries[index as usize]
}
}
impl Index<u16> for PageTable {
type Output = PageTableEntry;
fn index(&self, index: u16) -> &Self::Output {
assert!(index < 512, "Page table index out of bounds");
&self.entries[index as usize]
}
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PageTableEntry(PageTableEntryFlags);
impl Debug for PageTableEntry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PageTableEntry")
.field(
"flags",
&PageTableEntryFlags::from_bits_truncate(self.as_raw()),
)
.field("phy", &self.phy())
.field("pk", &self.pk())
.field("pat_index", &self.pat_index())
.field("free_bits", &format_args!("{:#b}", self.free_bits()))
.finish()
}
}
impl AsRef<PageTableEntryFlags> for PageTableEntry {
fn as_ref(&self) -> &PageTableEntryFlags {
&self.0
}
}
impl Borrow<PageTableEntryFlags> for PageTableEntry {
fn borrow(&self) -> &PageTableEntryFlags {
&self.0
}
}
impl Deref for PageTableEntry {
type Target = PageTableEntryFlags;
fn deref(&self) -> &Self::Target {
&self.0
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PageTableEntryFlags: u64 {
const PRESENT = 1 << 0;
const WRITABLE = 1 << 1;
const USER_ACCESSIBLE = 1 << 2;
const WRITE_THROUGH = 1 << 3;
const NO_CACHE = 1 << 4;
const WRITE_THROUGH = 1 << 3; // PWT
const NO_CACHE = 1 << 4; // PCD
const ACCESSED = 1 << 5;
const DIRTY = 1 << 6;
const HUGE_PAGE = 1 << 7;
const GLOBAL = 1 << 8;
const NO_EXECUTE = 1 << 63;
const PAT_4K = 1 << 7;
const PAT_HUGE = 1 << 12;
}
}
impl PageTableEntryFlags {
impl PageTableEntry {
pub fn from_raw(bits: u64) -> Self {
Self::from_bits_retain(bits)
Self(PageTableEntryFlags::from_bits_retain(bits))
}
pub fn as_raw(&self) -> u64 {
self.bits()
self.0.bits()
}
pub fn as_mut_raw(&mut self) -> &mut u64 {
self.0.0.bits_mut()
}
pub fn present(&self) -> bool {
self.contains(PageTableEntryFlags::PRESENT)
}
pub fn phy(&self) -> PhyAddr {
PhyAddr(self.as_raw().get_bits(12..52) << 12)
}
pub fn try_as_page_table(&self) -> Option<&PageTable> {
if !self.contains(PageTableEntryFlags::PRESENT) {
return None;
}
if self.contains(PageTableEntryFlags::HUGE_PAGE) {
return None;
}
unsafe {
Some(
self.phy()
.into_hhdm_virt()
.as_ptr::<PageTable>()
.as_ref()
.unwrap_unchecked(),
)
}
}
pub unsafe fn as_page_table(&self) -> &PageTable {
// entry must be present
assert!(self.contains(PageTableEntryFlags::PRESENT));
// if the entry is a huge page, it does not point to a deeper page table.
assert!(!self.contains(PageTableEntryFlags::HUGE_PAGE));
unsafe {
self.phy()
.into_hhdm_virt()
.as_ptr::<PageTable>()
.as_ref()
.unwrap_unchecked()
}
}
pub fn pk(&self) -> u8 {
self.as_raw().get_bits(59..63) as u8
}
pub fn pat_index(&self) -> u8 {
let pat = if self.contains(PageTableEntryFlags::HUGE_PAGE) {
self.as_raw().get_bit(12) as u8
} else {
self.as_raw().get_bit(7) as u8
};
self.as_raw().get_bits(3..=4) as u8 | (pat << 2)
}
pub fn phy(&self) -> u64 {
self.bits().get_bits(12..52) << 12
/// Returns the free bits in the page table entry, which are bits 9-11 and
/// 52-58, combined into a single 10-bit value.
pub fn free_bits(&self) -> u16 {
let low = self.bits().get_bits(9..12) as u16;
let high = self.bits().get_bits(52..59) as u16;
low | (high << 3)
}
pub fn set_free_bits(&mut self, value: u16) {
let low = (value & 0b111) as u64;
let high = (value >> 3) as u64;
self.as_mut_raw().set_bits(9..12, low);
self.as_mut_raw().set_bits(52..59, high);
}
}
impl VirtAddrTranslationExt for VirtAddr {
fn into_phy_addr(self) -> Option<PhyAddr> {
get_physical_addr(self)
}
}
pub fn get_physical_addr(virt: VirtAddr) -> Option<PhyAddr> {
let cr3 = crate::x86_64::registers::Cr3::read();
let cr4 = crate::x86_64::registers::Cr4::read();
serial_println!("cr3: {:?}", cr3);
serial_println!("cr4: {:?}", cr4);
let frame = cr3.phy();
let page_table = unsafe {
frame
.into_hhdm_virt()
.as_ptr::<PageTable>()
.as_ref()
.unwrap_unchecked()
};
let pml4_entry = if unlikely(cr4.contains(Cr4::LA57)) {
let l5_entry = page_table[virt.page_table_index::<{ VirtAddr::PML5 }>()];
l5_entry
.try_as_page_table()?
.get_unchecked(virt.page_table_index::<{ VirtAddr::PML4 }>())
} else {
page_table[virt.page_table_index::<{ VirtAddr::PML4 }>()]
};
serial_println!("pml4_entry: {:?}", pml4_entry);
let pdpt_entry = pml4_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PDPT }>()];
serial_println!("pdpt_entry: {:?}", pdpt_entry);
if pdpt_entry.contains(PageTableEntryFlags::HUGE_PAGE) {
const PHY_MASK_1G: u64 = !((1 << 30) - 1);
let phys_addr = (pdpt_entry.phy().0 & PHY_MASK_1G) + virt.offset_1g() as u64;
return Some(PhyAddr(phys_addr));
}
let pd_entry = pdpt_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PD }>()];
serial_println!("pd_entry: {:?}", pd_entry);
if pd_entry.contains(PageTableEntryFlags::HUGE_PAGE) {
const PHY_MASK_2M: u64 = !((1 << 21) - 1);
let phys_addr = (pd_entry.phy().0 & PHY_MASK_2M) + virt.offset_2m() as u64;
return Some(PhyAddr(phys_addr));
}
let pt_entry = pd_entry.try_as_page_table()?[virt.page_table_index::<{ VirtAddr::PT }>()];
serial_println!("pt_entry: {:?}", pt_entry);
if !pt_entry.contains(PageTableEntryFlags::PRESENT) {
return None;
}
let phy_addr = pt_entry.phy().into_hhdm_virt().0 + virt.offset_4k() as u64;
Some(PhyAddr(phy_addr))
}

View file

@ -1,4 +1,9 @@
use core::{arch::asm, ops::Index};
use core::{arch::asm, fmt::Debug, ops::Index};
use bit_field::BitField;
use bitflags::bitflags;
use crate::memory::PhyAddr;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -35,30 +40,31 @@ impl Cr0Flags {
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cr4Flags: u64 {
pub struct Cr4: u64 {
const VIRTUAL_8086_MODE_EXTENSIONS = 1 << 0;
const PROTECTED_MODE_VIRTUAL_INTERRUPTS = 1 << 1;
const TIME_STAMP_DISABLE = 1 << 2;
const DEBUGGING_EXTENSIONS = 1 << 3;
const PAGE_SIZE_EXTENSIONS = 1 << 4;
const PHYSICAL_ADDRESS_EXTENSION = 1 << 5;
const MACHINE_CHECK_ENABLE = 1 << 6;
const PAGE_GLOBAL_ENABLE = 1 << 7;
const PERFORMANCE_MONITOR_COUNTER_ENABLE = 1 << 8;
const MACHINE_CHECK = 1 << 6;
const PAGE_GLOBAL = 1 << 7;
const PERFORMANCE_MONITOR_COUNTER = 1 << 8;
const OSFXSR_SUPPORT = 1 << 9;
const OSXMMEXCPT_SUPPORT = 1 << 10;
const USER_MODE_INSTRUCTION_PREVENTION = 1 << 11;
const VMX_ENABLE = 1 << 13;
const SMX_ENABLE = 1 << 14;
const FSGSBASE_ENABLE = 1 << 16;
const PCID_ENABLE = 1 << 17;
const OSXSAVE_ENABLE = 1 << 18;
const SMEP_ENABLE = 1 << 20;
const SMAP_ENABLE = 1 << 21;
const LA57 = 1 << 12;
const VMX = 1 << 13;
const SMX = 1 << 14;
const FSGSBASE = 1 << 16;
const PCID = 1 << 17;
const OSXSAVE = 1 << 18;
const SMEP = 1 << 20;
const SMAP = 1 << 21;
}
}
impl Cr4Flags {
impl Cr4 {
pub fn read() -> Self {
let value: u64;
unsafe {
@ -100,13 +106,44 @@ impl IA32EferFlags {
pub struct IA32Pat(u64);
const impl Default for IA32Pat {
fn default() -> Self {
Self::from_entries([
IA32PatEntry::WRITE_BACK,
IA32PatEntry::WRITE_THROUGH,
IA32PatEntry::UNCACHEABLE,
IA32PatEntry::UNCACHED,
IA32PatEntry::WRITE_BACK,
IA32PatEntry::WRITE_THROUGH,
IA32PatEntry::UNCACHEABLE,
IA32PatEntry::UNCACHED,
])
}
}
impl IA32Pat {
pub fn from_entries(entries: [IA32PatEntry; 8]) -> Self {
let mut value = 0u64;
for (i, entry) in entries.iter().enumerate() {
value |= (entry.0 as u64) << (i * 8);
}
Self(value)
pub const fn from_entries(entries: [IA32PatEntry; 8]) -> Self {
let [
IA32PatEntry(e0),
IA32PatEntry(e1),
IA32PatEntry(e2),
IA32PatEntry(e3),
IA32PatEntry(e4),
IA32PatEntry(e5),
IA32PatEntry(e6),
IA32PatEntry(e7),
] = entries;
let val = e0 as u64
| ((e1 as u64) << 8)
| ((e2 as u64) << 16)
| ((e3 as u64) << 24)
| ((e4 as u64) << 32)
| ((e5 as u64) << 40)
| ((e6 as u64) << 48)
| ((e7 as u64) << 56);
Self(val)
}
pub fn get(&self, index: u8) -> IA32PatEntry {
assert!(index < 8, "Index out of bounds for IA32Pat");
@ -131,3 +168,60 @@ impl IA32PatEntry {
pub const WRITE_BACK: Self = Self(6);
pub const UNCACHED: Self = Self(7);
}
pub struct Cr3(u64);
impl Debug for Cr3 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Cr3")
.field("phy", &self.phy())
.field("flags", &self.flags())
.field("free_bits", &format_args!("{:#x}", self.free_bits()))
.finish()
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cr3Flags: u64 {
const PAGE_LEVEL_WRITE_THROUGH = 1 << 3;
const PAGE_LEVEL_CACHE_DISABLE = 1 << 4;
}
}
impl Cr3 {
pub fn read() -> Self {
let value: u64;
unsafe {
asm!("mov {}, cr3", out(reg) value, options(nomem, nostack, preserves_flags));
}
Self(value)
}
pub unsafe fn write(&self) {
unsafe {
asm!("mov cr3, {}", in(reg) self.0, options(nomem, nostack, preserves_flags));
}
}
pub fn flags(&self) -> Cr3Flags {
Cr3Flags::from_bits_truncate(self.0)
}
pub fn set_flags(&mut self, flags: Cr3Flags) {
const MASK: u64 = Cr3Flags::all().bits();
self.0 = (self.0 & !MASK) | flags.bits();
}
pub fn phy(&self) -> PhyAddr {
PhyAddr(self.0.get_bits(12..52) << 12)
}
pub fn free_bits(&self) -> u16 {
let low = (self.0 & 0x7) as u16;
let high = self.0.get_bits(5..12) as u16;
low | (high << 3)
}
pub fn set_free_bits(&mut self, value: u16) {
let low = (value & 0x7) as u64;
let high = (value >> 3) as u64;
self.0.set_bits(0..3, low);
self.0.set_bits(5..12, high);
}
}

View file

@ -7,11 +7,13 @@ use core::{cell::UnsafeCell, mem::offset_of};
use kernel::{
sync::LazyLock,
x86_64::{
gdt::{DF_STACK, GDT, GlobalDescriptorTable, RING0},
gdt::{DF_STACK, GlobalDescriptorTable, RING0},
idt::{self, Entry, InterruptDescriptorTable},
},
};
pub static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptorTable::new);
#[unsafe(export_name = "_start")]
pub extern "C" fn main() -> ! {
kernel::serial_println!("Hello, world!");