Open
Description
I want to use the I/O Permission Bitmap (IOBP) to allow a user space program to access COM1 only. But the current TSS and GDT code doesn't make it easy. This is what I did to get it to work:
/// `N` is the size of the IOBP in **bytes**
/// Do not change `iomap_base`. It is set to the correct value by the `default` function.
/// By default, the IOPB is set to all 1s, so that no port access is allowed
#[derive(Debug, Clone, Copy)]
#[repr(C, packed(4))]
pub struct TssWithIoBitMap<const N: usize> {
pub above_iobp: TaskStateSegment,
pub actual_io_bitmap: [u8; N],
/// According to Section 20.5.2 in *Intel® 64 and IA-32 Architectures Software Developer’s Manual*
/// > Last byte of bitmap must be followed by a byte with all bits set.
io_bit_map_last_byte: u8,
}
impl<const N: usize> Default for TssWithIoBitMap<N> {
fn default() -> Self {
Self {
above_iobp: {
let mut tss = TaskStateSegment::default();
tss.iomap_base = (offset_of!(Self, actual_io_bitmap)).try_into().unwrap();
tss
},
// If a bit is 0 it means the port is allowed, 1 is not allowed
actual_io_bitmap: [u8::MAX; N],
io_bit_map_last_byte: u8::MAX,
}
}
}
and
impl Descriptor {
/// Similar to [`Descriptor::tss_segment`], but unsafe since it does not enforce a lifetime
/// constraint on the provided TSS.
///
/// # Safety
/// The caller must ensure that the passed pointer is valid for as long as the descriptor is
/// being used.
#[inline]
pub unsafe fn tss_segment_unchecked_with_size(
tss: *const TaskStateSegment,
size: u64,
) -> Descriptor {
use self::DescriptorFlags as Flags;
use core::mem::size_of;
let ptr = tss as u64;
let mut low = Flags::PRESENT.bits();
// base
low.set_bits(16..40, ptr.get_bits(0..24));
low.set_bits(56..64, ptr.get_bits(24..32));
// limit (the `-1` in needed since the bound is inclusive)
low.set_bits(0..16, size - 1);
// type (0b1001 = available 64-bit tss)
low.set_bits(40..44, 0b1001);
let mut high = 0;
high.set_bits(0..32, ptr.get_bits(32..64));
Descriptor::SystemSegment(low, high)
}
}
and then in my GDT code:
let tss_selector = gdt.append(unsafe {
Descriptor::tss_segment_unchecked_with_size(
tss as *const _ as *const TaskStateSegment,
size_of::<TssWithIoBitMap<N>>() as u64,
)
});
I only did this to test out using the IOPB. Ideally this library would make it easier and safer.
I can make a PR for this feature, but there are some things to decide first:
- The IOPB can be variable length, which would make TSS variable length
- Should we have a
<const N: usize>
generic for the IOPB in the TSS? Would this make it too inconvenient for the majority of people who don't want a IOPB? - IMO the
iomap_base
inTaskStateSegment
should not be public or modifyable outside of this library because it should always be104
- The IOPB can be changed at any time by the kernel after initializing the tss and gdt, without needing to reload anything (I think). How can there be a safe way of doing this without
unsafe
whenpub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor
takes an immutable reference to the tss? Perhaps the function could be changed toPin<&TaskStateSegment>
? Idk much about pin though.
Also I just realized #194 exists
I actually implemented this at https://github.com/ChocolateLoverRaj/x86_64/tree/develop, I think it's a good API.
Metadata
Metadata
Assignees
Labels
No labels