diff --git a/src/os/uefi/disk.rs b/src/os/uefi/disk.rs index 25b9aa01bb1fa1e0cb5e78d308f07f8ebc19167d..b8ca967ae04a134d1ec75e2f71af81cf107fb6d3 100644 --- a/src/os/uefi/disk.rs +++ b/src/os/uefi/disk.rs @@ -1,11 +1,12 @@ use core::ops::{ControlFlow, Try}; +use core::slice; use redoxfs::{BLOCK_SIZE, Disk}; use syscall::{EIO, Error, Result}; use std::proto::Protocol; use uefi::guid::{Guid, BLOCK_IO_GUID}; use uefi::block_io::BlockIo as UefiBlockIo; -pub struct DiskEfi(pub &'static mut UefiBlockIo); +pub struct DiskEfi(pub &'static mut UefiBlockIo, &'static mut [u8]); impl Protocol<UefiBlockIo> for DiskEfi { fn guid() -> Guid { @@ -13,7 +14,16 @@ impl Protocol<UefiBlockIo> for DiskEfi { } fn new(inner: &'static mut UefiBlockIo) -> Self { - Self(inner) + // Hack to get aligned buffer + let block = unsafe { + let ptr = super::alloc_zeroed_page_aligned(BLOCK_SIZE as usize); + slice::from_raw_parts_mut( + ptr, + BLOCK_SIZE as usize, + ) + }; + + Self(inner, block) } } @@ -31,12 +41,27 @@ impl Disk for DiskEfi { } } - let block_size = self.0.Media.BlockSize as u64; + // Use aligned buffer if necessary + let mut ptr = buffer.as_mut_ptr(); + if self.0.Media.IoAlign != 0 { + if (ptr as usize) % (self.0.Media.IoAlign as usize) != 0 { + if buffer.len() == self.1.len() { + ptr = self.1.as_mut_ptr(); + } + } + } + let block_size = self.0.Media.BlockSize as u64; let lba = block * BLOCK_SIZE / block_size; - match (self.0.ReadBlocks)(self.0, self.0.Media.MediaId, lba, buffer.len(), buffer.as_mut_ptr()).branch() { - ControlFlow::Continue(_) => Ok(buffer.len()), + match (self.0.ReadBlocks)(self.0, self.0.Media.MediaId, lba, buffer.len(), ptr).branch() { + ControlFlow::Continue(_) => { + // Copy to original buffer if using aligned buffer + if ptr != buffer.as_mut_ptr() { + buffer.copy_from_slice(&self.1); + } + Ok(buffer.len()) + }, ControlFlow::Break(err) => { println!("DiskEfi::read_at 0x{:X} failed: {:?}", block, err); Err(Error::new(EIO)) diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs index 87c3a67fc3e407e01cbcd7db19ba53fa06c212c0..f9348e0220b4979c9354fa75b636d9153d1486bb 100644 --- a/src/os/uefi/mod.rs +++ b/src/os/uefi/mod.rs @@ -34,6 +34,36 @@ mod dtb; mod memory_map; mod video_mode; +pub(crate) fn page_size() -> usize { + // EDK2 always uses 4096 as the page size + 4096 +} + +pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 { + assert!(size != 0); + + let page_size = page_size(); + let pages = (size + page_size - 1) / page_size; + + let ptr = { + // Max address mapped by src/arch paging code (8 GiB) + let mut ptr = 0x2_0000_0000; + status_to_result( + (std::system_table().BootServices.AllocatePages)( + 1, // AllocateMaxAddress + MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list + pages, + &mut ptr + ) + ).unwrap(); + ptr as *mut u8 + }; + + assert!(!ptr.is_null()); + unsafe { ptr::write_bytes(ptr, 0, pages * page_size) }; + ptr +} + pub struct OsEfi { st: &'static SystemTable, } @@ -53,32 +83,11 @@ impl Os< } fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 { - assert!(size != 0); - - let page_size = self.page_size(); - let pages = (size + page_size - 1) / page_size; - - let ptr = { - // Max address mapped by src/arch paging code (8 GiB) - let mut ptr = 0x2_0000_0000; - status_to_result( - (self.st.BootServices.AllocatePages)( - 1, // AllocateMaxAddress - MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list - pages, - &mut ptr - ) - ).unwrap(); - ptr as *mut u8 - }; - - assert!(!ptr.is_null()); - unsafe { ptr::write_bytes(ptr, 0, pages * page_size) }; - ptr + alloc_zeroed_page_aligned(size) } fn page_size(&self) -> usize { - 4096 + page_size() } fn filesystem(&self, password_opt: Option<&[u8]>) -> syscall::Result<redoxfs::FileSystem<DiskEfi>> {