From 6f5cee8e71b028253d8a007e4775eae10ec191ae Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 5 Aug 2025 00:06:30 +0800 Subject: [PATCH 01/21] fix(uefi): Split the efi loading logic in uefi runtime. --- src/main.rs | 10 ++++- src/medium/ramdisk_cpio.rs | 8 ++-- src/medium/virtio_disk.rs | 4 +- src/runtime/entry.rs | 11 +++++ src/runtime/loader.rs | 43 ++++++++++++++++++ src/runtime/memory.rs | 27 +++++++++++ src/runtime/mod.rs | 92 ++++++-------------------------------- src/runtime/table.rs | 2 +- src/shell/stdio.rs | 2 +- 9 files changed, 109 insertions(+), 90 deletions(-) create mode 100644 src/runtime/entry.rs create mode 100644 src/runtime/loader.rs create mode 100644 src/runtime/memory.rs diff --git a/src/main.rs b/src/main.rs index 25dfe6a..b71031e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,8 +52,14 @@ pub extern "C" fn rust_main(_cpu_id: usize, _dtb: usize) -> ! { } ctor_bare::call_ctors(); - info!("current root dir: {}", crate::medium::current_dir().unwrap()); - info!("read test file context: {}", crate::medium::read_to_string("/test/arceboot.txt").unwrap()); + info!( + "current root dir: {}", + crate::medium::current_dir().unwrap() + ); + info!( + "read test file context: {}", + crate::medium::read_to_string("/test/arceboot.txt").unwrap() + ); crate::shell::shell_main(); diff --git a/src/medium/ramdisk_cpio.rs b/src/medium/ramdisk_cpio.rs index e92cf4a..9ff4f74 100644 --- a/src/medium/ramdisk_cpio.rs +++ b/src/medium/ramdisk_cpio.rs @@ -1,9 +1,9 @@ extern crate alloc; -use core::{ptr, str}; +use alloc::{string::String, string::ToString, vec::Vec}; use axhal::mem::phys_to_virt; use axio::{self as io, prelude::*}; -use alloc::{string::String, vec::Vec, string::ToString}; +use core::{ptr, str}; const CPIO_MAGIC: &[u8; 6] = b"070701"; const CPIO_BASE: usize = 0x8400_0000; @@ -76,9 +76,7 @@ pub fn read(path: &str) -> io::Result> { }; if is_match { - let data = unsafe { - core::slice::from_raw_parts(file_start as *const u8, filesize) - }; + let data = unsafe { core::slice::from_raw_parts(file_start as *const u8, filesize) }; let mut bytes = Vec::with_capacity(filesize as usize); bytes.extend_from_slice(data); return Ok(bytes); diff --git a/src/medium/virtio_disk.rs b/src/medium/virtio_disk.rs index c1ebae6..68ab0c6 100644 --- a/src/medium/virtio_disk.rs +++ b/src/medium/virtio_disk.rs @@ -1,7 +1,7 @@ extern crate alloc; -use axio::{self as io}; use alloc::{string::String, vec::Vec}; +use axio::{self as io}; /// Returns the current working directory as a [`String`]. pub fn current_dir() -> io::Result { @@ -16,4 +16,4 @@ pub fn read(path: &str) -> io::Result> { /// Read the entire contents of a file into a string. pub fn read_to_string(path: &str) -> io::Result { axfs::api::read_to_string(path) -} \ No newline at end of file +} diff --git a/src/runtime/entry.rs b/src/runtime/entry.rs new file mode 100644 index 0000000..f9214e3 --- /dev/null +++ b/src/runtime/entry.rs @@ -0,0 +1,11 @@ +pub type EfiMainFn = + extern "efiapi" fn(image_handle: *mut core::ffi::c_void, system_table: *mut SystemTable) -> u64; + +use core::mem::transmute; + +use uefi_raw::table::system::SystemTable; + +pub fn resolve_entry_func(mapping: *const u8, entry: u64, base_va: u64) -> EfiMainFn { + let func_addr = (mapping as usize + (entry - base_va) as usize) as *const (); + unsafe { transmute(func_addr) } +} diff --git a/src/runtime/loader.rs b/src/runtime/loader.rs new file mode 100644 index 0000000..383ec6d --- /dev/null +++ b/src/runtime/loader.rs @@ -0,0 +1,43 @@ +extern crate alloc; + +use object::{File, Object, ObjectSection}; + +pub fn load_efi_file(path: &str) -> alloc::vec::Vec { + axfs::api::read(path).expect("Failed to read EFI file from ramdisk") +} + +pub fn parse_efi_file(data: &[u8]) -> File { + File::parse(data).expect("Failed to parse EFI file") +} + +pub fn analyze_sections(file: &File) -> (u64, u64) { + let mut base_va = u64::MAX; + let mut max_va = 0; + + for section in file.sections() { + let size = section.size(); + let start = section.address(); + let end = start + size; + base_va = base_va.min(start); + max_va = max_va.max(end); + } + + (base_va, max_va) +} + +pub fn load_sections(file: &File, mapping: *mut u8, base_va: u64) { + for section in file.sections() { + if let Ok(data) = section.data() { + let offset = (section.address() - base_va) as usize; + info!( + "Loading section {} to offset 0x{:x}, size 0x{:x}", + section.name().unwrap_or(""), + offset, + data.len() + ); + unsafe { + core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.add(offset), data.len()); + } + } + } +} diff --git a/src/runtime/memory.rs b/src/runtime/memory.rs new file mode 100644 index 0000000..a1ecb88 --- /dev/null +++ b/src/runtime/memory.rs @@ -0,0 +1,27 @@ +use axhal::{ + mem::{MemoryAddr, VirtAddr}, + paging::MappingFlags, +}; + +pub fn alloc_and_map_memory(size: usize, source_data: &[u8]) -> *mut u8 { + let layout = core::alloc::Layout::from_size_align(source_data.len(), 4096) + .expect("Invalid layout for shellcode"); + + let ptr = axalloc::global_allocator() + .alloc(layout) + .expect("Failed to allocate memory for shellcode") + .as_ptr(); + + let page_count = (size + 4095) / 4096; + + axmm::kernel_aspace() + .lock() + .protect( + VirtAddr::from_ptr_of(ptr).align_down(4096usize), + page_count * 4096, + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + ) + .expect("Failed to protect EFI memory"); + + ptr +} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 6534ada..2769376 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,96 +1,30 @@ -use core::{ - mem::transmute, - ptr::{copy_nonoverlapping, null_mut}, -}; - -use axhal::{ - mem::{MemoryAddr, VirtAddr}, - paging::MappingFlags, -}; -use object::File; use object::Object; -use object::ObjectSection; -use uefi_raw::table::system::SystemTable; - -extern crate alloc; - -use crate::runtime::table::{get_system_table_raw, init_system_table}; +mod entry; +mod loader; +mod memory; mod protocol; mod service; mod table; -pub type EfiMainFn = - extern "efiapi" fn(image_handle: *mut core::ffi::c_void, system_table: *mut SystemTable) -> u64; - pub fn efi_runtime_init() { - let shellcode = - axfs::api::read("/EFI/BOOT/BOOTRISCV64.EFI").expect("Failed to read EFI file from ramdisk"); - - let file = File::parse(&*shellcode).unwrap(); - let entry = file.entry(); - info!("Entry point: 0x{:x}", entry); - - let mut base_va = u64::MAX; - let mut max_va = 0; - for section in file.sections() { - let size = section.size(); - let start = section.address(); - let end = start + size; - base_va = base_va.min(start); - max_va = max_va.max(end); - } + let load_bootloader = loader::load_efi_file("/EFI/BOOT/BOOTRISCV64.EFI"); + let file = loader::parse_efi_file(&load_bootloader); + let (base_va, max_va) = loader::analyze_sections(&file); let mem_size = (max_va - base_va) as usize; - info!( - "Mapping memory: 0x{:x} bytes at RVA base 0x{:x}", - mem_size, base_va - ); - - let page_count = (mem_size + 4095) / 4096; - - // alloc memory for the EFI image - let flags = MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE; - let layout: core::alloc::Layout = core::alloc::Layout::from_size_align(shellcode.len(), 4096) - .expect("Invalid layout for shellcode"); - - let mapping = axalloc::global_allocator() - .alloc(layout) - .expect("Failed to allocate memory for shellcode"); - let mapping = mapping.as_ptr(); - axmm::kernel_aspace() - .lock() - .protect( - VirtAddr::from_ptr_of(mapping).align_down(4096usize), - page_count * 4096, - flags, - ) - .expect("Failed to protect efi memory"); + let mapping = memory::alloc_and_map_memory(mem_size, &load_bootloader); - for section in file.sections() { - if let Ok(data) = section.data() { - let offset = (section.address() - base_va) as usize; - info!( - "Loading section {} to offset 0x{:x}, size 0x{:x}", - section.name().unwrap_or(""), - offset, - data.len() - ); - unsafe { - copy_nonoverlapping(data.as_ptr(), (mapping as *mut u8).add(offset), data.len()); - } - } - } + loader::load_sections(&file, mapping, base_va); - let func_addr = (mapping as usize + (entry - base_va) as usize) as *const (); - let func: EfiMainFn = unsafe { transmute(func_addr) }; + let func = entry::resolve_entry_func(mapping, file.entry(), base_va); let system_table = { - init_system_table(); - get_system_table_raw() + table::init_system_table(); + table::get_system_table_raw() }; - let result = func(null_mut(), system_table); - info!("efi_main return: {}", result) + let result = func(core::ptr::null_mut(), system_table); + info!("efi_main return: {}", result); } diff --git a/src/runtime/table.rs b/src/runtime/table.rs index 35b9f77..da2565a 100644 --- a/src/runtime/table.rs +++ b/src/runtime/table.rs @@ -52,7 +52,7 @@ pub fn init_system_table() { let configuration_table_raw = Box::into_raw(configuration_table); let system_table = Box::new(SystemTable { - // Build the UEFI Table Header. + // Build the UEFI Table Header. // For the System Table, its signature is 'IBI SYST' (little-endian). // The Header size is the size of the entire Header structure, // and the CRC32 calculation will first fill the CRC32 field with 0 before calculation. diff --git a/src/shell/stdio.rs b/src/shell/stdio.rs index f1a0568..054f68a 100644 --- a/src/shell/stdio.rs +++ b/src/shell/stdio.rs @@ -73,7 +73,7 @@ impl Stdin { if buf.is_empty() || read_len > 0 { return Ok(read_len); } - return Ok(0) + return Ok(0); } } From 7118439a2dd1b8500d2c6a6849351a029ab95056 Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 5 Aug 2025 03:34:24 +0800 Subject: [PATCH 02/21] feat(uefi): supports pe file reloc segment mapping. --- src/runtime/loader.rs | 58 ++++++++++++++++++++++++++++++++++++++++++- src/runtime/mod.rs | 5 ++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/runtime/loader.rs b/src/runtime/loader.rs index 383ec6d..f9f2130 100644 --- a/src/runtime/loader.rs +++ b/src/runtime/loader.rs @@ -1,6 +1,8 @@ extern crate alloc; -use object::{File, Object, ObjectSection}; +use object::FileKind; +use object::read::pe::ImageOptionalHeader; +use object::{File, Object, ObjectSection, pe, read::pe::ImageNtHeaders}; pub fn load_efi_file(path: &str) -> alloc::vec::Vec { axfs::api::read(path).expect("Failed to read EFI file from ramdisk") @@ -10,6 +12,24 @@ pub fn parse_efi_file(data: &[u8]) -> File { File::parse(data).expect("Failed to parse EFI file") } +pub fn get_pe_image_base(data: &[u8]) -> Option { + let mut offset = pe::ImageDosHeader::parse(data) + .ok()? + .nt_headers_offset() + .into(); + let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?; + Some(nt_headers.optional_header().image_base()) +} + +pub fn detect_and_get_image_base(data: &[u8]) -> Option { + let kind = FileKind::parse(data).ok()?; + match kind { + FileKind::Pe32 => get_pe_image_base::(data), + FileKind::Pe64 => get_pe_image_base::(data), + _ => None, + } +} + pub fn analyze_sections(file: &File) -> (u64, u64) { let mut base_va = u64::MAX; let mut max_va = 0; @@ -41,3 +61,39 @@ pub fn load_sections(file: &File, mapping: *mut u8, base_va: u64) { } } } + +pub fn apply_relocations(file: &File, mapping: *mut u8, loaded_base: u64, original_base: u64) { + let delta = loaded_base as i64 - original_base as i64; + + for section in file.sections() { + if section.name().unwrap_or("") == ".reloc" { + let data = section.data().unwrap(); + let mut offset = 0; + while offset < data.len() { + let va = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap()); + let size = u32::from_le_bytes(data[offset + 4..offset + 8].try_into().unwrap()); + offset += 8; + + let count = (size - 8) / 2; + for _ in 0..count { + let entry = u16::from_le_bytes(data[offset..offset + 2].try_into().unwrap()); + offset += 2; + + let rtype = entry >> 12; + let roffset = entry & 0x0fff; + + if rtype == 3 { + // IMAGE_REL_BASED_HIGHLOW + let patch_va = va as u64 + roffset as u64; + let patch_ptr = + unsafe { mapping.add((patch_va - loaded_base) as usize) as *mut u32 }; + unsafe { + let orig = patch_ptr.read(); + patch_ptr.write(orig.wrapping_add(delta as u32)); + } + } + } + } + } + } +} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 2769376..563acb8 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -9,6 +9,8 @@ mod table; pub fn efi_runtime_init() { let load_bootloader = loader::load_efi_file("/EFI/BOOT/BOOTRISCV64.EFI"); + let image_base = + loader::detect_and_get_image_base(&load_bootloader).expect("Failed to get PE image base"); let file = loader::parse_efi_file(&load_bootloader); let (base_va, max_va) = loader::analyze_sections(&file); @@ -17,6 +19,9 @@ pub fn efi_runtime_init() { let mapping = memory::alloc_and_map_memory(mem_size, &load_bootloader); loader::load_sections(&file, mapping, base_va); + loader::apply_relocations(&file, mapping, base_va, image_base); + + info!("Loaded EFI file with base VA: 0x{:x}, max VA: 0x{:x}, image base {:x}", base_va, max_va, image_base); let func = entry::resolve_entry_func(mapping, file.entry(), base_va); From ea50b28517e0de8c8fc1969f26c5bddbbf33789d Mon Sep 17 00:00:00 2001 From: hanbings Date: Wed, 6 Aug 2025 22:43:10 +0800 Subject: [PATCH 03/21] fix(uefi): fix access that caused a page fault exception. --- Cargo.toml | 2 +- src/runtime/loader.rs | 38 ++++++++++++++++++++++++++++++-------- src/runtime/mod.rs | 5 ++++- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7105bd..0d6122e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ axio = { version = "0.1", features = ["alloc"] } crate_interface = "0.1" ctor_bare = "0.2" cfg-if = "1.0.1" -object = { version = "0.37.1", default-features = false, features = [ "read" ] } +object = { version = "0.37.1", default-features = false, features = [ "read", "pe" ] } chrono = { version = "0.4.38", default-features = false } uefi-raw = "0.11.0" lazyinit = "0.2.2" diff --git a/src/runtime/loader.rs b/src/runtime/loader.rs index f9f2130..b251ed2 100644 --- a/src/runtime/loader.rs +++ b/src/runtime/loader.rs @@ -69,27 +69,49 @@ pub fn apply_relocations(file: &File, mapping: *mut u8, loaded_base: u64, origin if section.name().unwrap_or("") == ".reloc" { let data = section.data().unwrap(); let mut offset = 0; + while offset < data.len() { + if offset + 8 > data.len() { + break; + } + let va = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap()); let size = u32::from_le_bytes(data[offset + 4..offset + 8].try_into().unwrap()); offset += 8; let count = (size - 8) / 2; for _ in 0..count { + if offset + 2 > data.len() { + break; + } + let entry = u16::from_le_bytes(data[offset..offset + 2].try_into().unwrap()); offset += 2; let rtype = entry >> 12; let roffset = entry & 0x0fff; - if rtype == 3 { - // IMAGE_REL_BASED_HIGHLOW - let patch_va = va as u64 + roffset as u64; - let patch_ptr = - unsafe { mapping.add((patch_va - loaded_base) as usize) as *mut u32 }; - unsafe { - let orig = patch_ptr.read(); - patch_ptr.write(orig.wrapping_add(delta as u32)); + let patch_va = va as u64 + roffset as u64; + let patch_offset = (patch_va - loaded_base) as usize; + + match rtype { + 10 => { // IMAGE_REL_BASED_DIR64 + let patch_ptr = unsafe { mapping.add(patch_offset) as *mut u64 }; + unsafe { + let orig = patch_ptr.read(); + patch_ptr.write(orig.wrapping_add(delta as u64)); + } + } + 3 => { // IMAGE_REL_BASED_HIGHLOW (32-bit) + let patch_ptr = unsafe { mapping.add(patch_offset) as *mut u32 }; + unsafe { + let orig = patch_ptr.read(); + patch_ptr.write(orig.wrapping_add(delta as u32)); + } + } + 0 => { /* IMAGE_REL_BASED_ABSOLUTE, do nothing */ } + _ => { + warn!("Unsupported relocation type: {}", rtype); } } } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 563acb8..b4d8937 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -21,7 +21,10 @@ pub fn efi_runtime_init() { loader::load_sections(&file, mapping, base_va); loader::apply_relocations(&file, mapping, base_va, image_base); - info!("Loaded EFI file with base VA: 0x{:x}, max VA: 0x{:x}, image base {:x}", base_va, max_va, image_base); + info!( + "Loaded EFI file with base VA: 0x{:x}, max VA: 0x{:x}, image base {:x}, mapping {:?}, entry: 0x{:x}", + base_va, max_va, image_base, mapping, file.entry() + ); let func = entry::resolve_entry_func(mapping, file.entry(), base_va); From e2dd1e362fd775a9e8642d6d78c5ceb0344c5df7 Mon Sep 17 00:00:00 2001 From: hanbings Date: Thu, 7 Aug 2025 12:07:07 +0800 Subject: [PATCH 04/21] feat(uefi): declare necessary ffi for the protocols. --- src/runtime/loader.rs | 6 +- src/runtime/mod.rs | 6 +- src/runtime/protocol/block_io.rs | 80 ++++++++ src/runtime/protocol/device_path.rs | 188 +++++++++++++++++++ src/runtime/protocol/mod.rs | 2 + src/runtime/protocol/simple_file_system.rs | 207 +++++++++++++++++++++ 6 files changed, 486 insertions(+), 3 deletions(-) create mode 100644 src/runtime/protocol/block_io.rs create mode 100644 src/runtime/protocol/device_path.rs diff --git a/src/runtime/loader.rs b/src/runtime/loader.rs index b251ed2..3f70cb9 100644 --- a/src/runtime/loader.rs +++ b/src/runtime/loader.rs @@ -95,14 +95,16 @@ pub fn apply_relocations(file: &File, mapping: *mut u8, loaded_base: u64, origin let patch_offset = (patch_va - loaded_base) as usize; match rtype { - 10 => { // IMAGE_REL_BASED_DIR64 + 10 => { + // IMAGE_REL_BASED_DIR64 let patch_ptr = unsafe { mapping.add(patch_offset) as *mut u64 }; unsafe { let orig = patch_ptr.read(); patch_ptr.write(orig.wrapping_add(delta as u64)); } } - 3 => { // IMAGE_REL_BASED_HIGHLOW (32-bit) + 3 => { + // IMAGE_REL_BASED_HIGHLOW (32-bit) let patch_ptr = unsafe { mapping.add(patch_offset) as *mut u32 }; unsafe { let orig = patch_ptr.read(); diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index b4d8937..15a1277 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -23,7 +23,11 @@ pub fn efi_runtime_init() { info!( "Loaded EFI file with base VA: 0x{:x}, max VA: 0x{:x}, image base {:x}, mapping {:?}, entry: 0x{:x}", - base_va, max_va, image_base, mapping, file.entry() + base_va, + max_va, + image_base, + mapping, + file.entry() ); let func = entry::resolve_entry_func(mapping, file.entry(), base_va); diff --git a/src/runtime/protocol/block_io.rs b/src/runtime/protocol/block_io.rs new file mode 100644 index 0000000..4a189a9 --- /dev/null +++ b/src/runtime/protocol/block_io.rs @@ -0,0 +1,80 @@ +use core::ffi::c_void; + +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Boolean, Status, + protocol::block::{BlockIoMedia, BlockIoProtocol, Lba}, +}; + +extern crate alloc; +use alloc::boxed::Box; + +static BLOCK_IO: LazyInit> = LazyInit::new(); + +#[derive(Debug)] +pub struct BlockIo { + protocol: &'static mut BlockIoProtocol, + protocol_raw: *mut BlockIoProtocol, +} + +impl BlockIo { + pub fn new() -> Self { + let media: *const BlockIoMedia = core::ptr::null(); // stub; replace with actual media if needed + + let protocol = BlockIoProtocol { + revision: 0x00010000, + media, + reset, + read_blocks, + write_blocks, + flush_blocks, + }; + + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut BlockIoProtocol { + self.protocol_raw + } +} + +unsafe impl Send for BlockIo {} +unsafe impl Sync for BlockIo {} + +pub extern "efiapi" fn reset( + _this: *mut BlockIoProtocol, + _extended_verification: Boolean, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn read_blocks( + _this: *const BlockIoProtocol, + _media_id: u32, + _lba: Lba, + _buffer_size: usize, + _buffer: *mut c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn write_blocks( + _this: *mut BlockIoProtocol, + _media_id: u32, + _lba: Lba, + _buffer_size: usize, + _buffer: *const c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn flush_blocks(_this: *mut BlockIoProtocol) -> Status { + Status::UNSUPPORTED +} diff --git a/src/runtime/protocol/device_path.rs b/src/runtime/protocol/device_path.rs new file mode 100644 index 0000000..e6583fd --- /dev/null +++ b/src/runtime/protocol/device_path.rs @@ -0,0 +1,188 @@ +use core::ptr::null_mut; + +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Boolean, Char16, + protocol::device_path::{ + DevicePathFromTextProtocol, DevicePathProtocol, DevicePathToTextProtocol, + DevicePathUtilitiesProtocol, DeviceSubType, DeviceType, + }, +}; + +extern crate alloc; +use alloc::boxed::Box; + +static DEVICE_PATH_TO_TEXT: LazyInit> = LazyInit::new(); +static DEVICE_PATH_FROM_TEXT: LazyInit> = LazyInit::new(); +static DEVICE_PATH_UTILITIES: LazyInit> = LazyInit::new(); + +#[derive(Debug)] +pub struct DevicePathToText { + protocol: &'static mut DevicePathToTextProtocol, + protocol_raw: *mut DevicePathToTextProtocol, +} + +impl DevicePathToText { + pub fn new() -> Self { + let protocol = DevicePathToTextProtocol { + convert_device_node_to_text, + convert_device_path_to_text, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut DevicePathToTextProtocol { + self.protocol_raw + } +} + +unsafe impl Send for DevicePathToText {} +unsafe impl Sync for DevicePathToText {} + +pub extern "efiapi" fn convert_device_node_to_text( + _device_node: *const DevicePathProtocol, + _display_only: Boolean, + _allow_shortcuts: Boolean, +) -> *const Char16 { + core::ptr::null() +} + +pub extern "efiapi" fn convert_device_path_to_text( + _device_path: *const DevicePathProtocol, + _display_only: Boolean, + _allow_shortcuts: Boolean, +) -> *const Char16 { + core::ptr::null() +} + +#[derive(Debug)] +pub struct DevicePathFromText { + protocol: &'static mut DevicePathFromTextProtocol, + protocol_raw: *mut DevicePathFromTextProtocol, +} + +impl DevicePathFromText { + pub fn new() -> Self { + let protocol = DevicePathFromTextProtocol { + convert_text_to_device_node, + convert_text_to_device_path, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut DevicePathFromTextProtocol { + self.protocol_raw + } +} + +unsafe impl Send for DevicePathFromText {} +unsafe impl Sync for DevicePathFromText {} + +pub extern "efiapi" fn convert_text_to_device_node( + _text_device_node: *const Char16, +) -> *const DevicePathProtocol { + core::ptr::null() +} + +pub extern "efiapi" fn convert_text_to_device_path( + _text_device_path: *const Char16, +) -> *const DevicePathProtocol { + core::ptr::null() +} + +pub struct DevicePathUtilities { + protocol: &'static mut DevicePathUtilitiesProtocol, + protocol_raw: *mut DevicePathUtilitiesProtocol, +} + +impl DevicePathUtilities { + pub fn new() -> Self { + let protocol = DevicePathUtilitiesProtocol { + get_device_path_size, + duplicate_device_path, + append_device_path, + append_device_node, + append_device_path_instance, + get_next_device_path_instance, + is_device_path_multi_instance, + create_device_node, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + DevicePathUtilities { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut DevicePathUtilitiesProtocol { + let guard = DEVICE_PATH_UTILITIES.lock(); + guard.protocol_raw + } +} + +unsafe impl Send for DevicePathUtilities {} +unsafe impl Sync for DevicePathUtilities {} + +pub extern "efiapi" fn get_device_path_size(_device_path: *const DevicePathProtocol) -> usize { + 0 +} + +pub extern "efiapi" fn duplicate_device_path( + _device_path: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + null_mut() +} + +pub extern "efiapi" fn append_device_path( + _src1: *const DevicePathProtocol, + _src2: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + null_mut() +} + +pub extern "efiapi" fn append_device_node( + _device_path: *const DevicePathProtocol, + _device_node: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + null_mut() +} + +pub extern "efiapi" fn append_device_path_instance( + _device_path: *const DevicePathProtocol, + _device_path_instance: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + null_mut() +} + +pub extern "efiapi" fn get_next_device_path_instance( + _device_path_instance: *mut *const DevicePathProtocol, + _device_path_instance_size: *mut usize, +) -> *const DevicePathProtocol { + null_mut() +} + +pub extern "efiapi" fn is_device_path_multi_instance( + _device_path: *const DevicePathProtocol, +) -> bool { + true +} + +pub extern "efiapi" fn create_device_node( + _node_type: DeviceType, + _node_sub_type: DeviceSubType, + _node_length: u16, +) -> *const DevicePathProtocol { + null_mut() +} diff --git a/src/runtime/protocol/mod.rs b/src/runtime/protocol/mod.rs index 2a17d3a..d08970d 100644 --- a/src/runtime/protocol/mod.rs +++ b/src/runtime/protocol/mod.rs @@ -1,3 +1,5 @@ +pub(crate) mod block_io; +pub(crate) mod device_path; pub(crate) mod graphics_output; pub(crate) mod handle; pub(crate) mod simple_file_system; diff --git a/src/runtime/protocol/simple_file_system.rs b/src/runtime/protocol/simple_file_system.rs index 8b13789..dfefd3f 100644 --- a/src/runtime/protocol/simple_file_system.rs +++ b/src/runtime/protocol/simple_file_system.rs @@ -1 +1,208 @@ +use core::ffi::c_void; +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Char16, Guid, Status, + protocol::file_system::{ + FileAttribute, FileIoToken, FileMode, FileProtocolRevision, FileProtocolV1, FileProtocolV2, + SimpleFileSystemProtocol, + }, +}; + +extern crate alloc; +use alloc::boxed::Box; + +static TEXT_FILE_SYSTEM: LazyInit> = LazyInit::new(); + +#[derive(Debug)] +pub struct SimpleFileSystem { + protocol: &'static mut SimpleFileSystemProtocol, + protocol_raw: *mut SimpleFileSystemProtocol, +} + +impl SimpleFileSystem { + pub fn new() -> Self { + let protocol = SimpleFileSystemProtocol { + revision: 0x00010000, + open_volume, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut SimpleFileSystemProtocol { + self.protocol_raw + } +} + +unsafe impl Send for SimpleFileSystem {} +unsafe impl Sync for SimpleFileSystem {} + +pub extern "efiapi" fn open_volume( + _this: *mut SimpleFileSystemProtocol, + _root: *mut *mut FileProtocolV1, +) -> Status { + Status::UNSUPPORTED +} + +#[derive(Debug)] +pub struct FileProtocolV1Impl { + protocol: &'static mut FileProtocolV1, + protocol_raw: *mut FileProtocolV1, +} + +impl FileProtocolV1Impl { + pub fn new() -> Self { + let protocol = FileProtocolV1 { + revision: FileProtocolRevision::REVISION_1, + open, + close, + delete, + read, + write, + get_position, + set_position, + get_info, + set_info, + flush, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut FileProtocolV1 { + self.protocol_raw + } +} + +unsafe impl Send for FileProtocolV1Impl {} +unsafe impl Sync for FileProtocolV1Impl {} + +#[derive(Debug)] +pub struct FileProtocolV2Impl { + protocol: &'static mut FileProtocolV2, + protocol_raw: *mut FileProtocolV2, +} + +impl FileProtocolV2Impl { + pub fn new() -> Self { + let v1 = FileProtocolV1Impl::new(); // reuse V1 implementation + let protocol = FileProtocolV2 { + v1: unsafe { *Box::from_raw(v1.get_protocol()) }, // clone contents, not share pointer + open_ex, + read_ex, + write_ex, + flush_ex, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut FileProtocolV2 { + self.protocol_raw + } +} + +unsafe impl Send for FileProtocolV2Impl {} +unsafe impl Sync for FileProtocolV2Impl {} + +pub extern "efiapi" fn open( + _this: *mut FileProtocolV1, + _new_handle: *mut *mut FileProtocolV1, + _file_name: *const Char16, + _open_mode: FileMode, + _attributes: FileAttribute, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn close(_this: *mut FileProtocolV1) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn delete(_this: *mut FileProtocolV1) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn read( + _this: *mut FileProtocolV1, + _buffer_size: *mut usize, + _buffer: *mut c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn write( + _this: *mut FileProtocolV1, + _buffer_size: *mut usize, + _buffer: *const c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn get_position(_this: *const FileProtocolV1, _position: *mut u64) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn set_position(_this: *mut FileProtocolV1, _position: u64) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn get_info( + _this: *mut FileProtocolV1, + _information_type: *const Guid, + _buffer_size: *mut usize, + _buffer: *mut c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn set_info( + _this: *mut FileProtocolV1, + _information_type: *const Guid, + _buffer_size: usize, + _buffer: *const c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn flush(_this: *mut FileProtocolV1) -> Status { + Status::UNSUPPORTED +} + +// v2-specific +pub extern "efiapi" fn open_ex( + _this: *mut FileProtocolV2, + _new_handle: *mut *mut FileProtocolV2, + _file_name: *const Char16, + _open_mode: FileMode, + _attributes: FileAttribute, + _token: *mut FileIoToken, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn read_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn write_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn flush_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { + Status::UNSUPPORTED +} From 918e1581c10aa2d1ff4716688bd3bd6a8c052e84 Mon Sep 17 00:00:00 2001 From: hanbings Date: Thu, 7 Aug 2025 21:43:17 +0800 Subject: [PATCH 05/21] refactor(main): centralize in main.rs --- src/main.rs | 1 + src/medium/ramdisk_cpio.rs | 2 -- src/medium/virtio_disk.rs | 2 -- src/runtime/loader.rs | 2 -- src/runtime/protocol/block_io.rs | 1 - src/runtime/protocol/device_path.rs | 1 - src/runtime/protocol/handle.rs | 1 - src/runtime/protocol/simple_file_system.rs | 1 - src/runtime/protocol/simple_text_output.rs | 1 - src/runtime/table.rs | 1 - src/shell/stdio.rs | 2 -- 11 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index b71031e..0c2535d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ #[macro_use] extern crate axlog; +extern crate alloc; mod log; mod medium; diff --git a/src/medium/ramdisk_cpio.rs b/src/medium/ramdisk_cpio.rs index 9ff4f74..78c55b3 100644 --- a/src/medium/ramdisk_cpio.rs +++ b/src/medium/ramdisk_cpio.rs @@ -1,5 +1,3 @@ -extern crate alloc; - use alloc::{string::String, string::ToString, vec::Vec}; use axhal::mem::phys_to_virt; use axio::{self as io, prelude::*}; diff --git a/src/medium/virtio_disk.rs b/src/medium/virtio_disk.rs index 68ab0c6..d3a6371 100644 --- a/src/medium/virtio_disk.rs +++ b/src/medium/virtio_disk.rs @@ -1,5 +1,3 @@ -extern crate alloc; - use alloc::{string::String, vec::Vec}; use axio::{self as io}; diff --git a/src/runtime/loader.rs b/src/runtime/loader.rs index 3f70cb9..e33b710 100644 --- a/src/runtime/loader.rs +++ b/src/runtime/loader.rs @@ -1,5 +1,3 @@ -extern crate alloc; - use object::FileKind; use object::read::pe::ImageOptionalHeader; use object::{File, Object, ObjectSection, pe, read::pe::ImageNtHeaders}; diff --git a/src/runtime/protocol/block_io.rs b/src/runtime/protocol/block_io.rs index 4a189a9..ce25a74 100644 --- a/src/runtime/protocol/block_io.rs +++ b/src/runtime/protocol/block_io.rs @@ -7,7 +7,6 @@ use uefi_raw::{ protocol::block::{BlockIoMedia, BlockIoProtocol, Lba}, }; -extern crate alloc; use alloc::boxed::Box; static BLOCK_IO: LazyInit> = LazyInit::new(); diff --git a/src/runtime/protocol/device_path.rs b/src/runtime/protocol/device_path.rs index e6583fd..ce166ee 100644 --- a/src/runtime/protocol/device_path.rs +++ b/src/runtime/protocol/device_path.rs @@ -10,7 +10,6 @@ use uefi_raw::{ }, }; -extern crate alloc; use alloc::boxed::Box; static DEVICE_PATH_TO_TEXT: LazyInit> = LazyInit::new(); diff --git a/src/runtime/protocol/handle.rs b/src/runtime/protocol/handle.rs index 9074789..1a77452 100644 --- a/src/runtime/protocol/handle.rs +++ b/src/runtime/protocol/handle.rs @@ -2,7 +2,6 @@ use axsync::Mutex; use lazyinit::LazyInit; use uefi_raw::{Guid, Handle}; -extern crate alloc; use alloc::collections::BTreeMap; use alloc::vec::Vec; diff --git a/src/runtime/protocol/simple_file_system.rs b/src/runtime/protocol/simple_file_system.rs index dfefd3f..c7d187f 100644 --- a/src/runtime/protocol/simple_file_system.rs +++ b/src/runtime/protocol/simple_file_system.rs @@ -10,7 +10,6 @@ use uefi_raw::{ }, }; -extern crate alloc; use alloc::boxed::Box; static TEXT_FILE_SYSTEM: LazyInit> = LazyInit::new(); diff --git a/src/runtime/protocol/simple_text_output.rs b/src/runtime/protocol/simple_text_output.rs index f7cb7fe..4b7d719 100644 --- a/src/runtime/protocol/simple_text_output.rs +++ b/src/runtime/protocol/simple_text_output.rs @@ -5,7 +5,6 @@ use uefi_raw::{ protocol::console::{SimpleTextOutputMode, SimpleTextOutputProtocol}, }; -extern crate alloc; use alloc::boxed::Box; static TEXT_OUTPUT: LazyInit> = LazyInit::new(); diff --git a/src/runtime/table.rs b/src/runtime/table.rs index da2565a..f07265c 100644 --- a/src/runtime/table.rs +++ b/src/runtime/table.rs @@ -8,7 +8,6 @@ use crate::runtime::protocol::simple_text_output::{ get_simple_text_output, init_simple_text_output, }; -extern crate alloc; use alloc::boxed::Box; #[derive(Debug)] diff --git a/src/shell/stdio.rs b/src/shell/stdio.rs index 054f68a..dfe7aa5 100644 --- a/src/shell/stdio.rs +++ b/src/shell/stdio.rs @@ -1,5 +1,3 @@ -extern crate alloc; - use alloc::{string::String, vec::Vec}; pub use axio::{BufRead, BufReader, Read, Write}; use axsync::{Mutex, MutexGuard}; From 882c236cf476a99e30e66616ba97186b5a7437f5 Mon Sep 17 00:00:00 2001 From: hanbings Date: Sat, 9 Aug 2025 03:29:12 +0800 Subject: [PATCH 06/21] feat(uefi): declare necessary ffi for the protocols. --- src/runtime/protocol/graphics_output.rs | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/runtime/protocol/graphics_output.rs b/src/runtime/protocol/graphics_output.rs index 8b13789..45f0c3a 100644 --- a/src/runtime/protocol/graphics_output.rs +++ b/src/runtime/protocol/graphics_output.rs @@ -1 +1,78 @@ +use alloc::boxed::Box; +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Status, + protocol::console::{ + GraphicsOutputBltOperation, GraphicsOutputBltPixel, GraphicsOutputModeInformation, + GraphicsOutputProtocol, GraphicsOutputProtocolMode, + }, +}; + +static GOP: LazyInit> = LazyInit::new(); + +#[derive(Debug)] +pub struct GraphicsOutput { + protocol: &'static mut GraphicsOutputProtocol, + protocol_raw: *mut GraphicsOutputProtocol, +} + +impl GraphicsOutput { + pub fn new() -> Self { + let mode: *mut GraphicsOutputProtocolMode = core::ptr::null_mut(); + + let protocol = GraphicsOutputProtocol { + query_mode, + set_mode, + blt, + mode, + }; + + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut GraphicsOutputProtocol { + self.protocol_raw + } +} + +unsafe impl Send for GraphicsOutput {} +unsafe impl Sync for GraphicsOutput {} + +pub unsafe extern "efiapi" fn query_mode( + _this: *const GraphicsOutputProtocol, + _mode_number: u32, + _size_of_info: *mut usize, + _info: *mut *const GraphicsOutputModeInformation, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn set_mode( + _this: *mut GraphicsOutputProtocol, + _mode_number: u32, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn blt( + _this: *mut GraphicsOutputProtocol, + _blt_buffer: *mut GraphicsOutputBltPixel, + _blt_operation: GraphicsOutputBltOperation, + _source_x: usize, + _source_y: usize, + _destination_x: usize, + _destination_y: usize, + _width: usize, + _height: usize, + _delta: usize, +) -> Status { + Status::UNSUPPORTED +} From 4dd66a38edf950f46b1356a4cb06fc3ba2cb8706 Mon Sep 17 00:00:00 2001 From: hanbings Date: Sun, 10 Aug 2025 00:31:51 +0800 Subject: [PATCH 07/21] feat(uefi): initialize axdisplay and drawing colors. --- src/main.rs | 5 +++- src/runtime/protocol/block_io.rs | 4 ++++ src/runtime/protocol/device_path.rs | 6 +++++ src/runtime/protocol/graphics_output.rs | 27 +++++++++++++++++++++- src/runtime/protocol/simple_file_system.rs | 4 ++++ src/runtime/table.rs | 13 +++++++++-- 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0c2535d..ce40381 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,7 @@ pub extern "C" fn rust_main(_cpu_id: usize, _dtb: usize) -> ! { info!("Initialize platform devices..."); axhal::platform_init(); - #[cfg(any(feature = "fs", feature = "net"))] + #[cfg(any(feature = "fs", feature = "net", feature = "display"))] { #[allow(unused_variables)] let all_devices = axdriver::init_drivers(); @@ -50,6 +50,9 @@ pub extern "C" fn rust_main(_cpu_id: usize, _dtb: usize) -> ! { #[cfg(feature = "net")] axnet::init_network(all_devices.net); + + #[cfg(feature = "display")] + axdisplay::init_display(all_devices.display); } ctor_bare::call_ctors(); diff --git a/src/runtime/protocol/block_io.rs b/src/runtime/protocol/block_io.rs index ce25a74..e064d99 100644 --- a/src/runtime/protocol/block_io.rs +++ b/src/runtime/protocol/block_io.rs @@ -47,6 +47,10 @@ impl BlockIo { unsafe impl Send for BlockIo {} unsafe impl Sync for BlockIo {} +pub fn init_block_io() { + BLOCK_IO.init_once(Mutex::new(BlockIo::new())); +} + pub extern "efiapi" fn reset( _this: *mut BlockIoProtocol, _extended_verification: Boolean, diff --git a/src/runtime/protocol/device_path.rs b/src/runtime/protocol/device_path.rs index ce166ee..6600df7 100644 --- a/src/runtime/protocol/device_path.rs +++ b/src/runtime/protocol/device_path.rs @@ -44,6 +44,12 @@ impl DevicePathToText { unsafe impl Send for DevicePathToText {} unsafe impl Sync for DevicePathToText {} +pub fn init_device_path() { + DEVICE_PATH_TO_TEXT.init_once(Mutex::new(DevicePathToText::new())); + DEVICE_PATH_FROM_TEXT.init_once(Mutex::new(DevicePathFromText::new())); + DEVICE_PATH_UTILITIES.init_once(Mutex::new(DevicePathUtilities::new())); +} + pub extern "efiapi" fn convert_device_node_to_text( _device_node: *const DevicePathProtocol, _display_only: Boolean, diff --git a/src/runtime/protocol/graphics_output.rs b/src/runtime/protocol/graphics_output.rs index 45f0c3a..5e7e3e0 100644 --- a/src/runtime/protocol/graphics_output.rs +++ b/src/runtime/protocol/graphics_output.rs @@ -10,7 +10,7 @@ use uefi_raw::{ }, }; -static GOP: LazyInit> = LazyInit::new(); +static GRAPHICS_OUTPUT: LazyInit> = LazyInit::new(); #[derive(Debug)] pub struct GraphicsOutput { @@ -46,6 +46,31 @@ impl GraphicsOutput { unsafe impl Send for GraphicsOutput {} unsafe impl Sync for GraphicsOutput {} +impl Drop for GraphicsOutput { + fn drop(&mut self) { + unsafe { + drop(Box::from_raw(self.protocol_raw)); + } + } +} + +pub fn init_graphics_output() { + #[cfg(feature = "display")] + { + let display_info = axdisplay::framebuffer_info(); + let frame_buffer_base = display_info.fb_base_vaddr; + let frame_buffer_size = display_info.fb_size; + + unsafe { + core::ptr::write_bytes(frame_buffer_base as *mut u8, 0xFF, frame_buffer_size); + } + + axdisplay::framebuffer_flush(); + + GRAPHICS_OUTPUT.init_once(Mutex::new(GraphicsOutput::new())); + } +} + pub unsafe extern "efiapi" fn query_mode( _this: *const GraphicsOutputProtocol, _mode_number: u32, diff --git a/src/runtime/protocol/simple_file_system.rs b/src/runtime/protocol/simple_file_system.rs index c7d187f..8b90034 100644 --- a/src/runtime/protocol/simple_file_system.rs +++ b/src/runtime/protocol/simple_file_system.rs @@ -42,6 +42,10 @@ impl SimpleFileSystem { unsafe impl Send for SimpleFileSystem {} unsafe impl Sync for SimpleFileSystem {} +pub fn init_simple_file_system() { + TEXT_FILE_SYSTEM.init_once(Mutex::new(SimpleFileSystem::new())); +} + pub extern "efiapi" fn open_volume( _this: *mut SimpleFileSystemProtocol, _root: *mut *mut FileProtocolV1, diff --git a/src/runtime/table.rs b/src/runtime/table.rs index f07265c..299903e 100644 --- a/src/runtime/table.rs +++ b/src/runtime/table.rs @@ -4,8 +4,10 @@ use axsync::Mutex; use lazyinit::LazyInit; use uefi_raw::table::{Header, configuration::ConfigurationTable, system::SystemTable}; -use crate::runtime::protocol::simple_text_output::{ - get_simple_text_output, init_simple_text_output, +use crate::runtime::protocol::{ + block_io::init_block_io, + device_path::init_device_path, + simple_text_output::{get_simple_text_output, init_simple_text_output}, }; use alloc::boxed::Box; @@ -39,6 +41,13 @@ static VENDOR: &[u16] = &[ static REVERSION: u32 = 0x0001_0000; pub fn init_system_table() { + init_block_io(); + init_device_path(); + #[cfg(feature = "display")] + crate::runtime::protocol::graphics_output::init_graphics_output(); + #[cfg(feature = "fs")] + crate::runtime::protocol::simple_file_system::init_simple_file_system(); + let simple_text_output = { init_simple_text_output(); get_simple_text_output().lock().get_protocol() From b6d20d49dfc77e7356e40f33287178861f2e686c Mon Sep 17 00:00:00 2001 From: hanbings Date: Mon, 15 Sep 2025 18:41:58 +0800 Subject: [PATCH 08/21] feat(uefi): added stub functions in the boot and runtime services. --- src/runtime/event/mod.rs | 0 src/runtime/image/mod.rs | 0 src/runtime/mod.rs | 6 +- src/runtime/service/boot_service.rs | 307 ++++++++++++++++++++++ src/runtime/service/image_service.rs | 1 - src/runtime/service/mod.rs | 1 - src/runtime/service/runtime_service.rs | 121 +++++++++ src/runtime/{table.rs => system_table.rs} | 0 8 files changed, 431 insertions(+), 5 deletions(-) create mode 100644 src/runtime/event/mod.rs create mode 100644 src/runtime/image/mod.rs delete mode 100644 src/runtime/service/image_service.rs rename src/runtime/{table.rs => system_table.rs} (100%) diff --git a/src/runtime/event/mod.rs b/src/runtime/event/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/runtime/image/mod.rs b/src/runtime/image/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 15a1277..61d4e2e 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -5,7 +5,7 @@ mod loader; mod memory; mod protocol; mod service; -mod table; +mod system_table; pub fn efi_runtime_init() { let load_bootloader = loader::load_efi_file("/EFI/BOOT/BOOTRISCV64.EFI"); @@ -33,8 +33,8 @@ pub fn efi_runtime_init() { let func = entry::resolve_entry_func(mapping, file.entry(), base_va); let system_table = { - table::init_system_table(); - table::get_system_table_raw() + system_table::init_system_table(); + system_table::get_system_table_raw() }; let result = func(core::ptr::null_mut(), system_table); diff --git a/src/runtime/service/boot_service.rs b/src/runtime/service/boot_service.rs index 8b13789..a7b38c7 100644 --- a/src/runtime/service/boot_service.rs +++ b/src/runtime/service/boot_service.rs @@ -1 +1,308 @@ +use core::ffi::c_void; +use uefi_raw::{ + Boolean, Char16, Event, Guid, Handle, PhysicalAddress, Status, + protocol::device_path::DevicePathProtocol, + table::boot::{ + EventNotifyFn, EventType, InterfaceType, MemoryDescriptor, MemoryType, + OpenProtocolInformationEntry, TimerDelay, Tpl, + }, +}; + +/// Type of allocation to perform. +#[derive(Debug, Copy, Clone)] +pub enum AllocateType { + /// Allocate any possible pages. + AnyPages, + /// Allocate pages at any address below the given address. + MaxAddress(PhysicalAddress), + /// Allocate pages at the specified address. + Address(PhysicalAddress), +} + +// Task Priority services +pub unsafe extern "efiapi" fn raise_tpl(_new_tpl: Tpl) -> Tpl { + Tpl::APPLICATION +} +pub unsafe extern "efiapi" fn restore_tpl(_old_tpl: Tpl) {} + +// Memory allocation functions +pub unsafe extern "efiapi" fn allocate_pages( + _alloc_ty: AllocateType, + _mem_ty: MemoryType, + _count: usize, + _addr: *mut PhysicalAddress, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn free_pages(_addr: PhysicalAddress, _pages: usize) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn get_memory_map( + _size: *mut usize, + _map: *mut MemoryDescriptor, + _key: *mut usize, + _desc_size: *mut usize, + _desc_version: *mut u32, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn allocate_pool( + _pool_type: MemoryType, + _size: usize, + _buffer: *mut *mut u8, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn free_pool(_buffer: *mut u8) -> Status { + Status::UNSUPPORTED +} + +// Event & timer functions +pub unsafe extern "efiapi" fn create_event( + _ty: EventType, + _notify_tpl: Tpl, + _notify_func: Option, + _notify_ctx: *mut c_void, + _out_event: *mut Event, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn set_timer( + _event: Event, + _ty: TimerDelay, + _trigger_time: u64, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn wait_for_event( + _number_of_events: usize, + _events: *mut Event, + _out_index: *mut usize, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn signal_event(_event: Event) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn close_event(_event: Event) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn check_event(_event: Event) -> Status { + Status::UNSUPPORTED +} + +// Protocol handlers +pub unsafe extern "efiapi" fn install_protocol_interface( + _handle: *mut Handle, + _guid: *const Guid, + _interface_type: InterfaceType, + _interface: *const c_void, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn reinstall_protocol_interface( + _handle: Handle, + _protocol: *const Guid, + _old_interface: *const c_void, + _new_interface: *const c_void, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn uninstall_protocol_interface( + _handle: Handle, + _protocol: *const Guid, + _interface: *const c_void, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn handle_protocol( + _handle: Handle, + _proto: *const Guid, + _out_proto: *mut *mut c_void, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn register_protocol_notify( + _protocol: *const Guid, + _event: Event, + _registration: *mut *const c_void, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn locate_handle( + _search_ty: i32, + _proto: *const Guid, + _key: *const c_void, + _buf_sz: *mut usize, + _buf: *mut Handle, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn locate_device_path( + _proto: *const Guid, + _device_path: *mut *const DevicePathProtocol, + _out_handle: *mut Handle, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn install_configuration_table( + _guid_entry: *const Guid, + _table_ptr: *const c_void, +) -> Status { + Status::UNSUPPORTED +} + +// Image services +pub unsafe extern "efiapi" fn load_image( + _boot_policy: Boolean, + _parent_image_handle: Handle, + _device_path: *const DevicePathProtocol, + _source_buffer: *const u8, + _source_size: usize, + _image_handle: *mut Handle, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn start_image( + _image_handle: Handle, + _exit_data_size: *mut usize, + _exit_data: *mut *mut Char16, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn exit( + _image_handle: Handle, + _exit_status: Status, + _exit_data_size: usize, + _exit_data: *mut Char16, +) -> ! { + loop {} +} +pub unsafe extern "efiapi" fn unload_image(_image_handle: Handle) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn exit_boot_services(_image_handle: Handle, _map_key: usize) -> Status { + Status::UNSUPPORTED +} + +// Misc services +pub unsafe extern "efiapi" fn get_next_monotonic_count(_count: *mut u64) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn stall(_microseconds: usize) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn set_watchdog_timer( + _timeout: usize, + _watchdog_code: u64, + _data_size: usize, + _watchdog_data: *const u16, +) -> Status { + Status::UNSUPPORTED +} + +// Driver support +pub unsafe extern "efiapi" fn connect_controller( + _controller: Handle, + _driver_image: Handle, + _remaining_device_path: *const DevicePathProtocol, + _recursive: Boolean, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn disconnect_controller( + _controller: Handle, + _driver_image: Handle, + _child: Handle, +) -> Status { + Status::UNSUPPORTED +} + +// Protocol open/close +pub unsafe extern "efiapi" fn open_protocol( + _handle: Handle, + _protocol: *const Guid, + _interface: *mut *mut c_void, + _agent_handle: Handle, + _controller_handle: Handle, + _attributes: u32, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn close_protocol( + _handle: Handle, + _protocol: *const Guid, + _agent_handle: Handle, + _controller_handle: Handle, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn open_protocol_information( + _handle: Handle, + _protocol: *const Guid, + _entry_buffer: *mut *const OpenProtocolInformationEntry, + _entry_count: *mut usize, +) -> Status { + Status::UNSUPPORTED +} + +// Library services +pub unsafe extern "efiapi" fn protocols_per_handle( + _handle: Handle, + _protocol_buffer: *mut *mut *const Guid, + _protocol_buffer_count: *mut usize, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn locate_handle_buffer( + _search_ty: i32, + _proto: *const Guid, + _key: *const c_void, + _no_handles: *mut usize, + _buf: *mut *mut Handle, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn locate_protocol( + _proto: *const Guid, + _registration: *mut c_void, + _out_proto: *mut *mut c_void, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "C" fn install_multiple_protocol_interfaces( + _handle: *mut Handle, + // variadic, ignored +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "C" fn uninstall_multiple_protocol_interfaces( + _handle: Handle, + // variadic, ignored +) -> Status { + Status::UNSUPPORTED +} + +// CRC / memory +pub unsafe extern "efiapi" fn calculate_crc32( + _data: *const c_void, + _data_size: usize, + _crc32: *mut u32, +) -> Status { + Status::UNSUPPORTED +} +pub unsafe extern "efiapi" fn copy_mem(_dest: *mut u8, _src: *const u8, _len: usize) {} +pub unsafe extern "efiapi" fn set_mem(_buffer: *mut u8, _len: usize, _value: u8) {} + +// New event (UEFI 2.0+) +pub unsafe extern "efiapi" fn create_event_ex( + _ty: EventType, + _notify_tpl: Tpl, + _notify_fn: Option, + _notify_ctx: *mut c_void, + _event_group: *mut Guid, + _out_event: *mut Event, +) -> Status { + Status::UNSUPPORTED +} diff --git a/src/runtime/service/image_service.rs b/src/runtime/service/image_service.rs deleted file mode 100644 index 55a3c17..0000000 --- a/src/runtime/service/image_service.rs +++ /dev/null @@ -1 +0,0 @@ -pub fn init_image_service() {} diff --git a/src/runtime/service/mod.rs b/src/runtime/service/mod.rs index 2a25b55..fcbd5b4 100644 --- a/src/runtime/service/mod.rs +++ b/src/runtime/service/mod.rs @@ -1,3 +1,2 @@ pub(crate) mod boot_service; -pub(crate) mod image_service; pub(crate) mod runtime_service; diff --git a/src/runtime/service/runtime_service.rs b/src/runtime/service/runtime_service.rs index 8b13789..fe7789d 100644 --- a/src/runtime/service/runtime_service.rs +++ b/src/runtime/service/runtime_service.rs @@ -1 +1,122 @@ +use core::ffi::c_void; +use uefi_raw::{ + Char16, Guid, PhysicalAddress, Status, + capsule::CapsuleHeader, + table::{ + boot::MemoryDescriptor, + runtime::{ResetType, TimeCapabilities, VariableAttributes}, + }, + time::Time, +}; +// Time services +pub unsafe extern "efiapi" fn get_time( + _time: *mut Time, + _capabilities: *mut TimeCapabilities, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn set_time(_time: *const Time) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn get_wakeup_time( + _enabled: *mut u8, + _pending: *mut u8, + _time: *mut Time, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn set_wakeup_time(_enable: u8, _time: *const Time) -> Status { + Status::UNSUPPORTED +} + +// Virtual memory services +pub unsafe extern "efiapi" fn set_virtual_address_map( + _map_size: usize, + _desc_size: usize, + _desc_version: u32, + _virtual_map: *mut MemoryDescriptor, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn convert_pointer( + _debug_disposition: usize, + _address: *mut *const c_void, +) -> Status { + Status::UNSUPPORTED +} + +// Variable services +pub unsafe extern "efiapi" fn get_variable( + _variable_name: *const Char16, + _vendor_guid: *const Guid, + _attributes: *mut VariableAttributes, + _data_size: *mut usize, + _data: *mut u8, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn get_next_variable_name( + _variable_name_size: *mut usize, + _variable_name: *mut u16, + _vendor_guid: *mut Guid, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn set_variable( + _variable_name: *const Char16, + _vendor_guid: *const Guid, + _attributes: VariableAttributes, + _data_size: usize, + _data: *const u8, +) -> Status { + Status::UNSUPPORTED +} + +// Misc services +pub unsafe extern "efiapi" fn get_next_high_monotonic_count(_high_count: *mut u32) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn reset_system( + _rt: ResetType, + _status: Status, + _data_size: usize, + _data: *const u8, +) -> ! { + loop {} +} + +// Capsule services +pub unsafe extern "efiapi" fn update_capsule( + _capsule_header_array: *const *const CapsuleHeader, + _capsule_count: usize, + _scatter_gather_list: PhysicalAddress, +) -> Status { + Status::UNSUPPORTED +} + +pub unsafe extern "efiapi" fn query_capsule_capabilities( + _capsule_header_array: *const *const CapsuleHeader, + _capsule_count: usize, + _maximum_capsule_size: *mut u64, + _reset_type: *mut ResetType, +) -> Status { + Status::UNSUPPORTED +} + +// Variable info +pub unsafe extern "efiapi" fn query_variable_info( + _attributes: VariableAttributes, + _maximum_variable_storage_size: *mut u64, + _remaining_variable_storage_size: *mut u64, + _maximum_variable_size: *mut u64, +) -> Status { + Status::UNSUPPORTED +} diff --git a/src/runtime/table.rs b/src/runtime/system_table.rs similarity index 100% rename from src/runtime/table.rs rename to src/runtime/system_table.rs From 8084a0d250d5da820d475579cea9c8c4e34fa95d Mon Sep 17 00:00:00 2001 From: hanbings Date: Mon, 15 Sep 2025 23:01:09 +0800 Subject: [PATCH 09/21] fix(uefi): fix build. --- src/runtime/service/boot_service.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtime/service/boot_service.rs b/src/runtime/service/boot_service.rs index a7b38c7..3fb5a7e 100644 --- a/src/runtime/service/boot_service.rs +++ b/src/runtime/service/boot_service.rs @@ -10,6 +10,7 @@ use uefi_raw::{ }; /// Type of allocation to perform. +#[repr(C)] #[derive(Debug, Copy, Clone)] pub enum AllocateType { /// Allocate any possible pages. From fc8edcc40570a1ea5f0c571978c8225e03499298 Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 16 Sep 2025 05:02:03 +0800 Subject: [PATCH 10/21] feat(uefi): added initialization for boot_service and runtime_service. --- src/runtime/service/mod.rs | 112 ++++++++++++++++++++++++++++++++++++ src/runtime/system_table.rs | 8 +-- 2 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/runtime/service/mod.rs b/src/runtime/service/mod.rs index fcbd5b4..13409d5 100644 --- a/src/runtime/service/mod.rs +++ b/src/runtime/service/mod.rs @@ -1,2 +1,114 @@ +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::table::{boot::BootServices, runtime::RuntimeServices}; + +use crate::runtime::service::{ + boot_service::{ + allocate_pages, allocate_pool, calculate_crc32, check_event, close_event, close_protocol, + connect_controller, copy_mem, create_event, create_event_ex, disconnect_controller, exit, + exit_boot_services, free_pages, free_pool, get_memory_map, get_next_monotonic_count, + handle_protocol, install_configuration_table, install_multiple_protocol_interfaces, + install_protocol_interface, load_image, locate_device_path, locate_handle, + locate_handle_buffer, locate_protocol, open_protocol, open_protocol_information, + protocols_per_handle, raise_tpl, register_protocol_notify, reinstall_protocol_interface, + restore_tpl, set_mem, set_timer, set_watchdog_timer, signal_event, stall, start_image, + uninstall_multiple_protocol_interfaces, uninstall_protocol_interface, unload_image, + wait_for_event, + }, + runtime_service::{ + convert_pointer, get_next_high_monotonic_count, get_next_variable_name, get_time, + get_variable, get_wakeup_time, query_capsule_capabilities, query_variable_info, + reset_system, set_time, set_variable, set_virtual_address_map, set_wakeup_time, + update_capsule, + }, +}; + pub(crate) mod boot_service; pub(crate) mod runtime_service; + +static BOOT_SERVICE: LazyInit> = LazyInit::new(); +static RUNTIME_SERVICE: LazyInit> = LazyInit::new(); + +fn init_service() { + BOOT_SERVICE.init_once(Mutex::new(BootServices { + header: Default::default(), + raise_tpl, + restore_tpl, + allocate_pages, + free_pages, + get_memory_map, + allocate_pool, + free_pool, + create_event, + set_timer, + wait_for_event, + signal_event, + close_event, + check_event, + install_protocol_interface, + reinstall_protocol_interface, + uninstall_protocol_interface, + handle_protocol, + reserved: None, + register_protocol_notify, + locate_handle, + locate_device_path, + install_configuration_table, + load_image, + start_image, + exit, + unload_image, + exit_boot_services, + get_next_monotonic_count, + stall, + set_watchdog_timer, + connect_controller, + disconnect_controller, + open_protocol, + close_protocol, + open_protocol_information, + protocols_per_handle, + locate_handle_buffer, + locate_protocol, + install_multiple_protocol_interfaces, + uninstall_multiple_protocol_interfaces, + calculate_crc32, + copy_mem, + set_mem, + create_event_ex, + })); + + RUNTIME_SERVICE.init_once(Mutex::new(RuntimeServices { + header: Default::default(), + get_time, + set_time, + get_wakeup_time, + set_wakeup_time, + set_virtual_address_map, + convert_pointer, + get_variable, + get_next_variable_name, + set_variable, + reset_system, + update_capsule, + query_capsule_capabilities, + query_variable_info, + get_next_high_monotonic_count, + })); +} + +pub fn get_boot_service() -> *mut BootServices { + BOOT_SERVICE + .get() + .expect("BootService not initialized") + .lock() + .as_mut() +} + +pub fn get_runtime_service() -> *mut RuntimeServices { + RUNTIME_SERVICE + .get() + .expect("RuntimeService not initialized") + .lock() + .as_mut() +} diff --git a/src/runtime/system_table.rs b/src/runtime/system_table.rs index 299903e..4d74379 100644 --- a/src/runtime/system_table.rs +++ b/src/runtime/system_table.rs @@ -4,11 +4,11 @@ use axsync::Mutex; use lazyinit::LazyInit; use uefi_raw::table::{Header, configuration::ConfigurationTable, system::SystemTable}; -use crate::runtime::protocol::{ +use crate::runtime::{protocol::{ block_io::init_block_io, device_path::init_device_path, simple_text_output::{get_simple_text_output, init_simple_text_output}, -}; +}, service::{get_boot_service, get_runtime_service}}; use alloc::boxed::Box; @@ -78,8 +78,8 @@ pub fn init_system_table() { stderr_handle: null_mut(), stderr: simple_text_output, - runtime_services: null_mut(), - boot_services: null_mut(), + runtime_services: get_runtime_service(), + boot_services: get_boot_service(), number_of_configuration_table_entries: 0, configuration_table: configuration_table_raw, From 979b178fc6efad286dafaeb85219440ec4699a08 Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 16 Sep 2025 12:52:53 +0800 Subject: [PATCH 11/21] refactor(uefi): adjust the code structure of uefi service. --- src/main.rs | 1 + src/runtime/service/boot_service.rs | 96 ++++++++++++++++++++++---- src/runtime/service/mod.rs | 96 +++----------------------- src/runtime/service/runtime_service.rs | 51 ++++++++++++++ src/runtime/system_table.rs | 18 +++-- 5 files changed, 155 insertions(+), 107 deletions(-) diff --git a/src/main.rs b/src/main.rs index ce40381..d02908b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(test), no_std)] #![no_main] #![allow(dead_code)] +#![feature(c_variadic)] #[macro_use] extern crate axlog; diff --git a/src/runtime/service/boot_service.rs b/src/runtime/service/boot_service.rs index 3fb5a7e..c7ac0ef 100644 --- a/src/runtime/service/boot_service.rs +++ b/src/runtime/service/boot_service.rs @@ -9,16 +9,85 @@ use uefi_raw::{ }, }; -/// Type of allocation to perform. -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub enum AllocateType { - /// Allocate any possible pages. - AnyPages, - /// Allocate pages at any address below the given address. - MaxAddress(PhysicalAddress), - /// Allocate pages at the specified address. - Address(PhysicalAddress), +use alloc::boxed::Box; + +#[derive(Debug)] +pub struct Boot { + services: &'static mut uefi_raw::table::boot::BootServices, + services_raw: *mut uefi_raw::table::boot::BootServices, +} + +impl Boot { + pub fn new() -> Self { + let services = uefi_raw::table::boot::BootServices { + header: Default::default(), + raise_tpl, + restore_tpl, + allocate_pages, + free_pages, + get_memory_map, + allocate_pool, + free_pool, + create_event, + set_timer, + wait_for_event, + signal_event, + close_event, + check_event, + install_protocol_interface, + reinstall_protocol_interface, + uninstall_protocol_interface, + handle_protocol, + reserved: core::ptr::null_mut::(), + register_protocol_notify, + locate_handle, + locate_device_path, + install_configuration_table, + load_image, + start_image, + exit, + unload_image, + exit_boot_services, + get_next_monotonic_count, + stall, + set_watchdog_timer, + connect_controller, + disconnect_controller, + open_protocol, + close_protocol, + open_protocol_information, + protocols_per_handle, + locate_handle_buffer, + locate_protocol, + install_multiple_protocol_interfaces, + uninstall_multiple_protocol_interfaces, + calculate_crc32, + copy_mem, + set_mem, + create_event_ex, // UEFI 2.0+ + }; + let services_raw = Box::into_raw(Box::new(services)); + let services = unsafe { &mut *services_raw }; + Boot { + services, + services_raw, + } + } + + pub fn get_services(&self) -> *mut uefi_raw::table::boot::BootServices { + self.services_raw + } +} + +unsafe impl Send for Boot {} +unsafe impl Sync for Boot {} + +impl Drop for Boot { + fn drop(&mut self) { + unsafe { + drop(Box::from_raw(self.services_raw)); + } + } } // Task Priority services @@ -29,7 +98,7 @@ pub unsafe extern "efiapi" fn restore_tpl(_old_tpl: Tpl) {} // Memory allocation functions pub unsafe extern "efiapi" fn allocate_pages( - _alloc_ty: AllocateType, + _alloc_ty: u32, _mem_ty: MemoryType, _count: usize, _addr: *mut PhysicalAddress, @@ -274,13 +343,14 @@ pub unsafe extern "efiapi" fn locate_protocol( } pub unsafe extern "C" fn install_multiple_protocol_interfaces( _handle: *mut Handle, - // variadic, ignored + ... ) -> Status { Status::UNSUPPORTED } + pub unsafe extern "C" fn uninstall_multiple_protocol_interfaces( _handle: Handle, - // variadic, ignored + ... ) -> Status { Status::UNSUPPORTED } diff --git a/src/runtime/service/mod.rs b/src/runtime/service/mod.rs index 13409d5..c717a25 100644 --- a/src/runtime/service/mod.rs +++ b/src/runtime/service/mod.rs @@ -3,98 +3,18 @@ use lazyinit::LazyInit; use uefi_raw::table::{boot::BootServices, runtime::RuntimeServices}; use crate::runtime::service::{ - boot_service::{ - allocate_pages, allocate_pool, calculate_crc32, check_event, close_event, close_protocol, - connect_controller, copy_mem, create_event, create_event_ex, disconnect_controller, exit, - exit_boot_services, free_pages, free_pool, get_memory_map, get_next_monotonic_count, - handle_protocol, install_configuration_table, install_multiple_protocol_interfaces, - install_protocol_interface, load_image, locate_device_path, locate_handle, - locate_handle_buffer, locate_protocol, open_protocol, open_protocol_information, - protocols_per_handle, raise_tpl, register_protocol_notify, reinstall_protocol_interface, - restore_tpl, set_mem, set_timer, set_watchdog_timer, signal_event, stall, start_image, - uninstall_multiple_protocol_interfaces, uninstall_protocol_interface, unload_image, - wait_for_event, - }, - runtime_service::{ - convert_pointer, get_next_high_monotonic_count, get_next_variable_name, get_time, - get_variable, get_wakeup_time, query_capsule_capabilities, query_variable_info, - reset_system, set_time, set_variable, set_virtual_address_map, set_wakeup_time, - update_capsule, - }, + boot_service::Boot, runtime_service::Runtime }; pub(crate) mod boot_service; pub(crate) mod runtime_service; -static BOOT_SERVICE: LazyInit> = LazyInit::new(); -static RUNTIME_SERVICE: LazyInit> = LazyInit::new(); +static BOOT_SERVICE: LazyInit> = LazyInit::new(); +static RUNTIME_SERVICE: LazyInit> = LazyInit::new(); -fn init_service() { - BOOT_SERVICE.init_once(Mutex::new(BootServices { - header: Default::default(), - raise_tpl, - restore_tpl, - allocate_pages, - free_pages, - get_memory_map, - allocate_pool, - free_pool, - create_event, - set_timer, - wait_for_event, - signal_event, - close_event, - check_event, - install_protocol_interface, - reinstall_protocol_interface, - uninstall_protocol_interface, - handle_protocol, - reserved: None, - register_protocol_notify, - locate_handle, - locate_device_path, - install_configuration_table, - load_image, - start_image, - exit, - unload_image, - exit_boot_services, - get_next_monotonic_count, - stall, - set_watchdog_timer, - connect_controller, - disconnect_controller, - open_protocol, - close_protocol, - open_protocol_information, - protocols_per_handle, - locate_handle_buffer, - locate_protocol, - install_multiple_protocol_interfaces, - uninstall_multiple_protocol_interfaces, - calculate_crc32, - copy_mem, - set_mem, - create_event_ex, - })); - - RUNTIME_SERVICE.init_once(Mutex::new(RuntimeServices { - header: Default::default(), - get_time, - set_time, - get_wakeup_time, - set_wakeup_time, - set_virtual_address_map, - convert_pointer, - get_variable, - get_next_variable_name, - set_variable, - reset_system, - update_capsule, - query_capsule_capabilities, - query_variable_info, - get_next_high_monotonic_count, - })); +pub(crate) fn init_service() { + BOOT_SERVICE.init_once(Mutex::new(Boot::new())); + RUNTIME_SERVICE.init_once(Mutex::new(Runtime::new())); } pub fn get_boot_service() -> *mut BootServices { @@ -102,7 +22,7 @@ pub fn get_boot_service() -> *mut BootServices { .get() .expect("BootService not initialized") .lock() - .as_mut() + .get_services() } pub fn get_runtime_service() -> *mut RuntimeServices { @@ -110,5 +30,5 @@ pub fn get_runtime_service() -> *mut RuntimeServices { .get() .expect("RuntimeService not initialized") .lock() - .as_mut() + .get_services() } diff --git a/src/runtime/service/runtime_service.rs b/src/runtime/service/runtime_service.rs index fe7789d..6aa9ae6 100644 --- a/src/runtime/service/runtime_service.rs +++ b/src/runtime/service/runtime_service.rs @@ -9,6 +9,57 @@ use uefi_raw::{ time::Time, }; +use alloc::boxed::Box; + +#[derive(Debug)] +pub struct Runtime { + services: &'static mut uefi_raw::table::runtime::RuntimeServices, + services_raw: *mut uefi_raw::table::runtime::RuntimeServices, +} + +impl Runtime { + pub fn new() -> Self { + let services = uefi_raw::table::runtime::RuntimeServices { + header: Default::default(), + get_time, + set_time, + get_wakeup_time, + set_wakeup_time, + set_virtual_address_map, + convert_pointer, + get_variable, + get_next_variable_name, + set_variable, + reset_system, + update_capsule, + query_capsule_capabilities, + query_variable_info, + get_next_high_monotonic_count, + }; + let services_raw = Box::into_raw(Box::new(services)); + let services = unsafe { &mut *services_raw }; + Self { + services, + services_raw, + } + } + + pub fn get_services(&self) -> *mut uefi_raw::table::runtime::RuntimeServices { + self.services_raw + } +} + +unsafe impl Send for Runtime {} +unsafe impl Sync for Runtime {} + +impl Drop for Runtime { + fn drop(&mut self) { + unsafe { + drop(Box::from_raw(self.services_raw)); + } + } +} + // Time services pub unsafe extern "efiapi" fn get_time( _time: *mut Time, diff --git a/src/runtime/system_table.rs b/src/runtime/system_table.rs index 4d74379..5131403 100644 --- a/src/runtime/system_table.rs +++ b/src/runtime/system_table.rs @@ -41,23 +41,29 @@ static VENDOR: &[u16] = &[ static REVERSION: u32 = 0x0001_0000; pub fn init_system_table() { + // Initialize the protocols init_block_io(); init_device_path(); #[cfg(feature = "display")] crate::runtime::protocol::graphics_output::init_graphics_output(); #[cfg(feature = "fs")] crate::runtime::protocol::simple_file_system::init_simple_file_system(); - let simple_text_output = { init_simple_text_output(); get_simple_text_output().lock().get_protocol() }; + // Initialize the services + crate::runtime::service::init_service(); + let runtime_services = get_runtime_service(); + let boot_services = get_boot_service(); + + // Initialize the * table let configuration_table = Box::new(ConfigurationTable { vendor_guid: uefi_raw::Guid::default(), vendor_table: null_mut(), }); - let configuration_table_raw = Box::into_raw(configuration_table); + let configuration_table = Box::into_raw(configuration_table); let system_table = Box::new(SystemTable { // Build the UEFI Table Header. @@ -78,11 +84,11 @@ pub fn init_system_table() { stderr_handle: null_mut(), stderr: simple_text_output, - runtime_services: get_runtime_service(), - boot_services: get_boot_service(), + runtime_services, + boot_services, number_of_configuration_table_entries: 0, - configuration_table: configuration_table_raw, + configuration_table, }); let system_table_raw = Box::into_raw(system_table); let system_table = unsafe { &mut *system_table_raw }; @@ -99,4 +105,4 @@ pub fn get_system_table_raw() -> *mut SystemTable { .expect("SystemTable not initialized") .lock() .system_table_raw -} +} \ No newline at end of file From 24318b6acae7ccf7e6d7ef4a8c2e934dac621982 Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 23 Sep 2025 05:32:25 +0800 Subject: [PATCH 12/21] refactor(memory): adjust the memory manager interface according to the spec. --- src/runtime/memory.rs | 27 ------------ src/runtime/mod.rs | 11 ++++- src/runtime/service/boot_service.rs | 39 ++++++++++------- src/runtime/service/memory.rs | 66 +++++++++++++++++++++++++++++ src/runtime/service/mod.rs | 5 +-- src/runtime/system_table.rs | 15 ++++--- 6 files changed, 110 insertions(+), 53 deletions(-) delete mode 100644 src/runtime/memory.rs create mode 100644 src/runtime/service/memory.rs diff --git a/src/runtime/memory.rs b/src/runtime/memory.rs deleted file mode 100644 index a1ecb88..0000000 --- a/src/runtime/memory.rs +++ /dev/null @@ -1,27 +0,0 @@ -use axhal::{ - mem::{MemoryAddr, VirtAddr}, - paging::MappingFlags, -}; - -pub fn alloc_and_map_memory(size: usize, source_data: &[u8]) -> *mut u8 { - let layout = core::alloc::Layout::from_size_align(source_data.len(), 4096) - .expect("Invalid layout for shellcode"); - - let ptr = axalloc::global_allocator() - .alloc(layout) - .expect("Failed to allocate memory for shellcode") - .as_ptr(); - - let page_count = (size + 4095) / 4096; - - axmm::kernel_aspace() - .lock() - .protect( - VirtAddr::from_ptr_of(ptr).align_down(4096usize), - page_count * 4096, - MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, - ) - .expect("Failed to protect EFI memory"); - - ptr -} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 61d4e2e..13fd350 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,8 +1,10 @@ use object::Object; +use uefi_raw::table::boot::MemoryType; + +use crate::runtime::service::memory::AllocateType; mod entry; mod loader; -mod memory; mod protocol; mod service; mod system_table; @@ -16,7 +18,12 @@ pub fn efi_runtime_init() { let (base_va, max_va) = loader::analyze_sections(&file); let mem_size = (max_va - base_va) as usize; - let mapping = memory::alloc_and_map_memory(mem_size, &load_bootloader); + // let mapping = crate::runtime::service::memory::alloc_and_map_memory(mem_size, &load_bootloader); + let mapping = crate::runtime::service::memory::alloc_pages( + AllocateType::AnyPages, + MemoryType::LOADER_CODE, + mem_size / 4096 + 1, + ); loader::load_sections(&file, mapping, base_va); loader::apply_relocations(&file, mapping, base_va, image_base); diff --git a/src/runtime/service/boot_service.rs b/src/runtime/service/boot_service.rs index c7ac0ef..42f12ae 100644 --- a/src/runtime/service/boot_service.rs +++ b/src/runtime/service/boot_service.rs @@ -11,6 +11,8 @@ use uefi_raw::{ use alloc::boxed::Box; +use crate::runtime::service::memory::AllocateType; + #[derive(Debug)] pub struct Boot { services: &'static mut uefi_raw::table::boot::BootServices, @@ -98,15 +100,28 @@ pub unsafe extern "efiapi" fn restore_tpl(_old_tpl: Tpl) {} // Memory allocation functions pub unsafe extern "efiapi" fn allocate_pages( - _alloc_ty: u32, - _mem_ty: MemoryType, - _count: usize, - _addr: *mut PhysicalAddress, + alloc_ty: u32, + mem_ty: MemoryType, + count: usize, + addr: *mut PhysicalAddress, ) -> Status { - Status::UNSUPPORTED + let alloc_ty = match AllocateType::try_from(alloc_ty) { + Ok(t) => t, + Err(_) => return Status::INVALID_PARAMETER, + }; + + let ptr = crate::runtime::service::memory::alloc_pages(alloc_ty, mem_ty, count); + if ptr.is_null() { + return Status::OUT_OF_RESOURCES; + } + + unsafe { *addr = ptr as u64 }; + Status::SUCCESS } -pub unsafe extern "efiapi" fn free_pages(_addr: PhysicalAddress, _pages: usize) -> Status { - Status::UNSUPPORTED +pub unsafe extern "efiapi" fn free_pages(addr: PhysicalAddress, pages: usize) -> Status { + unsafe { free_pages(addr, pages) }; + + Status::SUCCESS } pub unsafe extern "efiapi" fn get_memory_map( _size: *mut usize, @@ -341,17 +356,11 @@ pub unsafe extern "efiapi" fn locate_protocol( ) -> Status { Status::UNSUPPORTED } -pub unsafe extern "C" fn install_multiple_protocol_interfaces( - _handle: *mut Handle, - ... -) -> Status { +pub unsafe extern "C" fn install_multiple_protocol_interfaces(_handle: *mut Handle, ...) -> Status { Status::UNSUPPORTED } -pub unsafe extern "C" fn uninstall_multiple_protocol_interfaces( - _handle: Handle, - ... -) -> Status { +pub unsafe extern "C" fn uninstall_multiple_protocol_interfaces(_handle: Handle, ...) -> Status { Status::UNSUPPORTED } diff --git a/src/runtime/service/memory.rs b/src/runtime/service/memory.rs new file mode 100644 index 0000000..584024b --- /dev/null +++ b/src/runtime/service/memory.rs @@ -0,0 +1,66 @@ +use alloc::vec::Vec; +use axhal::{ + mem::{MemoryAddr, PhysAddr, VirtAddr}, + paging::MappingFlags, +}; +use axsync::Mutex; +use uefi_raw::table::boot::MemoryType; + +static ALLOCATED_PAGES: Mutex> = Mutex::new(Vec::new()); + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum AllocateType { + AnyPages = 0, // AllocateAnyPages + MaxAddress = 1, // AllocateMaxAddress + Address = 2, // AllocateAddress +} + +impl TryFrom for AllocateType { + type Error = (); + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(AllocateType::AnyPages), + 1 => Ok(AllocateType::MaxAddress), + 2 => Ok(AllocateType::Address), + _ => Err(()), + } + } +} + +impl From for u32 { + fn from(v: AllocateType) -> u32 { + v as u32 + } +} + +pub fn alloc_pages(alloc_type: AllocateType, memory_type: MemoryType, count: usize) -> *mut u8 { + let layout = core::alloc::Layout::from_size_align(count * 4096, 4096) + .expect("Invalid layout for allocate_pages"); + let ptr = axalloc::global_allocator() + .alloc(layout) + .expect("Failed to allocate pages for EFI") + .as_ptr(); + + let page_count = (layout.size() + 4095) / 4096; + + axmm::kernel_aspace() + .lock() + .protect( + VirtAddr::from_ptr_of(ptr).align_down(4096usize), + page_count * 4096, + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE, + ) + .expect("Failed to protect EFI memory"); + + ptr +} + +pub fn free_pages(addr: PhysAddr, page: usize) {} + +pub fn allocate_pool(memory_type: MemoryType, size: usize) -> *mut u8 { + core::ptr::null_mut() +} + +pub fn free_pool(buffer: *mut u8) {} \ No newline at end of file diff --git a/src/runtime/service/mod.rs b/src/runtime/service/mod.rs index c717a25..cd64f13 100644 --- a/src/runtime/service/mod.rs +++ b/src/runtime/service/mod.rs @@ -2,10 +2,9 @@ use axsync::Mutex; use lazyinit::LazyInit; use uefi_raw::table::{boot::BootServices, runtime::RuntimeServices}; -use crate::runtime::service::{ - boot_service::Boot, runtime_service::Runtime -}; +use crate::runtime::service::{boot_service::Boot, runtime_service::Runtime}; +pub(crate) mod memory; pub(crate) mod boot_service; pub(crate) mod runtime_service; diff --git a/src/runtime/system_table.rs b/src/runtime/system_table.rs index 5131403..713bea5 100644 --- a/src/runtime/system_table.rs +++ b/src/runtime/system_table.rs @@ -4,11 +4,14 @@ use axsync::Mutex; use lazyinit::LazyInit; use uefi_raw::table::{Header, configuration::ConfigurationTable, system::SystemTable}; -use crate::runtime::{protocol::{ - block_io::init_block_io, - device_path::init_device_path, - simple_text_output::{get_simple_text_output, init_simple_text_output}, -}, service::{get_boot_service, get_runtime_service}}; +use crate::runtime::{ + protocol::{ + block_io::init_block_io, + device_path::init_device_path, + simple_text_output::{get_simple_text_output, init_simple_text_output}, + }, + service::{get_boot_service, get_runtime_service}, +}; use alloc::boxed::Box; @@ -105,4 +108,4 @@ pub fn get_system_table_raw() -> *mut SystemTable { .expect("SystemTable not initialized") .lock() .system_table_raw -} \ No newline at end of file +} From 84e0f0da8a741e10472212ab2b38b67d2f7e8d3a Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 23 Sep 2025 05:43:58 +0800 Subject: [PATCH 13/21] chore(uefi): run cargo fmt. --- src/runtime/service/boot_service.rs | 4 +++- src/runtime/service/memory.rs | 12 ++++++------ src/runtime/service/mod.rs | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/runtime/service/boot_service.rs b/src/runtime/service/boot_service.rs index 42f12ae..d67fa3b 100644 --- a/src/runtime/service/boot_service.rs +++ b/src/runtime/service/boot_service.rs @@ -1,5 +1,6 @@ use core::ffi::c_void; +use axhal::mem::PhysAddr; use uefi_raw::{ Boolean, Char16, Event, Guid, Handle, PhysicalAddress, Status, protocol::device_path::DevicePathProtocol, @@ -119,7 +120,8 @@ pub unsafe extern "efiapi" fn allocate_pages( Status::SUCCESS } pub unsafe extern "efiapi" fn free_pages(addr: PhysicalAddress, pages: usize) -> Status { - unsafe { free_pages(addr, pages) }; + let phys_addr = PhysAddr::from_usize(addr.try_into().unwrap()); + let _ = crate::runtime::service::memory::free_pages(phys_addr, pages); Status::SUCCESS } diff --git a/src/runtime/service/memory.rs b/src/runtime/service/memory.rs index 584024b..2c8815e 100644 --- a/src/runtime/service/memory.rs +++ b/src/runtime/service/memory.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use axhal::{ mem::{MemoryAddr, PhysAddr, VirtAddr}, - paging::MappingFlags, + paging::MappingFlags, }; use axsync::Mutex; use uefi_raw::table::boot::MemoryType; @@ -15,7 +15,7 @@ pub(crate) enum AllocateType { MaxAddress = 1, // AllocateMaxAddress Address = 2, // AllocateAddress } - + impl TryFrom for AllocateType { type Error = (); @@ -35,7 +35,7 @@ impl From for u32 { } } -pub fn alloc_pages(alloc_type: AllocateType, memory_type: MemoryType, count: usize) -> *mut u8 { +pub fn alloc_pages(_alloc_type: AllocateType, _memory_type: MemoryType, count: usize) -> *mut u8 { let layout = core::alloc::Layout::from_size_align(count * 4096, 4096) .expect("Invalid layout for allocate_pages"); let ptr = axalloc::global_allocator() @@ -57,10 +57,10 @@ pub fn alloc_pages(alloc_type: AllocateType, memory_type: MemoryType, count: usi ptr } -pub fn free_pages(addr: PhysAddr, page: usize) {} +pub fn free_pages(_addr: PhysAddr, _page: usize) {} -pub fn allocate_pool(memory_type: MemoryType, size: usize) -> *mut u8 { +pub fn allocate_pool(_memory_type: MemoryType, _size: usize) -> *mut u8 { core::ptr::null_mut() } -pub fn free_pool(buffer: *mut u8) {} \ No newline at end of file +pub fn free_pool(_buffer: *mut u8) {} diff --git a/src/runtime/service/mod.rs b/src/runtime/service/mod.rs index cd64f13..ce0ea7c 100644 --- a/src/runtime/service/mod.rs +++ b/src/runtime/service/mod.rs @@ -4,8 +4,8 @@ use uefi_raw::table::{boot::BootServices, runtime::RuntimeServices}; use crate::runtime::service::{boot_service::Boot, runtime_service::Runtime}; -pub(crate) mod memory; pub(crate) mod boot_service; +pub(crate) mod memory; pub(crate) mod runtime_service; static BOOT_SERVICE: LazyInit> = LazyInit::new(); From 85887628c8a9fc3fd91f6003d6630790393fb629 Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 23 Sep 2025 18:26:07 +0800 Subject: [PATCH 14/21] feat(fs): implements the file protocol v1 in the simple file protocol. (#2) --- src/runtime/mod.rs | 1 + src/runtime/protocol/fs/file_protocol_v1.rs | 468 ++++++++++++++++++ src/runtime/protocol/fs/file_protocol_v2.rs | 65 +++ src/runtime/protocol/fs/mod.rs | 9 + src/runtime/protocol/fs/simple_file_system.rs | 57 +++ src/runtime/protocol/mod.rs | 2 +- src/runtime/protocol/simple_file_system.rs | 211 -------- src/runtime/system_table.rs | 2 +- src/runtime/utils.rs | 64 +++ 9 files changed, 666 insertions(+), 213 deletions(-) create mode 100644 src/runtime/protocol/fs/file_protocol_v1.rs create mode 100644 src/runtime/protocol/fs/file_protocol_v2.rs create mode 100644 src/runtime/protocol/fs/mod.rs create mode 100644 src/runtime/protocol/fs/simple_file_system.rs delete mode 100644 src/runtime/protocol/simple_file_system.rs create mode 100644 src/runtime/utils.rs diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 13fd350..541b41d 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -8,6 +8,7 @@ mod loader; mod protocol; mod service; mod system_table; +mod utils; pub fn efi_runtime_init() { let load_bootloader = loader::load_efi_file("/EFI/BOOT/BOOTRISCV64.EFI"); diff --git a/src/runtime/protocol/fs/file_protocol_v1.rs b/src/runtime/protocol/fs/file_protocol_v1.rs new file mode 100644 index 0000000..dc7f033 --- /dev/null +++ b/src/runtime/protocol/fs/file_protocol_v1.rs @@ -0,0 +1,468 @@ +use core::ffi::c_void; + +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Char16, Guid, Status, + protocol::file_system::{FileAttribute, FileMode, FileProtocolRevision, FileProtocolV1}, +}; + +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; + +use crate::runtime::{ + protocol::fs::HandleKind, + utils::{normalize_uefi_path, utf16_cstr_to_string}, +}; + +#[derive(Debug)] +pub struct FileHandlerCounter { + path: String, + count: usize, + file_handle: FileProtocolV1Impl, +} + +#[allow(dead_code)] +#[derive(Clone)] +struct DirEnt { + name: String, + is_dir: bool, + size: u64, + create_time: u64, + access_time: u64, + modify_time: u64, +} + +static FILE_HANDLER_MAP: LazyInit>> = + LazyInit::new(); + +#[repr(C)] +#[derive(Debug)] +pub struct FileProtocolV1Impl { + protocol: &'static mut FileProtocolV1, + protocol_raw: *mut FileProtocolV1, + path: String, + mode: FileMode, + kind: HandleKind, + position: usize, // current read/write position for files +} + +impl FileProtocolV1Impl { + pub fn new(path: &str, mode: FileMode, kind: HandleKind) -> Self { + let protocol = FileProtocolV1 { + revision: FileProtocolRevision::REVISION_1, + open, + close, + delete, + read, + write, + get_position, + set_position, + get_info, + set_info, + flush, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + path: path.to_string(), + mode, + kind, + position: 0, + } + } + + pub fn get_protocol(&self) -> *mut FileProtocolV1 { + self.protocol_raw + } +} + +unsafe impl Send for FileProtocolV1Impl {} +unsafe impl Sync for FileProtocolV1Impl {} + +#[inline] +unsafe fn from_this(this: *mut FileProtocolV1) -> *mut FileProtocolV1Impl { + this as *mut FileProtocolV1Impl +} + +pub fn open_root() -> *mut FileProtocolV1 { + // The OpenVolume() function opens a volume, and returns a file handle to the volume’s root directory. + // This handle is used to perform all other file I/O operations. + // The volume remains open until all the file handles to it are closed. + let mut mapper = FILE_HANDLER_MAP + .get() + .expect("Failed to get FILE_HANDLER_MAP") + .lock(); + + if let Some(counter_box) = mapper.get_mut("/") { + counter_box.count += 1; + return counter_box.file_handle.get_protocol(); + } + + let file_handle = + FileProtocolV1Impl::new("/", FileMode::READ | FileMode::WRITE, HandleKind::Dir); + let counter = FileHandlerCounter { + path: "/".to_string(), + file_handle: file_handle, + count: 1, + }; + + mapper.insert("/", counter); + let counter_box = mapper.get_mut("/").expect("inserted but missing"); + + return counter_box.file_handle.get_protocol(); +} + +// impl FileProtocolV1. Refer to UEFI Spec 2.11 Section 13.5.1. +pub extern "efiapi" fn open( + this: *mut FileProtocolV1, + new_handle: *mut *mut FileProtocolV1, + file_name: *const Char16, + open_mode: FileMode, + attributes: FileAttribute, +) -> Status { + if this.is_null() || new_handle.is_null() || file_name.is_null() { + return Status::INVALID_PARAMETER; + } + + let r = open_mode.contains(FileMode::READ); + let w = open_mode.contains(FileMode::WRITE); + let c = open_mode.contains(FileMode::CREATE); + match (r, w, c) { + (true, false, false) | (true, true, false) | (true, true, true) => {} + _ => return Status::INVALID_PARAMETER, + } + + let base = unsafe { &*(this as *mut FileProtocolV1Impl) }.path.clone(); + let name = unsafe { utf16_cstr_to_string(file_name) }.unwrap_or_default(); + let resolved = if name.is_empty() || name == "." { + base + } else { + normalize_uefi_path(&base, &name) + }; + + // Read the actual existence and type (Some(true)=directory, Some(false)=file, None=does not exist) + let (exists, exists_is_dir) = match axfs::api::metadata(&resolved) { + Ok(md) => (true, md.is_dir()), + Err(_) => (false, false), + }; + + // CREATE checks attributes and uses the DIRECTORY bit to determine the "expected type" + let want_dir = if c { + const VALID: u32 = (FileAttribute::READ_ONLY.bits() + | FileAttribute::HIDDEN.bits() + | FileAttribute::SYSTEM.bits() + | FileAttribute::DIRECTORY.bits() + | FileAttribute::ARCHIVE.bits()) as u32; + if (attributes.bits() as u32) & !VALID != 0 { + return Status::INVALID_PARAMETER; + } + attributes.contains(FileAttribute::DIRECTORY) + } else { + false + }; + + // 1. If CREATE is requested (c = true): + // - If the target already exists: we must check whether the actual type + // (exists_is_dir) matches the requested type (want_dir). + // * If they mismatch → return ACCESS_DENIED. + // * If they match → just open the existing object (need_create = false). + // - If the target does not exist: create a new object based on want_dir + // (directory vs file) (need_create = true). + // + // 2. If CREATE is not requested (c = false): + // - The target must already exist, otherwise return NOT_FOUND. + // - If it exists, simply use its actual type (final_is_dir = exists_is_dir). + // + // This logic boils down to two outputs: + // - final_is_dir: whether the resulting handle should represent a directory or a file + // - need_create : whether we must create a new object in the filesystem + let (final_is_dir, need_create) = if c { + if exists { + if exists_is_dir != want_dir { + return Status::ACCESS_DENIED; + } + (exists_is_dir, false) + } else { + (want_dir, true) + } + } else { + if !exists { + return Status::NOT_FOUND; + } + (exists_is_dir, false) + }; + + if need_create { + let res = if final_is_dir { + axfs::api::create_dir(&resolved) + } else { + axfs::api::write(&resolved, &[]) + }; + if let Err(_e) = res { + return Status::ACCESS_DENIED; + } + } + + let handle = FileProtocolV1Impl::new( + &resolved, + open_mode, + if final_is_dir { + HandleKind::Dir + } else { + HandleKind::File + }, + ); + unsafe { + *new_handle = handle.get_protocol(); + } + + Status::SUCCESS +} + +pub extern "efiapi" fn close(this: *mut FileProtocolV1) -> Status { + if this.is_null() { + return Status::INVALID_PARAMETER; + } + + if unsafe { &mut *from_this(this) }.path == "/" { + // Root directory is special: never remove it, just decrement count + let mut mapper = FILE_HANDLER_MAP + .get() + .expect("Failed to get FILE_HANDLER_MAP") + .lock(); + if let Some(counter_box) = mapper.get_mut("/") { + if counter_box.count > 0 { + counter_box.count -= 1; + } + + return Status::SUCCESS; + } + } + + unsafe { + drop(Box::from_raw(from_this(this))); + } + + Status::SUCCESS +} + +pub extern "efiapi" fn delete(this: *mut FileProtocolV1) -> Status { + if this.is_null() { + return Status::INVALID_PARAMETER; + } + let this = unsafe { &mut *from_this(this) }; + let path = this.path.clone(); + + let result = match this.kind { + HandleKind::Dir => axfs::api::remove_dir(&path), + HandleKind::File => axfs::api::remove_file(&path), + }; + + if let Err(_e) = result { + return Status::WARN_DELETE_FAILURE; + } + + unsafe { + drop(Box::from_raw(from_this(this.protocol))); + } + + Status::SUCCESS +} + +pub extern "efiapi" fn read( + this: *mut FileProtocolV1, + buffer_size: *mut usize, + buffer: *mut c_void, +) -> Status { + if this.is_null() || buffer_size.is_null() { + return Status::INVALID_PARAMETER; + } + + let this = unsafe { &mut *from_this(this) }; + + if !this.mode.contains(FileMode::READ) { + return Status::ACCESS_DENIED; + } + + match this.kind { + HandleKind::Dir => { + // TODO: implement directory reading + // The spec does not seem to explicitly mention directory entries, + // and axfs has limited capabilities and may not be able to construct valid information, + // so it is not implemented yet. + return Status::UNSUPPORTED; + } + HandleKind::File => { + if buffer.is_null() { + return Status::INVALID_PARAMETER; + } + + let want = unsafe { *buffer_size }; + if want == 0 { + return Status::SUCCESS; + } + + let data = match axfs::api::read(&this.path) { + Ok(v) => v, + Err(_) => return Status::DEVICE_ERROR, + }; + + let len = data.len(); + let pos = this.position; + + if pos > len { + return Status::DEVICE_ERROR; + } + + let remain = len - pos; + let take = core::cmp::min(remain, want); + + if take > 0 { + let src = &data[pos..pos + take]; + let dst = unsafe { core::slice::from_raw_parts_mut(buffer as *mut u8, take) }; + dst.copy_from_slice(src); + this.position = pos + take; + } + + unsafe { *buffer_size = take }; + Status::SUCCESS + } + } +} + +pub extern "efiapi" fn write( + this: *mut FileProtocolV1, + buffer_size: *mut usize, + buffer: *const c_void, +) -> Status { + if this.is_null() || buffer_size.is_null() { + return Status::INVALID_PARAMETER; + } + + let this = unsafe { &mut *from_this(this) }; + if !this.mode.contains(FileMode::WRITE) { + return Status::ACCESS_DENIED; + } + + match this.kind { + HandleKind::Dir => { + return Status::UNSUPPORTED; + } + HandleKind::File => { + let want = unsafe { *buffer_size }; + if want == 0 { + return Status::SUCCESS; + } + + if buffer.is_null() { + return Status::INVALID_PARAMETER; + } + + let mut data = match axfs::api::read(&this.path) { + Ok(v) => v, + Err(_) => Vec::new(), + }; + + let pos = this.position; + if pos > data.len() { + return Status::DEVICE_ERROR; + } + + if data.len() < pos { + data.resize(pos, 0); + } + + let end = pos.saturating_add(want); + if data.len() < end { + data.resize(end, 0); + } + + let src = unsafe { core::slice::from_raw_parts(buffer as *const u8, want) }; + data[pos..end].copy_from_slice(src); + if let Err(_e) = axfs::api::write(&this.path, &data) { + return Status::DEVICE_ERROR; + } + this.position = end; + unsafe { *buffer_size = want }; + Status::SUCCESS + } + } +} + +pub extern "efiapi" fn get_position(this: *const FileProtocolV1, position: *mut u64) -> Status { + if this.is_null() || position.is_null() { + return Status::INVALID_PARAMETER; + } + let this = unsafe { &*from_this(this as *mut FileProtocolV1) }; + + match this.kind { + HandleKind::Dir => Status::UNSUPPORTED, + HandleKind::File => { + unsafe { + *position = this.position as u64; + } + Status::SUCCESS + } + } +} + +pub extern "efiapi" fn set_position(this: *mut FileProtocolV1, position: u64) -> Status { + if this.is_null() { + return Status::INVALID_PARAMETER; + } + let this = unsafe { &mut *from_this(this) }; + + match this.kind { + HandleKind::Dir => Status::UNSUPPORTED, + HandleKind::File => { + if position == u64::MAX { + // 定位到 EOF:取当前文件长度 + let size = match axfs::api::metadata(&this.path) { + Ok(md) => md.len() as u64, + Err(_) => return Status::DEVICE_ERROR, + }; + if let Ok(sz) = usize::try_from(size) { + this.position = sz; + Status::SUCCESS + } else { + Status::INVALID_PARAMETER + } + } else { + if let Ok(p) = usize::try_from(position) { + this.position = p; + Status::SUCCESS + } else { + Status::INVALID_PARAMETER + } + } + } + } +} + +pub extern "efiapi" fn get_info( + _this: *mut FileProtocolV1, + _information_type: *const Guid, + _buffer_size: *mut usize, + _buffer: *mut c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn set_info( + _this: *mut FileProtocolV1, + _information_type: *const Guid, + _buffer_size: usize, + _buffer: *const c_void, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn flush(_this: *mut FileProtocolV1) -> Status { + Status::UNSUPPORTED +} diff --git a/src/runtime/protocol/fs/file_protocol_v2.rs b/src/runtime/protocol/fs/file_protocol_v2.rs new file mode 100644 index 0000000..f293915 --- /dev/null +++ b/src/runtime/protocol/fs/file_protocol_v2.rs @@ -0,0 +1,65 @@ +use uefi_raw::{ + Char16, Status, + protocol::file_system::{FileAttribute, FileIoToken, FileMode, FileProtocolV2}, +}; + +use crate::runtime::protocol::fs::{HandleKind, file_protocol_v1::FileProtocolV1Impl}; + +use alloc::boxed::Box; + +#[repr(C)] +#[derive(Debug)] +pub struct FileProtocolV2Impl { + protocol: &'static mut FileProtocolV2, + protocol_raw: *mut FileProtocolV2, +} + +impl FileProtocolV2Impl { + pub fn new(path: &str, mode: FileMode, kind: HandleKind) -> Self { + let v1 = FileProtocolV1Impl::new(path, mode, kind); // reuse V1 implementation + let protocol = FileProtocolV2 { + v1: unsafe { *Box::from_raw(v1.get_protocol()) }, // clone contents, not share pointer + open_ex, + read_ex, + write_ex, + flush_ex, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut FileProtocolV2 { + self.protocol_raw + } +} + +unsafe impl Send for FileProtocolV2Impl {} +unsafe impl Sync for FileProtocolV2Impl {} + +// v2-specific. Refer to UEFI Spec 2.11 Section 13.5.7 - 13.5.10. +pub extern "efiapi" fn open_ex( + _this: *mut FileProtocolV2, + _new_handle: *mut *mut FileProtocolV2, + _file_name: *const Char16, + _open_mode: FileMode, + _attributes: FileAttribute, + _token: *mut FileIoToken, +) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn read_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn write_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { + Status::UNSUPPORTED +} + +pub extern "efiapi" fn flush_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { + Status::UNSUPPORTED +} diff --git a/src/runtime/protocol/fs/mod.rs b/src/runtime/protocol/fs/mod.rs new file mode 100644 index 0000000..cbc97a1 --- /dev/null +++ b/src/runtime/protocol/fs/mod.rs @@ -0,0 +1,9 @@ +pub(crate) mod file_protocol_v1; +pub(crate) mod file_protocol_v2; +pub(crate) mod simple_file_system; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum HandleKind { + Dir, + File, +} diff --git a/src/runtime/protocol/fs/simple_file_system.rs b/src/runtime/protocol/fs/simple_file_system.rs new file mode 100644 index 0000000..75fe541 --- /dev/null +++ b/src/runtime/protocol/fs/simple_file_system.rs @@ -0,0 +1,57 @@ +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Status, + protocol::file_system::{FileProtocolV1, SimpleFileSystemProtocol}, +}; + +use alloc::boxed::Box; + +use crate::runtime::protocol::fs::file_protocol_v1::open_root; + +static TEXT_FILE_SYSTEM: LazyInit> = LazyInit::new(); + +#[repr(C)] +#[derive(Debug)] +pub struct SimpleFileSystem { + protocol: &'static mut SimpleFileSystemProtocol, + protocol_raw: *mut SimpleFileSystemProtocol, +} + +impl SimpleFileSystem { + pub fn new() -> Self { + let protocol = SimpleFileSystemProtocol { + revision: 0x00010000, + open_volume, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut SimpleFileSystemProtocol { + self.protocol_raw + } +} + +unsafe impl Send for SimpleFileSystem {} +unsafe impl Sync for SimpleFileSystem {} + +pub fn init_simple_file_system() { + TEXT_FILE_SYSTEM.init_once(Mutex::new(SimpleFileSystem::new())); +} + +// impl SimpleFileSystem. Refer to UEFI Spec 2.11 Section 13.4. +pub extern "efiapi" fn open_volume( + _this: *mut SimpleFileSystemProtocol, + root: *mut *mut FileProtocolV1, +) -> Status { + unsafe { + *root = open_root(); + } + + Status::SUCCESS +} diff --git a/src/runtime/protocol/mod.rs b/src/runtime/protocol/mod.rs index d08970d..dfb4978 100644 --- a/src/runtime/protocol/mod.rs +++ b/src/runtime/protocol/mod.rs @@ -1,6 +1,6 @@ pub(crate) mod block_io; pub(crate) mod device_path; +pub(crate) mod fs; pub(crate) mod graphics_output; pub(crate) mod handle; -pub(crate) mod simple_file_system; pub(crate) mod simple_text_output; diff --git a/src/runtime/protocol/simple_file_system.rs b/src/runtime/protocol/simple_file_system.rs deleted file mode 100644 index 8b90034..0000000 --- a/src/runtime/protocol/simple_file_system.rs +++ /dev/null @@ -1,211 +0,0 @@ -use core::ffi::c_void; - -use axsync::Mutex; -use lazyinit::LazyInit; -use uefi_raw::{ - Char16, Guid, Status, - protocol::file_system::{ - FileAttribute, FileIoToken, FileMode, FileProtocolRevision, FileProtocolV1, FileProtocolV2, - SimpleFileSystemProtocol, - }, -}; - -use alloc::boxed::Box; - -static TEXT_FILE_SYSTEM: LazyInit> = LazyInit::new(); - -#[derive(Debug)] -pub struct SimpleFileSystem { - protocol: &'static mut SimpleFileSystemProtocol, - protocol_raw: *mut SimpleFileSystemProtocol, -} - -impl SimpleFileSystem { - pub fn new() -> Self { - let protocol = SimpleFileSystemProtocol { - revision: 0x00010000, - open_volume, - }; - let protocol_raw = Box::into_raw(Box::new(protocol)); - let protocol = unsafe { &mut *protocol_raw }; - Self { - protocol, - protocol_raw, - } - } - - pub fn get_protocol(&self) -> *mut SimpleFileSystemProtocol { - self.protocol_raw - } -} - -unsafe impl Send for SimpleFileSystem {} -unsafe impl Sync for SimpleFileSystem {} - -pub fn init_simple_file_system() { - TEXT_FILE_SYSTEM.init_once(Mutex::new(SimpleFileSystem::new())); -} - -pub extern "efiapi" fn open_volume( - _this: *mut SimpleFileSystemProtocol, - _root: *mut *mut FileProtocolV1, -) -> Status { - Status::UNSUPPORTED -} - -#[derive(Debug)] -pub struct FileProtocolV1Impl { - protocol: &'static mut FileProtocolV1, - protocol_raw: *mut FileProtocolV1, -} - -impl FileProtocolV1Impl { - pub fn new() -> Self { - let protocol = FileProtocolV1 { - revision: FileProtocolRevision::REVISION_1, - open, - close, - delete, - read, - write, - get_position, - set_position, - get_info, - set_info, - flush, - }; - let protocol_raw = Box::into_raw(Box::new(protocol)); - let protocol = unsafe { &mut *protocol_raw }; - Self { - protocol, - protocol_raw, - } - } - - pub fn get_protocol(&self) -> *mut FileProtocolV1 { - self.protocol_raw - } -} - -unsafe impl Send for FileProtocolV1Impl {} -unsafe impl Sync for FileProtocolV1Impl {} - -#[derive(Debug)] -pub struct FileProtocolV2Impl { - protocol: &'static mut FileProtocolV2, - protocol_raw: *mut FileProtocolV2, -} - -impl FileProtocolV2Impl { - pub fn new() -> Self { - let v1 = FileProtocolV1Impl::new(); // reuse V1 implementation - let protocol = FileProtocolV2 { - v1: unsafe { *Box::from_raw(v1.get_protocol()) }, // clone contents, not share pointer - open_ex, - read_ex, - write_ex, - flush_ex, - }; - let protocol_raw = Box::into_raw(Box::new(protocol)); - let protocol = unsafe { &mut *protocol_raw }; - Self { - protocol, - protocol_raw, - } - } - - pub fn get_protocol(&self) -> *mut FileProtocolV2 { - self.protocol_raw - } -} - -unsafe impl Send for FileProtocolV2Impl {} -unsafe impl Sync for FileProtocolV2Impl {} - -pub extern "efiapi" fn open( - _this: *mut FileProtocolV1, - _new_handle: *mut *mut FileProtocolV1, - _file_name: *const Char16, - _open_mode: FileMode, - _attributes: FileAttribute, -) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn close(_this: *mut FileProtocolV1) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn delete(_this: *mut FileProtocolV1) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn read( - _this: *mut FileProtocolV1, - _buffer_size: *mut usize, - _buffer: *mut c_void, -) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn write( - _this: *mut FileProtocolV1, - _buffer_size: *mut usize, - _buffer: *const c_void, -) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn get_position(_this: *const FileProtocolV1, _position: *mut u64) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn set_position(_this: *mut FileProtocolV1, _position: u64) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn get_info( - _this: *mut FileProtocolV1, - _information_type: *const Guid, - _buffer_size: *mut usize, - _buffer: *mut c_void, -) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn set_info( - _this: *mut FileProtocolV1, - _information_type: *const Guid, - _buffer_size: usize, - _buffer: *const c_void, -) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn flush(_this: *mut FileProtocolV1) -> Status { - Status::UNSUPPORTED -} - -// v2-specific -pub extern "efiapi" fn open_ex( - _this: *mut FileProtocolV2, - _new_handle: *mut *mut FileProtocolV2, - _file_name: *const Char16, - _open_mode: FileMode, - _attributes: FileAttribute, - _token: *mut FileIoToken, -) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn read_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn write_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { - Status::UNSUPPORTED -} - -pub extern "efiapi" fn flush_ex(_this: *mut FileProtocolV2, _token: *mut FileIoToken) -> Status { - Status::UNSUPPORTED -} diff --git a/src/runtime/system_table.rs b/src/runtime/system_table.rs index 713bea5..c6fb15d 100644 --- a/src/runtime/system_table.rs +++ b/src/runtime/system_table.rs @@ -50,7 +50,7 @@ pub fn init_system_table() { #[cfg(feature = "display")] crate::runtime::protocol::graphics_output::init_graphics_output(); #[cfg(feature = "fs")] - crate::runtime::protocol::simple_file_system::init_simple_file_system(); + crate::runtime::protocol::fs::simple_file_system::init_simple_file_system(); let simple_text_output = { init_simple_text_output(); get_simple_text_output().lock().get_protocol() diff --git a/src/runtime/utils.rs b/src/runtime/utils.rs new file mode 100644 index 0000000..35c91c1 --- /dev/null +++ b/src/runtime/utils.rs @@ -0,0 +1,64 @@ +use alloc::format; +use alloc::{string::String, string::ToString, vec::Vec}; + +/// Convert a UEFI CHAR16* (NUL-terminated) to a Rust UTF-8 String. +/// Returns None if the pointer is NULL or decoding fails. +pub unsafe fn utf16_cstr_to_string(p: *const uefi_raw::Char16) -> Option { + if p.is_null() { + return None; + } + // Count length until NUL terminator + let mut len = 0usize; + loop { + let ch = unsafe { *p.add(len) }; + if ch == 0 { + break; + } + len += 1; + } + let slice = unsafe { core::slice::from_raw_parts(p as *const u16, len) }; + String::from_utf16(slice).ok() +} + +/// Normalize a UEFI-style path: +/// - Uses backslash '\' as separator +/// - If name starts with '\' → absolute path from volume root +/// - Otherwise path is relative to `base` +/// - Handles empty string, ".", ".." +/// - Root directory is represented as "\" (a single backslash) +pub fn normalize_uefi_path(base: &str, name: &str) -> String { + if name.is_empty() || name == "." { + return base.to_string(); + } + + let combined = if name.starts_with('\\') { + name.to_string() + } else if base == "\\" { + format!("\\{name}") + } else if base.ends_with('\\') { + format!("{base}{name}") + } else { + format!("{base}\\{name}") + }; + + // Split into components, remove ".", handle ".." + let mut parts: Vec<&str> = Vec::new(); + for seg in combined.split('\\') { + if seg.is_empty() || seg == "." { + continue; + } + if seg == ".." { + if !parts.is_empty() { + parts.pop(); + } + continue; + } + parts.push(seg); + } + + if parts.is_empty() { + "\\".to_string() + } else { + format!("\\{}", parts.join("\\")) + } +} From 32af3b4d6153755f810751a4daed8faf98b4baaa Mon Sep 17 00:00:00 2001 From: hanbings Date: Sat, 27 Sep 2025 02:22:58 +0800 Subject: [PATCH 15/21] feat(gop): implements the graphics output protocol via axdisplay. (#3) * feat: initialize gop through axdisplay. * feat: full implementation of gop via axdisplay. * fix: fix build. * typo: fix make's subcommands for running qemu. --- Cargo.lock | 1 + Cargo.toml | 2 +- Makefile | 3 + README.md | 14 ++ scripts/make/build.mk | 12 +- src/runtime/protocol/graphics_output.rs | 254 ++++++++++++++++++++++-- src/runtime/system_table.rs | 2 + 7 files changed, 273 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d916e5..85ecb85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,6 +249,7 @@ source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2 dependencies = [ "axdriver_base", "axdriver_block", + "axdriver_display", "virtio-drivers", ] diff --git a/Cargo.toml b/Cargo.toml index 0d6122e..bb77a19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ axconfig = { git = "https://github.com/arceos-org/arceos.git" } axsync = { git = "https://github.com/arceos-org/arceos.git" } axalloc = { git = "https://github.com/arceos-org/arceos.git", optional = true } axmm = { git = "https://github.com/arceos-org/arceos.git", optional = true } -axdriver = { git = "https://github.com/arceos-org/arceos.git", optional = true, features = ["virtio-blk"] } +axdriver = { git = "https://github.com/arceos-org/arceos.git", optional = true, features = ["virtio-blk", "virtio-gpu"] } axfs = { git = "https://github.com/arceos-org/arceos.git", optional = true } axnet = { git = "https://github.com/arceos-org/arceos.git", optional = true } axdisplay = { git = "https://github.com/arceos-org/arceos.git", optional = true } diff --git a/Makefile b/Makefile index 76b2349..0496d13 100644 --- a/Makefile +++ b/Makefile @@ -47,3 +47,6 @@ build: clean defconfig all qemu-run: qemu-system-riscv64 -m 128M -serial mon:stdio -bios $(SBI) -nographic -machine virt -nographic -machine virt -device virtio-blk-pci,drive=disk0 -drive id=disk0,if=none,format=raw,file=$(DISK) + +qemu-display: + qemu-system-riscv64 -m 128M -serial mon:stdio -bios $(SBI) -machine virt -device virtio-blk-pci,drive=disk0 -drive id=disk0,if=none,format=raw,file=$(DISK) -device virtio-gpu-pci,xres=1024,yres=768 diff --git a/README.md b/README.md index 2a6be38..c71aa3a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # ArceBoot Reuse [ArceOS](https://github.com/arceos-org/arceos) components to build a cross-platform operating system bootloader + +# Build + +```bash +# for serial output build: +$ make +$ make qemu-run + +# for qemu virtual monitor: +# this may require a desktop system or graphical infrastructure +# such as x11 forwarding configured on your host machine. +$ make EXACT_FEATURES="display" +$ make qemu-display +``` \ No newline at end of file diff --git a/scripts/make/build.mk b/scripts/make/build.mk index a4df56a..256b2cf 100644 --- a/scripts/make/build.mk +++ b/scripts/make/build.mk @@ -8,6 +8,16 @@ ELF := $(ROOT_DIR)/target/$(TARGET)/release/arceboot BIN := $(ROOT_DIR)/target/$(TARGET)/release/arceboot.bin RUSTSBI_DIR := $(ROOT_DIR)/rustsbi +# 用逗号或者空格分隔 feature 名称 +EXACT_FEATURES ?= +EXACT_FEATURES_CLEANED := $(subst $(space),$(comma),$(strip $(EXACT_FEATURES))) + +ifeq ($(strip $(EXACT_FEATURES)),) + FEATURE_ARGS := +else + FEATURE_ARGS := --features '$(EXACT_FEATURES_CLEANED)' +endif + # 彩色打印 define print_info @printf "\033[1;37m%s\033[0m" "[RustSBI-Arceboot Build] " @@ -22,7 +32,7 @@ all: build-arceboot extract-bin clone-rustsbi build-rustsbi build-arceboot: $(call print_info,开始编译 ArceBoot...) - cargo rustc --release --target $(TARGET) -- \ + cargo rustc --release --target $(TARGET) $(FEATURE_ARGS) -- \ -C opt-level=z \ -C panic=abort \ -C relocation-model=static \ diff --git a/src/runtime/protocol/graphics_output.rs b/src/runtime/protocol/graphics_output.rs index 5e7e3e0..cbc2fb1 100644 --- a/src/runtime/protocol/graphics_output.rs +++ b/src/runtime/protocol/graphics_output.rs @@ -6,7 +6,7 @@ use uefi_raw::{ Status, protocol::console::{ GraphicsOutputBltOperation, GraphicsOutputBltPixel, GraphicsOutputModeInformation, - GraphicsOutputProtocol, GraphicsOutputProtocolMode, + GraphicsOutputProtocol, GraphicsOutputProtocolMode, GraphicsPixelFormat, PixelBitmask, }, }; @@ -16,17 +16,55 @@ static GRAPHICS_OUTPUT: LazyInit> = LazyInit::new(); pub struct GraphicsOutput { protocol: &'static mut GraphicsOutputProtocol, protocol_raw: *mut GraphicsOutputProtocol, + + mode_box: *mut GraphicsOutputProtocolMode, + info_box: *mut GraphicsOutputModeInformation, + + width: u32, + height: u32, + fb_base_vaddr: usize, + fb_size: usize, + stride_pixels: u32, } impl GraphicsOutput { - pub fn new() -> Self { - let mode: *mut GraphicsOutputProtocolMode = core::ptr::null_mut(); + pub fn new( + width: u32, + height: u32, + fb_base_vaddr: usize, + fb_size: usize, + stride_pixels: u32, + ) -> Self { + let info = GraphicsOutputModeInformation { + version: 0, + horizontal_resolution: width, + vertical_resolution: height, + pixel_format: GraphicsPixelFormat::PIXEL_BLUE_GREEN_RED_RESERVED_8_BIT_PER_COLOR, + pixel_information: PixelBitmask { + red: 0, + green: 0, + blue: 0, + reserved: 0, + }, + pixels_per_scan_line: stride_pixels, + }; + let info_box = Box::into_raw(Box::new(info)); + + let mode = GraphicsOutputProtocolMode { + max_mode: 1, + mode: 0, + info: info_box, + size_of_info: size_of::(), + frame_buffer_base: fb_base_vaddr as u64, + frame_buffer_size: fb_size, + }; + let mode_box = Box::into_raw(Box::new(mode)); let protocol = GraphicsOutputProtocol { query_mode, set_mode, blt, - mode, + mode: mode_box, }; let protocol_raw = Box::into_raw(Box::new(protocol)); @@ -35,6 +73,13 @@ impl GraphicsOutput { Self { protocol, protocol_raw, + mode_box, + info_box, + width, + height, + fb_base_vaddr, + fb_size, + stride_pixels, } } @@ -54,10 +99,23 @@ impl Drop for GraphicsOutput { } } +#[inline] +fn with_go(_this: *const GraphicsOutputProtocol, f: F) -> Option +where + F: FnOnce(&GraphicsOutput) -> R, +{ + GRAPHICS_OUTPUT.get().map(|m| { + let lock = m.lock(); + f(&*lock) + }) +} + pub fn init_graphics_output() { #[cfg(feature = "display")] { let display_info = axdisplay::framebuffer_info(); + info!("Graphics Output Protocol initialized: {:?}", display_info); + let frame_buffer_base = display_info.fb_base_vaddr; let frame_buffer_size = display_info.fb_size; @@ -67,26 +125,196 @@ pub fn init_graphics_output() { axdisplay::framebuffer_flush(); - GRAPHICS_OUTPUT.init_once(Mutex::new(GraphicsOutput::new())); + GRAPHICS_OUTPUT.init_once(Mutex::new(GraphicsOutput::new( + display_info.width, + display_info.height, + display_info.fb_base_vaddr, + display_info.fb_size, + // FIXME: in most real device environments, pixels are not equal to width. + // refer: https://docs.rs/bootloader_api/latest/bootloader_api/info/struct.FrameBufferInfo.html#structfield.stride + display_info.width, + ))); } } pub unsafe extern "efiapi" fn query_mode( - _this: *const GraphicsOutputProtocol, - _mode_number: u32, - _size_of_info: *mut usize, - _info: *mut *const GraphicsOutputModeInformation, + this: *const GraphicsOutputProtocol, + mode_number: u32, + size_of_info: *mut usize, + info: *mut *const GraphicsOutputModeInformation, ) -> Status { - Status::UNSUPPORTED + if mode_number != 0 { + return Status::UNSUPPORTED; + } + if size_of_info.is_null() || info.is_null() { + return Status::INVALID_PARAMETER; + } + + match with_go(this, |go| (go.mode_box, go.info_box)) { + Some((mode_box, info_box)) => unsafe { + let mode = &*mode_box; + *size_of_info = mode.size_of_info; + *info = info_box as *const GraphicsOutputModeInformation; + Status::SUCCESS + }, + None => Status::DEVICE_ERROR, + } } pub unsafe extern "efiapi" fn set_mode( - _this: *mut GraphicsOutputProtocol, - _mode_number: u32, + this: *mut GraphicsOutputProtocol, + mode_number: u32, ) -> Status { - Status::UNSUPPORTED + if mode_number != 0 { + return Status::UNSUPPORTED; + } + match with_go(this, |go| go.mode_box) { + Some(mode_box) => { + let mode = unsafe { &mut *mode_box }; + mode.mode = 0; + // TODO: if resolution switching is supported in the future, + // info / stride / fb will be updated here, etc. + Status::SUCCESS + } + None => Status::DEVICE_ERROR, + } +} + +#[cfg(feature = "display")] +pub unsafe extern "efiapi" fn blt( + this: *mut GraphicsOutputProtocol, + blt_buffer: *mut GraphicsOutputBltPixel, + blt_operation: GraphicsOutputBltOperation, + source_x: usize, + source_y: usize, + destination_x: usize, + destination_y: usize, + width: usize, + height: usize, + delta: usize, +) -> Status { + let Some((fb_base, fb_size, w, h, stride_px)) = with_go(this, |go| { + ( + go.fb_base_vaddr, + go.fb_size, + go.width as usize, + go.height as usize, + go.stride_pixels as usize, + ) + }) else { + return Status::DEVICE_ERROR; + }; + + if width == 0 || height == 0 { + return Status::SUCCESS; + } + + if destination_x >= w + || destination_y >= h + || destination_x + width > w + || destination_y + height > h + { + return Status::INVALID_PARAMETER; + } + + let bytes_per_pixel = 4usize; + let fb_pitch = stride_px * bytes_per_pixel; + let fb = fb_base as *mut u8; + + unsafe { + match blt_operation { + GraphicsOutputBltOperation::BLT_VIDEO_FILL => { + // fill (dest_x, dest_y, width, height) with the color of blt_buffer[0] + if blt_buffer.is_null() { + return Status::INVALID_PARAMETER; + } + let px = *blt_buffer; + let color = [px.blue, px.green, px.red, 0]; // BGRA + for row in 0..height { + let row_ptr = + fb.add((destination_y + row) * fb_pitch + destination_x * bytes_per_pixel); + for col in 0..width { + let p = row_ptr.add(col * bytes_per_pixel); + // 写 B,G,R,A + *p.add(0) = color[0]; + *p.add(1) = color[1]; + *p.add(2) = color[2]; + *p.add(3) = color[3]; + } + } + axdisplay::framebuffer_flush(); + Status::SUCCESS + } + GraphicsOutputBltOperation::BLT_VIDEO_TO_BLT_BUFFER => { + if blt_buffer.is_null() { + return Status::INVALID_PARAMETER; + } + + // Each row in BLT buffer: if Delta == 0, tightly packed (Width * 4 bytes). + let dst_pitch = if delta == 0 { + width * bytes_per_pixel + } else { + delta + }; + + for row in 0..height { + // Source: read pixels from framebuffer at (source_x, source_y + row). + let src = fb.add((source_y + row) * fb_pitch + source_x * bytes_per_pixel); + + // Destination: write into BLT buffer at (destination_x, destination_y + row). + let dst = (blt_buffer as *mut u8) + .add((destination_y + row) * dst_pitch + destination_x * bytes_per_pixel); + + // Copy one scan line (Width * 4 bytes). + core::ptr::copy_nonoverlapping(src, dst, width * bytes_per_pixel); + } + + Status::SUCCESS + } + GraphicsOutputBltOperation::BLT_BUFFER_TO_VIDEO => { + if blt_buffer.is_null() { + return Status::INVALID_PARAMETER; + } + // in UEFI GOP spec, Delta = bytes per scan line in the BLT buffer. + // if Delta == 0, it means the buffer is tightly packed: Width * sizeof(BltPixel) = Width * 4. + let src_pitch = if delta == 0 { + width * bytes_per_pixel + } else { + delta + }; + + for row in 0..height { + let dst = + fb.add((destination_y + row) * fb_pitch + destination_x * bytes_per_pixel); + let src = (blt_buffer as *const u8) + .add((source_y + row) * src_pitch + source_x * bytes_per_pixel); + core::ptr::copy_nonoverlapping(src, dst, width * bytes_per_pixel); + } + axdisplay::framebuffer_flush(); + Status::SUCCESS + } + GraphicsOutputBltOperation::BLT_VIDEO_TO_VIDEO => { + // internal memory transfer, supporting overlap (memmove semantics) + let src_x = source_x; + let src_y = source_y; + if src_x >= w || src_y >= h || src_x + width > w || src_y + height > h { + return Status::INVALID_PARAMETER; + } + for row in 0..height { + let dst = + fb.add((destination_y + row) * fb_pitch + destination_x * bytes_per_pixel); + let src = fb.add((src_y + row) * fb_pitch + src_x * bytes_per_pixel); + core::ptr::copy(src, dst, width * bytes_per_pixel); + } + axdisplay::framebuffer_flush(); + Status::SUCCESS + } + _ => Status::UNSUPPORTED, + } + } } +#[cfg(not(feature = "display"))] pub unsafe extern "efiapi" fn blt( _this: *mut GraphicsOutputProtocol, _blt_buffer: *mut GraphicsOutputBltPixel, diff --git a/src/runtime/system_table.rs b/src/runtime/system_table.rs index c6fb15d..919bc26 100644 --- a/src/runtime/system_table.rs +++ b/src/runtime/system_table.rs @@ -47,10 +47,12 @@ pub fn init_system_table() { // Initialize the protocols init_block_io(); init_device_path(); + #[cfg(feature = "display")] crate::runtime::protocol::graphics_output::init_graphics_output(); #[cfg(feature = "fs")] crate::runtime::protocol::fs::simple_file_system::init_simple_file_system(); + let simple_text_output = { init_simple_text_output(); get_simple_text_output().lock().get_protocol() From b9e05a043f84da3188c8a32d9955887457964c1f Mon Sep 17 00:00:00 2001 From: hanbings Date: Sat, 27 Sep 2025 22:16:14 +0800 Subject: [PATCH 16/21] test(allocate): added test program for allocate_page(). (#4) * test(allocate): added test program for allocate_page(). * chore(uefi): check script for test allocate_page(). * chore(uefi): clean code. --- scripts/test/build_edk2.sh | 13 ++- scripts/test/check_allocate_test.sh | 18 ++++ tests/edk2-AllocatePage/AllocatePage.c | 128 +++++++++++++++++++++++ tests/edk2-AllocatePage/AllocatePage.dsc | 14 +++ tests/edk2-AllocatePage/AllocatePage.inf | 16 +++ tests/edk2-AllocatePage/payload.S | 22 ++++ tests/edk2-AllocatePage/payload_embed.S | 21 ++++ 7 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 scripts/test/check_allocate_test.sh create mode 100644 tests/edk2-AllocatePage/AllocatePage.c create mode 100644 tests/edk2-AllocatePage/AllocatePage.dsc create mode 100644 tests/edk2-AllocatePage/AllocatePage.inf create mode 100644 tests/edk2-AllocatePage/payload.S create mode 100644 tests/edk2-AllocatePage/payload_embed.S diff --git a/scripts/test/build_edk2.sh b/scripts/test/build_edk2.sh index 6cc967d..cea1ed4 100644 --- a/scripts/test/build_edk2.sh +++ b/scripts/test/build_edk2.sh @@ -7,7 +7,7 @@ EDK_DIR="$WORKSPACE_DIR/edk2" mkdir -p "$WORKSPACE_DIR" -echo "[1/3] 准备 EDK2 构建环境..." +echo "[1/4] 准备 EDK2 构建环境..." cd "$WORKSPACE_DIR" if [ ! -d "ToolChain/RISCV" ]; then @@ -19,7 +19,7 @@ else echo "RISCV 工具链已存在,跳过下载。" fi -echo "[2/3] 克隆 EDK2 仓库..." +echo "[2/4] 克隆 EDK2 仓库..." if [ ! -d "$EDK_DIR" ]; then git clone --recurse-submodule https://github.com/tianocore/edk2.git "$EDK_DIR" else @@ -28,7 +28,7 @@ fi export PATH="$WORKSPACE_DIR/ToolChain/RISCV/riscv/bin:$PATH" -echo "[3/3] 构建 EDK2..." +echo "[3/4] 构建 EDK2..." cd "$WORKSPACE_DIR" export WORKSPACE=`pwd` @@ -40,6 +40,7 @@ export EDK_TOOLS_PATH=$WORKSPACE/edk2/BaseTools make -C edk2/BaseTools . "$EDK_DIR/edksetup.sh" BaseTools +echo "[4/4] 准备 HelloRiscv 和 AllocatePage 示例..." # mkdir -p "$EDK_DIR/Hello" # cp -r "$PROJECT_ROOT/tests/edk2-Hello" "$EDK_DIR" # mv "$EDK_DIR/edk2-Hello"/* "$EDK_DIR/Hello/" @@ -50,4 +51,8 @@ cp -r "$PROJECT_ROOT/tests/edk2-HelloRiscv" "$EDK_DIR" mv "$EDK_DIR/edk2-HelloRiscv" "$EDK_DIR/HelloRiscv/" build -a RISCV64 -t GCC5 -p "$EDK_DIR/HelloRiscv/HelloRiscv.dsc" -echo "EDK2 构建完成。生成的镜像位于:$WORKSPACE_DIR/Build/DEBUG_GCC5/RISCV64/HelloRiscv.efi" +cp -r "$PROJECT_ROOT/tests/edk2-AllocatePage" "$EDK_DIR" +mv "$EDK_DIR/edk2-AllocatePage" "$EDK_DIR/AllocatePage/" +build -a RISCV64 -t GCC5 -p "$EDK_DIR/AllocatePage/AllocatePage.dsc" + +echo "EDK2 与示例构建完成。生成的文件位于:$WORKSPACE_DIR/Build/DEBUG_GCC5/RISCV64" diff --git a/scripts/test/check_allocate_test.sh b/scripts/test/check_allocate_test.sh new file mode 100644 index 0000000..754f8ca --- /dev/null +++ b/scripts/test/check_allocate_test.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +LOG_FILE="qemu.log" +TARGET_STRING="EFI Output: 0xDEADBEEF12345678" + +if [ ! -f "$LOG_FILE" ]; then + echo "❌ $LOG_FILE 不存在" + exit 1 +fi + +if grep -qF "$TARGET_STRING" "$LOG_FILE"; then + echo "✅ 找到匹配日志行" + exit 0 +else + echo "❌ 未找到匹配日志行" + exit 2 +fi \ No newline at end of file diff --git a/tests/edk2-AllocatePage/AllocatePage.c b/tests/edk2-AllocatePage/AllocatePage.c new file mode 100644 index 0000000..ffb680e --- /dev/null +++ b/tests/edk2-AllocatePage/AllocatePage.c @@ -0,0 +1,128 @@ +#ifndef EFIAPI +#define EFIAPI +#endif + +/* basic integer aliases */ +typedef unsigned long long u64; +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; + +/* ---- Offsets (match the layout that produced SystemTable) ---- */ +enum { OFF_ST_CONOUT = 64 }; /* EfiSystemTable->conOut */ +enum { OFF_ST_BOOTSERVICES = 96 }; /* EfiSystemTable->bootServices */ +enum { OFF_CONOUT_OUTPUTSTRING = 8 };/* EfiSimpleTextOutputProtocol->output_string */ +enum { OFF_BS_ALLOCATEPAGES = 40 }; /* BootServices->AllocatePages */ + +/* AllocatePages() parameters */ +enum { + kAllocateAnyPages = 0, + kEfiBootServicesCode = 3 +}; + + +#define MAX_OUTPUT_CHARS 260 + +typedef u64 (*EfiTextString)(void* This, u16* String); +typedef u64 (*EfiAllocatePages)(u32 Type, u32 MemoryType, u64 Pages, u64* Memory); + +/* + * Ensure instruction cache is synchronized with recently written code. + * On RISC-V, `fence.i` flushes the instruction pipeline and makes sure + * subsequent instruction fetches observe the updated memory contents. + */ +static inline void fence_i(void) { __asm__ __volatile__("fence.i" ::: "memory"); } + +static void mem_copy(void* dst, const void* src, u64 n) { + u8* d = (u8*)dst; + const u8* s = (const u8*)src; + while (n--) *d++ = *s++; +} + +/* ASCII -> UTF-16 (Char16) and print via ConOut->OutputString */ +static void put_ascii(void* SystemTable, const char* s) { + if (!SystemTable || !s) return; + + void* con_out = *(void**)((u8*)SystemTable + OFF_ST_CONOUT); + if (!con_out) return; + + EfiTextString OutputString = *(EfiTextString*)((u8*)con_out + OFF_CONOUT_OUTPUTSTRING); + if (!OutputString) return; + + u16 buf[MAX_OUTPUT_CHARS]; + u32 i = 0; + for (; s[i] && i < (MAX_OUTPUT_CHARS - 1); ++i) buf[i] = (u16)(u8)s[i]; + buf[i] = 0; + + OutputString(con_out, buf); +} + +/* print hex64 with optional label, ends with CRLF */ +static void put_hex64(void* SystemTable, const char* label, u64 v) { + static const char hex[] = "0123456789ABCDEF"; + char tmp[2 + 16 + 2 + 1]; /* "0x" + 16 nybbles + "\r\n" + NUL */ + int p = 0; + + if (label) put_ascii(SystemTable, label); + + tmp[p++] = '0'; + tmp[p++] = 'x'; + for (int i = 15; i >= 0; --i) + tmp[p++] = hex[(unsigned)((v >> (i * 4)) & 0xF)]; + tmp[p++] = '\r'; + tmp[p++] = '\n'; + tmp[p] = 0; + + put_ascii(SystemTable, tmp); +} + +/* tiny payload: "ret" for RISC-V (jalr x0, x1, 0 -> 0x00008067) */ +static const u8 payload_ret_only[4] __attribute__((section(".payload"))) = { 0x67, 0x80, 0x00, 0x00 }; + +typedef u64 (*payload_entry_t)(u64, u64, u64, u64, u64); + +/* Entry: alloc exec pages, copy payload, fence.i, call, log, return */ +u64 EFIAPI _ModuleEntryPoint(void* ImageHandle, void* SystemTable) { + (void)ImageHandle; + + void* BootServices = *(void**)((u8*)SystemTable + OFF_ST_BOOTSERVICES); + if (!BootServices) return 1; + + EfiAllocatePages AllocatePages = *(EfiAllocatePages*)((u8*)BootServices + OFF_BS_ALLOCATEPAGES); + if (!AllocatePages) return 2; + + put_ascii(SystemTable, "[OK] C AllocatePages started\r\n"); + + u64 payload_size = (u64)sizeof(payload_ret_only); + if (payload_size == 0) { + put_ascii(SystemTable, "[ERR] payload_size=0\r\n"); + return 5; + } + + u64 pages = (payload_size + 0xFFFu) >> 12; + if (pages == 0) pages = 1; + + u64 exec = 0; + u64 st = AllocatePages(kAllocateAnyPages, kEfiBootServicesCode, pages, &exec); + if (st != 0 || exec == 0) { + put_hex64(SystemTable, "[ERR] AllocatePages st=", st); + return st ? st : 6; + } + + put_hex64(SystemTable, "[OK] exec_addr=", exec); + put_hex64(SystemTable, "[OK] pages =", pages); + + mem_copy((void*)(u64)exec, payload_ret_only, payload_size); + fence_i(); + + put_ascii(SystemTable, "[OK] calling payload...\r\n"); + + payload_entry_t entry = (payload_entry_t)(void*)(u64)exec; + u64 expected = 0xDEADBEEF12345678ull; + u64 ret = entry(expected, 0, 0, 0, 0); + + put_hex64(SystemTable, "[OK] payload_ret=", ret); + put_ascii(SystemTable, "[OK] done\r\n"); + + return 0; +} diff --git a/tests/edk2-AllocatePage/AllocatePage.dsc b/tests/edk2-AllocatePage/AllocatePage.dsc new file mode 100644 index 0000000..487003d --- /dev/null +++ b/tests/edk2-AllocatePage/AllocatePage.dsc @@ -0,0 +1,14 @@ +[Defines] + PLATFORM_NAME = AllocatePage + PLATFORM_GUID = 01234567-89ab-cdef-0123-456789abcdef + PLATFORM_VERSION = 1.0 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build + SUPPORTED_ARCHITECTURES = RISCV64 + BUILD_TARGETS = DEBUG + +[Components] + AllocatePage/AllocatePage.inf + +[Packages] + MdePkg/MdePkg.dec diff --git a/tests/edk2-AllocatePage/AllocatePage.inf b/tests/edk2-AllocatePage/AllocatePage.inf new file mode 100644 index 0000000..6e59dc6 --- /dev/null +++ b/tests/edk2-AllocatePage/AllocatePage.inf @@ -0,0 +1,16 @@ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = AllocatePage + FILE_GUID = 01234567-89ab-cdef-0123-456789abcdef + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = efi_main + +[Sources] + AllocatePage.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + diff --git a/tests/edk2-AllocatePage/payload.S b/tests/edk2-AllocatePage/payload.S new file mode 100644 index 0000000..cf3587e --- /dev/null +++ b/tests/edk2-AllocatePage/payload.S @@ -0,0 +1,22 @@ + .text + .p2align 2 + .global _start # entry symbol name for payload (not used by EFI; we will call by address) +_start: + # Convention: + # a0 = framebuffer base (u64) + # a1 = framebuffer size (u64) + # a2 = stride_pixels (u32) (optional) + # a3 = width (u32) (optional) + # a4 = height (u32) (optional) + + # Simple, position-independent loop: write 0xFF408DEB (32-bit) across framebuffer. + li t0, 0xff408deb # color constant (fits 32-bit) + mv t1, a0 # ptr = fb_base + add t2, a0, a1 # end = fb_base + fb_size + +1: sw t0, 0(t1) + addi t1, t1, 4 + bltu t1, t2, 1b + + # never return: infinite loop +2: j 2b diff --git a/tests/edk2-AllocatePage/payload_embed.S b/tests/edk2-AllocatePage/payload_embed.S new file mode 100644 index 0000000..09c5af2 --- /dev/null +++ b/tests/edk2-AllocatePage/payload_embed.S @@ -0,0 +1,21 @@ +// payload_embed.S — return-only payload; + + .section .payload, "ax", @progbits + .p2align 2 + .globl _payload_start + .globl _payload_end +_payload_start: + + .globl entry + .type entry, @function +entry: + add t0, a0, a1 + add t0, t0, a2 + add t0, t0, a3 + add t0, t0, a4 + li t1, 0x123456789ABCDEF0 + add a0, t0, t1 + ret + + .size entry, .-entry +_payload_end: From c4776dcf14e3d11125db5f283bd6043089ece98a Mon Sep 17 00:00:00 2001 From: hanbings Date: Sun, 28 Sep 2025 04:25:47 +0800 Subject: [PATCH 17/21] feat(device): impl utilities to assist in managing device path. (#5) * feat(device): impl utilities to assist in managing device path instances. * refactor(device): split device path protocol tools into separate modules. --- src/runtime/mod.rs | 13 +- src/runtime/protocol/block_io.rs | 3 +- .../protocol/device/device_path_from_text.rs | 54 +++ .../protocol/device/device_path_to_text.rs | 58 +++ .../protocol/device/device_path_utilities.rs | 417 ++++++++++++++++++ src/runtime/protocol/device/mod.rs | 3 + src/runtime/protocol/device_path.rs | 193 -------- src/runtime/protocol/mod.rs | 2 +- src/runtime/system_table.rs | 13 +- 9 files changed, 552 insertions(+), 204 deletions(-) create mode 100644 src/runtime/protocol/device/device_path_from_text.rs create mode 100644 src/runtime/protocol/device/device_path_to_text.rs create mode 100644 src/runtime/protocol/device/device_path_utilities.rs create mode 100644 src/runtime/protocol/device/mod.rs delete mode 100644 src/runtime/protocol/device_path.rs diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 541b41d..b3c6684 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -11,6 +11,13 @@ mod system_table; mod utils; pub fn efi_runtime_init() { + // Prepare the UEFI System Table + let system_table = { + system_table::init_system_table(); + system_table::get_system_table_raw() + }; + + // Load the bootloader EFI file let load_bootloader = loader::load_efi_file("/EFI/BOOT/BOOTRISCV64.EFI"); let image_base = loader::detect_and_get_image_base(&load_bootloader).expect("Failed to get PE image base"); @@ -39,12 +46,6 @@ pub fn efi_runtime_init() { ); let func = entry::resolve_entry_func(mapping, file.entry(), base_va); - - let system_table = { - system_table::init_system_table(); - system_table::get_system_table_raw() - }; - let result = func(core::ptr::null_mut(), system_table); info!("efi_main return: {}", result); } diff --git a/src/runtime/protocol/block_io.rs b/src/runtime/protocol/block_io.rs index e064d99..670ba7b 100644 --- a/src/runtime/protocol/block_io.rs +++ b/src/runtime/protocol/block_io.rs @@ -17,9 +17,10 @@ pub struct BlockIo { protocol_raw: *mut BlockIoProtocol, } +// https://uefi.org/specs/UEFI/2.11/13_Protocols_Media_Access.html#block-i-o-protocol impl BlockIo { pub fn new() -> Self { - let media: *const BlockIoMedia = core::ptr::null(); // stub; replace with actual media if needed + let media: *const BlockIoMedia = core::ptr::null(); let protocol = BlockIoProtocol { revision: 0x00010000, diff --git a/src/runtime/protocol/device/device_path_from_text.rs b/src/runtime/protocol/device/device_path_from_text.rs new file mode 100644 index 0000000..7643615 --- /dev/null +++ b/src/runtime/protocol/device/device_path_from_text.rs @@ -0,0 +1,54 @@ +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Char16, + protocol::device_path::{DevicePathFromTextProtocol, DevicePathProtocol}, +}; + +use alloc::boxed::Box; + +static DEVICE_PATH_FROM_TEXT: LazyInit> = LazyInit::new(); + +#[derive(Debug)] +pub struct DevicePathFromText { + protocol: &'static mut DevicePathFromTextProtocol, + protocol_raw: *mut DevicePathFromTextProtocol, +} + +impl DevicePathFromText { + pub fn new() -> Self { + let protocol = DevicePathFromTextProtocol { + convert_text_to_device_node, + convert_text_to_device_path, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut DevicePathFromTextProtocol { + self.protocol_raw + } +} + +unsafe impl Send for DevicePathFromText {} +unsafe impl Sync for DevicePathFromText {} + +pub fn init_device_path_from_text() { + DEVICE_PATH_FROM_TEXT.init_once(Mutex::new(DevicePathFromText::new())); +} + +pub extern "efiapi" fn convert_text_to_device_node( + _text_device_node: *const Char16, +) -> *const DevicePathProtocol { + core::ptr::null() +} + +pub extern "efiapi" fn convert_text_to_device_path( + _text_device_path: *const Char16, +) -> *const DevicePathProtocol { + core::ptr::null() +} diff --git a/src/runtime/protocol/device/device_path_to_text.rs b/src/runtime/protocol/device/device_path_to_text.rs new file mode 100644 index 0000000..12d86ba --- /dev/null +++ b/src/runtime/protocol/device/device_path_to_text.rs @@ -0,0 +1,58 @@ +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::{ + Boolean, Char16, + protocol::device_path::{DevicePathProtocol, DevicePathToTextProtocol}, +}; + +use alloc::boxed::Box; + +static DEVICE_PATH_TO_TEXT: LazyInit> = LazyInit::new(); + +#[derive(Debug)] +pub struct DevicePathToText { + protocol: &'static mut DevicePathToTextProtocol, + protocol_raw: *mut DevicePathToTextProtocol, +} + +impl DevicePathToText { + pub fn new() -> Self { + let protocol = DevicePathToTextProtocol { + convert_device_node_to_text, + convert_device_path_to_text, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + Self { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut DevicePathToTextProtocol { + self.protocol_raw + } +} + +unsafe impl Send for DevicePathToText {} +unsafe impl Sync for DevicePathToText {} + +pub fn init_device_path_to_text() { + DEVICE_PATH_TO_TEXT.init_once(Mutex::new(DevicePathToText::new())); +} + +pub extern "efiapi" fn convert_device_node_to_text( + _device_node: *const DevicePathProtocol, + _display_only: Boolean, + _allow_shortcuts: Boolean, +) -> *const Char16 { + core::ptr::null() +} + +pub extern "efiapi" fn convert_device_path_to_text( + _device_path: *const DevicePathProtocol, + _display_only: Boolean, + _allow_shortcuts: Boolean, +) -> *const Char16 { + core::ptr::null() +} diff --git a/src/runtime/protocol/device/device_path_utilities.rs b/src/runtime/protocol/device/device_path_utilities.rs new file mode 100644 index 0000000..e121c20 --- /dev/null +++ b/src/runtime/protocol/device/device_path_utilities.rs @@ -0,0 +1,417 @@ +use core::{ + mem, + ptr::{self}, + slice, +}; + +use axsync::Mutex; +use lazyinit::LazyInit; +use uefi_raw::protocol::device_path::{ + DevicePathProtocol, DevicePathUtilitiesProtocol, DeviceSubType, DeviceType, +}; + +use alloc::boxed::Box; +use alloc::vec::Vec; + +// Device Path node type: 0x7F = End of Hardware Device Path +const END_DEVICE_PATH_TYPE: u8 = 0x7F; +// End node subtype: 0x01 = End Instance (marks end of one instance, more may follow) +const END_INSTANCE_DEVICE_PATH_SUBTYPE: u8 = 0x01; +// End node subtype: 0xFF = End Entire (marks end of the whole device path) +const END_ENTIRE_DEVICE_PATH_SUBTYPE: u8 = 0xFF; +// Common header size for every Device Path node (Type + SubType + Length[2]) +const DEV_PATH_HEADER_LEN: usize = 4; + +static DEVICE_PATH_UTILITIES: LazyInit> = LazyInit::new(); + +#[inline] +unsafe fn node_type(p: *const DevicePathProtocol) -> u8 { + unsafe { *(p as *const u8) } +} +#[inline] +unsafe fn node_subtype(p: *const DevicePathProtocol) -> u8 { + unsafe { *((p as *const u8).add(1)) } +} +#[inline] +unsafe fn node_len_u16(p: *const DevicePathProtocol) -> u16 { + // Little-endian 2 bytes at offset 2 + // equivalent to read_unaligned((b+2) as *const u16) + unsafe { *(p as *const u16).add(1) } +} +#[inline] +unsafe fn node_len(p: *const DevicePathProtocol) -> usize { + unsafe { node_len_u16(p) as usize } +} +#[inline] +unsafe fn is_end(p: *const DevicePathProtocol) -> bool { + unsafe { node_type(p) == END_DEVICE_PATH_TYPE && node_len(p) >= DEV_PATH_HEADER_LEN } +} +#[inline] +unsafe fn is_end_entire(p: *const DevicePathProtocol) -> bool { + unsafe { is_end(p) && node_subtype(p) == END_ENTIRE_DEVICE_PATH_SUBTYPE } +} +#[inline] +unsafe fn is_end_instance(p: *const DevicePathProtocol) -> bool { + unsafe { is_end(p) && node_subtype(p) == END_INSTANCE_DEVICE_PATH_SUBTYPE } +} + +#[inline] +unsafe fn total_path_size(dp: *const DevicePathProtocol) -> Option { + if dp.is_null() { + return Some(0); + } + let mut cur = dp; + let mut total: usize = 0; + let hard_cap: usize = 1 << 20; + + loop { + let len = unsafe { node_len(cur) }; + if len < DEV_PATH_HEADER_LEN { + return None; + } + total = total.checked_add(len)?; + if total > hard_cap { + return None; + } + if unsafe { is_end_entire(cur) } { + break; + } + cur = unsafe { (cur as *const u8).add(len) } as *const DevicePathProtocol; + } + Some(total) +} + +#[inline] +unsafe fn copy_bytes_to_box( + dp: *const DevicePathProtocol, + size: usize, +) -> *const DevicePathProtocol { + if size == 0 { + return ptr::null(); + } + let src = unsafe { slice::from_raw_parts(dp as *const u8, size) }; + let mut buf = Vec::::with_capacity(size); + buf.extend_from_slice(src); + let boxed = buf.into_boxed_slice(); + let ptr_u8 = Box::into_raw(boxed) as *mut u8; + ptr_u8 as *const DevicePathProtocol +} + +pub struct DevicePathUtilities { + protocol: &'static mut DevicePathUtilitiesProtocol, + protocol_raw: *mut DevicePathUtilitiesProtocol, +} + +impl DevicePathUtilities { + pub fn new() -> Self { + let protocol = DevicePathUtilitiesProtocol { + get_device_path_size, + duplicate_device_path, + append_device_path, + append_device_node, + append_device_path_instance, + get_next_device_path_instance, + is_device_path_multi_instance, + create_device_node, + }; + let protocol_raw = Box::into_raw(Box::new(protocol)); + let protocol = unsafe { &mut *protocol_raw }; + DevicePathUtilities { + protocol, + protocol_raw, + } + } + + pub fn get_protocol(&self) -> *mut DevicePathUtilitiesProtocol { + let guard = DEVICE_PATH_UTILITIES.lock(); + guard.protocol_raw + } +} + +unsafe impl Send for DevicePathUtilities {} +unsafe impl Sync for DevicePathUtilities {} + +pub fn init_device_path_uttilities() { + DEVICE_PATH_UTILITIES.init_once(Mutex::new(DevicePathUtilities::new())); +} + +pub extern "efiapi" fn get_device_path_size(device_path: *const DevicePathProtocol) -> usize { + unsafe { + match total_path_size(device_path) { + Some(sz) => sz, + None => 0, + } + } +} + +pub extern "efiapi" fn duplicate_device_path( + device_path: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + unsafe { + match total_path_size(device_path) { + Some(0) => ptr::null_mut(), + Some(sz) => copy_bytes_to_box(device_path, sz) as *const _, + None => ptr::null_mut(), + } + } +} + +pub extern "efiapi" fn append_device_path( + src1: *const DevicePathProtocol, + src2: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + unsafe { + let s1_size = match total_path_size(src1) { + Some(0) => DEV_PATH_HEADER_LEN, + Some(sz) => sz, + None => return ptr::null_mut(), + }; + let s2_size = match total_path_size(src2) { + Some(0) => DEV_PATH_HEADER_LEN, + Some(sz) => sz, + None => return ptr::null_mut(), + }; + + let s1_head = if s1_size >= DEV_PATH_HEADER_LEN { + s1_size - DEV_PATH_HEADER_LEN + } else { + 0 + }; + let out_size = s1_head.checked_add(s2_size).unwrap_or(0); + if out_size == 0 { + return ptr::null_mut(); + } + + let mut out = Vec::::with_capacity(out_size); + if s1_head > 0 && !src1.is_null() { + out.extend_from_slice(slice::from_raw_parts(src1 as *const u8, s1_head)); + } else { + } + if s2_size > 0 && !src2.is_null() { + out.extend_from_slice(slice::from_raw_parts(src2 as *const u8, s2_size)); + } else { + out.extend_from_slice(&[ + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + 0x04, + 0x00, + ]); + } + + let boxed = out.into_boxed_slice(); + Box::into_raw(boxed) as *const DevicePathProtocol + } +} + +pub extern "efiapi" fn append_device_node( + device_path: *const DevicePathProtocol, + device_node: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + unsafe { + if device_node.is_null() { + return duplicate_device_path(device_path); + } + let node_len_b = node_len(device_node); + if node_len_b < DEV_PATH_HEADER_LEN || is_end(device_node) { + return duplicate_device_path(device_path); + } + + let dp_size = match total_path_size(device_path) { + Some(0) => DEV_PATH_HEADER_LEN, + Some(sz) => sz, + None => return ptr::null_mut(), + }; + + let dp_head = dp_size - DEV_PATH_HEADER_LEN; + let out_size = dp_head + .checked_add(node_len_b) + .and_then(|v| v.checked_add(DEV_PATH_HEADER_LEN)) + .unwrap_or(0); + if out_size == 0 { + return ptr::null_mut(); + } + + let mut out = Vec::::with_capacity(out_size); + if dp_head > 0 && !device_path.is_null() { + out.extend_from_slice(slice::from_raw_parts(device_path as *const u8, dp_head)); + } + out.extend_from_slice(slice::from_raw_parts(device_node as *const u8, node_len_b)); + // End Entire + out.extend_from_slice(&[ + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + 0x04, + 0x00, + ]); + + let boxed = out.into_boxed_slice(); + Box::into_raw(boxed) as *const DevicePathProtocol + } +} + +pub extern "efiapi" fn append_device_path_instance( + device_path: *const DevicePathProtocol, + device_path_instance: *const DevicePathProtocol, +) -> *const DevicePathProtocol { + unsafe { + let dp_size = match total_path_size(device_path) { + Some(0) => DEV_PATH_HEADER_LEN, + Some(sz) => sz, + None => return ptr::null_mut(), + }; + let dp_head = dp_size - DEV_PATH_HEADER_LEN; + if device_path_instance.is_null() { + return duplicate_device_path(device_path); + } + + let mut cur = device_path_instance; + let mut inst_len: usize = 0; + loop { + let len = node_len(cur); + if len < DEV_PATH_HEADER_LEN { + return ptr::null_mut(); + } + inst_len = inst_len.checked_add(len).unwrap_or(0); + if is_end_instance(cur) || is_end_entire(cur) { + break; + } + cur = (cur as *const u8).add(len) as *const DevicePathProtocol; + } + + // out = dp_head + inst_len + (End Entire 4B) + let out_size = dp_head + .checked_add(inst_len) + .and_then(|v| v.checked_add(DEV_PATH_HEADER_LEN)) + .unwrap_or(0); + if out_size == 0 { + return ptr::null_mut(); + } + + let mut out = Vec::::with_capacity(out_size); + if dp_head > 0 && !device_path.is_null() { + out.extend_from_slice(slice::from_raw_parts(device_path as *const u8, dp_head)); + out.extend_from_slice(&[ + END_DEVICE_PATH_TYPE, + END_INSTANCE_DEVICE_PATH_SUBTYPE, + 0x04, + 0x00, + ]); + } + out.extend_from_slice(slice::from_raw_parts( + device_path_instance as *const u8, + inst_len, + )); + if let Some(_last4) = out.get(out.len().saturating_sub(DEV_PATH_HEADER_LEN)..) { + out.truncate(out.len().saturating_sub(DEV_PATH_HEADER_LEN)); + } + out.extend_from_slice(&[ + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + 0x04, + 0x00, + ]); + + let boxed = out.into_boxed_slice(); + Box::into_raw(boxed) as *const DevicePathProtocol + } +} + +pub extern "efiapi" fn get_next_device_path_instance( + device_path_instance: *mut *const DevicePathProtocol, + device_path_instance_size: *mut usize, +) -> *const DevicePathProtocol { + unsafe { + if device_path_instance.is_null() || device_path_instance_size.is_null() { + return ptr::null_mut(); + } + let cur = *device_path_instance; + if cur.is_null() { + *device_path_instance_size = 0; + return ptr::null_mut(); + } + + let start = cur as *const u8; + let mut p = cur; + let mut inst_bytes: usize = 0; + loop { + let len = node_len(p); + if len < DEV_PATH_HEADER_LEN { + return ptr::null_mut(); + } + inst_bytes = inst_bytes.checked_add(len).unwrap_or(0); + if is_end_instance(p) || is_end_entire(p) { + break; + } + p = (p as *const u8).add(len) as *const DevicePathProtocol; + } + + let mut out = Vec::::with_capacity(inst_bytes); + out.extend_from_slice(slice::from_raw_parts(start, inst_bytes)); + if out.len() >= DEV_PATH_HEADER_LEN { + out.truncate(out.len() - DEV_PATH_HEADER_LEN); + } + out.extend_from_slice(&[ + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + 0x04, + 0x00, + ]); + + let after = (p as *const u8).add(DEV_PATH_HEADER_LEN); + if is_end_instance(p) { + *device_path_instance = after as *const DevicePathProtocol; + } else { + *device_path_instance = ptr::null(); + } + + *device_path_instance_size = out.len(); + let boxed = out.into_boxed_slice(); + Box::into_raw(boxed) as *const DevicePathProtocol + } +} + +pub extern "efiapi" fn is_device_path_multi_instance( + device_path: *const DevicePathProtocol, +) -> bool { + unsafe { + if device_path.is_null() { + return false; + } + let mut p = device_path; + loop { + if is_end_instance(p) { + return true; + } + if is_end_entire(p) { + return false; + } + let len = node_len(p); + if len < DEV_PATH_HEADER_LEN { + return false; + } + p = (p as *const u8).add(len) as *const DevicePathProtocol; + } + } +} + +pub extern "efiapi" fn create_device_node( + node_type: DeviceType, + node_sub_type: DeviceSubType, + node_length: u16, +) -> *const DevicePathProtocol { + unsafe { + if usize::from(node_length) < DEV_PATH_HEADER_LEN { + return ptr::null_mut(); + } + let mut buf = Vec::::with_capacity(node_length as usize); + buf.resize(node_length as usize, 0u8); + + buf[0] = mem::transmute::(node_type); + buf[1] = mem::transmute::(node_sub_type); + buf[2] = (node_length & 0x00FF) as u8; + buf[3] = (node_length >> 8) as u8; + + let boxed = buf.into_boxed_slice(); + Box::into_raw(boxed) as *const DevicePathProtocol + } +} diff --git a/src/runtime/protocol/device/mod.rs b/src/runtime/protocol/device/mod.rs new file mode 100644 index 0000000..1e97d81 --- /dev/null +++ b/src/runtime/protocol/device/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod device_path_from_text; +pub(crate) mod device_path_to_text; +pub(crate) mod device_path_utilities; diff --git a/src/runtime/protocol/device_path.rs b/src/runtime/protocol/device_path.rs deleted file mode 100644 index 6600df7..0000000 --- a/src/runtime/protocol/device_path.rs +++ /dev/null @@ -1,193 +0,0 @@ -use core::ptr::null_mut; - -use axsync::Mutex; -use lazyinit::LazyInit; -use uefi_raw::{ - Boolean, Char16, - protocol::device_path::{ - DevicePathFromTextProtocol, DevicePathProtocol, DevicePathToTextProtocol, - DevicePathUtilitiesProtocol, DeviceSubType, DeviceType, - }, -}; - -use alloc::boxed::Box; - -static DEVICE_PATH_TO_TEXT: LazyInit> = LazyInit::new(); -static DEVICE_PATH_FROM_TEXT: LazyInit> = LazyInit::new(); -static DEVICE_PATH_UTILITIES: LazyInit> = LazyInit::new(); - -#[derive(Debug)] -pub struct DevicePathToText { - protocol: &'static mut DevicePathToTextProtocol, - protocol_raw: *mut DevicePathToTextProtocol, -} - -impl DevicePathToText { - pub fn new() -> Self { - let protocol = DevicePathToTextProtocol { - convert_device_node_to_text, - convert_device_path_to_text, - }; - let protocol_raw = Box::into_raw(Box::new(protocol)); - let protocol = unsafe { &mut *protocol_raw }; - Self { - protocol, - protocol_raw, - } - } - - pub fn get_protocol(&self) -> *mut DevicePathToTextProtocol { - self.protocol_raw - } -} - -unsafe impl Send for DevicePathToText {} -unsafe impl Sync for DevicePathToText {} - -pub fn init_device_path() { - DEVICE_PATH_TO_TEXT.init_once(Mutex::new(DevicePathToText::new())); - DEVICE_PATH_FROM_TEXT.init_once(Mutex::new(DevicePathFromText::new())); - DEVICE_PATH_UTILITIES.init_once(Mutex::new(DevicePathUtilities::new())); -} - -pub extern "efiapi" fn convert_device_node_to_text( - _device_node: *const DevicePathProtocol, - _display_only: Boolean, - _allow_shortcuts: Boolean, -) -> *const Char16 { - core::ptr::null() -} - -pub extern "efiapi" fn convert_device_path_to_text( - _device_path: *const DevicePathProtocol, - _display_only: Boolean, - _allow_shortcuts: Boolean, -) -> *const Char16 { - core::ptr::null() -} - -#[derive(Debug)] -pub struct DevicePathFromText { - protocol: &'static mut DevicePathFromTextProtocol, - protocol_raw: *mut DevicePathFromTextProtocol, -} - -impl DevicePathFromText { - pub fn new() -> Self { - let protocol = DevicePathFromTextProtocol { - convert_text_to_device_node, - convert_text_to_device_path, - }; - let protocol_raw = Box::into_raw(Box::new(protocol)); - let protocol = unsafe { &mut *protocol_raw }; - Self { - protocol, - protocol_raw, - } - } - - pub fn get_protocol(&self) -> *mut DevicePathFromTextProtocol { - self.protocol_raw - } -} - -unsafe impl Send for DevicePathFromText {} -unsafe impl Sync for DevicePathFromText {} - -pub extern "efiapi" fn convert_text_to_device_node( - _text_device_node: *const Char16, -) -> *const DevicePathProtocol { - core::ptr::null() -} - -pub extern "efiapi" fn convert_text_to_device_path( - _text_device_path: *const Char16, -) -> *const DevicePathProtocol { - core::ptr::null() -} - -pub struct DevicePathUtilities { - protocol: &'static mut DevicePathUtilitiesProtocol, - protocol_raw: *mut DevicePathUtilitiesProtocol, -} - -impl DevicePathUtilities { - pub fn new() -> Self { - let protocol = DevicePathUtilitiesProtocol { - get_device_path_size, - duplicate_device_path, - append_device_path, - append_device_node, - append_device_path_instance, - get_next_device_path_instance, - is_device_path_multi_instance, - create_device_node, - }; - let protocol_raw = Box::into_raw(Box::new(protocol)); - let protocol = unsafe { &mut *protocol_raw }; - DevicePathUtilities { - protocol, - protocol_raw, - } - } - - pub fn get_protocol(&self) -> *mut DevicePathUtilitiesProtocol { - let guard = DEVICE_PATH_UTILITIES.lock(); - guard.protocol_raw - } -} - -unsafe impl Send for DevicePathUtilities {} -unsafe impl Sync for DevicePathUtilities {} - -pub extern "efiapi" fn get_device_path_size(_device_path: *const DevicePathProtocol) -> usize { - 0 -} - -pub extern "efiapi" fn duplicate_device_path( - _device_path: *const DevicePathProtocol, -) -> *const DevicePathProtocol { - null_mut() -} - -pub extern "efiapi" fn append_device_path( - _src1: *const DevicePathProtocol, - _src2: *const DevicePathProtocol, -) -> *const DevicePathProtocol { - null_mut() -} - -pub extern "efiapi" fn append_device_node( - _device_path: *const DevicePathProtocol, - _device_node: *const DevicePathProtocol, -) -> *const DevicePathProtocol { - null_mut() -} - -pub extern "efiapi" fn append_device_path_instance( - _device_path: *const DevicePathProtocol, - _device_path_instance: *const DevicePathProtocol, -) -> *const DevicePathProtocol { - null_mut() -} - -pub extern "efiapi" fn get_next_device_path_instance( - _device_path_instance: *mut *const DevicePathProtocol, - _device_path_instance_size: *mut usize, -) -> *const DevicePathProtocol { - null_mut() -} - -pub extern "efiapi" fn is_device_path_multi_instance( - _device_path: *const DevicePathProtocol, -) -> bool { - true -} - -pub extern "efiapi" fn create_device_node( - _node_type: DeviceType, - _node_sub_type: DeviceSubType, - _node_length: u16, -) -> *const DevicePathProtocol { - null_mut() -} diff --git a/src/runtime/protocol/mod.rs b/src/runtime/protocol/mod.rs index dfb4978..e33a787 100644 --- a/src/runtime/protocol/mod.rs +++ b/src/runtime/protocol/mod.rs @@ -1,5 +1,5 @@ pub(crate) mod block_io; -pub(crate) mod device_path; +pub(crate) mod device; pub(crate) mod fs; pub(crate) mod graphics_output; pub(crate) mod handle; diff --git a/src/runtime/system_table.rs b/src/runtime/system_table.rs index 919bc26..26a51e0 100644 --- a/src/runtime/system_table.rs +++ b/src/runtime/system_table.rs @@ -7,7 +7,11 @@ use uefi_raw::table::{Header, configuration::ConfigurationTable, system::SystemT use crate::runtime::{ protocol::{ block_io::init_block_io, - device_path::init_device_path, + device::{ + device_path_from_text::init_device_path_from_text, + device_path_to_text::init_device_path_to_text, + device_path_utilities::init_device_path_uttilities, + }, simple_text_output::{get_simple_text_output, init_simple_text_output}, }, service::{get_boot_service, get_runtime_service}, @@ -44,9 +48,12 @@ static VENDOR: &[u16] = &[ static REVERSION: u32 = 0x0001_0000; pub fn init_system_table() { - // Initialize the protocols + // Initialize the tools. + init_device_path_from_text(); + init_device_path_to_text(); + init_device_path_uttilities(); + init_block_io(); - init_device_path(); #[cfg(feature = "display")] crate::runtime::protocol::graphics_output::init_graphics_output(); From bfbab66309ede322b7173af3072d8887c343dc47 Mon Sep 17 00:00:00 2001 From: hanbings Date: Sun, 28 Sep 2025 04:37:39 +0800 Subject: [PATCH 18/21] feat(uefi): impl crc calculation and memory utility tools. --- src/runtime/service/boot_service.rs | 66 ++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/src/runtime/service/boot_service.rs b/src/runtime/service/boot_service.rs index d67fa3b..db019ff 100644 --- a/src/runtime/service/boot_service.rs +++ b/src/runtime/service/boot_service.rs @@ -1,4 +1,4 @@ -use core::ffi::c_void; +use core::{ffi::c_void, ptr}; use axhal::mem::PhysAddr; use uefi_raw::{ @@ -367,15 +367,67 @@ pub unsafe extern "C" fn uninstall_multiple_protocol_interfaces(_handle: Handle, } // CRC / memory +const CRC32_TABLE: [u32; 256] = { + const P: u32 = 0xEDB8_8320; + let mut tbl = [0u32; 256]; + let mut i = 0usize; + while i < 256 { + let mut c = i as u32; + let mut j = 0; + while j < 8 { + // reflected step + c = if (c & 1) != 0 { (c >> 1) ^ P } else { c >> 1 }; + j += 1; + } + tbl[i] = c; + i += 1; + } + tbl +}; + pub unsafe extern "efiapi" fn calculate_crc32( - _data: *const c_void, - _data_size: usize, - _crc32: *mut u32, + data: *const c_void, + data_size: usize, + crc32_out: *mut u32, ) -> Status { - Status::UNSUPPORTED + // Parameter validation + if crc32_out.is_null() || (data.is_null() && data_size > 0) { + return Status::INVALID_PARAMETER; + } + + if data_size == 0 { + // Zero-length -> 0 + unsafe { *crc32_out = 0 }; + return Status::SUCCESS; + } + + let bytes = unsafe { core::slice::from_raw_parts(data as *const u8, data_size) }; + + // Reflected algorithm with init/final XOR + let mut crc: u32 = 0xFFFF_FFFF; + for &b in bytes { + let idx = ((crc ^ (b as u32)) & 0xFF) as usize; + crc = (crc >> 8) ^ CRC32_TABLE[idx]; + } + crc ^= 0xFFFF_FFFF; + + unsafe { *crc32_out = crc }; + Status::SUCCESS +} + +pub unsafe extern "efiapi" fn copy_mem(dest: *mut u8, src: *const u8, len: usize) { + if len == 0 || dest.is_null() || src.is_null() { + return; + } + unsafe { ptr::copy(src, dest, len) }; +} + +pub unsafe extern "efiapi" fn set_mem(buffer: *mut u8, len: usize, value: u8) { + if len == 0 || buffer.is_null() { + return; + } + unsafe { ptr::write_bytes(buffer, value, len) }; } -pub unsafe extern "efiapi" fn copy_mem(_dest: *mut u8, _src: *const u8, _len: usize) {} -pub unsafe extern "efiapi" fn set_mem(_buffer: *mut u8, _len: usize, _value: u8) {} // New event (UEFI 2.0+) pub unsafe extern "efiapi" fn create_event_ex( From abaa268e4488a0d4ca3bbed16df3f65640be9d05 Mon Sep 17 00:00:00 2001 From: hanbings Date: Sun, 28 Sep 2025 05:39:21 +0800 Subject: [PATCH 19/21] chore(uefi): clean code. --- .../protocol/device/device_path_utilities.rs | 318 ++++++++++-------- src/runtime/protocol/fs/file_protocol_v1.rs | 1 - src/runtime/protocol/fs/simple_file_system.rs | 4 +- src/runtime/protocol/graphics_output.rs | 235 +++++++------ 4 files changed, 309 insertions(+), 249 deletions(-) diff --git a/src/runtime/protocol/device/device_path_utilities.rs b/src/runtime/protocol/device/device_path_utilities.rs index e121c20..1d9e581 100644 --- a/src/runtime/protocol/device/device_path_utilities.rs +++ b/src/runtime/protocol/device/device_path_utilities.rs @@ -20,81 +20,89 @@ const END_INSTANCE_DEVICE_PATH_SUBTYPE: u8 = 0x01; // End node subtype: 0xFF = End Entire (marks end of the whole device path) const END_ENTIRE_DEVICE_PATH_SUBTYPE: u8 = 0xFF; // Common header size for every Device Path node (Type + SubType + Length[2]) -const DEV_PATH_HEADER_LEN: usize = 4; +const DEVICE_PATH_HEADER_LENGTH: usize = 4; static DEVICE_PATH_UTILITIES: LazyInit> = LazyInit::new(); #[inline] -unsafe fn node_type(p: *const DevicePathProtocol) -> u8 { - unsafe { *(p as *const u8) } +unsafe fn get_node_type(protocol: *const DevicePathProtocol) -> u8 { + unsafe { *(protocol as *const u8) } } #[inline] -unsafe fn node_subtype(p: *const DevicePathProtocol) -> u8 { - unsafe { *((p as *const u8).add(1)) } +unsafe fn get_node_subtype(protocol: *const DevicePathProtocol) -> u8 { + unsafe { *((protocol as *const u8).add(1)) } } #[inline] -unsafe fn node_len_u16(p: *const DevicePathProtocol) -> u16 { +unsafe fn get_node_length_u16(protocol: *const DevicePathProtocol) -> u16 { // Little-endian 2 bytes at offset 2 // equivalent to read_unaligned((b+2) as *const u16) - unsafe { *(p as *const u16).add(1) } + unsafe { *(protocol as *const u16).add(1) } } #[inline] -unsafe fn node_len(p: *const DevicePathProtocol) -> usize { - unsafe { node_len_u16(p) as usize } +unsafe fn get_node_length(protocol: *const DevicePathProtocol) -> usize { + unsafe { get_node_length_u16(protocol) as usize } } #[inline] -unsafe fn is_end(p: *const DevicePathProtocol) -> bool { - unsafe { node_type(p) == END_DEVICE_PATH_TYPE && node_len(p) >= DEV_PATH_HEADER_LEN } +unsafe fn is_end_node(protocol: *const DevicePathProtocol) -> bool { + unsafe { + get_node_type(protocol) == END_DEVICE_PATH_TYPE + && get_node_length(protocol) >= DEVICE_PATH_HEADER_LENGTH + } } #[inline] -unsafe fn is_end_entire(p: *const DevicePathProtocol) -> bool { - unsafe { is_end(p) && node_subtype(p) == END_ENTIRE_DEVICE_PATH_SUBTYPE } +unsafe fn is_end_entire_node(protocol: *const DevicePathProtocol) -> bool { + unsafe { is_end_node(protocol) && get_node_subtype(protocol) == END_ENTIRE_DEVICE_PATH_SUBTYPE } } #[inline] -unsafe fn is_end_instance(p: *const DevicePathProtocol) -> bool { - unsafe { is_end(p) && node_subtype(p) == END_INSTANCE_DEVICE_PATH_SUBTYPE } +unsafe fn is_end_instance_node(protocol: *const DevicePathProtocol) -> bool { + unsafe { + is_end_node(protocol) && get_node_subtype(protocol) == END_INSTANCE_DEVICE_PATH_SUBTYPE + } } #[inline] -unsafe fn total_path_size(dp: *const DevicePathProtocol) -> Option { - if dp.is_null() { +unsafe fn compute_total_device_path_size( + device_path_ptr: *const DevicePathProtocol, +) -> Option { + if device_path_ptr.is_null() { return Some(0); } - let mut cur = dp; - let mut total: usize = 0; - let hard_cap: usize = 1 << 20; + let mut current_ptr = device_path_ptr; + let mut total_size: usize = 0; + let hard_size_cap: usize = 1 << 20; loop { - let len = unsafe { node_len(cur) }; - if len < DEV_PATH_HEADER_LEN { + let node_length = unsafe { get_node_length(current_ptr) }; + if node_length < DEVICE_PATH_HEADER_LENGTH { return None; } - total = total.checked_add(len)?; - if total > hard_cap { + total_size = total_size.checked_add(node_length)?; + if total_size > hard_size_cap { return None; } - if unsafe { is_end_entire(cur) } { + if unsafe { is_end_entire_node(current_ptr) } { break; } - cur = unsafe { (cur as *const u8).add(len) } as *const DevicePathProtocol; + current_ptr = + unsafe { (current_ptr as *const u8).add(node_length) } as *const DevicePathProtocol; } - Some(total) + Some(total_size) } #[inline] -unsafe fn copy_bytes_to_box( - dp: *const DevicePathProtocol, - size: usize, +unsafe fn copy_device_path_bytes_to_box( + device_path_ptr: *const DevicePathProtocol, + total_size: usize, ) -> *const DevicePathProtocol { - if size == 0 { + if total_size == 0 { return ptr::null(); } - let src = unsafe { slice::from_raw_parts(dp as *const u8, size) }; - let mut buf = Vec::::with_capacity(size); - buf.extend_from_slice(src); - let boxed = buf.into_boxed_slice(); - let ptr_u8 = Box::into_raw(boxed) as *mut u8; - ptr_u8 as *const DevicePathProtocol + let source_slice = unsafe { slice::from_raw_parts(device_path_ptr as *const u8, total_size) }; + let mut buffer = Vec::::with_capacity(total_size); + buffer.extend_from_slice(source_slice); + let boxed_slice = buffer.into_boxed_slice(); + let raw_u8_ptr = Box::into_raw(boxed_slice) as *mut u8; + raw_u8_ptr as *const DevicePathProtocol } pub struct DevicePathUtilities { @@ -137,8 +145,8 @@ pub fn init_device_path_uttilities() { pub extern "efiapi" fn get_device_path_size(device_path: *const DevicePathProtocol) -> usize { unsafe { - match total_path_size(device_path) { - Some(sz) => sz, + match compute_total_device_path_size(device_path) { + Some(total_size) => total_size, None => 0, } } @@ -148,49 +156,57 @@ pub extern "efiapi" fn duplicate_device_path( device_path: *const DevicePathProtocol, ) -> *const DevicePathProtocol { unsafe { - match total_path_size(device_path) { + match compute_total_device_path_size(device_path) { Some(0) => ptr::null_mut(), - Some(sz) => copy_bytes_to_box(device_path, sz) as *const _, + Some(total_size) => copy_device_path_bytes_to_box(device_path, total_size) as *const _, None => ptr::null_mut(), } } } pub extern "efiapi" fn append_device_path( - src1: *const DevicePathProtocol, - src2: *const DevicePathProtocol, + first_path: *const DevicePathProtocol, + second_path: *const DevicePathProtocol, ) -> *const DevicePathProtocol { unsafe { - let s1_size = match total_path_size(src1) { - Some(0) => DEV_PATH_HEADER_LEN, - Some(sz) => sz, + let first_total_size = match compute_total_device_path_size(first_path) { + Some(0) => DEVICE_PATH_HEADER_LENGTH, + Some(total_size) => total_size, None => return ptr::null_mut(), }; - let s2_size = match total_path_size(src2) { - Some(0) => DEV_PATH_HEADER_LEN, - Some(sz) => sz, + let second_total_size = match compute_total_device_path_size(second_path) { + Some(0) => DEVICE_PATH_HEADER_LENGTH, + Some(total_size) => total_size, None => return ptr::null_mut(), }; - let s1_head = if s1_size >= DEV_PATH_HEADER_LEN { - s1_size - DEV_PATH_HEADER_LEN + let first_without_end_length = if first_total_size >= DEVICE_PATH_HEADER_LENGTH { + first_total_size - DEVICE_PATH_HEADER_LENGTH } else { 0 }; - let out_size = s1_head.checked_add(s2_size).unwrap_or(0); - if out_size == 0 { + let output_total_size = first_without_end_length + .checked_add(second_total_size) + .unwrap_or(0); + if output_total_size == 0 { return ptr::null_mut(); } - let mut out = Vec::::with_capacity(out_size); - if s1_head > 0 && !src1.is_null() { - out.extend_from_slice(slice::from_raw_parts(src1 as *const u8, s1_head)); - } else { + let mut output_bytes = Vec::::with_capacity(output_total_size); + if first_without_end_length > 0 && !first_path.is_null() { + output_bytes.extend_from_slice(slice::from_raw_parts( + first_path as *const u8, + first_without_end_length, + )); } - if s2_size > 0 && !src2.is_null() { - out.extend_from_slice(slice::from_raw_parts(src2 as *const u8, s2_size)); + if second_total_size > 0 && !second_path.is_null() { + output_bytes.extend_from_slice(slice::from_raw_parts( + second_path as *const u8, + second_total_size, + )); } else { - out.extend_from_slice(&[ + // If second path is empty, terminate with End Entire node + output_bytes.extend_from_slice(&[ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, 0x04, @@ -198,7 +214,7 @@ pub extern "efiapi" fn append_device_path( ]); } - let boxed = out.into_boxed_slice(); + let boxed = output_bytes.into_boxed_slice(); Box::into_raw(boxed) as *const DevicePathProtocol } } @@ -211,40 +227,46 @@ pub extern "efiapi" fn append_device_node( if device_node.is_null() { return duplicate_device_path(device_path); } - let node_len_b = node_len(device_node); - if node_len_b < DEV_PATH_HEADER_LEN || is_end(device_node) { + let device_node_length = get_node_length(device_node); + if device_node_length < DEVICE_PATH_HEADER_LENGTH || is_end_node(device_node) { return duplicate_device_path(device_path); } - let dp_size = match total_path_size(device_path) { - Some(0) => DEV_PATH_HEADER_LEN, - Some(sz) => sz, + let device_path_total_size = match compute_total_device_path_size(device_path) { + Some(0) => DEVICE_PATH_HEADER_LENGTH, + Some(total_size) => total_size, None => return ptr::null_mut(), }; - let dp_head = dp_size - DEV_PATH_HEADER_LEN; - let out_size = dp_head - .checked_add(node_len_b) - .and_then(|v| v.checked_add(DEV_PATH_HEADER_LEN)) + let device_path_without_end_length = device_path_total_size - DEVICE_PATH_HEADER_LENGTH; + let output_total_size = device_path_without_end_length + .checked_add(device_node_length) + .and_then(|v| v.checked_add(DEVICE_PATH_HEADER_LENGTH)) .unwrap_or(0); - if out_size == 0 { + if output_total_size == 0 { return ptr::null_mut(); } - let mut out = Vec::::with_capacity(out_size); - if dp_head > 0 && !device_path.is_null() { - out.extend_from_slice(slice::from_raw_parts(device_path as *const u8, dp_head)); + let mut output_bytes = Vec::::with_capacity(output_total_size); + if device_path_without_end_length > 0 && !device_path.is_null() { + output_bytes.extend_from_slice(slice::from_raw_parts( + device_path as *const u8, + device_path_without_end_length, + )); } - out.extend_from_slice(slice::from_raw_parts(device_node as *const u8, node_len_b)); - // End Entire - out.extend_from_slice(&[ + output_bytes.extend_from_slice(slice::from_raw_parts( + device_node as *const u8, + device_node_length, + )); + // Append End Entire node + output_bytes.extend_from_slice(&[ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, 0x04, 0x00, ]); - let boxed = out.into_boxed_slice(); + let boxed = output_bytes.into_boxed_slice(); Box::into_raw(boxed) as *const DevicePathProtocol } } @@ -254,118 +276,128 @@ pub extern "efiapi" fn append_device_path_instance( device_path_instance: *const DevicePathProtocol, ) -> *const DevicePathProtocol { unsafe { - let dp_size = match total_path_size(device_path) { - Some(0) => DEV_PATH_HEADER_LEN, - Some(sz) => sz, + let device_path_total_size = match compute_total_device_path_size(device_path) { + Some(0) => DEVICE_PATH_HEADER_LENGTH, + Some(total_size) => total_size, None => return ptr::null_mut(), }; - let dp_head = dp_size - DEV_PATH_HEADER_LEN; + let device_path_without_end_length = device_path_total_size - DEVICE_PATH_HEADER_LENGTH; if device_path_instance.is_null() { return duplicate_device_path(device_path); } - let mut cur = device_path_instance; - let mut inst_len: usize = 0; + let mut current_ptr = device_path_instance; + let mut instance_total_length: usize = 0; loop { - let len = node_len(cur); - if len < DEV_PATH_HEADER_LEN { + let node_length = get_node_length(current_ptr); + if node_length < DEVICE_PATH_HEADER_LENGTH { return ptr::null_mut(); } - inst_len = inst_len.checked_add(len).unwrap_or(0); - if is_end_instance(cur) || is_end_entire(cur) { + instance_total_length = instance_total_length.checked_add(node_length).unwrap_or(0); + if is_end_instance_node(current_ptr) || is_end_entire_node(current_ptr) { break; } - cur = (cur as *const u8).add(len) as *const DevicePathProtocol; + current_ptr = (current_ptr as *const u8).add(node_length) as *const DevicePathProtocol; } - // out = dp_head + inst_len + (End Entire 4B) - let out_size = dp_head - .checked_add(inst_len) - .and_then(|v| v.checked_add(DEV_PATH_HEADER_LEN)) + // output = device_path_without_end_length + instance_total_length + End Entire (4 bytes) + let output_total_size = device_path_without_end_length + .checked_add(instance_total_length) + .and_then(|v| v.checked_add(DEVICE_PATH_HEADER_LENGTH)) .unwrap_or(0); - if out_size == 0 { + if output_total_size == 0 { return ptr::null_mut(); } - let mut out = Vec::::with_capacity(out_size); - if dp_head > 0 && !device_path.is_null() { - out.extend_from_slice(slice::from_raw_parts(device_path as *const u8, dp_head)); - out.extend_from_slice(&[ + let mut output_bytes = Vec::::with_capacity(output_total_size); + if device_path_without_end_length > 0 && !device_path.is_null() { + output_bytes.extend_from_slice(slice::from_raw_parts( + device_path as *const u8, + device_path_without_end_length, + )); + // Insert an End Instance node between instances + output_bytes.extend_from_slice(&[ END_DEVICE_PATH_TYPE, END_INSTANCE_DEVICE_PATH_SUBTYPE, 0x04, 0x00, ]); } - out.extend_from_slice(slice::from_raw_parts( + output_bytes.extend_from_slice(slice::from_raw_parts( device_path_instance as *const u8, - inst_len, + instance_total_length, )); - if let Some(_last4) = out.get(out.len().saturating_sub(DEV_PATH_HEADER_LEN)..) { - out.truncate(out.len().saturating_sub(DEV_PATH_HEADER_LEN)); + // Remove the last 4 bytes of the copied instance (its End node), then add End Entire + if let Some(_) = + output_bytes.get(output_bytes.len().saturating_sub(DEVICE_PATH_HEADER_LENGTH)..) + { + output_bytes.truncate(output_bytes.len().saturating_sub(DEVICE_PATH_HEADER_LENGTH)); } - out.extend_from_slice(&[ + output_bytes.extend_from_slice(&[ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, 0x04, 0x00, ]); - let boxed = out.into_boxed_slice(); + let boxed = output_bytes.into_boxed_slice(); Box::into_raw(boxed) as *const DevicePathProtocol } } pub extern "efiapi" fn get_next_device_path_instance( - device_path_instance: *mut *const DevicePathProtocol, - device_path_instance_size: *mut usize, + device_path_instance_ptr: *mut *const DevicePathProtocol, + device_path_instance_size_out: *mut usize, ) -> *const DevicePathProtocol { unsafe { - if device_path_instance.is_null() || device_path_instance_size.is_null() { + if device_path_instance_ptr.is_null() || device_path_instance_size_out.is_null() { return ptr::null_mut(); } - let cur = *device_path_instance; - if cur.is_null() { - *device_path_instance_size = 0; + let current_instance_ptr = *device_path_instance_ptr; + if current_instance_ptr.is_null() { + *device_path_instance_size_out = 0; return ptr::null_mut(); } - let start = cur as *const u8; - let mut p = cur; - let mut inst_bytes: usize = 0; + let start_bytes_ptr = current_instance_ptr as *const u8; + let mut cursor_ptr = current_instance_ptr; + let mut instance_bytes_length: usize = 0; loop { - let len = node_len(p); - if len < DEV_PATH_HEADER_LEN { + let node_length = get_node_length(cursor_ptr); + if node_length < DEVICE_PATH_HEADER_LENGTH { return ptr::null_mut(); } - inst_bytes = inst_bytes.checked_add(len).unwrap_or(0); - if is_end_instance(p) || is_end_entire(p) { + instance_bytes_length = instance_bytes_length.checked_add(node_length).unwrap_or(0); + if is_end_instance_node(cursor_ptr) || is_end_entire_node(cursor_ptr) { break; } - p = (p as *const u8).add(len) as *const DevicePathProtocol; + cursor_ptr = (cursor_ptr as *const u8).add(node_length) as *const DevicePathProtocol; } - let mut out = Vec::::with_capacity(inst_bytes); - out.extend_from_slice(slice::from_raw_parts(start, inst_bytes)); - if out.len() >= DEV_PATH_HEADER_LEN { - out.truncate(out.len() - DEV_PATH_HEADER_LEN); + let mut output_bytes = Vec::::with_capacity(instance_bytes_length); + output_bytes.extend_from_slice(slice::from_raw_parts( + start_bytes_ptr, + instance_bytes_length, + )); + if output_bytes.len() >= DEVICE_PATH_HEADER_LENGTH { + output_bytes.truncate(output_bytes.len() - DEVICE_PATH_HEADER_LENGTH); } - out.extend_from_slice(&[ + output_bytes.extend_from_slice(&[ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, 0x04, 0x00, ]); - let after = (p as *const u8).add(DEV_PATH_HEADER_LEN); - if is_end_instance(p) { - *device_path_instance = after as *const DevicePathProtocol; + let after_current_instance = (cursor_ptr as *const u8).add(DEVICE_PATH_HEADER_LENGTH); + if is_end_instance_node(cursor_ptr) { + *device_path_instance_ptr = after_current_instance as *const DevicePathProtocol; } else { - *device_path_instance = ptr::null(); + *device_path_instance_ptr = ptr::null(); } - *device_path_instance_size = out.len(); - let boxed = out.into_boxed_slice(); + *device_path_instance_size_out = output_bytes.len(); + let boxed = output_bytes.into_boxed_slice(); Box::into_raw(boxed) as *const DevicePathProtocol } } @@ -377,19 +409,19 @@ pub extern "efiapi" fn is_device_path_multi_instance( if device_path.is_null() { return false; } - let mut p = device_path; + let mut cursor_ptr = device_path; loop { - if is_end_instance(p) { + if is_end_instance_node(cursor_ptr) { return true; } - if is_end_entire(p) { + if is_end_entire_node(cursor_ptr) { return false; } - let len = node_len(p); - if len < DEV_PATH_HEADER_LEN { + let node_length = get_node_length(cursor_ptr); + if node_length < DEVICE_PATH_HEADER_LENGTH { return false; } - p = (p as *const u8).add(len) as *const DevicePathProtocol; + cursor_ptr = (cursor_ptr as *const u8).add(node_length) as *const DevicePathProtocol; } } } @@ -400,18 +432,18 @@ pub extern "efiapi" fn create_device_node( node_length: u16, ) -> *const DevicePathProtocol { unsafe { - if usize::from(node_length) < DEV_PATH_HEADER_LEN { + if usize::from(node_length) < DEVICE_PATH_HEADER_LENGTH { return ptr::null_mut(); } - let mut buf = Vec::::with_capacity(node_length as usize); - buf.resize(node_length as usize, 0u8); + let mut buffer = Vec::::with_capacity(node_length as usize); + buffer.resize(node_length as usize, 0u8); - buf[0] = mem::transmute::(node_type); - buf[1] = mem::transmute::(node_sub_type); - buf[2] = (node_length & 0x00FF) as u8; - buf[3] = (node_length >> 8) as u8; + buffer[0] = mem::transmute::(node_type); + buffer[1] = mem::transmute::(node_sub_type); + buffer[2] = (node_length & 0x00FF) as u8; + buffer[3] = (node_length >> 8) as u8; - let boxed = buf.into_boxed_slice(); + let boxed = buffer.into_boxed_slice(); Box::into_raw(boxed) as *const DevicePathProtocol } } diff --git a/src/runtime/protocol/fs/file_protocol_v1.rs b/src/runtime/protocol/fs/file_protocol_v1.rs index dc7f033..b9efee8 100644 --- a/src/runtime/protocol/fs/file_protocol_v1.rs +++ b/src/runtime/protocol/fs/file_protocol_v1.rs @@ -422,7 +422,6 @@ pub extern "efiapi" fn set_position(this: *mut FileProtocolV1, position: u64) -> HandleKind::Dir => Status::UNSUPPORTED, HandleKind::File => { if position == u64::MAX { - // 定位到 EOF:取当前文件长度 let size = match axfs::api::metadata(&this.path) { Ok(md) => md.len() as u64, Err(_) => return Status::DEVICE_ERROR, diff --git a/src/runtime/protocol/fs/simple_file_system.rs b/src/runtime/protocol/fs/simple_file_system.rs index 75fe541..5a8da38 100644 --- a/src/runtime/protocol/fs/simple_file_system.rs +++ b/src/runtime/protocol/fs/simple_file_system.rs @@ -9,7 +9,7 @@ use alloc::boxed::Box; use crate::runtime::protocol::fs::file_protocol_v1::open_root; -static TEXT_FILE_SYSTEM: LazyInit> = LazyInit::new(); +static SIMPLE_FILE_SYSTEM: LazyInit> = LazyInit::new(); #[repr(C)] #[derive(Debug)] @@ -41,7 +41,7 @@ unsafe impl Send for SimpleFileSystem {} unsafe impl Sync for SimpleFileSystem {} pub fn init_simple_file_system() { - TEXT_FILE_SYSTEM.init_once(Mutex::new(SimpleFileSystem::new())); + SIMPLE_FILE_SYSTEM.init_once(Mutex::new(SimpleFileSystem::new())); } // impl SimpleFileSystem. Refer to UEFI Spec 2.11 Section 13.4. diff --git a/src/runtime/protocol/graphics_output.rs b/src/runtime/protocol/graphics_output.rs index cbc2fb1..2e047c5 100644 --- a/src/runtime/protocol/graphics_output.rs +++ b/src/runtime/protocol/graphics_output.rs @@ -20,25 +20,25 @@ pub struct GraphicsOutput { mode_box: *mut GraphicsOutputProtocolMode, info_box: *mut GraphicsOutputModeInformation, - width: u32, - height: u32, - fb_base_vaddr: usize, - fb_size: usize, + width_pixels: u32, + height_pixels: u32, + frame_buffer_base_virtual_address: usize, + frame_buffer_size_bytes: usize, stride_pixels: u32, } impl GraphicsOutput { pub fn new( - width: u32, - height: u32, - fb_base_vaddr: usize, - fb_size: usize, + width_pixels: u32, + height_pixels: u32, + frame_buffer_base_virtual_address: usize, + frame_buffer_size_bytes: usize, stride_pixels: u32, ) -> Self { - let info = GraphicsOutputModeInformation { + let mode_info = GraphicsOutputModeInformation { version: 0, - horizontal_resolution: width, - vertical_resolution: height, + horizontal_resolution: width_pixels, + vertical_resolution: height_pixels, pixel_format: GraphicsPixelFormat::PIXEL_BLUE_GREEN_RED_RESERVED_8_BIT_PER_COLOR, pixel_information: PixelBitmask { red: 0, @@ -48,15 +48,15 @@ impl GraphicsOutput { }, pixels_per_scan_line: stride_pixels, }; - let info_box = Box::into_raw(Box::new(info)); + let info_box = Box::into_raw(Box::new(mode_info)); let mode = GraphicsOutputProtocolMode { max_mode: 1, mode: 0, info: info_box, - size_of_info: size_of::(), - frame_buffer_base: fb_base_vaddr as u64, - frame_buffer_size: fb_size, + size_of_info: core::mem::size_of::(), + frame_buffer_base: frame_buffer_base_virtual_address as u64, + frame_buffer_size: frame_buffer_size_bytes, }; let mode_box = Box::into_raw(Box::new(mode)); @@ -75,10 +75,10 @@ impl GraphicsOutput { protocol_raw, mode_box, info_box, - width, - height, - fb_base_vaddr, - fb_size, + width_pixels, + height_pixels, + frame_buffer_base_virtual_address, + frame_buffer_size_bytes, stride_pixels, } } @@ -94,18 +94,21 @@ unsafe impl Sync for GraphicsOutput {} impl Drop for GraphicsOutput { fn drop(&mut self) { unsafe { + // Free in reverse construction order to avoid leaks. drop(Box::from_raw(self.protocol_raw)); + drop(Box::from_raw(self.mode_box)); + drop(Box::from_raw(self.info_box)); } } } #[inline] -fn with_go(_this: *const GraphicsOutputProtocol, f: F) -> Option +fn with_graphics_output(_this: *const GraphicsOutputProtocol, f: F) -> Option where F: FnOnce(&GraphicsOutput) -> R, { - GRAPHICS_OUTPUT.get().map(|m| { - let lock = m.lock(); + GRAPHICS_OUTPUT.get().map(|guard| { + let lock = guard.lock(); f(&*lock) }) } @@ -116,13 +119,17 @@ pub fn init_graphics_output() { let display_info = axdisplay::framebuffer_info(); info!("Graphics Output Protocol initialized: {:?}", display_info); - let frame_buffer_base = display_info.fb_base_vaddr; - let frame_buffer_size = display_info.fb_size; + let frame_buffer_base_virtual_address = display_info.fb_base_vaddr; + let frame_buffer_size_bytes = display_info.fb_size; + // Paint the framebuffer white for a quick visual check. unsafe { - core::ptr::write_bytes(frame_buffer_base as *mut u8, 0xFF, frame_buffer_size); + core::ptr::write_bytes( + frame_buffer_base_virtual_address as *mut u8, + 0xFF, + frame_buffer_size_bytes, + ); } - axdisplay::framebuffer_flush(); GRAPHICS_OUTPUT.init_once(Mutex::new(GraphicsOutput::new( @@ -130,8 +137,8 @@ pub fn init_graphics_output() { display_info.height, display_info.fb_base_vaddr, display_info.fb_size, - // FIXME: in most real device environments, pixels are not equal to width. - // refer: https://docs.rs/bootloader_api/latest/bootloader_api/info/struct.FrameBufferInfo.html#structfield.stride + // Note: on many devices, stride (pixels per scan line) differs from width. + // Here we temporarily set it to width. display_info.width, ))); } @@ -140,24 +147,26 @@ pub fn init_graphics_output() { pub unsafe extern "efiapi" fn query_mode( this: *const GraphicsOutputProtocol, mode_number: u32, - size_of_info: *mut usize, - info: *mut *const GraphicsOutputModeInformation, + size_of_info_out: *mut usize, + info_out: *mut *const GraphicsOutputModeInformation, ) -> Status { if mode_number != 0 { return Status::UNSUPPORTED; } - if size_of_info.is_null() || info.is_null() { + if size_of_info_out.is_null() || info_out.is_null() { return Status::INVALID_PARAMETER; } - match with_go(this, |go| (go.mode_box, go.info_box)) { - Some((mode_box, info_box)) => unsafe { - let mode = &*mode_box; - *size_of_info = mode.size_of_info; - *info = info_box as *const GraphicsOutputModeInformation; - Status::SUCCESS - }, - None => Status::DEVICE_ERROR, + unsafe { + match with_graphics_output(this, |go| (go.mode_box, go.info_box)) { + Some((mode_box, info_box)) => { + let mode = &*mode_box; + *size_of_info_out = mode.size_of_info; + *info_out = info_box as *const GraphicsOutputModeInformation; + Status::SUCCESS + } + None => Status::DEVICE_ERROR, + } } } @@ -168,12 +177,11 @@ pub unsafe extern "efiapi" fn set_mode( if mode_number != 0 { return Status::UNSUPPORTED; } - match with_go(this, |go| go.mode_box) { + match with_graphics_output(this, |go| go.mode_box) { Some(mode_box) => { let mode = unsafe { &mut *mode_box }; mode.mode = 0; - // TODO: if resolution switching is supported in the future, - // info / stride / fb will be updated here, etc. + // If resolution switching is added in the future, update info/stride/framebuffer here. Status::SUCCESS } None => Status::DEVICE_ERROR, @@ -189,53 +197,61 @@ pub unsafe extern "efiapi" fn blt( source_y: usize, destination_x: usize, destination_y: usize, - width: usize, - height: usize, - delta: usize, + width_pixels: usize, + height_pixels: usize, + blt_buffer_delta_bytes: usize, ) -> Status { - let Some((fb_base, fb_size, w, h, stride_px)) = with_go(this, |go| { + let Some(( + frame_buffer_base_virtual_address, + _frame_buffer_size_bytes, + screen_width_pixels, + screen_height_pixels, + stride_pixels, + )) = with_graphics_output(this, |go| { ( - go.fb_base_vaddr, - go.fb_size, - go.width as usize, - go.height as usize, + go.frame_buffer_base_virtual_address, + go.frame_buffer_size_bytes, + go.width_pixels as usize, + go.height_pixels as usize, go.stride_pixels as usize, ) - }) else { + }) + else { return Status::DEVICE_ERROR; }; - if width == 0 || height == 0 { + if width_pixels == 0 || height_pixels == 0 { return Status::SUCCESS; } - if destination_x >= w - || destination_y >= h - || destination_x + width > w - || destination_y + height > h + if destination_x >= screen_width_pixels + || destination_y >= screen_height_pixels + || destination_x + width_pixels > screen_width_pixels + || destination_y + height_pixels > screen_height_pixels { return Status::INVALID_PARAMETER; } - let bytes_per_pixel = 4usize; - let fb_pitch = stride_px * bytes_per_pixel; - let fb = fb_base as *mut u8; + const BYTES_PER_PIXEL: usize = 4; + let frame_buffer_pitch_bytes = stride_pixels * BYTES_PER_PIXEL; + let frame_buffer_ptr = frame_buffer_base_virtual_address as *mut u8; unsafe { match blt_operation { GraphicsOutputBltOperation::BLT_VIDEO_FILL => { - // fill (dest_x, dest_y, width, height) with the color of blt_buffer[0] + // Fill (destination_x, destination_y, width, height) with blt_buffer[0]. if blt_buffer.is_null() { return Status::INVALID_PARAMETER; } let px = *blt_buffer; let color = [px.blue, px.green, px.red, 0]; // BGRA - for row in 0..height { - let row_ptr = - fb.add((destination_y + row) * fb_pitch + destination_x * bytes_per_pixel); - for col in 0..width { - let p = row_ptr.add(col * bytes_per_pixel); - // 写 B,G,R,A + for row in 0..height_pixels { + let row_ptr = frame_buffer_ptr.add( + (destination_y + row) * frame_buffer_pitch_bytes + + destination_x * BYTES_PER_PIXEL, + ); + for col in 0..width_pixels { + let p = row_ptr.add(col * BYTES_PER_PIXEL); *p.add(0) = color[0]; *p.add(1) = color[1]; *p.add(2) = color[2]; @@ -245,70 +261,83 @@ pub unsafe extern "efiapi" fn blt( axdisplay::framebuffer_flush(); Status::SUCCESS } + GraphicsOutputBltOperation::BLT_VIDEO_TO_BLT_BUFFER => { if blt_buffer.is_null() { return Status::INVALID_PARAMETER; } - - // Each row in BLT buffer: if Delta == 0, tightly packed (Width * 4 bytes). - let dst_pitch = if delta == 0 { - width * bytes_per_pixel + // BLT buffer line stride: if Delta == 0, it is tightly packed (width * 4). + let destination_pitch_bytes = if blt_buffer_delta_bytes == 0 { + width_pixels * BYTES_PER_PIXEL } else { - delta + blt_buffer_delta_bytes }; - for row in 0..height { - // Source: read pixels from framebuffer at (source_x, source_y + row). - let src = fb.add((source_y + row) * fb_pitch + source_x * bytes_per_pixel); - + for row in 0..height_pixels { + // Source: read from framebuffer at (source_x, source_y + row). + let src = frame_buffer_ptr.add( + (source_y + row) * frame_buffer_pitch_bytes + source_x * BYTES_PER_PIXEL, + ); // Destination: write into BLT buffer at (destination_x, destination_y + row). - let dst = (blt_buffer as *mut u8) - .add((destination_y + row) * dst_pitch + destination_x * bytes_per_pixel); - - // Copy one scan line (Width * 4 bytes). - core::ptr::copy_nonoverlapping(src, dst, width * bytes_per_pixel); + let dst = (blt_buffer as *mut u8).add( + (destination_y + row) * destination_pitch_bytes + + destination_x * BYTES_PER_PIXEL, + ); + core::ptr::copy_nonoverlapping(src, dst, width_pixels * BYTES_PER_PIXEL); } - Status::SUCCESS } + GraphicsOutputBltOperation::BLT_BUFFER_TO_VIDEO => { if blt_buffer.is_null() { return Status::INVALID_PARAMETER; } - // in UEFI GOP spec, Delta = bytes per scan line in the BLT buffer. - // if Delta == 0, it means the buffer is tightly packed: Width * sizeof(BltPixel) = Width * 4. - let src_pitch = if delta == 0 { - width * bytes_per_pixel + // In GOP, Delta is bytes per scan line in the BLT buffer; 0 means tightly packed. + let source_pitch_bytes = if blt_buffer_delta_bytes == 0 { + width_pixels * BYTES_PER_PIXEL } else { - delta + blt_buffer_delta_bytes }; - for row in 0..height { - let dst = - fb.add((destination_y + row) * fb_pitch + destination_x * bytes_per_pixel); + for row in 0..height_pixels { + let dst = frame_buffer_ptr.add( + (destination_y + row) * frame_buffer_pitch_bytes + + destination_x * BYTES_PER_PIXEL, + ); let src = (blt_buffer as *const u8) - .add((source_y + row) * src_pitch + source_x * bytes_per_pixel); - core::ptr::copy_nonoverlapping(src, dst, width * bytes_per_pixel); + .add((source_y + row) * source_pitch_bytes + source_x * BYTES_PER_PIXEL); + core::ptr::copy_nonoverlapping(src, dst, width_pixels * BYTES_PER_PIXEL); } axdisplay::framebuffer_flush(); Status::SUCCESS } + GraphicsOutputBltOperation::BLT_VIDEO_TO_VIDEO => { - // internal memory transfer, supporting overlap (memmove semantics) - let src_x = source_x; - let src_y = source_y; - if src_x >= w || src_y >= h || src_x + width > w || src_y + height > h { + // Internal framebuffer transfer; overlapping is allowed (memmove semantics). + let source_x_pixels = source_x; + let source_y_pixels = source_y; + if source_x_pixels >= screen_width_pixels + || source_y_pixels >= screen_height_pixels + || source_x_pixels + width_pixels > screen_width_pixels + || source_y_pixels + height_pixels > screen_height_pixels + { return Status::INVALID_PARAMETER; } - for row in 0..height { - let dst = - fb.add((destination_y + row) * fb_pitch + destination_x * bytes_per_pixel); - let src = fb.add((src_y + row) * fb_pitch + src_x * bytes_per_pixel); - core::ptr::copy(src, dst, width * bytes_per_pixel); + for row in 0..height_pixels { + let dst = frame_buffer_ptr.add( + (destination_y + row) * frame_buffer_pitch_bytes + + destination_x * BYTES_PER_PIXEL, + ); + let src = frame_buffer_ptr.add( + (source_y_pixels + row) * frame_buffer_pitch_bytes + + source_x_pixels * BYTES_PER_PIXEL, + ); + core::ptr::copy(src, dst, width_pixels * BYTES_PER_PIXEL); } axdisplay::framebuffer_flush(); Status::SUCCESS } + _ => Status::UNSUPPORTED, } } @@ -323,9 +352,9 @@ pub unsafe extern "efiapi" fn blt( _source_y: usize, _destination_x: usize, _destination_y: usize, - _width: usize, - _height: usize, - _delta: usize, + _width_pixels: usize, + _height_pixels: usize, + _blt_buffer_delta_bytes: usize, ) -> Status { Status::UNSUPPORTED } From 2ee6475e44b2574243aecd77bdf9693e889501ff Mon Sep 17 00:00:00 2001 From: hanbings Date: Sun, 28 Sep 2025 06:15:14 +0800 Subject: [PATCH 20/21] feat(ci): added script for allocate_page() test. (#6) * chore(ci): removed scripts used to handle outdated dependencies. * feat(ci): added script for allocate_page() test. * fix(ci): fixed github actions. --- .github/workflows/test.yml | 55 +++++++++++++++-------------- scripts/test/check_allocate_test.sh | 2 +- scripts/test/check_hello_test.sh | 2 +- scripts/test/make_esp.sh | 7 +++- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1ee873..5587fcc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,24 +15,11 @@ jobs: with: target: riscv64gc-unknown-none-elf toolchain: nightly-2025-03-31 - + - name: Install cargo-binutils and rust-objcopy run: | rustup component add llvm-tools-preview cargo install cargo-binutils - - - name: Prepare for Build - run: | - # Fix breaking update in rust dependency. - cargo fetch - find ~/.cargo/git/checkouts/ -type f -name '*.rs' -exec sed -i 's/#\[unsafe(naked)\]/#[naked]/g' {} + - - # Correct incorrect build directory name in build script. - sed -i '/^SBI :=/s#riscv64imac-unknown-none-elf#riscv64gc-unknown-none-elf#' Makefile - sed -i '/^SBI :=/s#riscv64imac-unknown-none-elf#riscv64gc-unknown-none-elf#' scripts/make/build.mk - - make clone-rustsbi - sed -i -E '/^\s*#\[repr\(align\(16\)\)\]\s*$/d' rustsbi/prototyper/prototyper/src/sbi/early_trap.rs - name: Build with Makefile run: | @@ -51,27 +38,43 @@ jobs: - name: Build EDK2 run: sh scripts/test/build_edk2.sh - + - name: Generate disk image run: sh scripts/test/disk.sh - - name: Create EFI System Partition (ESP) - run: sh scripts/test/make_esp.sh - - name: Set up QEMU run: | sudo apt update sudo apt install -y qemu-system-misc - - name: Run QEMU - run: | - make qemu-run > qemu.log + # Test 1: HelloRiscv + - name: Create ESP (HelloRiscv) + run: EFI_NAME=HelloRiscv sh scripts/test/make_esp.sh + + - name: Run QEMU (HelloRiscv) + run: make qemu-run > qemu-hello.log + + - name: Upload QEMU log (HelloRiscv) + uses: actions/upload-artifact@v4 + with: + name: log-hello + path: qemu-hello.log + + - name: Check QEMU output (HelloRiscv) + run: sh scripts/test/check_hello_test.sh + + # Test 2: AllocatePage + - name: Create ESP (AllocatePage) + run: EFI_NAME=AllocatePage sh scripts/test/make_esp.sh + + - name: Run QEMU (AllocatePage) + run: make qemu-run > qemu-allocate.log - - name: Upload QEMU log + - name: Upload QEMU log (AllocatePage) uses: actions/upload-artifact@v4 with: - name: log - path: qemu.log + name: log-allocate + path: qemu-allocate.log - - name: Check QEMU output - run: sh scripts/test/check_hello_test.sh \ No newline at end of file + - name: Check QEMU output (AllocatePage) + run: sh scripts/test/check_allocate_test.sh diff --git a/scripts/test/check_allocate_test.sh b/scripts/test/check_allocate_test.sh index 754f8ca..e930a04 100644 --- a/scripts/test/check_allocate_test.sh +++ b/scripts/test/check_allocate_test.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -LOG_FILE="qemu.log" +LOG_FILE="qemu-allocate.log" TARGET_STRING="EFI Output: 0xDEADBEEF12345678" if [ ! -f "$LOG_FILE" ]; then diff --git a/scripts/test/check_hello_test.sh b/scripts/test/check_hello_test.sh index d5dc560..80d46f5 100644 --- a/scripts/test/check_hello_test.sh +++ b/scripts/test/check_hello_test.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -LOG_FILE="qemu.log" +LOG_FILE="qemu-hello.log" TARGET_STRING="EFI Output: Hello, World!" if [ ! -f "$LOG_FILE" ]; then diff --git a/scripts/test/make_esp.sh b/scripts/test/make_esp.sh index 1f942f4..5b99e93 100644 --- a/scripts/test/make_esp.sh +++ b/scripts/test/make_esp.sh @@ -5,6 +5,9 @@ PROJECT_ROOT=$(pwd) IMG_NAME="disk.img" MOUNT_DIR="mnt_fat32" ESP_DIR="$MOUNT_DIR/EFI/BOOT" +EFI_NAME="${EFI_NAME:-HelloRiscv}" +BUILD_FLAVOR="${BUILD_FLAVOR:-DEBUG_GCC5}" +ARCH_DIR="${ARCH_DIR:-RISCV64}" if [ ! -d "$MOUNT_DIR" ]; then mkdir "$MOUNT_DIR" @@ -17,7 +20,9 @@ echo "[2/3] 创建 ESP 目录结构..." sudo mkdir -p "$ESP_DIR" echo "[3/3] 复制 efi 文件到 ESP..." -sudo cp "$PROJECT_ROOT/edk2/Build/DEBUG_GCC5/RISCV64/HelloRiscv.efi" "$ESP_DIR/BOOTRISCV64.EFI" +SRC_EFI="$PROJECT_ROOT/edk2/Build/$BUILD_FLAVOR/$ARCH_DIR/${EFI_NAME}.efi" +echo "源文件: $SRC_EFI" +sudo cp "$SRC_EFI" "$ESP_DIR/BOOTRISCV64.EFI" sudo find "$ESP_DIR" -type d | while read -r dir; do echo "$dir" From f34a3a187243bc361c0e9ac9e266f129af6ee37a Mon Sep 17 00:00:00 2001 From: hanbings Date: Sun, 12 Oct 2025 06:46:38 -0400 Subject: [PATCH 21/21] fix(uefi): add feature cfg! for ramdisk functionality. --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 95216a6..ee626be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -66,6 +66,7 @@ pub extern "C" fn rust_main(_cpu_id: usize, dtb: usize) -> ! { } // ramdisk check + #[cfg(feature = "ramdisk_cpio")] crate::medium::ramdisk_cpio::check_ramdisk(); crate::shell::shell_main();