rust-osdev / rust-osdev/x86_64

Safe API for using I/O Permission Bitmap in TSS

Open
#539 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
939
Forks
158
Avg merge
1d 10h
Merged PRs (30d)
3

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 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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the existing TaskStateSegment and Descriptor::tss_segment APIs, then review issue #194 and the referenced develop implementation. Resolve the variable-length I/O Permission Bitmap, iomap_base visibility, and safe mutation questions before proposing an API; done means the design is agreed and covered by appropriate repository tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
operating-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.