#![cfg_attr(test, allow(dead_code))]
#![cfg_attr(test, allow(unused_imports))]
use core::ptr::{addr_of, addr_of_mut};
use patina::SIZE_4GB;
struct GdtEntry {
limit15_0: u16,
base15_0: u16,
base23_16: u8,
type_: u8,
limit19_16_and_flags: u8,
base31_24: u8,
}
impl From<GdtEntry> for u64 {
fn from(entry: GdtEntry) -> Self {
u64::from(entry.limit15_0)
| (u64::from(entry.base15_0) << 16)
| (u64::from(entry.base23_16) << 32)
| (u64::from(entry.type_) << 40)
| (u64::from(entry.limit19_16_and_flags) << 48)
| (u64::from(entry.base31_24) << 56)
}
}
const NULL_SEL: GdtEntry = GdtEntry {
limit15_0: 0,
base15_0: 0,
base23_16: 0,
type_: 0, limit19_16_and_flags: 0,
base31_24: 0,
};
const LINEAR_SEL: GdtEntry = GdtEntry {
limit15_0: 0xffff,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x92, limit19_16_and_flags: 0xCF, base31_24: 0x00,
};
const LINEAR_CODE_SEL: GdtEntry = GdtEntry {
limit15_0: 0xffff,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x9F, limit19_16_and_flags: 0xCF, base31_24: 0x00,
};
const SYS_DATA_SEL: GdtEntry = GdtEntry {
limit15_0: 0xffff,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x93, limit19_16_and_flags: 0xCF, base31_24: 0x00,
};
const SYS_CODE_SEL: GdtEntry = GdtEntry {
limit15_0: 0xffff,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x9A, limit19_16_and_flags: 0xCF, base31_24: 0x00,
};
const SYS_CODE16_SEL: GdtEntry = GdtEntry {
limit15_0: 0xffff,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x9A, limit19_16_and_flags: 0x8F, base31_24: 0x00,
};
const LINEAR_DATA64_SEL: GdtEntry = GdtEntry {
limit15_0: 0xffff,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x92, limit19_16_and_flags: 0xCF, base31_24: 0x00,
};
const LINEAR_CODE64_SEL: GdtEntry = GdtEntry {
limit15_0: 0xffff,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x9A, limit19_16_and_flags: 0xAF, base31_24: 0x00,
};
const SPARE5_SEL: GdtEntry = GdtEntry {
limit15_0: 0x0000,
base15_0: 0x0000,
base23_16: 0x00,
type_: 0x00,
limit19_16_and_flags: 0x00,
base31_24: 0x00,
};
const TSS_SIZE: usize = 104;
const TSS_IST1_OFFSET: usize = 36;
const STACK_SIZE: usize = 4096 * 5;
const GDT_ENTRY_COUNT: usize = 11;
pub(crate) const CODE_SELECTOR: u16 = 7 * 8; const DATA_SELECTOR: u16 = 6 * 8; const TSS_SELECTOR: u16 = 8 * 8;
static mut SEPARATE_EXCEPTION_STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
static mut TSS: [u8; TSS_SIZE] = [0; TSS_SIZE];
fn tss_descriptor(base: u64, limit: u32) -> (u64, u64) {
let low: u64 =
(u64::from(limit) & 0xFFFF)
| ((base & 0xFFFF) << 16)
| (((base >> 16) & 0xFF) << 32)
| (0x9u64 << 40)
| (1u64 << 47)
| ((u64::from(limit >> 16) & 0xF) << 48)
| (((base >> 24) & 0xFF) << 56);
let high: u64 = base >> 32;
(low, high)
}
static GDT: spin::LazyLock<[u64; GDT_ENTRY_COUNT]> = spin::LazyLock::new(|| {
unsafe {
let ist_addr = addr_of!(SEPARATE_EXCEPTION_STACK) as u64 + STACK_SIZE as u64;
let ist_bytes = ist_addr.to_ne_bytes();
core::ptr::copy_nonoverlapping(ist_bytes.as_ptr(), addr_of_mut!(TSS).cast::<u8>().add(TSS_IST1_OFFSET), 8);
}
let tss_base = addr_of!(TSS) as u64;
let (tss_low, tss_high) = tss_descriptor(tss_base, (TSS_SIZE - 1) as u32);
[
NULL_SEL.into(),
LINEAR_SEL.into(),
LINEAR_CODE_SEL.into(),
SYS_DATA_SEL.into(),
SYS_CODE_SEL.into(),
SYS_CODE16_SEL.into(),
LINEAR_DATA64_SEL.into(),
LINEAR_CODE64_SEL.into(),
tss_low,
tss_high,
SPARE5_SEL.into(),
]
});
#[repr(C, packed)]
struct GdtPointer {
limit: u16,
base: u64,
}
#[cfg_attr(coverage, coverage(off))]
pub fn init() {
let gdt_ptr = GDT.as_ptr() as usize;
assert!(gdt_ptr < SIZE_4GB, "GDT above 4GB, MP services will fail");
let gdtr = GdtPointer { limit: (core::mem::size_of::<[u64; GDT_ENTRY_COUNT]>() - 1) as u16, base: gdt_ptr as u64 };
unsafe {
core::arch::asm!("lgdt [{}]", in(reg) &raw const gdtr, options(nostack, preserves_flags));
core::arch::asm!(
"push {sel}",
"lea {tmp}, [rip + 2f]",
"push {tmp}",
"retfq",
"2:",
sel = in(reg) u64::from(CODE_SELECTOR),
tmp = lateout(reg) _,
options(preserves_flags),
);
core::arch::asm!("mov ss, {0:x}", in(reg) DATA_SELECTOR, options(nostack, preserves_flags));
core::arch::asm!("mov ds, {0:x}", in(reg) DATA_SELECTOR, options(nostack, preserves_flags));
core::arch::asm!("mov es, {0:x}", in(reg) DATA_SELECTOR, options(nostack, preserves_flags));
core::arch::asm!("mov fs, {0:x}", in(reg) DATA_SELECTOR, options(nostack, preserves_flags));
core::arch::asm!("mov gs, {0:x}", in(reg) DATA_SELECTOR, options(nostack, preserves_flags));
core::arch::asm!("ltr {0:x}", in(reg) TSS_SELECTOR, options(nostack, preserves_flags));
}
log::info!("Loaded GDT @ {:p}", GDT.as_ptr());
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::*;
#[test]
pub fn test_dxe_default_entries() {
for (i, &entry) in GDT.iter().enumerate() {
match i {
0 => assert_eq!(entry, NULL_SEL.into()),
1 => assert_eq!(entry, LINEAR_SEL.into()),
2 => assert_eq!(entry, LINEAR_CODE_SEL.into()),
3 => assert_eq!(entry, SYS_DATA_SEL.into()),
4 => assert_eq!(entry, SYS_CODE_SEL.into()),
5 => assert_eq!(entry, SYS_CODE16_SEL.into()),
6 => assert_eq!(entry, LINEAR_DATA64_SEL.into()),
7 => assert_eq!(entry, LINEAR_CODE64_SEL.into()),
8 => assert!(
entry & 0xFF > 0 && ((entry & (((1 << 4) - 1) << 40)) >> 40 == 0x9) && entry & (0x1 << 47) > 0, "TSS Segment Descriptor is Not Valid"
),
9 => assert!(entry & 0xFFFFFFFF > 0, "TSS Segment Descriptor Base must be set"), 10 => assert_eq!(entry, SPARE5_SEL.into()),
_ => panic!("Unexpected GDT entry"),
}
}
assert_eq!(GDT.len(), 11);
}
}