use crate::{
emulation::memory::{AddressSpace, MemoryProtection, MemoryRegion},
Result,
};
#[derive(Clone, Debug)]
pub struct MappedRegionInfo {
pub base_address: u64,
pub size: usize,
pub label: String,
pub protection: MemoryProtection,
}
pub struct DataLoader;
impl DataLoader {
pub fn map_at(
address_space: &AddressSpace,
address: u64,
data: &[u8],
label: impl Into<String>,
protection: MemoryProtection,
) -> Result<MappedRegionInfo> {
let label = label.into();
let size = data.len();
let region = MemoryRegion::mapped_data(address, data, label.clone(), protection);
address_space.map_at(address, region)?;
Ok(MappedRegionInfo {
base_address: address,
size,
label,
protection,
})
}
pub fn map(
address_space: &AddressSpace,
data: &[u8],
label: impl Into<String>,
protection: MemoryProtection,
) -> Result<MappedRegionInfo> {
let label = label.into();
let size = data.len();
let region = MemoryRegion::mapped_data(0, data, label.clone(), protection);
let base_address = address_space.map(region)?;
Ok(MappedRegionInfo {
base_address,
size,
label,
protection,
})
}
pub fn map_readonly(
address_space: &AddressSpace,
address: u64,
data: &[u8],
label: impl Into<String>,
) -> Result<MappedRegionInfo> {
Self::map_at(address_space, address, data, label, MemoryProtection::READ)
}
pub fn map_readwrite(
address_space: &AddressSpace,
address: u64,
data: &[u8],
label: impl Into<String>,
) -> Result<MappedRegionInfo> {
Self::map_at(
address_space,
address,
data,
label,
MemoryProtection::READ | MemoryProtection::WRITE,
)
}
pub fn map_executable(
address_space: &AddressSpace,
address: u64,
data: &[u8],
label: impl Into<String>,
) -> Result<MappedRegionInfo> {
Self::map_at(
address_space,
address,
data,
label,
MemoryProtection::READ | MemoryProtection::EXECUTE,
)
}
pub fn map_file(
address_space: &AddressSpace,
path: &std::path::Path,
address: u64,
protection: MemoryProtection,
) -> Result<MappedRegionInfo> {
let data = std::fs::read(path)
.map_err(|e| crate::Error::Other(format!("Failed to read file: {e}")))?;
let label = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("mapped_file")
.to_string();
Self::map_at(address_space, address, &data, label, protection)
}
pub fn map_zeroed(
address_space: &AddressSpace,
address: u64,
size: usize,
label: impl Into<String>,
protection: MemoryProtection,
) -> Result<MappedRegionInfo> {
let data = vec![0u8; size];
Self::map_at(address_space, address, &data, label, protection)
}
}
impl Default for DataLoader {
fn default() -> Self {
Self
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_address_space() -> AddressSpace {
AddressSpace::with_config(1024 * 1024, 0x1_0000_0000)
}
#[test]
fn test_map_at() {
let address_space = create_test_address_space();
let data = vec![0x01, 0x02, 0x03, 0x04];
let info = DataLoader::map_at(
&address_space,
0x10000,
&data,
"test_data",
MemoryProtection::READ | MemoryProtection::WRITE,
)
.unwrap();
assert_eq!(info.base_address, 0x10000);
assert_eq!(info.size, 4);
assert_eq!(info.label, "test_data");
let read_data = address_space.read(0x10000, 4).unwrap();
assert_eq!(read_data, data);
}
#[test]
fn test_map_readonly() {
let address_space = create_test_address_space();
let data = vec![0xDE, 0xAD, 0xBE, 0xEF];
let info = DataLoader::map_readonly(&address_space, 0x20000, &data, "readonly").unwrap();
assert_eq!(info.protection, MemoryProtection::READ);
let read_data = address_space.read(0x20000, 4).unwrap();
assert_eq!(read_data, data);
}
#[test]
fn test_map_zeroed() {
let address_space = create_test_address_space();
let info = DataLoader::map_zeroed(
&address_space,
0x30000,
0x100,
"bss",
MemoryProtection::READ | MemoryProtection::WRITE,
)
.unwrap();
assert_eq!(info.size, 0x100);
let read_data = address_space.read(0x30000, 0x100).unwrap();
assert!(read_data.iter().all(|&b| b == 0));
}
#[test]
fn test_mapped_region_info() {
let info = MappedRegionInfo {
base_address: 0x40000,
size: 0x1000,
label: "test".to_string(),
protection: MemoryProtection::READ | MemoryProtection::EXECUTE,
};
assert_eq!(info.base_address, 0x40000);
assert_eq!(info.size, 0x1000);
assert!(info.protection.contains(MemoryProtection::READ));
assert!(info.protection.contains(MemoryProtection::EXECUTE));
assert!(!info.protection.contains(MemoryProtection::WRITE));
}
}