use crate::cuda_ffi::{self, AccessFlags, DevicePtr};
use crate::error::{Result, VmmError};
use crate::physical_memory::PhysicalMemoryHandle;
pub struct VirtualAddressRange {
ptr: DevicePtr,
size: usize,
alignment: usize,
}
impl VirtualAddressRange {
pub fn new(size: usize, alignment: usize) -> Result<Self> {
if !alignment.is_power_of_two() {
return Err(VmmError::InvalidAlignment {
actual: alignment,
required: 0, });
}
let ptr = unsafe { cuda_ffi::mem_address_reserve(size, alignment, 0)? };
Ok(Self {
ptr,
size,
alignment,
})
}
pub unsafe fn from_raw(ptr: DevicePtr, size: usize, alignment: usize) -> Self {
Self {
ptr,
size,
alignment,
}
}
pub fn base_address(&self) -> usize {
self.ptr as usize
}
pub fn size(&self) -> usize {
self.size
}
pub fn alignment(&self) -> usize {
self.alignment
}
pub fn as_ptr(&self) -> DevicePtr {
self.ptr
}
pub fn ptr_at_offset(&self, offset: usize) -> Result<DevicePtr> {
if offset >= self.size {
return Err(VmmError::InvalidOffset {
offset,
size: 0,
capacity: self.size,
});
}
Ok(self.ptr + offset as u64)
}
pub fn into_raw(self) -> (DevicePtr, usize) {
let ptr = self.ptr;
let size = self.size;
std::mem::forget(self); (ptr, size)
}
}
impl Drop for VirtualAddressRange {
fn drop(&mut self) {
unsafe {
if let Err(e) = cuda_ffi::mem_address_free(self.ptr, self.size) {
eprintln!("Warning: Failed to free virtual address space: {}", e);
}
}
}
}
unsafe impl Send for VirtualAddressRange {}
pub fn map_memory(
virtual_range: &VirtualAddressRange,
offset: usize,
physical_handle: &PhysicalMemoryHandle,
physical_offset: usize,
size: usize,
) -> Result<()> {
if offset + size > virtual_range.size() {
return Err(VmmError::InvalidOffset {
offset,
size,
capacity: virtual_range.size(),
});
}
if physical_offset + size > physical_handle.size() {
return Err(VmmError::InvalidOffset {
offset: physical_offset,
size,
capacity: physical_handle.size(),
});
}
let ptr = virtual_range.ptr_at_offset(offset)?;
unsafe {
cuda_ffi::mem_map(ptr, size, physical_offset, physical_handle.as_raw())?;
}
Ok(())
}
pub fn unmap_memory(virtual_range: &VirtualAddressRange, offset: usize, size: usize) -> Result<()> {
if offset + size > virtual_range.size() {
return Err(VmmError::InvalidOffset {
offset,
size,
capacity: virtual_range.size(),
});
}
let ptr = virtual_range.ptr_at_offset(offset)?;
unsafe {
cuda_ffi::mem_unmap(ptr, size)?;
}
Ok(())
}
pub fn set_memory_access(
virtual_range: &VirtualAddressRange,
offset: usize,
size: usize,
device_ordinal: i32,
flags: AccessFlags,
) -> Result<()> {
if offset + size > virtual_range.size() {
return Err(VmmError::InvalidOffset {
offset,
size,
capacity: virtual_range.size(),
});
}
let ptr = virtual_range.ptr_at_offset(offset)?;
unsafe {
cuda_ffi::mem_set_access(ptr, size, device_ordinal, flags)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_virtual_address_range_properties() {
let range = unsafe { VirtualAddressRange::from_raw(0x1000000, 1024 * 1024, 4096) };
assert_eq!(range.base_address(), 0x1000000);
assert_eq!(range.size(), 1024 * 1024);
assert_eq!(range.alignment(), 4096);
std::mem::forget(range);
}
#[test]
fn test_ptr_at_offset() {
let range = unsafe { VirtualAddressRange::from_raw(0x1000000, 1024 * 1024, 4096) };
let ptr = range.ptr_at_offset(4096).unwrap();
assert_eq!(ptr, 0x1000000 + 4096);
assert!(range.ptr_at_offset(2 * 1024 * 1024).is_err());
std::mem::forget(range);
}
#[test]
fn test_invalid_alignment() {
}
}