use alloc::boxed::Box;
use x86_64::{
PrivilegeLevel, VirtAddr,
instructions::tables::{lgdt, load_tss},
registers::{
model_specific::Star,
segmentation::{CS, Segment},
},
structures::{
DescriptorTablePointer,
gdt::{Descriptor, SegmentSelector},
tss::TaskStateSegment,
},
};
use crate::cpu::local::{CpuLocal, StaticCpuLocal};
pub(super) unsafe fn init_on_cpu() {
let tss_ptr = LOCAL_TSS.as_ptr();
let tss_desc = unsafe { Descriptor::tss_segment_unchecked(tss_ptr) };
let (tss0, tss1) = match tss_desc {
Descriptor::SystemSegment(tss0, tss1) => (tss0, tss1),
_ => unreachable!(),
};
assert_eq!(CS::get_reg(), KERNEL_CS);
let gdt = Box::new([
0, KCODE64, KDATA, 0, UDATA, UCODE64, tss0, tss1,
]);
let gdt = &*Box::leak(gdt);
assert_eq!(gdt[KERNEL_CS.index() as usize], KCODE64);
assert_eq!(gdt[KERNEL_SS.index() as usize], KDATA);
assert_eq!(gdt[USER_CS.index() as usize], UCODE64);
assert_eq!(gdt[USER_SS.index() as usize], UDATA);
let gdtr = DescriptorTablePointer {
limit: (size_of_val(gdt) - 1) as u16,
base: VirtAddr::new(gdt.as_ptr().addr() as u64),
};
unsafe { lgdt(&gdtr) };
let tss_sel = SegmentSelector::new(6, PrivilegeLevel::Ring0);
assert_eq!(gdt[tss_sel.index() as usize], tss0);
assert_eq!(gdt[(tss_sel.index() + 1) as usize], tss1);
unsafe { load_tss(tss_sel) };
let sysret = SegmentSelector::new(3, PrivilegeLevel::Ring3);
assert_eq!(gdt[(sysret.index() + 1) as usize], UDATA);
assert_eq!(gdt[(sysret.index() + 2) as usize], UCODE64);
let syscall = SegmentSelector::new(1, PrivilegeLevel::Ring0);
assert_eq!(gdt[syscall.index() as usize], KCODE64);
assert_eq!(gdt[(syscall.index() + 1) as usize], KDATA);
unsafe { Star::write_raw(sysret.0, syscall.0) };
}
#[unsafe(link_section = ".cpu_local_tss")]
static LOCAL_TSS: StaticCpuLocal<TaskStateSegment> = {
let tss = TaskStateSegment::new();
unsafe { CpuLocal::__new_static(tss) }
};
pub(in crate::arch) const KCODE64: u64 = 0x00AF_9B00_0000_FFFF;
pub(in crate::arch) const KDATA: u64 = 0x00CF_9300_0000_FFFF;
pub(in crate::arch) const KCODE32: u64 = 0x00CF_9B00_0000_FFFF;
const UCODE64: u64 = 0x00AF_FB00_0000_FFFF;
const UDATA: u64 = 0x00CF_F300_0000_FFFF;
const KERNEL_CS: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0);
const KERNEL_SS: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0);
pub(super) const USER_CS: SegmentSelector = SegmentSelector::new(5, PrivilegeLevel::Ring3);
pub(super) const USER_SS: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3);