1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
/* Copyright (c) [2023] [Syswonder Community]
* [Rukos] is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
use core::fmt;
use x86_64::instructions::tables::{lgdt, load_tss};
use x86_64::registers::segmentation::{Segment, SegmentSelector, CS};
use x86_64::structures::gdt::{Descriptor, DescriptorFlags};
use x86_64::structures::{tss::TaskStateSegment, DescriptorTablePointer};
use x86_64::{addr::VirtAddr, PrivilegeLevel};
/// A wrapper of the Global Descriptor Table (GDT) with maximum 16 entries.
#[repr(align(16))]
pub struct GdtStruct {
table: [u64; 16],
}
impl GdtStruct {
/// Kernel code segment for 32-bit mode.
pub const KCODE32_SELECTOR: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0);
/// Kernel code segment for 64-bit mode.
pub const KCODE64_SELECTOR: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0);
/// Kernel data segment.
pub const KDATA_SELECTOR: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring0);
/// User code segment for 32-bit mode.
pub const UCODE32_SELECTOR: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3);
/// User data segment.
pub const UDATA_SELECTOR: SegmentSelector = SegmentSelector::new(5, PrivilegeLevel::Ring3);
/// User code segment for 64-bit mode.
pub const UCODE64_SELECTOR: SegmentSelector = SegmentSelector::new(6, PrivilegeLevel::Ring3);
/// TSS segment.
pub const TSS_SELECTOR: SegmentSelector = SegmentSelector::new(7, PrivilegeLevel::Ring0);
/// Constructs a new GDT struct that filled with the default segment
/// descriptors, including the given TSS segment.
pub fn new(tss: &'static TaskStateSegment) -> Self {
let mut table = [0; 16];
// first 3 entries are the same as in multiboot.S
table[1] = DescriptorFlags::KERNEL_CODE32.bits(); // 0x00cf9b000000ffff
table[2] = DescriptorFlags::KERNEL_CODE64.bits(); // 0x00af9b000000ffff
table[3] = DescriptorFlags::KERNEL_DATA.bits(); // 0x00cf93000000ffff
table[4] = DescriptorFlags::USER_CODE32.bits(); // 0x00cffb000000ffff
table[5] = DescriptorFlags::USER_DATA.bits(); // 0x00cff3000000ffff
table[6] = DescriptorFlags::USER_CODE64.bits(); // 0x00affb000000ffff
if let Descriptor::SystemSegment(low, high) = Descriptor::tss_segment(tss) {
table[7] = low;
table[8] = high;
}
Self { table }
}
/// Returns the GDT pointer (base and limit) that can be used in `lgdt`
/// instruction.
pub fn pointer(&self) -> DescriptorTablePointer {
DescriptorTablePointer {
base: VirtAddr::new(self.table.as_ptr() as u64),
limit: (core::mem::size_of_val(&self.table) - 1) as u16,
}
}
/// Loads the GDT into the CPU (executes the `lgdt` instruction), and
/// updates the code segment register (`CS`).
///
/// # Safety
///
/// This function is unsafe because it manipulates the CPU's privileged
/// states.
pub unsafe fn load(&'static self) {
lgdt(&self.pointer());
CS::set_reg(Self::KCODE64_SELECTOR);
}
/// Loads the TSS into the CPU (executes the `ltr` instruction).
///
/// # Safety
///
/// This function is unsafe because it manipulates the CPU's privileged
/// states.
pub unsafe fn load_tss(&'static self) {
load_tss(Self::TSS_SELECTOR);
}
}
impl fmt::Debug for GdtStruct {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("GdtStruct")
.field("pointer", &self.pointer())
.field("table", &self.table)
.finish()
}
}