@@ -9,6 +9,7 @@ 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 } ;
12
+ use crate :: structures:: tss:: InvalidIoMap ;
12
13
13
14
#[ cfg( all( feature = "instructions" , target_arch = "x86_64" ) ) ]
14
15
use core:: sync:: atomic:: { AtomicU64 as EntryValue , Ordering } ;
@@ -439,10 +440,31 @@ impl Descriptor {
439
440
/// being used.
440
441
#[ inline]
441
442
pub unsafe fn tss_segment_unchecked ( tss : * const TaskStateSegment ) -> Descriptor {
442
- // TODO: Remove this with a call to a function that takes a method
443
- // instead of a static reference.
444
443
// SAFETY: if iomap_size is zero, there are no requirements to uphold.
445
- unsafe { Self :: tss_segment_with_iomap ( & * tss, 0 ) }
444
+ unsafe { Self :: tss_segment_raw ( tss, 0 ) }
445
+ }
446
+
447
+ /// Creates a TSS system descriptor for the given TSS, setting up the IO permissions bitmap.
448
+ pub fn tss_segment_with_iomap (
449
+ tss : & ' static TaskStateSegment ,
450
+ iomap : & ' static [ u8 ] ,
451
+ ) -> Result < Descriptor , InvalidIoMap > {
452
+ if iomap. len ( ) > 8193 {
453
+ return Err ( InvalidIoMap :: TooLong { len : iomap. len ( ) } )
454
+ }
455
+
456
+ let distance = iomap. as_ptr ( ) as usize - tss as * const _ as usize ;
457
+ if distance > 0xdfff {
458
+ return Err ( InvalidIoMap :: TooFarFromTss { distance } )
459
+ }
460
+
461
+ let last_byte = * iomap. last ( ) . unwrap_or ( & 0xff ) ;
462
+ if last_byte != 0xff {
463
+ return Err ( InvalidIoMap :: InvalidTerminatingByte { byte : last_byte } )
464
+ }
465
+
466
+ // SAFETY: all invariants checked above
467
+ Ok ( unsafe { Self :: tss_segment_raw ( tss, iomap. len ( ) as u16 ) } )
446
468
}
447
469
448
470
/// Creates a TSS system descriptor for the given TSS, setting up the IO permissions bitmap.
@@ -452,20 +474,20 @@ impl Descriptor {
452
474
/// There must be a valid IO map at `(tss as *const u8).offset(tss.iomap_base)`
453
475
/// of length `iomap_size`, with the terminating `0xFF` byte. Additionally, `iomap_base` must
454
476
/// not exceed `0xDFFF`.
455
- pub unsafe fn tss_segment_with_iomap (
456
- tss : & ' static TaskStateSegment ,
477
+ unsafe fn tss_segment_raw (
478
+ tss : * const TaskStateSegment ,
457
479
iomap_size : u16 ,
458
480
) -> Descriptor {
459
481
use self :: DescriptorFlags as Flags ;
460
482
461
- let ptr = tss as * const _ as u64 ;
483
+ let ptr = tss as u64 ;
462
484
463
485
let mut low = Flags :: PRESENT . bits ( ) ;
464
486
// base
465
487
low. set_bits ( 16 ..40 , ptr. get_bits ( 0 ..24 ) ) ;
466
488
low. set_bits ( 56 ..64 , ptr. get_bits ( 24 ..32 ) ) ;
467
489
// limit (the `-1` is needed since the bound is inclusive)
468
- let iomap_limit = tss. iomap_base as u64 + iomap_size as u64 ;
490
+ let iomap_limit = unsafe { ( * tss) . iomap_base } as u64 + iomap_size as u64 ;
469
491
low. set_bits (
470
492
0 ..16 ,
471
493
cmp:: max ( mem:: size_of :: < TaskStateSegment > ( ) as u64 , iomap_limit) - 1 ,
0 commit comments