Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safe API for using I/O Permission Bitmap in TSS #539

Open
ChocolateLoverRaj opened this issue Mar 18, 2025 · 0 comments
Open

Safe API for using I/O Permission Bitmap in TSS #539

ChocolateLoverRaj opened this issue Mar 18, 2025 · 0 comments

Comments

@ChocolateLoverRaj
Copy link

ChocolateLoverRaj commented Mar 18, 2025

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 in TaskStateSegment should not be public or modifyable outside of this library because it should always be 104
  • 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 when pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor takes an immutable reference to the tss? Perhaps the function could be changed to Pin<&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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant