@@ -49,6 +49,7 @@ use thiserror::Error;
4949use vmcore:: device_state:: ChangeDeviceState ;
5050use vmcore:: line_interrupt:: LineInterrupt ;
5151use zerocopy:: IntoBytes ;
52+ //use guestmem::GuestMemoryErrorKind;
5253
5354open_enum ! {
5455 pub enum IdeIoPort : u16 {
@@ -708,15 +709,16 @@ impl Channel {
708709 return ;
709710 }
710711
711- let mut dma_avail = match drive. dma_request ( ) {
712+ let ( dma_type , mut dma_avail) = match drive. dma_request ( ) {
712713 Some ( ( dma_type, avail) ) if * dma_type == self . bus_master_state . dma_io_type ( ) => {
713- avail as u32
714+ ( Some ( * dma_type ) , avail as u32 )
714715 }
715716 _ => {
716717 // No active, appropriate DMA buffer.
717718 return ;
718719 }
719720 } ;
721+ tracing:: trace!( dma_type = ?dma_type, "DMA TYPE here" ) ;
720722 let Some ( dma) = & mut self . bus_master_state . dma_state else {
721723 return ;
722724 } ;
@@ -738,6 +740,9 @@ impl Channel {
738740 . wrapping_add ( 8 * ( dma. descriptor_idx as u32 ) )
739741 . into ( ) ;
740742
743+ tracing:: trace!( gm = ?self . guest_memory, "guest_memory" ) ;
744+
745+ tracing:: trace!( desc_addr = ?descriptor_addr, "desc_addr" ) ;
741746 let cur_desc_table_entry = match self
742747 . guest_memory
743748 . read_plain :: < protocol:: BusMasterDmaDesc > ( descriptor_addr)
@@ -764,8 +769,44 @@ impl Channel {
764769 dma. transfer_bytes_left = 0x10000 ;
765770 }
766771
767- dma. transfer_base_addr = cur_desc_table_entry. mem_physical_base . into ( ) ;
772+ // Check that the base address is within the guest's physical address space.
773+ // This is a sanity check, the guest should not be able to program the DMA
774+ // controller with an invalid address.
775+
776+ if let Some ( dma_type) = dma_type {
777+ let end_addr = cur_desc_table_entry
778+ . mem_physical_base
779+ . checked_add ( dma. transfer_bytes_left ) ;
780+ let r = match ( dma_type, end_addr) {
781+ ( DmaType :: Read , Some ( end) ) => {
782+ self . guest_memory
783+ . probe_gpa_readable ( end. into ( ) )
784+ } ,
785+ ( DmaType :: Write , Some ( end) ) => {
786+ self . guest_memory
787+ . probe_gpa_writable ( end. into ( ) )
788+ } ,
789+ ( _, None ) => Err ( guestmem:: GuestMemoryErrorKind :: OutOfRange ) ,
790+ } ;
791+ if let Err ( err) = r {
792+ // If there is an error and there is no other IO in parallel,
793+ // we need to stop the current DMA transfer and set the error bit
794+ // in the Bus Master Status register.
795+
796+ self . bus_master_state . dma_state = None ;
797+ if !drive. handle_read_dma_descriptor_error ( ) {
798+ self . bus_master_state . dma_error = true ;
799+ }
768800
801+ tracelimit:: error_ratelimited!(
802+ error = ?err,
803+ "dma base address out-of-range error"
804+ ) ;
805+ return ;
806+ } ;
807+ }
808+
809+ dma. transfer_base_addr = cur_desc_table_entry. mem_physical_base . into ( ) ;
769810 dma. transfer_complete = ( cur_desc_table_entry. end_of_table & 0x80 ) != 0 ;
770811
771812 // Increment to the next descriptor.
@@ -795,7 +836,7 @@ impl Channel {
795836 drive. set_prd_exhausted ( ) ;
796837 drive. dma_advance_buffer ( dma_avail as usize ) ;
797838 }
798- tracing:: trace!( "dma transfer is complete" ) ;
839+ tracing:: trace!( dma_avail = ?dma_avail , "dma transfer is complete" ) ;
799840 self . bus_master_state . dma_state = None ;
800841 break ;
801842 }
@@ -1172,12 +1213,16 @@ impl Channel {
11721213 } ;
11731214
11741215 let status = self . drive_status ( drive_index) ;
1216+
1217+ tracing:: trace!( ?status, "post driveaccess" ) ;
1218+
11751219 let completed = match self . drive_type ( drive_index) {
11761220 DriveType :: Hard => !( status. bsy ( ) || status. drq ( ) ) ,
11771221 DriveType :: Optical => status. drdy ( ) ,
11781222 } ;
11791223 if completed {
11801224 // The command is done.
1225+ tracing:: trace!( completed, "post_drive_access: completed" ) ;
11811226 let write = self . enlightened_write . take ( ) . unwrap ( ) ;
11821227 match write {
11831228 EnlightenedWrite :: Hard ( write) => {
@@ -1251,6 +1296,9 @@ impl Channel {
12511296 // Save this for restoring in the enlightened path.
12521297 self . state . shadow_adapter_control_reg = data;
12531298 let v = DeviceControlReg :: from_bits_truncate ( data) ;
1299+
1300+ tracing:: trace!( Reset = ?v. reset( ) , ?v, "Reset set" ) ;
1301+
12541302 if v. reset ( ) && ( self . drives [ 0 ] . is_some ( ) || self . drives [ 1 ] . is_some ( ) ) {
12551303 self . state = ChannelState :: default ( ) ;
12561304 }
@@ -2351,6 +2399,74 @@ mod tests {
23512399 assert_eq ! ( buffer, file_contents. as_bytes( ) [ ..buffer. len( ) ] ) ;
23522400 }
23532401
2402+ #[ async_test]
2403+ async fn enlightened_cmd_test_invalid_dma_base ( ) {
2404+ /*
2405+ This is a negative test case where the DMA base address is invalid.
2406+ The test sets the DMA base address to an out-of-bounds memory
2407+ address of the guest range and expects the device to not read any data.
2408+ */
2409+ const SECTOR_COUNT : u16 = 8 ;
2410+ const BYTE_COUNT : u16 = SECTOR_COUNT * protocol:: HARD_DRIVE_SECTOR_BYTES as u16 ;
2411+
2412+ let test_guest_mem = GuestMemory :: allocate ( 16384 ) ;
2413+
2414+ let table_gpa = 0x1000 ;
2415+ let data_gpa = 0x4000 ; // Invalid out-of-bounds memory address
2416+ test_guest_mem
2417+ . write_plain (
2418+ table_gpa,
2419+ & BusMasterDmaDesc {
2420+ mem_physical_base : data_gpa,
2421+ byte_count : BYTE_COUNT / 2 ,
2422+ unused : 0 ,
2423+ end_of_table : 0x80 ,
2424+ } ,
2425+ )
2426+ . unwrap ( ) ;
2427+
2428+ let data_buffer = table_gpa as u32 ;
2429+ let byte_count = 0 ;
2430+
2431+ let eint13_command = protocol:: EnlightenedInt13Command {
2432+ command : IdeCommand :: WRITE_DMA_ALT ,
2433+ device_head : DeviceHeadReg :: new ( ) . with_lba ( true ) ,
2434+ flags : 0 ,
2435+ result_status : 0 ,
2436+ lba_low : 0 ,
2437+ lba_high : 0 ,
2438+ block_count : SECTOR_COUNT ,
2439+ byte_count,
2440+ data_buffer,
2441+ skip_bytes_head : 0 ,
2442+ skip_bytes_tail : 0 ,
2443+ } ;
2444+ test_guest_mem. write_plain ( 0 , & eint13_command) . unwrap ( ) ;
2445+
2446+ let dev_path = IdePath :: default ( ) ;
2447+ let ( mut ide_device, _disk, _file_contents, _geometry) =
2448+ ide_test_setup ( Some ( test_guest_mem. clone ( ) ) , DriveType :: Hard ) ;
2449+
2450+ // select device [0,0] = primary channel, primary drive
2451+ device_select ( & mut ide_device, & dev_path) . await ;
2452+ prep_ide_channel ( & mut ide_device, DriveType :: Hard , & dev_path) ;
2453+
2454+ // READ SECTORS - enlightened
2455+ let r = ide_device. io_write ( IdeIoPort :: PRI_ENLIGHTENED . 0 , 0_u32 . as_bytes ( ) ) ; // read from gpa 0
2456+
2457+ match r {
2458+ IoResult :: Defer ( mut deferred) => {
2459+ poll_fn ( |cx| {
2460+ ide_device. poll_device ( cx) ;
2461+ deferred. poll_write ( cx)
2462+ } )
2463+ . await
2464+ . unwrap ( ) ;
2465+ }
2466+ _ => panic ! ( "{:?}" , r) ,
2467+ }
2468+ }
2469+
23542470 #[ async_test]
23552471 async fn identify_test_cd ( ) {
23562472 let dev_path = IdePath :: default ( ) ;
0 commit comments