diff --git a/kernel/src/bits.rs b/kernel/src/bits.rs new file mode 100644 index 0000000..64c75d9 --- /dev/null +++ b/kernel/src/bits.rs @@ -0,0 +1,75 @@ +use core::ops::{Bound, Range}; + +pub const trait Bits { + const BITS: usize; + fn get_bit(&self, index: usize) -> bool; + fn set_bit(&mut self, index: usize, value: bool) -> &mut Self; + fn get_bits(&self, range: Range) -> Self; + fn set_bits(&mut self, range: Range, value: Self) -> &mut Self; +} + +const fn range_to_bounds>( + range: &R, + max: usize, +) -> (usize, usize) { + let start = match range.start_bound() { + Bound::Included(&start) => start, + Bound::Excluded(&start) => start + 1, + Bound::Unbounded => 0, + }; + let end = match range.end_bound() { + Bound::Included(&end) => end + 1, + Bound::Excluded(&end) => end, + Bound::Unbounded => max, + }; + assert!( + start <= end, + "Start of range must be less than or equal to end" + ); + assert!(end <= max, "End of range must be less than or equal to max"); + (start, end) +} + +macro_rules! impl_bits_for { + ($($t:ty),*) => { + $( + const impl Bits for $t { + const BITS: usize = <$t>::BITS as usize; + fn get_bit(&self, index: usize) -> bool { + assert!(index < ::BITS, "Index out of bounds"); + + (*self & (1 << index)) != 0 + } + + fn set_bit(&mut self, index: usize, value: bool) -> &mut Self{ + assert!(index < ::BITS, "Index out of bounds"); + + if value { + *self |= 1 << index; + } else { + *self &= !(1 << index); + } + self + } + fn get_bits(&self, range: Range) -> Self { + let (start, end) = range_to_bounds(&range, ::BITS); + let leading = ::BITS - end; + let bits = (*self << leading) >> (end + start); + bits + } + + fn set_bits(&mut self, range: Range, value: Self) -> &mut Self{ + let (start, end) = range_to_bounds(&range, ::BITS); + let mask = ((1 << (end - start)) - 1); + let value = (value & mask) << start; + + *self &= !(mask << start); + *self |= value; + self + } + } + )* + }; +} + +impl_bits_for!(u8, u16, u32, u64, u128); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index ad53e9c..481f87e 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,10 +1,11 @@ #![no_std] -#![feature(const_trait_impl, const_default, abi_x86_interrupt, never_type)] +#![feature(const_trait_impl, const_default, const_range)] #![cfg_attr(test, feature(custom_test_frameworks))] #![cfg_attr(test, test_runner(crate::tests::test_runner))] #![cfg_attr(test, no_main)] #![cfg_attr(test, reexport_test_harness_main = "test_main")] +pub mod bits; pub mod limine; pub mod serial; pub mod sync; diff --git a/kernel/src/x86_64.rs b/kernel/src/x86_64.rs index 57645c4..1cce837 100644 --- a/kernel/src/x86_64.rs +++ b/kernel/src/x86_64.rs @@ -1,10 +1,13 @@ #![cfg(target_arch = "x86_64")] +pub mod gdt; pub mod idt; pub mod instructions; pub use instructions::hlt; +pub const PAGE_SIZE: usize = 4096; + pub fn halt_loop() -> ! { loop { hlt(); diff --git a/kernel/src/x86_64/gdt.rs b/kernel/src/x86_64/gdt.rs new file mode 100644 index 0000000..dac3d6a --- /dev/null +++ b/kernel/src/x86_64/gdt.rs @@ -0,0 +1,440 @@ +use core::ops::{Deref, DerefMut}; + +use bit_field::BitField; + +use crate::{sync::LazyLock, x86_64::idt::ToAddress}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct GlobalDescriptorTable { + pub null: RawGdtEntry, + pub kernel_code: RawGdtEntry, + pub kernel_data: RawGdtEntry, + pub tss: TssEntry, +} + +impl GlobalDescriptorTable { + pub fn set_tss(&mut self, tss: &'static TaskStateSegment) { + const TSS_SIZE: u32 = core::mem::size_of::() 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), + ), + tss: TssEntry::new( + tss as *const TaskStateSegment as u64, + core::mem::size_of::() as u32 - 1, + ), + } + } +} + +impl Default for GlobalDescriptorTable { + fn default() -> Self { + Self::new() + } +} + +#[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 { + Reserved = 0b0000, + Ldt = 0b0010, + TssAvailable = 0b1001, + TssBusy = 0b1011, + CallGate = 0b1100, + InterruptGate = 0b1110, + TrapGate = 0b1111, +} + +pub const RING0: u8 = 0; +pub const RING1: u8 = 1; +pub const RING2: u8 = 2; +pub const RING3: u8 = 3; + +bitflags::bitflags! { + pub struct CodeDataSegmentKind: u8 { + const CodeSegment = 1 << 4; + const Executable = 1 << 3; + /// `ReadWrite` is set for code segments if they are readable, and for + /// data segments if they are writable. + const ReadWrite = 1 << 1; + const Accessed = 1 << 0; + } +} + +impl SystemSegmentKind { + pub fn into_u8(self) -> u8 { + self as u8 + } + pub fn from_u8_or_reserved(value: u8) -> Self { + match value { + 0b0010 => SystemSegmentKind::Ldt, + 0b1001 => SystemSegmentKind::TssAvailable, + 0b1011 => SystemSegmentKind::TssBusy, + 0b1100 => SystemSegmentKind::CallGate, + 0b1110 => SystemSegmentKind::InterruptGate, + 0b1111 => SystemSegmentKind::TrapGate, + _ => SystemSegmentKind::Reserved, + } + } + + /// # Safety + /// the caller must ensure that the lower 4 bits of `value` are a valid `GdtKind` discriminant. + pub unsafe fn from_u8_unchecked(value: u8) -> Self { + unsafe { core::mem::transmute(value & 0b1111) } + } +} + +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)] +pub struct RawGdtEntry { + pub limit_low: u16, + pub base_low: u16, + pub base_middle: u8, + pub access: GdtAccess, + pub flags: GdtFlags, + pub base_high: u8, +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DefaultOperationSize { + Bit16 = 0, + Bit32 = 1, +} + +impl DefaultOperationSize { + pub fn into_u8(self) -> u8 { + self as u8 + } + pub fn from_bool(value: bool) -> Self { + if value { + DefaultOperationSize::Bit32 + } else { + DefaultOperationSize::Bit16 + } + } +} + +impl AsRef for TssEntry { + fn as_ref(&self) -> &RawGdtEntry { + &self.gdt + } +} +impl AsRef for RawGdtEntry { + fn as_ref(&self) -> &GdtAccess { + &self.access + } +} + +impl AsRef for RawGdtEntry { + fn as_ref(&self) -> &GdtFlags { + &self.flags + } +} + +impl RawGdtEntry { + pub fn new(limit: u32, base: u32, access: GdtAccess, flags: GdtFlags) -> Self { + let limit_low = limit as u16; + let limit_high = (limit >> 16) as u8; + let base_low = base as u16; + let base_middle = (base >> 16) as u8; + let base_high = (base >> 24) as u8; + + RawGdtEntry { + limit_low, + base_low, + base_middle, + access, + flags: flags.with_limit_high(limit_high), + base_high, + } + } + + pub fn access(&self) -> GdtAccess { + self.access + } + + pub fn limit(&self) -> u32 { + let limit_low = self.limit_low as u32; + let limit_high = (self.flags.limit_high() as u32) << 16; + limit_low | limit_high + } + + pub fn base32(&self) -> u32 { + let base_low = self.base_low as u32; + let base_middle = (self.base_middle as u32) << 16; + let base_high = (self.base_high as u32) << 24; + base_low | base_middle | base_high + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TssEntry { + gdt: RawGdtEntry, + pub base_ext: u32, + reserved: u32, +} + +impl TssEntry { + pub fn base64(&self) -> u64 { + let base_low = self.gdt.base32() as u64; + let base_ext = self.base_ext as u64; + (base_ext << 32) | base_low + } + + pub fn new(base: u64, limit: u32) -> 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), + base_ext, + reserved: 0, + } + } +} + +impl Deref for TssEntry { + type Target = RawGdtEntry; + + fn deref(&self) -> &Self::Target { + &self.gdt + } +} + +impl DerefMut for TssEntry { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.gdt + } +} + +#[repr(C, packed(4))] +pub struct TaskStateSegment { + _reserved1: [u8; 4], + pub privilege_stack_table: [u64; 3], + _reserved2: [u8; 8], + pub interrupt_stack_table: [u64; 7], + _reserved3: [u8; 10], + pub iomap_base: u16, +} + +impl TaskStateSegment { + pub const fn new() -> Self { + Self { + privilege_stack_table: [0; 3], + interrupt_stack_table: [0; 7], + iomap_base: size_of::() as u16, + _reserved1: [0; 4], + _reserved2: [0; 8], + _reserved3: [0; 10], + } + } +} + +const impl Default for TaskStateSegment { + fn default() -> Self { + Self::new() + } +} + +pub const DF_STACK: u8 = 0; +pub const NMI_STACK: u8 = 1; +pub const MC_STACK: u8 = 2; + +static TSS: LazyLock = 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]; + + let stack_top = |n: usize| unsafe { STACKS[n].as_ptr().add(STACK_SIZE).to_address() }; + + tss.interrupt_stack_table[DF_STACK as usize] = stack_top(DF_STACK as usize); + tss.interrupt_stack_table[NMI_STACK as usize] = stack_top(NMI_STACK as usize); + tss.interrupt_stack_table[MC_STACK as usize] = stack_top(MC_STACK as usize); + + tss +});