Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,19 @@ uspace = []
arm-el2 = []

[dependencies]
cfg-if = "1.0"
linkme = "0.3"
log = "0.4"
cfg-if = "1.0"
memory_addr = "0.4"
page_table_entry = "0.6"
static_assertions = "1.1.0"

[target.'cfg(target_arch = "x86_64")'.dependencies]
x86 = "0.52"
x86_64 = "0.15.2"
percpu = "0.4"
lazyinit = "0.2"
spin = "0.10"
x86 = "0.52"
x86_64 = { version = "0.15", default-features = false, features = [
"instructions",
] }

[target.'cfg(target_arch = "aarch64")'.dependencies]
aarch64-cpu = "11.2"
Expand Down
2 changes: 1 addition & 1 deletion src/x86_64/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub struct FxsaveArea {
_padding: [u64; 12],
}

static_assertions::const_assert_eq!(core::mem::size_of::<FxsaveArea>(), 512);
const _: () = assert!(core::mem::size_of::<FxsaveArea>() == 512);

/// Extended state of a task, such as FP/SIMD states.
pub struct ExtendedState {
Expand Down
130 changes: 30 additions & 100 deletions src/x86_64/gdt.rs
Original file line number Diff line number Diff line change
@@ -1,112 +1,42 @@
use core::fmt;
use x86_64::{
instructions::tables::load_tss,
registers::segmentation::{Segment, SegmentSelector, CS},
structures::{
gdt::{Descriptor, GlobalDescriptorTable},
tss::TaskStateSegment,
},
PrivilegeLevel,
};

use lazyinit::LazyInit;
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};

#[unsafe(no_mangle)]
#[percpu::def_percpu]
#[unsafe(no_mangle)]
static TSS: TaskStateSegment = TaskStateSegment::new();

#[percpu::def_percpu]
static GDT: LazyInit<GdtStruct> = LazyInit::new();

/// A wrapper of the Global Descriptor Table (GDT) with maximum 16 entries.
#[repr(align(16))]
pub struct GdtStruct {
table: [u64; 16],
}
static GDT: GlobalDescriptorTable = GlobalDescriptorTable::new();

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) {
unsafe {
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) {
unsafe {
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()
}
}
/// Kernel code segment for 64-bit mode.
pub const KCODE64: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0);
/// Kernel data segment.
pub const KDATA: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0);
/// User data segment.
pub const UDATA: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring3);
/// User code segment for 64-bit mode.
pub const UCODE64: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3);

/// Initializes the per-CPU TSS and GDT structures and loads them into the
/// current CPU.
pub fn init_gdt() {
pub(super) fn init() {
let gdt = unsafe { GDT.current_ref_mut_raw() };
assert_eq!(gdt.append(Descriptor::kernel_code_segment()), KCODE64);
assert_eq!(gdt.append(Descriptor::kernel_data_segment()), KDATA);
assert_eq!(gdt.append(Descriptor::user_data_segment()), UDATA);
assert_eq!(gdt.append(Descriptor::user_code_segment()), UCODE64);
let tss = gdt.append(Descriptor::tss_segment(unsafe { TSS.current_ref_raw() }));
gdt.load();
unsafe {
let gdt = GDT.current_ref_raw();
gdt.init_once(GdtStruct::new(TSS.current_ref_raw()));
gdt.load();
gdt.load_tss();
CS::set_reg(KCODE64);
load_tss(tss);
}
}

Expand All @@ -125,5 +55,5 @@ pub(crate) fn read_tss_rsp0() -> memory_addr::VirtAddr {
#[cfg(feature = "uspace")]
pub(crate) unsafe fn write_tss_rsp0(rsp0: memory_addr::VirtAddr) {
let tss = unsafe { TSS.current_ref_mut_raw() };
tss.privilege_stack_table[0] = VirtAddr::new_truncate(rsp0.as_usize() as u64);
tss.privilege_stack_table[0] = x86_64::VirtAddr::new_truncate(rsp0.as_usize() as u64);
}
99 changes: 25 additions & 74 deletions src/x86_64/idt.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,32 @@
use core::fmt;

use lazyinit::LazyInit;
use x86_64::addr::VirtAddr;
use x86_64::structures::idt::{Entry, HandlerFunc, InterruptDescriptorTable};
use x86_64::structures::DescriptorTablePointer;

const NUM_INT: usize = 256;

static IDT: LazyInit<IdtStruct> = LazyInit::new();

/// A wrapper of the Interrupt Descriptor Table (IDT).
#[repr(transparent)]
pub struct IdtStruct {
table: InterruptDescriptorTable,
}

impl IdtStruct {
/// Constructs a new IDT struct that filled with entries from
/// `trap_handler_table`.
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
unsafe extern "C" {
#[link_name = "trap_handler_table"]
static ENTRIES: [extern "C" fn(); NUM_INT];
}
let mut idt = Self {
table: InterruptDescriptorTable::new(),
};

let entries = unsafe {
core::slice::from_raw_parts_mut(
&mut idt.table as *mut _ as *mut Entry<HandlerFunc>,
NUM_INT,
)
};
for i in 0..NUM_INT {
#[allow(clippy::missing_transmute_annotations)]
let opt = entries[i].set_handler_fn(unsafe { core::mem::transmute(ENTRIES[i]) });
if i == 0x3 || i == 0x80 {
// enable user space breakpoints and legacy int 0x80 syscall
opt.set_privilege_level(x86_64::PrivilegeLevel::Ring3);
}
}
idt
use spin::Lazy;
use x86_64::{
addr::VirtAddr,
structures::idt::{Entry, InterruptDescriptorTable},
};

static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
const NUM_INT: usize = 256;

unsafe extern "C" {
#[link_name = "trap_handler_table"]
static ENTRIES: [VirtAddr; NUM_INT];
}

/// Returns the IDT pointer (base and limit) that can be used in the `lidt`
/// instruction.
pub fn pointer(&self) -> DescriptorTablePointer {
DescriptorTablePointer {
base: VirtAddr::new(&self.table as *const _ as u64),
limit: (core::mem::size_of::<InterruptDescriptorTable>() - 1) as u16,
let mut table = InterruptDescriptorTable::new();
let entries = unsafe {
core::mem::transmute::<&mut InterruptDescriptorTable, &mut [Entry<()>; NUM_INT]>(&mut table)
};
for i in 0..NUM_INT {
let opt = unsafe { entries[i].set_handler_addr(ENTRIES[i]) };
if i == 0x3 || i == 0x80 {
// enable user space breakpoints and legacy int 0x80 syscall
opt.set_privilege_level(x86_64::PrivilegeLevel::Ring3);
}
}

/// Loads the IDT into the CPU (executes the `lidt` instruction).
///
/// # Safety
///
/// This function is unsafe because it manipulates the CPU's privileged
/// states.
pub unsafe fn load(&'static self) {
self.table.load();
}
}

impl fmt::Debug for IdtStruct {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IdtStruct")
.field("pointer", &self.pointer())
.field("table", &self.table)
.finish()
}
}
table
});

/// Initializes the global IDT and loads it into the current CPU.
pub fn init_idt() {
IDT.call_once(IdtStruct::new);
unsafe { IDT.load() };
pub(super) fn init() {
IDT.load();
}
Loading
Loading