Skip to content

Commit 3e0d4a4

Browse files
draft changes to address the DMA base address range issue
1 parent a68be63 commit 3e0d4a4

File tree

1 file changed

+120
-4
lines changed
  • vm/devices/storage/ide/src

1 file changed

+120
-4
lines changed

vm/devices/storage/ide/src/lib.rs

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ use thiserror::Error;
4949
use vmcore::device_state::ChangeDeviceState;
5050
use vmcore::line_interrupt::LineInterrupt;
5151
use zerocopy::IntoBytes;
52+
//use guestmem::GuestMemoryErrorKind;
5253

5354
open_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

Comments
 (0)