17030b15eSMuminul Islam // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 27030b15eSMuminul Islam // 37030b15eSMuminul Islam // Copyright © 2023, Microsoft Corporation 47030b15eSMuminul Islam // 57030b15eSMuminul Islam use range_map_vec::{Entry, RangeMap}; 67030b15eSMuminul Islam use thiserror::Error; 77030b15eSMuminul Islam use vm_memory::bitmap::AtomicBitmap; 87030b15eSMuminul Islam use vm_memory::{ 97030b15eSMuminul Islam Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestMemoryMmap, 107030b15eSMuminul Islam GuestMemoryRegion, 117030b15eSMuminul Islam }; 127030b15eSMuminul Islam 13*88a9f799SRob Bradford use crate::igvm::{BootPageAcceptance, StartupMemoryType, HV_PAGE_SIZE}; 14*88a9f799SRob Bradford 157030b15eSMuminul Islam /// Structure to hold the guest memory info/layout to check 167030b15eSMuminul Islam /// the if the memory is accepted within the layout. 177030b15eSMuminul Islam /// Adds up the total bytes written to the guest memory 187030b15eSMuminul Islam pub struct Loader { 197030b15eSMuminul Islam memory: GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>, 207030b15eSMuminul Islam accepted_ranges: RangeMap<u64, BootPageAcceptance>, 217030b15eSMuminul Islam bytes_written: u64, 227030b15eSMuminul Islam } 237030b15eSMuminul Islam 247030b15eSMuminul Islam #[derive(Debug)] 257030b15eSMuminul Islam pub struct ImportRegion { 267030b15eSMuminul Islam pub page_base: u64, 277030b15eSMuminul Islam pub page_count: u64, 287030b15eSMuminul Islam pub acceptance: BootPageAcceptance, 297030b15eSMuminul Islam } 307030b15eSMuminul Islam 317030b15eSMuminul Islam #[derive(Debug, Error)] 327030b15eSMuminul Islam pub enum Error { 337030b15eSMuminul Islam #[error("overlaps with existing import region {0:?}")] 347030b15eSMuminul Islam OverlapsExistingRegion(ImportRegion), 357030b15eSMuminul Islam #[error("memory unavailable")] 367030b15eSMuminul Islam MemoryUnavailable, 377030b15eSMuminul Islam #[error("failed to import pages")] 387030b15eSMuminul Islam ImportPagesFailed, 397030b15eSMuminul Islam #[error("invalid vp context memory")] 407030b15eSMuminul Islam InvalidVpContextMemory(&'static str), 417030b15eSMuminul Islam #[error("data larger than imported region")] 427030b15eSMuminul Islam DataTooLarge, 437030b15eSMuminul Islam } 447030b15eSMuminul Islam 457030b15eSMuminul Islam impl Loader { new(memory: GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>) -> Loader467030b15eSMuminul Islam pub fn new(memory: GuestMemoryAtomic<GuestMemoryMmap<AtomicBitmap>>) -> Loader { 477030b15eSMuminul Islam Loader { 487030b15eSMuminul Islam memory, 497030b15eSMuminul Islam accepted_ranges: RangeMap::new(), 507030b15eSMuminul Islam bytes_written: 0, 517030b15eSMuminul Islam } 527030b15eSMuminul Islam } 537030b15eSMuminul Islam 547030b15eSMuminul Islam /// Accept a new page range with a given acceptance into the map of accepted ranges. accept_new_range( &mut self, page_base: u64, page_count: u64, acceptance: BootPageAcceptance, ) -> Result<(), Error>557030b15eSMuminul Islam pub fn accept_new_range( 567030b15eSMuminul Islam &mut self, 577030b15eSMuminul Islam page_base: u64, 587030b15eSMuminul Islam page_count: u64, 597030b15eSMuminul Islam acceptance: BootPageAcceptance, 607030b15eSMuminul Islam ) -> Result<(), Error> { 617030b15eSMuminul Islam let page_end = page_base + page_count - 1; 627030b15eSMuminul Islam match self.accepted_ranges.entry(page_base..=page_end) { 637030b15eSMuminul Islam Entry::Overlapping(entry) => { 647030b15eSMuminul Islam let &(overlap_start, overlap_end, overlap_acceptance) = entry.get(); 657030b15eSMuminul Islam 667030b15eSMuminul Islam Err(Error::OverlapsExistingRegion(ImportRegion { 677030b15eSMuminul Islam page_base: overlap_start, 687030b15eSMuminul Islam page_count: overlap_end - overlap_start + 1, 697030b15eSMuminul Islam acceptance: overlap_acceptance, 707030b15eSMuminul Islam })) 717030b15eSMuminul Islam } 727030b15eSMuminul Islam Entry::Vacant(entry) => { 737030b15eSMuminul Islam entry.insert(acceptance); 747030b15eSMuminul Islam Ok(()) 757030b15eSMuminul Islam } 767030b15eSMuminul Islam } 777030b15eSMuminul Islam } 787030b15eSMuminul Islam import_pages( &mut self, page_base: u64, page_count: u64, acceptance: BootPageAcceptance, data: &[u8], ) -> Result<(), Error>797030b15eSMuminul Islam pub fn import_pages( 807030b15eSMuminul Islam &mut self, 817030b15eSMuminul Islam page_base: u64, 827030b15eSMuminul Islam page_count: u64, 837030b15eSMuminul Islam acceptance: BootPageAcceptance, 847030b15eSMuminul Islam data: &[u8], 857030b15eSMuminul Islam ) -> Result<(), Error> { 869b84c6c3SMuminul Islam // Once we are here at this point, we have a page with 879b84c6c3SMuminul Islam // some data or empty, empty does not mean there is no data, 889b84c6c3SMuminul Islam // it rather means it's full of zeros. We can skip writing the 899b84c6c3SMuminul Islam // data as the guest page is already zeroed. So we return with 909b84c6c3SMuminul Islam // updating the bytes_written variable 919b84c6c3SMuminul Islam if data.is_empty() { 929b84c6c3SMuminul Islam self.bytes_written += page_count * HV_PAGE_SIZE; 939b84c6c3SMuminul Islam return Ok(()); 949b84c6c3SMuminul Islam } 957030b15eSMuminul Islam // Page count must be larger or equal to data. 967030b15eSMuminul Islam if page_count * HV_PAGE_SIZE < data.len() as u64 { 977030b15eSMuminul Islam return Err(Error::DataTooLarge); 987030b15eSMuminul Islam } 997030b15eSMuminul Islam 1007030b15eSMuminul Islam // Track accepted ranges for duplicate imports. 1017030b15eSMuminul Islam self.accept_new_range(page_base, page_count, acceptance)?; 1027030b15eSMuminul Islam 1037030b15eSMuminul Islam let bytes_written = self 1047030b15eSMuminul Islam .memory 1057030b15eSMuminul Islam .memory() 1067030b15eSMuminul Islam .write(data, GuestAddress(page_base * HV_PAGE_SIZE)) 1077030b15eSMuminul Islam .map_err(|_e| { 1087030b15eSMuminul Islam debug!("Importing pages failed due to MemoryError"); 1097030b15eSMuminul Islam Error::MemoryUnavailable 1107030b15eSMuminul Islam })?; 1119b84c6c3SMuminul Islam 1129b84c6c3SMuminul Islam // A page could be partially filled and the rest of the content is zero. 1139b84c6c3SMuminul Islam // Our IGVM generation tool only fills data here if there is some data without zeros. 1149b84c6c3SMuminul Islam // Rest of them are padded. We only write data without padding and compare whether we 1159b84c6c3SMuminul Islam // complete writing the buffer content. Still it's a full page and update the variable 1169b84c6c3SMuminul Islam // with length of the page. 1179b84c6c3SMuminul Islam if bytes_written != data.len() { 1187030b15eSMuminul Islam return Err(Error::ImportPagesFailed); 1197030b15eSMuminul Islam } 1209b84c6c3SMuminul Islam self.bytes_written += page_count * HV_PAGE_SIZE; 1217030b15eSMuminul Islam Ok(()) 1227030b15eSMuminul Islam } 1237030b15eSMuminul Islam verify_startup_memory_available( &mut self, page_base: u64, page_count: u64, memory_type: StartupMemoryType, ) -> Result<(), Error>1247030b15eSMuminul Islam pub fn verify_startup_memory_available( 1257030b15eSMuminul Islam &mut self, 1267030b15eSMuminul Islam page_base: u64, 1277030b15eSMuminul Islam page_count: u64, 1287030b15eSMuminul Islam memory_type: StartupMemoryType, 1297030b15eSMuminul Islam ) -> Result<(), Error> { 1307030b15eSMuminul Islam if memory_type != StartupMemoryType::Ram { 1317030b15eSMuminul Islam return Err(Error::MemoryUnavailable); 1327030b15eSMuminul Islam } 1337030b15eSMuminul Islam 1347030b15eSMuminul Islam let mut memory_found = false; 1357030b15eSMuminul Islam 1367030b15eSMuminul Islam for range in self.memory.memory().iter() { 1377030b15eSMuminul Islam // Today, the memory layout only describes normal ram and mmio. Thus the memory 1387030b15eSMuminul Islam // request must live completely within a single range, since any gaps are mmio. 1397030b15eSMuminul Islam let base_address = page_base * HV_PAGE_SIZE; 1407030b15eSMuminul Islam let end_address = base_address + (page_count * HV_PAGE_SIZE) - 1; 1417030b15eSMuminul Islam 1427030b15eSMuminul Islam if base_address >= range.start_addr().0 && base_address < range.last_addr().0 { 1437030b15eSMuminul Islam if end_address > range.last_addr().0 { 1447030b15eSMuminul Islam debug!("startup memory end bigger than the current range"); 1457030b15eSMuminul Islam return Err(Error::MemoryUnavailable); 1467030b15eSMuminul Islam } 1477030b15eSMuminul Islam 1487030b15eSMuminul Islam memory_found = true; 1497030b15eSMuminul Islam } 1507030b15eSMuminul Islam } 1517030b15eSMuminul Islam 1527030b15eSMuminul Islam if memory_found { 1537030b15eSMuminul Islam Ok(()) 1547030b15eSMuminul Islam } else { 1557030b15eSMuminul Islam debug!("no valid memory range available for startup memory verify"); 1567030b15eSMuminul Islam Err(Error::MemoryUnavailable) 1577030b15eSMuminul Islam } 1587030b15eSMuminul Islam } 1597030b15eSMuminul Islam } 160