use bitflags::*;
use core::fmt;
use crate::Ring;
bitflags! {
pub struct SegmentSelector: u16 {
const RPL_0 = 0b00;
const RPL_1 = 0b01;
const RPL_2 = 0b10;
const RPL_3 = 0b11;
const TI_GDT = 0 << 2;
const TI_LDT = 1 << 2;
}
}
impl SegmentSelector {
pub const fn new(index: u16, rpl: Ring) -> SegmentSelector {
SegmentSelector {
bits: index << 3 | (rpl as u16),
}
}
pub const fn from_raw(bits: u16) -> SegmentSelector {
SegmentSelector { bits: bits }
}
}
impl fmt::Display for SegmentSelector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let r0 = match self.contains(SegmentSelector::RPL_0) {
false => "",
true => "Ring 0 segment selector.",
};
let r1 = match self.contains(SegmentSelector::RPL_1) {
false => "",
true => "Ring 1 segment selector.",
};
let r2 = match self.contains(SegmentSelector::RPL_2) {
false => "",
true => "Ring 2 segment selector.",
};
let r3 = match self.contains(SegmentSelector::RPL_3) {
false => "",
true => "Ring 3 segment selector.",
};
let tbl = match self.contains(SegmentSelector::TI_LDT) {
false => "GDT Table",
true => "LDT Table",
};
write!(
f,
"Index {} in {}, {}{}{}{}",
self.bits >> 3,
tbl,
r0,
r1,
r2,
r3
)
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SystemDescriptorTypes64 {
LDT = 0b0010,
TssAvailable = 0b1001,
TssBusy = 0b1011,
CallGate = 0b1100,
InterruptGate = 0b1110,
TrapGate = 0b1111,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SystemDescriptorTypes32 {
TSSAvailable16 = 0b0001,
LDT = 0b0010,
TSSBusy16 = 0b0011,
CallGate16 = 0b0100,
TaskGate = 0b0101,
InterruptGate16 = 0b0110,
TrapGate16 = 0b0111,
TssAvailable32 = 0b1001,
TssBusy32 = 0b1011,
CallGate32 = 0b1100,
InterruptGate32 = 0b1110,
TrapGate32 = 0b1111,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum DataSegmentType {
ReadOnly = 0b0000,
ReadOnlyAccessed = 0b0001,
ReadWrite = 0b0010,
ReadWriteAccessed = 0b0011,
ReadExpand = 0b0100,
ReadExpandAccessed = 0b0101,
ReadWriteExpand = 0b0110,
ReadWriteExpandAccessed = 0b0111,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CodeSegmentType {
Execute = 0b1000,
ExecuteAccessed = 0b1001,
ExecuteRead = 0b1010,
ExecuteReadAccessed = 0b1011,
ExecuteConforming = 0b1100,
ExecuteConformingAccessed = 0b1101,
ExecuteReadConforming = 0b1110,
ExecuteReadConformingAccessed = 0b1111,
}
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum DescriptorType {
System64(SystemDescriptorTypes64),
System32(SystemDescriptorTypes32),
Data(DataSegmentType),
Code(CodeSegmentType),
}
pub trait GateDescriptorBuilder<Size> {
fn tss_descriptor(base: u64, limit: u64, available: bool) -> Self;
fn call_gate_descriptor(selector: SegmentSelector, offset: Size) -> Self;
fn interrupt_descriptor(selector: SegmentSelector, offset: Size) -> Self;
fn trap_gate_descriptor(selector: SegmentSelector, offset: Size) -> Self;
}
pub trait TaskGateDescriptorBuilder {
fn task_gate_descriptor(selector: SegmentSelector) -> Self;
}
pub trait SegmentDescriptorBuilder<Size> {
fn code_descriptor(base: Size, limit: Size, cst: CodeSegmentType) -> Self;
fn data_descriptor(base: Size, limit: Size, dst: DataSegmentType) -> Self;
}
pub trait LdtDescriptorBuilder<Size> {
fn ldt_descriptor(base: Size, limit: Size) -> Self;
}
pub trait BuildDescriptor<Descriptor> {
fn finish(&self) -> Descriptor;
}
#[derive(Debug)]
pub struct DescriptorBuilder {
pub(crate) base_limit: Option<(u64, u64)>,
pub(crate) selector_offset: Option<(SegmentSelector, u64)>,
pub(crate) typ: Option<DescriptorType>,
pub(crate) dpl: Option<Ring>,
pub(crate) present: bool,
pub(crate) avl: bool,
pub(crate) db: bool,
pub(crate) limit_granularity_4k: bool,
pub(crate) l: bool,
}
impl DescriptorBuilder {
pub(crate) fn with_base_limit(base: u64, limit: u64) -> DescriptorBuilder {
DescriptorBuilder {
base_limit: Some((base, limit)),
selector_offset: None,
typ: None,
dpl: None,
present: false,
avl: false,
db: false,
limit_granularity_4k: false,
l: false,
}
}
pub(crate) fn with_selector_offset(
selector: SegmentSelector,
offset: u64,
) -> DescriptorBuilder {
DescriptorBuilder {
base_limit: None,
selector_offset: Some((selector, offset)),
typ: None,
dpl: None,
present: false,
avl: false,
db: false,
limit_granularity_4k: false,
l: false,
}
}
pub(crate) fn set_type(mut self, typ: DescriptorType) -> DescriptorBuilder {
self.typ = Some(typ);
self
}
pub fn limit_granularity_4kb(mut self) -> DescriptorBuilder {
self.limit_granularity_4k = true;
self
}
pub fn present(mut self) -> DescriptorBuilder {
self.present = true;
self
}
pub fn dpl(mut self, dpl: Ring) -> DescriptorBuilder {
self.dpl = Some(dpl);
self
}
pub fn avl(mut self) -> DescriptorBuilder {
self.avl = true;
self
}
pub fn db(mut self) -> DescriptorBuilder {
self.db = true;
self
}
pub fn l(mut self) -> DescriptorBuilder {
self.l = true;
self
}
}
impl GateDescriptorBuilder<u32> for DescriptorBuilder {
fn tss_descriptor(base: u64, limit: u64, available: bool) -> DescriptorBuilder {
let typ = match available {
true => DescriptorType::System32(SystemDescriptorTypes32::TssAvailable32),
false => DescriptorType::System32(SystemDescriptorTypes32::TssBusy32),
};
DescriptorBuilder::with_base_limit(base.into(), limit.into()).set_type(typ)
}
fn call_gate_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(
DescriptorType::System32(SystemDescriptorTypes32::CallGate32),
)
}
fn interrupt_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(
DescriptorType::System32(SystemDescriptorTypes32::InterruptGate32),
)
}
fn trap_gate_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(
DescriptorType::System32(SystemDescriptorTypes32::TrapGate32),
)
}
}
impl TaskGateDescriptorBuilder for DescriptorBuilder {
fn task_gate_descriptor(selector: SegmentSelector) -> DescriptorBuilder {
DescriptorBuilder::with_selector_offset(selector, 0)
.set_type(DescriptorType::System32(SystemDescriptorTypes32::TaskGate))
}
}
impl SegmentDescriptorBuilder<u32> for DescriptorBuilder {
fn code_descriptor(base: u32, limit: u32, cst: CodeSegmentType) -> DescriptorBuilder {
DescriptorBuilder::with_base_limit(base.into(), limit.into())
.set_type(DescriptorType::Code(cst))
}
fn data_descriptor(base: u32, limit: u32, dst: DataSegmentType) -> DescriptorBuilder {
DescriptorBuilder::with_base_limit(base.into(), limit.into())
.set_type(DescriptorType::Data(dst))
}
}
impl LdtDescriptorBuilder<u32> for DescriptorBuilder {
fn ldt_descriptor(base: u32, limit: u32) -> DescriptorBuilder {
DescriptorBuilder::with_base_limit(base.into(), limit.into())
.set_type(DescriptorType::System32(SystemDescriptorTypes32::LDT))
}
}
impl BuildDescriptor<Descriptor> for DescriptorBuilder {
fn finish(&self) -> Descriptor {
let mut desc: Descriptor = Default::default();
desc.apply_builder_settings(self);
let typ = match self.typ {
Some(DescriptorType::System64(_)) => {
panic!("You shall not use 64-bit types on 32-bit descriptor.")
}
Some(DescriptorType::System32(typ)) => typ as u8,
Some(DescriptorType::Data(typ)) => {
desc.set_s();
typ as u8
}
Some(DescriptorType::Code(typ)) => {
desc.set_s();
typ as u8
}
None => unreachable!("Type not set, this is a library bug in x86."),
};
desc.set_type(typ);
desc
}
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct Descriptor {
pub lower: u32,
pub upper: u32,
}
impl fmt::Display for Descriptor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Descriptor(0x{:x})", self.as_u64())
}
}
impl Descriptor {
pub const NULL: Descriptor = Descriptor { lower: 0, upper: 0 };
pub fn as_u64(&self) -> u64 {
(self.upper as u64) << 32 | self.lower as u64
}
pub(crate) fn apply_builder_settings(&mut self, builder: &DescriptorBuilder) {
builder.dpl.map(|ring| self.set_dpl(ring));
builder
.base_limit
.map(|(base, limit)| self.set_base_limit(base as u32, limit as u32));
builder
.selector_offset
.map(|(selector, offset)| self.set_selector_offset(selector, offset as u32));
if builder.present {
self.set_p();
}
if builder.avl {
self.set_avl();
}
if builder.db {
self.set_db();
}
if builder.limit_granularity_4k {
self.set_g();
}
if builder.l {
self.set_l();
}
}
pub fn set_base_limit(&mut self, base: u32, limit: u32) {
self.lower = 0;
self.upper = self.upper & 0x00F0FF00;
self.lower |= base << 16;
self.upper |= (base >> 16) & 0xff;
self.upper |= (base >> 24) << 24;
self.lower |= limit & 0xffff;
let limit_last_four_bits = (limit >> 16) & 0x0f;
self.upper |= limit_last_four_bits << 16;
}
pub fn set_selector_offset(&mut self, selector: SegmentSelector, offset: u32) {
self.lower = 0;
self.upper = self.upper & 0x0000ffff;
self.lower |= (selector.bits() as u32) << 16;
self.lower |= offset & 0x0000ffff;
self.upper |= offset & 0xffff0000;
}
pub fn set_type(&mut self, typ: u8) {
self.upper &= !(0x0f << 8);
self.upper |= (typ as u32 & 0x0f) << 8;
}
pub fn set_s(&mut self) {
self.upper |= bit!(12);
}
pub fn set_dpl(&mut self, ring: Ring) {
assert!(ring as u32 <= 0b11);
self.upper &= !(0b11 << 13);
self.upper |= (ring as u32) << 13;
}
pub fn set_p(&mut self) {
self.upper |= bit!(15);
}
pub fn set_avl(&mut self) {
self.upper |= bit!(20);
}
pub fn set_l(&mut self) {
self.upper |= bit!(21);
}
pub fn set_db(&mut self) {
self.upper |= bit!(22);
}
pub fn set_g(&mut self) {
self.upper |= bit!(23);
}
}
pub unsafe fn load_ss(sel: SegmentSelector) {
asm!("movw $0, %ss " :: "r" (sel.bits()) : "memory");
}
pub unsafe fn load_ds(sel: SegmentSelector) {
asm!("movw $0, %ds " :: "r" (sel.bits()) : "memory");
}
pub unsafe fn load_es(sel: SegmentSelector) {
asm!("movw $0, %es " :: "r" (sel.bits()) : "memory");
}
pub unsafe fn load_fs(sel: SegmentSelector) {
asm!("movw $0, %fs " :: "r" (sel.bits()) : "memory");
}
pub unsafe fn load_gs(sel: SegmentSelector) {
asm!("movw $0, %gs " :: "r" (sel.bits()) : "memory");
}
pub fn cs() -> SegmentSelector {
let segment: u16;
unsafe { asm!("mov %cs, $0" : "=r" (segment) ) };
SegmentSelector::from_raw(segment)
}
#[cfg(test)]
mod test {
use super::*;
use crate::Ring;
#[test]
fn test_x86_64_default_gdt() {
let null: Descriptor = Default::default();
let code_kernel: Descriptor =
DescriptorBuilder::code_descriptor(0, 0xFFFFF, CodeSegmentType::ExecuteRead)
.present()
.dpl(Ring::Ring0)
.limit_granularity_4kb()
.db()
.finish();
let stack_kernel: Descriptor =
DescriptorBuilder::data_descriptor(0, 0xFFFFF, DataSegmentType::ReadWrite)
.present()
.dpl(Ring::Ring0)
.limit_granularity_4kb()
.db()
.finish();
let code_user: Descriptor =
DescriptorBuilder::code_descriptor(0, 0xFFFFF, CodeSegmentType::ExecuteRead)
.present()
.limit_granularity_4kb()
.db()
.dpl(Ring::Ring3)
.finish();
let stack_user: Descriptor =
DescriptorBuilder::data_descriptor(0, 0xFFFFF, DataSegmentType::ReadWrite)
.present()
.limit_granularity_4kb()
.db()
.dpl(Ring::Ring3)
.finish();
assert_eq!(0x0000000000000000u64, null.as_u64());
assert_eq!(
0x00CF9A000000FFFFu64,
code_kernel.as_u64(),
"code_kernel: {:#b} first bit to differ is {}",
(code_kernel.as_u64() ^ 0x00CF9A000000FFFFu64),
(code_kernel.as_u64() ^ 0x00CF9A000000FFFFu64).trailing_zeros()
);
assert_eq!(
0x00CF92000000FFFFu64,
stack_kernel.as_u64(),
"stack_kernel: {:#b} first bit to differ is {}",
(stack_kernel.as_u64() ^ 0x00CF92000000FFFFu64),
(stack_kernel.as_u64() ^ 0x00CF92000000FFFFu64).trailing_zeros()
);
assert_eq!(
0x00CFFA000000FFFFu64,
code_user.as_u64(),
"code_user: {:#b} first bit to differ is {}",
(code_user.as_u64() ^ 0x00CFFA000000FFFFu64),
(code_user.as_u64() ^ 0x00CFFA000000FFFFu64).trailing_zeros()
);
assert_eq!(
0x00CFF2000000FFFFu64,
stack_user.as_u64(),
"stack_user: {:#b} first bit to differ is {}",
(stack_user.as_u64() ^ 0x00CFF2000000FFFFu64),
(stack_user.as_u64() ^ 0x00CFF2000000FFFFu64).trailing_zeros()
);
}
}