|
1 | 1 | //! Types for the Global Descriptor Table and segment selectors. |
2 | 2 |
|
3 | 3 | pub use crate::registers::segmentation::SegmentSelector; |
4 | | -use crate::structures::tss::TaskStateSegment; |
| 4 | +use crate::structures::tss::{InvalidIoMap, TaskStateSegment}; |
5 | 5 | use crate::PrivilegeLevel; |
6 | 6 | use bit_field::BitField; |
7 | 7 | use bitflags::bitflags; |
8 | | -use core::fmt; |
| 8 | +use core::{cmp, fmt, mem}; |
9 | 9 | // imports for intra-doc links |
10 | 10 | #[cfg(doc)] |
11 | 11 | use crate::registers::segmentation::{Segment, CS, SS}; |
@@ -439,17 +439,90 @@ impl Descriptor { |
439 | 439 | /// being used. |
440 | 440 | #[inline] |
441 | 441 | 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 { |
442 | 512 | use self::DescriptorFlags as Flags; |
443 | | - use core::mem::size_of; |
444 | 513 |
|
445 | 514 | let ptr = tss as u64; |
446 | 515 |
|
447 | 516 | let mut low = Flags::PRESENT.bits(); |
448 | 517 | // base |
449 | 518 | low.set_bits(16..40, ptr.get_bits(0..24)); |
450 | 519 | 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 | + ); |
453 | 526 | // type (0b1001 = available 64-bit tss) |
454 | 527 | low.set_bits(40..44, 0b1001); |
455 | 528 |
|
|
0 commit comments