130 lines
3.7 KiB
Rust
130 lines
3.7 KiB
Rust
// 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<Self, ()> {
|
|
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(()),
|
|
}
|
|
}
|
|
}
|
|
}
|