msrs, gdt, idt
This commit is contained in:
parent
4220f6628e
commit
30391aa2a1
|
|
@ -17,6 +17,10 @@ bench = false
|
|||
name = "simple"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "stack_overflow"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.10.3"
|
||||
bitflags = "2.13.1"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#![no_std]
|
||||
#![feature(const_trait_impl, const_default, const_range)]
|
||||
#![feature(const_trait_impl, const_default, const_range, debug_closure_helpers)]
|
||||
#![cfg_attr(test, feature(custom_test_frameworks))]
|
||||
#![cfg_attr(test, test_runner(crate::tests::test_runner))]
|
||||
#![cfg_attr(test, no_main)]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,17 @@
|
|||
use core::ops::{Deref, DerefMut};
|
||||
use core::{
|
||||
arch::asm,
|
||||
fmt::Debug,
|
||||
mem::offset_of,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use bit_field::BitField;
|
||||
|
||||
use crate::{sync::LazyLock, x86_64::idt::ToAddress};
|
||||
use crate::{
|
||||
serial_println,
|
||||
sync::LazyLock,
|
||||
x86_64::{idt::ToAddress, instructions::*},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
|
@ -14,47 +23,92 @@ pub struct GlobalDescriptorTable {
|
|||
}
|
||||
|
||||
impl GlobalDescriptorTable {
|
||||
pub fn set_tss(&mut self, tss: &'static TaskStateSegment) {
|
||||
const TSS_SIZE: u32 = core::mem::size_of::<TaskStateSegment>() as u32;
|
||||
let tss_base = tss as *const TaskStateSegment as u64;
|
||||
let tss_limit = TSS_SIZE - 1;
|
||||
self.tss = TssEntry::new(tss_base, tss_limit);
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let tss = &*TSS;
|
||||
GlobalDescriptorTable {
|
||||
null: RawGdtEntry::new(0, 0, GdtAccess(0), GdtFlags(0)),
|
||||
kernel_code: RawGdtEntry::new(
|
||||
0,
|
||||
0,
|
||||
GdtAccess(0)
|
||||
.with_segment(true)
|
||||
.with_privilege_level(RING0)
|
||||
.with_present(true)
|
||||
.with_segment_kind(
|
||||
CodeDataSegmentKind::CodeSegment
|
||||
| CodeDataSegmentKind::Executable
|
||||
| CodeDataSegmentKind::ReadWrite,
|
||||
),
|
||||
GdtFlags::new(0xff, false, true, DefaultOperationSize::Bit16, true),
|
||||
),
|
||||
kernel_data: RawGdtEntry::new(
|
||||
0,
|
||||
0,
|
||||
GdtAccess(0)
|
||||
.with_segment(true)
|
||||
.with_privilege_level(RING0)
|
||||
.with_present(true)
|
||||
.with_segment_kind(CodeDataSegmentKind::ReadWrite),
|
||||
GdtFlags::new(0xff, false, false, DefaultOperationSize::Bit16, true),
|
||||
),
|
||||
null: RawGdtEntry::new(0, 0, 0, 0),
|
||||
kernel_code: RawGdtEntry::new(0xfffff, 0, 0, 0)
|
||||
.with_segment(true)
|
||||
.with_privilege_level(RING0)
|
||||
.with_present(true)
|
||||
.with_segment_kind(
|
||||
CodeDataSegmentKind::CodeSegment
|
||||
| CodeDataSegmentKind::Executable
|
||||
| CodeDataSegmentKind::ReadWrite
|
||||
| CodeDataSegmentKind::Accessed,
|
||||
)
|
||||
.with_present(true)
|
||||
.with_code_segment64(true)
|
||||
.with_granularity(true),
|
||||
kernel_data: RawGdtEntry::new(0xfffff, 0, 0, 0)
|
||||
.with_segment(true)
|
||||
.with_privilege_level(RING0)
|
||||
.with_present(true)
|
||||
.with_segment_kind(CodeDataSegmentKind::ReadWrite | CodeDataSegmentKind::Accessed)
|
||||
.with_code_segment64(true)
|
||||
.with_granularity(true),
|
||||
tss: TssEntry::new(
|
||||
tss as *const TaskStateSegment as u64,
|
||||
core::mem::size_of::<TaskStateSegment>() as u32 - 1,
|
||||
tss as *const TaskStateSegment as u64,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(&'static self) {
|
||||
serial_println!("Loading GDT {:#?}", self);
|
||||
unsafe { Self::load_unsafe(self) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The caller must ensure that `this` is a valid pointer to a `GlobalDescriptorTable` that remains valid for the lifetime of the gdt.
|
||||
pub unsafe fn load_unsafe(this: *const Self) {
|
||||
let gdt_register = GdtRegister {
|
||||
limit: (core::mem::size_of::<Self>() - 1) as u16,
|
||||
base: this as u64,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"lgdt [{}]",
|
||||
in(reg) &gdt_register,
|
||||
options(readonly, nostack, preserves_flags)
|
||||
);
|
||||
|
||||
let gs = msr::read_msr(msr::MSR_GS_BASE);
|
||||
|
||||
asm!(
|
||||
"push {code_seg}",
|
||||
"lea rax, [rip + 2f]",
|
||||
"push rax",
|
||||
"retfq",
|
||||
"2:",
|
||||
"mov ax, {data_seg}",
|
||||
"mov ds, ax",
|
||||
"mov es, ax",
|
||||
"mov fs, ax",
|
||||
"mov gs, ax",
|
||||
"mov ss, ax",
|
||||
code_seg = const offset_of!(GlobalDescriptorTable, kernel_code),
|
||||
data_seg = const offset_of!(GlobalDescriptorTable, kernel_data),
|
||||
lateout("rax") _,
|
||||
);
|
||||
|
||||
msr::write_msr(msr::MSR_GS_BASE, gs);
|
||||
|
||||
asm!(
|
||||
"mov ax, {tss_seg}",
|
||||
"ltr ax",
|
||||
tss_seg = const offset_of!(GlobalDescriptorTable, tss),
|
||||
lateout("rax") _,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
struct GdtRegister {
|
||||
limit: u16,
|
||||
base: u64,
|
||||
}
|
||||
|
||||
impl Default for GlobalDescriptorTable {
|
||||
|
|
@ -63,90 +117,6 @@ impl Default for GlobalDescriptorTable {
|
|||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct GdtAccess(u8);
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct GdtFlags(u8);
|
||||
|
||||
impl GdtFlags {
|
||||
pub fn new(
|
||||
limit_high: u8,
|
||||
available: bool,
|
||||
code_segment64: bool,
|
||||
default_operation_size: DefaultOperationSize,
|
||||
granularity: bool,
|
||||
) -> Self {
|
||||
let mut flags = GdtFlags(0);
|
||||
flags.set_limit_high(limit_high);
|
||||
flags.set_available(available);
|
||||
flags.set_code_segment64(code_segment64);
|
||||
flags.set_default_operation_size(default_operation_size);
|
||||
flags.set_granularity(granularity);
|
||||
flags
|
||||
}
|
||||
|
||||
pub fn with_available(mut self, available: bool) -> Self {
|
||||
self.set_available(available);
|
||||
self
|
||||
}
|
||||
pub fn with_code_segment64(mut self, code_segment64: bool) -> Self {
|
||||
self.set_code_segment64(code_segment64);
|
||||
self
|
||||
}
|
||||
pub fn with_default_operation_size(mut self, size: DefaultOperationSize) -> Self {
|
||||
self.set_default_operation_size(size);
|
||||
self
|
||||
}
|
||||
pub fn with_granularity(mut self, granularity: bool) -> Self {
|
||||
self.set_granularity(granularity);
|
||||
self
|
||||
}
|
||||
pub fn with_limit_high(mut self, limit_high: u8) -> Self {
|
||||
self.set_limit_high(limit_high);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn limit_high(&self) -> u8 {
|
||||
self.0.get_bits(0..4)
|
||||
}
|
||||
pub fn set_limit_high(&mut self, limit_high: u8) {
|
||||
self.0.set_bits(0..4, limit_high);
|
||||
}
|
||||
pub fn available(&self) -> bool {
|
||||
self.0.get_bit(4)
|
||||
}
|
||||
pub fn set_available(&mut self, available: bool) {
|
||||
self.0.set_bit(4, available);
|
||||
}
|
||||
pub fn code_segment64(&self) -> bool {
|
||||
self.0.get_bit(5)
|
||||
}
|
||||
pub fn set_code_segment64(&mut self, code_segment64: bool) {
|
||||
self.0.set_bit(5, code_segment64);
|
||||
}
|
||||
|
||||
/// `default_operation_size` is the default size of operands for this segment. If `default_operation_size` is set, the default size is 32 bits, otherwise it is 16 bits.
|
||||
/// On 64-bit mode, this should be 1.
|
||||
pub fn default_operation_size(&self) -> DefaultOperationSize {
|
||||
DefaultOperationSize::from_bool(self.0.get_bit(6))
|
||||
}
|
||||
pub fn set_default_operation_size(&mut self, size: DefaultOperationSize) {
|
||||
self.0.set_bit(6, size.into_u8() != 0);
|
||||
}
|
||||
|
||||
/// `granularity` is the unit of the limit field. If `granularity` is set,
|
||||
/// the limit is in 4KiB blocks, otherwise it is in bytes.
|
||||
pub fn granularity(&self) -> bool {
|
||||
self.0.get_bit(7)
|
||||
}
|
||||
pub fn set_granularity(&mut self, granularity: bool) {
|
||||
self.0.set_bit(7, granularity);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SystemSegmentKind {
|
||||
|
|
@ -165,9 +135,10 @@ pub const RING2: u8 = 2;
|
|||
pub const RING3: u8 = 3;
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct CodeDataSegmentKind: u8 {
|
||||
const CodeSegment = 1 << 4;
|
||||
const Executable = 1 << 3;
|
||||
const CodeSegment = 1 << 3;
|
||||
const Executable = 1 << 2;
|
||||
/// `ReadWrite` is set for code segments if they are readable, and for
|
||||
/// data segments if they are writable.
|
||||
const ReadWrite = 1 << 1;
|
||||
|
|
@ -198,76 +169,65 @@ impl SystemSegmentKind {
|
|||
}
|
||||
}
|
||||
|
||||
impl GdtAccess {
|
||||
pub fn with_segment_kind(mut self, kind: CodeDataSegmentKind) -> Self {
|
||||
self.set_segment_kind(kind);
|
||||
self
|
||||
}
|
||||
pub fn with_system_segment_kind(mut self, kind: SystemSegmentKind) -> Self {
|
||||
self.set_system_segment_kind(kind);
|
||||
self
|
||||
}
|
||||
pub fn with_segment(mut self, segment: bool) -> Self {
|
||||
self.set_segment(segment);
|
||||
self
|
||||
}
|
||||
pub fn with_privilege_level(mut self, level: u8) -> Self {
|
||||
self.set_privilege_level(level);
|
||||
self
|
||||
}
|
||||
pub fn with_present(mut self, present: bool) -> Self {
|
||||
self.set_present(present);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn system_segment_kind(&self) -> SystemSegmentKind {
|
||||
SystemSegmentKind::from_u8_or_reserved(self.0.get_bits(0..4))
|
||||
}
|
||||
pub fn set_system_segment_kind(&mut self, kind: SystemSegmentKind) {
|
||||
self.0.set_bits(0..4, kind.into_u8());
|
||||
}
|
||||
/// `segment_kind` is valid if `segment` is set, otherwise
|
||||
/// `system_segment_kind` is valid. `segment_kind` is the type of segment,
|
||||
/// and is only valid for code and data segments.
|
||||
pub fn segment_kind(&self) -> CodeDataSegmentKind {
|
||||
CodeDataSegmentKind::from_bits_truncate(self.0.get_bits(0..4))
|
||||
}
|
||||
pub fn set_segment_kind(&mut self, kind: CodeDataSegmentKind) {
|
||||
self.0.set_bits(0..4, kind.bits());
|
||||
}
|
||||
|
||||
/// `segment` is set if this is a code or data segment, and clear if it is a system segment.
|
||||
pub fn segment(&self) -> bool {
|
||||
self.0.get_bit(4)
|
||||
}
|
||||
pub fn set_segment(&mut self, system_segment: bool) {
|
||||
self.0.set_bit(4, system_segment);
|
||||
}
|
||||
pub fn privilege_level(&self) -> u8 {
|
||||
self.0.get_bits(5..7)
|
||||
}
|
||||
pub fn set_privilege_level(&mut self, level: u8) {
|
||||
self.0.set_bits(5..7, level);
|
||||
}
|
||||
pub fn present(&self) -> bool {
|
||||
self.0.get_bit(7)
|
||||
}
|
||||
pub fn set_present(&mut self, present: bool) {
|
||||
self.0.set_bit(7, present);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RawGdtEntry {
|
||||
pub limit_low: u16,
|
||||
pub base_low: u16,
|
||||
pub base_middle: u8,
|
||||
pub access: GdtAccess,
|
||||
pub flags: GdtFlags,
|
||||
pub access: u8,
|
||||
pub flags: u8,
|
||||
pub base_high: u8,
|
||||
}
|
||||
|
||||
fn debug_commont_gdt_fields<'a, 'b>(
|
||||
entry: &RawGdtEntry,
|
||||
dbg: &'a mut core::fmt::DebugStruct<'a, 'b>,
|
||||
) -> &'a mut core::fmt::DebugStruct<'a, 'b> {
|
||||
dbg.field_with("limit", |f| {
|
||||
if entry.granularity() {
|
||||
f.write_fmt(format_args!("0x{:x} (4KiB blocks)", entry.limit()))
|
||||
} else {
|
||||
f.write_fmt(format_args!("0x{:x} (bytes)", entry.limit()))
|
||||
}
|
||||
})
|
||||
.field("segment", &entry.segment());
|
||||
|
||||
if entry.segment() {
|
||||
dbg.field("segment_kind", &entry.segment_kind());
|
||||
} else {
|
||||
dbg.field("system_segment_kind", &entry.system_segment_kind());
|
||||
}
|
||||
|
||||
dbg.field("privilege_level", &entry.privilege_level())
|
||||
.field("present", &entry.present())
|
||||
.field("available", &entry.available())
|
||||
.field("code_segment64", &entry.code_segment64())
|
||||
.field("default_operation_size", &entry.default_operation_size())
|
||||
.field_with("granularity", |f| {
|
||||
if entry.granularity() {
|
||||
f.write_str("4KiB")
|
||||
} else {
|
||||
f.write_str("bytes")
|
||||
}
|
||||
});
|
||||
|
||||
dbg
|
||||
}
|
||||
|
||||
impl Debug for RawGdtEntry {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let mut dbg = f.debug_struct("RawGdtEntry");
|
||||
|
||||
dbg.field_with("base", |f| {
|
||||
f.write_fmt(format_args!("0x{:x}", self.base32()))
|
||||
});
|
||||
let dbg = debug_commont_gdt_fields(self, &mut dbg);
|
||||
|
||||
dbg.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DefaultOperationSize {
|
||||
|
|
@ -293,20 +253,9 @@ impl AsRef<RawGdtEntry> for TssEntry {
|
|||
&self.gdt
|
||||
}
|
||||
}
|
||||
impl AsRef<GdtAccess> for RawGdtEntry {
|
||||
fn as_ref(&self) -> &GdtAccess {
|
||||
&self.access
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<GdtFlags> for RawGdtEntry {
|
||||
fn as_ref(&self) -> &GdtFlags {
|
||||
&self.flags
|
||||
}
|
||||
}
|
||||
|
||||
impl RawGdtEntry {
|
||||
pub fn new(limit: u32, base: u32, access: GdtAccess, flags: GdtFlags) -> Self {
|
||||
pub fn new(limit: u32, base: u32, access: u8, flags: u8) -> Self {
|
||||
let limit_low = limit as u16;
|
||||
let limit_high = (limit >> 16) as u8;
|
||||
let base_low = base as u16;
|
||||
|
|
@ -318,18 +267,139 @@ impl RawGdtEntry {
|
|||
base_low,
|
||||
base_middle,
|
||||
access,
|
||||
flags: flags.with_limit_high(limit_high),
|
||||
flags: (flags & 0xf0) | limit_high,
|
||||
base_high,
|
||||
}
|
||||
}
|
||||
pub fn empty() -> Self {
|
||||
RawGdtEntry {
|
||||
limit_low: 0,
|
||||
base_low: 0,
|
||||
base_middle: 0,
|
||||
access: 0,
|
||||
flags: 0,
|
||||
base_high: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn access(&self) -> GdtAccess {
|
||||
self.access
|
||||
pub fn with_segment_kind(mut self, kind: CodeDataSegmentKind) -> Self {
|
||||
self.set_segment_kind(kind);
|
||||
self
|
||||
}
|
||||
pub fn with_system_segment_kind(mut self, kind: SystemSegmentKind) -> Self {
|
||||
self.set_system_segment_kind(kind);
|
||||
self
|
||||
}
|
||||
pub fn with_segment(mut self, segment: bool) -> Self {
|
||||
self.set_segment(segment);
|
||||
self
|
||||
}
|
||||
pub fn with_privilege_level(mut self, level: u8) -> Self {
|
||||
self.set_privilege_level(level);
|
||||
self
|
||||
}
|
||||
pub fn with_present(mut self, present: bool) -> Self {
|
||||
self.set_present(present);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn system_segment_kind(&self) -> SystemSegmentKind {
|
||||
SystemSegmentKind::from_u8_or_reserved(self.access.get_bits(0..4))
|
||||
}
|
||||
pub fn set_system_segment_kind(&mut self, kind: SystemSegmentKind) {
|
||||
self.access.set_bits(0..4, kind.into_u8());
|
||||
}
|
||||
/// `segment_kind` is valid if `segment` is set, otherwise
|
||||
/// `system_segment_kind` is valid. `segment_kind` is the type of segment,
|
||||
/// and is only valid for code and data segments.
|
||||
pub fn segment_kind(&self) -> CodeDataSegmentKind {
|
||||
CodeDataSegmentKind::from_bits_truncate(self.access.get_bits(0..4))
|
||||
}
|
||||
pub fn set_segment_kind(&mut self, kind: CodeDataSegmentKind) {
|
||||
self.access.set_bits(0..4, kind.bits());
|
||||
}
|
||||
|
||||
/// `segment` is set if this is a code or data segment, and clear if it is a system segment.
|
||||
pub fn segment(&self) -> bool {
|
||||
self.access.get_bit(4)
|
||||
}
|
||||
pub fn set_segment(&mut self, system_segment: bool) {
|
||||
self.access.set_bit(4, system_segment);
|
||||
}
|
||||
pub fn privilege_level(&self) -> u8 {
|
||||
self.access.get_bits(5..7)
|
||||
}
|
||||
pub fn set_privilege_level(&mut self, level: u8) {
|
||||
self.access.set_bits(5..7, level);
|
||||
}
|
||||
pub fn present(&self) -> bool {
|
||||
self.access.get_bit(7)
|
||||
}
|
||||
pub fn set_present(&mut self, present: bool) {
|
||||
self.access.set_bit(7, present);
|
||||
}
|
||||
|
||||
pub fn with_available(mut self, available: bool) -> Self {
|
||||
self.set_available(available);
|
||||
self
|
||||
}
|
||||
pub fn with_code_segment64(mut self, code_segment64: bool) -> Self {
|
||||
self.set_code_segment64(code_segment64);
|
||||
self
|
||||
}
|
||||
pub fn with_default_operation_size(mut self, size: DefaultOperationSize) -> Self {
|
||||
self.set_default_operation_size(size);
|
||||
self
|
||||
}
|
||||
pub fn with_granularity(mut self, granularity: bool) -> Self {
|
||||
self.set_granularity(granularity);
|
||||
self
|
||||
}
|
||||
pub fn with_limit_high(mut self, limit_high: u8) -> Self {
|
||||
self.set_limit_high(limit_high);
|
||||
self
|
||||
}
|
||||
|
||||
fn limit_high(&self) -> u8 {
|
||||
self.flags.get_bits(0..4)
|
||||
}
|
||||
fn set_limit_high(&mut self, limit_high: u8) {
|
||||
self.flags.set_bits(0..4, limit_high);
|
||||
}
|
||||
pub fn available(&self) -> bool {
|
||||
self.flags.get_bit(4)
|
||||
}
|
||||
pub fn set_available(&mut self, available: bool) {
|
||||
self.flags.set_bit(4, available);
|
||||
}
|
||||
pub fn code_segment64(&self) -> bool {
|
||||
self.flags.get_bit(5)
|
||||
}
|
||||
pub fn set_code_segment64(&mut self, code_segment64: bool) {
|
||||
self.flags.set_bit(5, code_segment64);
|
||||
}
|
||||
|
||||
/// `default_operation_size` is the default size of operands for this segment. If `default_operation_size` is set, the default size is 32 bits, otherwise it is 16 bits.
|
||||
/// On 64-bit mode, this should be 1.
|
||||
pub fn default_operation_size(&self) -> DefaultOperationSize {
|
||||
DefaultOperationSize::from_bool(self.flags.get_bit(6))
|
||||
}
|
||||
pub fn set_default_operation_size(&mut self, size: DefaultOperationSize) {
|
||||
self.flags.set_bit(6, size.into_u8() != 0);
|
||||
}
|
||||
|
||||
/// `granularity` is the unit of the limit field. If `granularity` is set,
|
||||
/// the limit is in 4KiB blocks, otherwise it is in bytes.
|
||||
pub fn granularity(&self) -> bool {
|
||||
self.flags.get_bit(7)
|
||||
}
|
||||
pub fn set_granularity(&mut self, granularity: bool) {
|
||||
self.flags.set_bit(7, granularity);
|
||||
}
|
||||
|
||||
pub fn limit(&self) -> u32 {
|
||||
let limit_low = self.limit_low as u32;
|
||||
let limit_high = (self.flags.limit_high() as u32) << 16;
|
||||
let limit_high = (self.limit_high() as u32) << 16;
|
||||
limit_low | limit_high
|
||||
}
|
||||
|
||||
|
|
@ -342,13 +412,26 @@ impl RawGdtEntry {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct TssEntry {
|
||||
gdt: RawGdtEntry,
|
||||
pub base_ext: u32,
|
||||
reserved: u32,
|
||||
}
|
||||
|
||||
impl Debug for TssEntry {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let mut dbg = f.debug_struct("TssEntry");
|
||||
|
||||
dbg.field_with("base", |f| {
|
||||
f.write_fmt(format_args!("0x{:x}", self.base64()))
|
||||
});
|
||||
let dbg = debug_commont_gdt_fields(&self.gdt, &mut dbg);
|
||||
|
||||
dbg.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl TssEntry {
|
||||
pub fn base64(&self) -> u64 {
|
||||
let base_low = self.gdt.base32() as u64;
|
||||
|
|
@ -356,22 +439,15 @@ impl TssEntry {
|
|||
(base_ext << 32) | base_low
|
||||
}
|
||||
|
||||
pub fn new(base: u64, limit: u32) -> Self {
|
||||
pub fn new(limit: u32, base: u64) -> Self {
|
||||
let base_low = base as u32;
|
||||
let base_ext = (base >> 32) as u32;
|
||||
|
||||
let mut access = GdtAccess(0);
|
||||
access.set_system_segment_kind(SystemSegmentKind::TssAvailable);
|
||||
access.set_privilege_level(RING0);
|
||||
access.set_present(true);
|
||||
access.set_segment(true);
|
||||
|
||||
let mut granularity = GdtFlags(0);
|
||||
granularity.set_limit_high(limit.get_bits(16..20) as u8);
|
||||
granularity.set_granularity(false);
|
||||
|
||||
TssEntry {
|
||||
gdt: RawGdtEntry::new(limit, base_low, access, granularity),
|
||||
gdt: RawGdtEntry::new(limit, base_low, 0, 0)
|
||||
.with_present(true)
|
||||
.with_privilege_level(RING0)
|
||||
.with_system_segment_kind(SystemSegmentKind::TssAvailable),
|
||||
base_ext,
|
||||
reserved: 0,
|
||||
}
|
||||
|
|
@ -425,7 +501,7 @@ pub const DF_STACK: u8 = 0;
|
|||
pub const NMI_STACK: u8 = 1;
|
||||
pub const MC_STACK: u8 = 2;
|
||||
|
||||
static TSS: LazyLock<TaskStateSegment> = LazyLock::new(|| {
|
||||
pub static TSS: LazyLock<TaskStateSegment> = LazyLock::new(|| {
|
||||
let mut tss = TaskStateSegment::new();
|
||||
const STACK_SIZE: usize = super::PAGE_SIZE * 5;
|
||||
static mut STACKS: [[u8; STACK_SIZE]; 3] = [[0; STACK_SIZE]; 3];
|
||||
|
|
@ -438,3 +514,5 @@ static TSS: LazyLock<TaskStateSegment> = LazyLock::new(|| {
|
|||
|
||||
tss
|
||||
});
|
||||
|
||||
pub static GDT: LazyLock<GlobalDescriptorTable> = LazyLock::new(GlobalDescriptorTable::new);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,36 @@ impl core::fmt::Debug for Entry {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ExceptionVector;
|
||||
impl ExceptionVector {
|
||||
pub const DIVIDE_BY_ZERO: u8 = 0;
|
||||
pub const DEBUG: u8 = 1;
|
||||
pub const NON_MASKABLE_INTERRUPT: u8 = 2;
|
||||
pub const BREAKPOINT: u8 = 3;
|
||||
pub const OVERFLOW: u8 = 4;
|
||||
pub const BOUND_RANGE_EXCEEDED: u8 = 5;
|
||||
pub const INVALID_OPCODE: u8 = 6;
|
||||
pub const DEVICE_NOT_AVAILABLE: u8 = 7;
|
||||
pub const DOUBLE_FAULT: u8 = 8;
|
||||
pub const COPROCESSOR_SEGMENT_OVERRUN: u8 = 9;
|
||||
pub const INVALID_TSS: u8 = 10;
|
||||
pub const SEGMENT_NOT_PRESENT: u8 = 11;
|
||||
pub const STACK_SEGMENT_FAULT: u8 = 12;
|
||||
pub const GENERAL_PROTECTION_FAULT: u8 = 13;
|
||||
pub const PAGE_FAULT: u8 = 14;
|
||||
|
||||
pub const X87_FLOATING_POINT_EXCEPTION: u8 = 16;
|
||||
pub const ALIGNMENT_CHECK: u8 = 17;
|
||||
pub const MACHINE_CHECK: u8 = 18;
|
||||
pub const SIMD_FLOATING_POINT_EXCEPTION: u8 = 19;
|
||||
pub const VIRTUALIZATION_EXCEPTION: u8 = 20;
|
||||
pub const CONTROL_PROTECTION_EXCEPTION: u8 = 21;
|
||||
|
||||
pub const HYPERVISOR_EXCEPTION: u8 = 28;
|
||||
pub const VMM_COMMUNICATION_EXCEPTION: u8 = 29;
|
||||
pub const SECURITY_EXCEPTION: u8 = 30;
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
pub unsafe fn new<T: ToAddress>(handler: T, selector: u16, options: EntryOptions) -> Self {
|
||||
let handler_addr = handler.to_address();
|
||||
|
|
@ -52,6 +82,19 @@ impl Entry {
|
|||
_ => interrupt_trampoline_no_err::<IDX> as *const () as u64,
|
||||
};
|
||||
|
||||
match IDX {
|
||||
ExceptionVector::DOUBLE_FAULT => {
|
||||
options.set_interrupt_stack_table_index(super::gdt::DF_STACK);
|
||||
}
|
||||
ExceptionVector::NON_MASKABLE_INTERRUPT => {
|
||||
options.set_interrupt_stack_table_index(super::gdt::NMI_STACK);
|
||||
}
|
||||
ExceptionVector::MACHINE_CHECK => {
|
||||
options.set_interrupt_stack_table_index(super::gdt::MC_STACK);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
unsafe { Self::new(handler, cs, options) }
|
||||
}
|
||||
|
||||
|
|
@ -176,6 +219,24 @@ impl EntryOptions {
|
|||
pub const fn empty_trap_gate() -> Self {
|
||||
Self(0b1111_0000_0000)
|
||||
}
|
||||
|
||||
pub fn with_present(mut self, present: bool) -> Self {
|
||||
self.set_present(present);
|
||||
self
|
||||
}
|
||||
pub fn with_privilege_level(mut self, level: u8) -> Self {
|
||||
self.set_privilege_level(level);
|
||||
self
|
||||
}
|
||||
pub fn with_interrupt_stack_table_index(mut self, index: u8) -> Self {
|
||||
self.set_interrupt_stack_table_index(index);
|
||||
self
|
||||
}
|
||||
pub fn with_kind(mut self, kind: EntryType) -> Self {
|
||||
self.set_kind(kind);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn present(&self) -> bool {
|
||||
self.0.get_bit(15)
|
||||
}
|
||||
|
|
@ -258,7 +319,7 @@ bitflags::bitflags! {
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C, packed(2))]
|
||||
pub struct InterruptDescriptorTablePointer<'idt> {
|
||||
pub struct IdtRegister<'idt> {
|
||||
pub limit: u16,
|
||||
pub base: u64,
|
||||
_pd: PhantomData<&'idt ()>,
|
||||
|
|
@ -348,17 +409,21 @@ impl InterruptDescriptorTable {
|
|||
idt
|
||||
}
|
||||
|
||||
pub unsafe fn pointer(&self) -> InterruptDescriptorTablePointer<'_> {
|
||||
InterruptDescriptorTablePointer {
|
||||
pub unsafe fn register(&self) -> IdtRegister<'_> {
|
||||
IdtRegister {
|
||||
limit: (core::mem::size_of::<Self>() - 1) as u16,
|
||||
base: self as *const _ as u64,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn load(&self) {
|
||||
pub fn load(&'static self) {
|
||||
unsafe { Self::load_unsafe(self) };
|
||||
}
|
||||
|
||||
pub unsafe fn load_unsafe(&self) {
|
||||
unsafe {
|
||||
super::instructions::lidt(&self.pointer());
|
||||
super::instructions::lidt(&self.register());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -457,7 +522,7 @@ fn test_interrupt_handler_trait() {
|
|||
static IDT: LazyLock<InterruptDescriptorTable> =
|
||||
crate::sync::LazyLock::new(InterruptDescriptorTable::new_default);
|
||||
|
||||
unsafe { IDT.load() };
|
||||
unsafe { IDT.load_unsafe() };
|
||||
|
||||
super::instructions::int3(); // Trigger a breakpoint interrupt (interrupt 3)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub fn hlt() {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn lidt(idt: &super::idt::InterruptDescriptorTablePointer) {
|
||||
pub unsafe fn lidt(idt: &super::idt::IdtRegister) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"lidt [{}]",
|
||||
|
|
@ -43,3 +43,67 @@ macro_rules! read_segment {
|
|||
pub unsafe fn get_cs() -> u16 {
|
||||
read_segment!("cs")
|
||||
}
|
||||
|
||||
pub mod msr {
|
||||
use core::arch::asm;
|
||||
|
||||
pub const MSR_STAR: u32 = 0xC000_0081;
|
||||
pub const MSR_LSTAR: u32 = 0xC000_0082;
|
||||
pub const MSR_CSTAR: u32 = 0xC000_0083;
|
||||
pub const MSR_SFMASK: u32 = 0xC000_0084;
|
||||
pub const MSR_FS_BASE: u32 = 0xC000_0100;
|
||||
pub const MSR_GS_BASE: u32 = 0xC000_0101;
|
||||
pub const MSR_KERNEL_GS_BASE: u32 = 0xC000_0102;
|
||||
|
||||
/// Reads the value of the specified Model-Specific Register (MSR).
|
||||
/// # Safety
|
||||
/// This operation is inherently unsafe.
|
||||
#[inline]
|
||||
pub unsafe fn read_msr(msr: u32) -> u64 {
|
||||
unsafe {
|
||||
let eax: u32;
|
||||
let edx: u32;
|
||||
asm!(
|
||||
"rdmsr",
|
||||
in("ecx") msr,
|
||||
out("eax") eax,
|
||||
out("edx") edx,
|
||||
options(nomem, nostack, preserves_flags)
|
||||
);
|
||||
((edx as u64) << 32) | (eax as u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the value to the specified Model-Specific Register (MSR).
|
||||
/// # Safety
|
||||
/// This operation is inherently unsafe.
|
||||
#[inline]
|
||||
pub unsafe fn write_msr(msr: u32, value: u64) {
|
||||
unsafe {
|
||||
let eax = value as u32;
|
||||
let edx = (value >> 32) as u32;
|
||||
asm!(
|
||||
"wrmsr",
|
||||
in("ecx") msr,
|
||||
in("eax") eax,
|
||||
in("edx") edx,
|
||||
options(nomem, nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rdtsc() -> u64 {
|
||||
let low: u32;
|
||||
let high: u32;
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"rdtsc",
|
||||
out("eax") low,
|
||||
out("edx") high,
|
||||
options(nomem, nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
((high as u64) << 32) | (low as u64)
|
||||
}
|
||||
|
|
|
|||
65
kernel/tests/stack_overflow.rs
Normal file
65
kernel/tests/stack_overflow.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
|
||||
use core::{cell::UnsafeCell, mem::offset_of};
|
||||
|
||||
use kernel::{
|
||||
sync::LazyLock,
|
||||
x86_64::{
|
||||
gdt::{DF_STACK, GDT, GlobalDescriptorTable, RING0},
|
||||
idt::{self, Entry, InterruptDescriptorTable},
|
||||
},
|
||||
};
|
||||
|
||||
#[unsafe(export_name = "_start")]
|
||||
pub extern "C" fn main() -> ! {
|
||||
kernel::serial_println!("Hello, world!");
|
||||
|
||||
GDT.load();
|
||||
kernel::serial_println!("[ok] GDT loaded");
|
||||
|
||||
extern "x86-interrupt" fn double_fault_handler(
|
||||
_stack_frame: &mut idt::InterruptStackFrame,
|
||||
_error_code: u64,
|
||||
) -> ! {
|
||||
kernel::serial_println!("[ok] Double fault handler called");
|
||||
kernel::testing::exit_qemu(kernel::testing::QemuExitCode::Success)
|
||||
}
|
||||
|
||||
static IDT: LazyLock<InterruptDescriptorTable> = LazyLock::new(|| {
|
||||
let mut idt = InterruptDescriptorTable::new_default();
|
||||
idt.double_fault = unsafe {
|
||||
Entry::new(
|
||||
double_fault_handler as *const (),
|
||||
offset_of!(GlobalDescriptorTable, kernel_code) as u16,
|
||||
idt::EntryOptions::empty_interrupt_gate()
|
||||
.with_present(true)
|
||||
.with_privilege_level(RING0)
|
||||
.with_interrupt_stack_table_index(DF_STACK),
|
||||
)
|
||||
};
|
||||
|
||||
idt
|
||||
});
|
||||
|
||||
IDT.load();
|
||||
|
||||
stack_overflow();
|
||||
|
||||
panic!("Triggering a stack overflow to test double fault handling");
|
||||
}
|
||||
|
||||
#[allow(unconditional_recursion)]
|
||||
fn stack_overflow() {
|
||||
stack_overflow(); // for each recursion, the return address is pushed
|
||||
unsafe {
|
||||
let cell = UnsafeCell::new(0);
|
||||
cell.get().write_volatile(0);
|
||||
}; // prevent tail recursion optimizations
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_thunk(info: &core::panic::PanicInfo) -> ! {
|
||||
kernel::testing::test_panic_handler(info)
|
||||
}
|
||||
Loading…
Reference in a new issue