Skip to content

Commit 0242030

Browse files
authored
Merge pull request #194 from Restioson/iomap
Add ability to add iomap to TSS (take 2)
2 parents 14a177b + c3f515c commit 0242030

File tree

2 files changed

+143
-7
lines changed

2 files changed

+143
-7
lines changed

src/structures/gdt.rs

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
//! Types for the Global Descriptor Table and segment selectors.
22
33
pub use crate::registers::segmentation::SegmentSelector;
4-
use crate::structures::tss::TaskStateSegment;
4+
use crate::structures::tss::{InvalidIoMap, TaskStateSegment};
55
use crate::PrivilegeLevel;
66
use bit_field::BitField;
77
use bitflags::bitflags;
8-
use core::fmt;
8+
use core::{cmp, fmt, mem};
99
// imports for intra-doc links
1010
#[cfg(doc)]
1111
use crate::registers::segmentation::{Segment, CS, SS};
@@ -439,17 +439,90 @@ impl Descriptor {
439439
/// being used.
440440
#[inline]
441441
pub unsafe fn tss_segment_unchecked(tss: *const TaskStateSegment) -> Descriptor {
442+
// SAFETY: if iomap_size is zero, there are no requirements to uphold.
443+
unsafe { Self::tss_segment_raw(tss, 0) }
444+
}
445+
446+
/// Creates a TSS system descriptor for the given TSS, setting up the IO permissions bitmap.
447+
///
448+
/// # Example
449+
///
450+
/// ```
451+
/// use x86_64::structures::gdt::Descriptor;
452+
/// use x86_64::structures::tss::TaskStateSegment;
453+
///
454+
/// /// A helper that places some I/O map bytes behind a TSS.
455+
/// #[repr(C)]
456+
/// struct TssWithIOMap {
457+
/// tss: TaskStateSegment,
458+
/// iomap: [u8; 5],
459+
/// }
460+
///
461+
/// static TSS: TssWithIOMap = TssWithIOMap {
462+
/// tss: TaskStateSegment::new(),
463+
/// iomap: [0xff, 0xff, 0x00, 0x80, 0xff],
464+
/// };
465+
///
466+
/// let tss = Descriptor::tss_segment_with_iomap(&TSS.tss, &TSS.iomap).unwrap();
467+
/// ```
468+
pub fn tss_segment_with_iomap(
469+
tss: &'static TaskStateSegment,
470+
iomap: &'static [u8],
471+
) -> Result<Descriptor, InvalidIoMap> {
472+
if iomap.len() > 8193 {
473+
return Err(InvalidIoMap::TooLong { len: iomap.len() });
474+
}
475+
476+
let iomap_addr = iomap.as_ptr() as usize;
477+
let tss_addr = tss as *const _ as usize;
478+
479+
if tss_addr > iomap_addr {
480+
return Err(InvalidIoMap::IoMapBeforeTss);
481+
}
482+
483+
let base = iomap_addr - tss_addr;
484+
if base > 0xdfff {
485+
return Err(InvalidIoMap::TooFarFromTss { distance: base });
486+
}
487+
488+
let last_byte = *iomap.last().unwrap_or(&0xff);
489+
if last_byte != 0xff {
490+
return Err(InvalidIoMap::InvalidTerminatingByte { byte: last_byte });
491+
}
492+
493+
if tss.iomap_base != base as u16 {
494+
return Err(InvalidIoMap::InvalidBase {
495+
expected: base as u16,
496+
got: tss.iomap_base,
497+
});
498+
}
499+
500+
// SAFETY: all invariants checked above
501+
Ok(unsafe { Self::tss_segment_raw(tss, iomap.len() as u16) })
502+
}
503+
504+
/// Creates a TSS system descriptor for the given TSS, setting up the IO permissions bitmap.
505+
///
506+
/// # Safety
507+
///
508+
/// There must be a valid IO map at `(tss as *const u8).offset(tss.iomap_base)`
509+
/// of length `iomap_size`, with the terminating `0xFF` byte. Additionally, `iomap_base` must
510+
/// not exceed `0xDFFF`.
511+
unsafe fn tss_segment_raw(tss: *const TaskStateSegment, iomap_size: u16) -> Descriptor {
442512
use self::DescriptorFlags as Flags;
443-
use core::mem::size_of;
444513

445514
let ptr = tss as u64;
446515

447516
let mut low = Flags::PRESENT.bits();
448517
// base
449518
low.set_bits(16..40, ptr.get_bits(0..24));
450519
low.set_bits(56..64, ptr.get_bits(24..32));
451-
// limit (the `-1` in needed since the bound is inclusive)
452-
low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
520+
// limit (the `-1` is needed since the bound is inclusive)
521+
let iomap_limit = u64::from(unsafe { (*tss).iomap_base }) + u64::from(iomap_size);
522+
low.set_bits(
523+
0..16,
524+
cmp::max(mem::size_of::<TaskStateSegment>() as u64, iomap_limit) - 1,
525+
);
453526
// type (0b1001 = available 64-bit tss)
454527
low.set_bits(40..44, 0b1001);
455528

src/structures/tss.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Provides a type for the task state segment structure.
22
33
use crate::VirtAddr;
4-
use core::mem::size_of;
4+
use core::{
5+
fmt::{self, Display},
6+
mem::size_of,
7+
};
58

69
/// In 64-bit mode the TSS holds information that is not
710
/// directly related to the task-switch mechanism,
@@ -19,7 +22,8 @@ pub struct TaskStateSegment {
1922
pub interrupt_stack_table: [VirtAddr; 7],
2023
reserved_3: u64,
2124
reserved_4: u16,
22-
/// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
25+
/// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base. It must not
26+
/// exceed `0xDFFF`.
2327
pub iomap_base: u16,
2428
}
2529

@@ -51,6 +55,65 @@ impl Default for TaskStateSegment {
5155
}
5256
}
5357

58+
/// The given IO permissions bitmap is invalid.
59+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
60+
pub enum InvalidIoMap {
61+
/// The IO permissions bitmap is before the TSS. It must be located after the TSS.
62+
IoMapBeforeTss,
63+
/// The IO permissions bitmap is too far from the TSS. It must be within `0xdfff` bytes of the
64+
/// start of the TSS. Note that if the IO permissions bitmap is located before the TSS, then
65+
/// `IoMapBeforeTss` will be returned instead.
66+
TooFarFromTss {
67+
/// The distance of the IO permissions bitmap from the beginning of the TSS.
68+
distance: usize,
69+
},
70+
/// The final byte of the IO permissions bitmap was not 0xff
71+
InvalidTerminatingByte {
72+
/// The byte found at the end of the IO permissions bitmap.
73+
byte: u8,
74+
},
75+
/// The IO permissions bitmap exceeds the maximum length (8193).
76+
TooLong {
77+
/// The length of the IO permissions bitmap.
78+
len: usize,
79+
},
80+
/// The `iomap_base` in the `TaskStateSegment` struct was not what was expected.
81+
InvalidBase {
82+
/// The expected `iomap_base` to be set in the `TaskStateSegment` struct.
83+
expected: u16,
84+
/// The actual `iomap_base` set in the `TaskStateSegment` struct.
85+
got: u16,
86+
},
87+
}
88+
89+
impl Display for InvalidIoMap {
90+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91+
match *self {
92+
InvalidIoMap::IoMapBeforeTss => {
93+
write!(f, "the IO permissions bitmap is before the TSS")
94+
}
95+
InvalidIoMap::TooFarFromTss { distance } => write!(
96+
f,
97+
"the IO permissions bitmap is too far from the TSS (distance {distance})"
98+
),
99+
InvalidIoMap::InvalidTerminatingByte { byte } => write!(
100+
f,
101+
"The final byte of the IO permissions bitmap was not 0xff ({byte}"
102+
),
103+
InvalidIoMap::TooLong { len } => {
104+
write!(
105+
f,
106+
"The IO permissions bitmap exceeds the maximum length ({len} > 8193)"
107+
)
108+
}
109+
InvalidIoMap::InvalidBase { expected, got } => write!(
110+
f,
111+
"the `iomap_base` in the `TaskStateSegment` struct was not what was expected (expected {expected}, got {got})"
112+
),
113+
}
114+
}
115+
}
116+
54117
#[cfg(test)]
55118
mod tests {
56119
use super::*;

0 commit comments

Comments
 (0)