// Copyright (c) 2025 shibedrill // SPDX-License-Identifier: MIT use core::arch::asm; use core::ptr::addr_of_mut; use intbits::Bits; use x86_64::structures::gdt; use x86_64::structures::gdt::*; use crate::log::*; use crate::memory::alloc::format; use crate::{log_info, log_trace}; #[allow(dead_code)] pub trait GdtEntryRead { fn base(&self) -> usize; fn limit(&self) -> usize; fn flags(&self) -> u8; fn access(&self) -> u8; } impl GdtEntryRead for Entry { fn base(&self) -> usize { ((self.raw().bits(56..=63) << 24) & self.raw().bits(16..=39)) as usize } fn limit(&self) -> usize { ((self.raw().bits(48..=51) << 16) & self.raw().bits(16..=31)) as usize } fn access(&self) -> u8 { self.raw().bits(40..=47) as u8 } fn flags(&self) -> u8 { self.raw().bits(52..=55) as u8 } } #[allow(dead_code)] pub fn read_gdt() -> GlobalDescriptorTable { let mut addr: u128 = 0; unsafe { asm! { "sgdt [rax]", in("rax") addr_of_mut!(addr), } } // x86 gdtr: Lower 2 8-bit bytes (lower 16 bits) is the size of the GDT, // in 8-bit bytes, minus one. // Bits 16 through 79 are the virtual address of the GDT. let limit = addr.bits(0..=15) as usize + 1; let base = addr.bits(16..=79) as u64; let gdt_raw: &[u64] = unsafe { core::slice::from_raw_parts(base as *const u64, (limit / 8) - 1) }; GlobalDescriptorTable::from_raw_entries(gdt_raw) } pub fn dump_gdt() { let gdt = crate::arch::x86_64::gdt::read_gdt(); log_info!("GDT length: {}", gdt.limit() + 1); for entry in gdt.entries() { let flags = x86_64::structures::gdt::DescriptorFlags::from_bits_retain(entry.raw()); let seg_type = SegmentType::try_from(entry); log_trace!( "GDT entry: type: {:?} (ring {}), width: {}, length: 0x{:x}, raw flags: 0b{:04b}, access: 0b{:08b}", seg_type.unwrap(), entry.access().bits(5..=6), if flags.contains(gdt::DescriptorFlags::DEFAULT_SIZE) { "32-bit" } else if flags.contains(gdt::DescriptorFlags::LONG_MODE) { "64-bit" } else { "16-bit" }, entry.limit(), entry.raw().bits(52..=55), entry.access(), ); } } #[derive(Debug)] enum SegmentType { Null, Code, Data, Ldt, Tss16Avail, Tss16Busy, Tss32Avail, Tss32Busy, Tss64Avail, Tss64Busy, } impl TryFrom<&gdt::Entry> for SegmentType { type Error = (); fn try_from(value: &gdt::Entry) -> Result { let flags = x86_64::structures::gdt::DescriptorFlags::from_bits_retain(value.raw()); if value.access().bit(4) { if value.access().bit(3) { Ok(SegmentType::Code) } else { Ok(SegmentType::Data) } } else { match value.access().bits(0..=3) { 0b0000 => Ok(SegmentType::Null), 0b0001 => Ok(SegmentType::Tss16Avail), 0b0010 => Ok(SegmentType::Ldt), 0b0011 => Ok(SegmentType::Tss16Busy), 0b1001 => { if flags.contains(gdt::DescriptorFlags::LONG_MODE) { Ok(SegmentType::Tss64Avail) } else { Ok(SegmentType::Tss32Avail) } } 0b1011 => { if flags.contains(gdt::DescriptorFlags::LONG_MODE) { Ok(SegmentType::Tss64Busy) } else { Ok(SegmentType::Tss32Busy) } } _ => Err(()), } } } }