rust-osdev / rust-osdev/x86_64
Safe API for using I/O Permission Bitmap in TSS
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_baseinTaskStateSegmentshould 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
unsafewhenpub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptortakes 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.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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