diff --git a/components/patina_mm/src/component/communicator.rs b/components/patina_mm/src/component/communicator.rs index 304efa59f..fba635c51 100644 --- a/components/patina_mm/src/component/communicator.rs +++ b/components/patina_mm/src/component/communicator.rs @@ -16,7 +16,7 @@ mod comm_buffer_update; use crate::{ - config::{CommunicateBuffer, EfiMmCommunicateHeader, MmCommunicationConfiguration}, + config::{CommunicateBuffer, MmCommunicationConfiguration}, service::SwMmiTrigger, }; use patina::{ @@ -26,8 +26,9 @@ use patina::{ Storage, component, service::{IntoService, Service}, }, + pi::protocols::communication::EfiMmCommunicateHeader, }; -extern crate alloc; + use alloc::vec::Vec; use core::{ @@ -376,14 +377,16 @@ mod tests { communicator::{MmCommunicator, MockMmExecutor}, sw_mmi_manager::SwMmiManager, }, - config::{CommunicateBuffer, MmCommBufferStatus, MmCommunicationConfiguration}, + config::{CommunicateBuffer, MmCommunicationConfiguration}, + }; + use patina::{ + component::{IntoComponent, Storage}, + management_mode::MmCommBufferStatus, }; - use patina::component::{IntoComponent, Storage}; use core::{cell::RefCell, pin::Pin}; - extern crate alloc; - use alloc::vec::Vec; + use std::vec::Vec; /// Simple MM Executor for unit tests that simulates MM handlers echoing request data back as the response struct EchoMmExecutor; diff --git a/components/patina_mm/src/component/communicator/comm_buffer_update.rs b/components/patina_mm/src/component/communicator/comm_buffer_update.rs index 09f730cb4..dc0d5fbe7 100644 --- a/components/patina_mm/src/component/communicator/comm_buffer_update.rs +++ b/components/patina_mm/src/component/communicator/comm_buffer_update.rs @@ -10,19 +10,16 @@ //! //! SPDX-License-Identifier: Apache-2.0 -use crate::{ - config::CommunicateBuffer, - protocol::mm_comm_buffer_update::{self, MmCommBufferUpdateProtocol}, -}; +use crate::config::CommunicateBuffer; use patina::{ base::UEFI_PAGE_SIZE, boot_services::{BootServices, StandardBootServices, event::EventType, tpl::Tpl}, + management_mode::protocol::mm_comm_buffer_update::{self, MmCommBufferUpdateProtocol}, }; use zerocopy::FromBytes; use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; -extern crate alloc; use alloc::boxed::Box; /// Context for the MM Comm Buffer Update Protocol notify callback @@ -283,8 +280,7 @@ mod tests { }; use patina::boot_services::StandardBootServices; - extern crate alloc; - use alloc::{boxed::Box, vec}; + use alloc::boxed::Box; /// Helper to create a test protocol notify context without boot services fn create_test_context(updatable_buffer_id: u8) -> Box { diff --git a/components/patina_mm/src/config.rs b/components/patina_mm/src/config.rs index 958dbf420..d63ac9a7e 100644 --- a/components/patina_mm/src/config.rs +++ b/components/patina_mm/src/config.rs @@ -18,51 +18,13 @@ //! //! SPDX-License-Identifier: Apache-2.0 //! -extern crate alloc; use alloc::vec::Vec; use core::{fmt, pin::Pin, ptr::NonNull}; -use patina::{BinaryGuid, Guid, base::UEFI_PAGE_MASK}; -use zerocopy_derive::*; - -/// MM Communication Buffer Status -/// -/// Shared structure between DXE and MM environments to communicate the status -/// of MM communication operations. This structure is written by DXE before -/// triggering an MMI and read/written by MM during MMI processing. -/// -/// This is a structure currently used in some MM Supervisor MM implementations. -#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)] -#[repr(C, packed)] -pub struct MmCommBufferStatus { - /// Whether the data in the fixed MM communication buffer is valid when entering from non-MM to MM. - /// Must be set to TRUE before triggering MMI, will be set to FALSE by MM after processing. - pub is_comm_buffer_valid: u8, - - /// The channel used to communicate with MM. - /// FALSE = user buffer, TRUE = supervisor buffer - pub talk_to_supervisor: u8, - - /// The return status when returning from MM to non-MM. - pub return_status: u64, - - /// The size in bytes of the output buffer when returning from MM to non-MM. - pub return_buffer_size: u64, -} - -impl Default for MmCommBufferStatus { - #[coverage(off)] - fn default() -> Self { - Self::new() - } -} - -impl MmCommBufferStatus { - /// Create a new mailbox status with all fields zeroed - pub const fn new() -> Self { - Self { is_comm_buffer_valid: 0, talk_to_supervisor: 0, return_status: 0, return_buffer_size: 0 } - } -} +use patina::{ + BinaryGuid, Guid, base::UEFI_PAGE_MASK, management_mode::MmCommBufferStatus, + pi::protocols::communication::EfiMmCommunicateHeader, +}; /// Management Mode (MM) Configuration /// @@ -119,70 +81,6 @@ impl fmt::Display for MmCommunicationConfiguration { } } -/// UEFI MM Communicate Header -/// -/// A standard header that must be present at the beginning of any MM communication buffer. -/// -/// ## Notes -/// -/// - This only supports V1 and V2 of the MM Communicate header format. -#[derive(Debug, Clone, Copy)] -#[repr(C)] -pub struct EfiMmCommunicateHeader { - /// Allows for disambiguation of the message format. - /// Used to identify the registered MM handlers that should be given the message. - header_guid: patina::BinaryGuid, - /// The size of Data (in bytes) and does not include the size of the header. - message_length: usize, -} - -impl EfiMmCommunicateHeader { - /// Create a new communicate header with the specified GUID and message length. - pub fn new(header_guid: Guid, message_length: usize) -> Self { - Self { header_guid: header_guid.to_efi_guid().into(), message_length } - } - - /// Returns the communicate header as a slice of bytes using safe conversion. - /// - /// Useful if byte-level access to the header structure is needed. - pub fn as_bytes(&self) -> &[u8] { - // SAFETY: EfiMmCommunicateHeader is repr(C) with well-defined layout and size - unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, Self::size()) } - } - - /// Returns the size of the header in bytes. - pub const fn size() -> usize { - core::mem::size_of::() - } - - /// Get the header GUID from the communication buffer. - /// - /// Returns `Some(guid)` if the buffer has been properly initialized with a GUID, - /// or `None` if the buffer is not initialized. - /// - /// # Returns - /// - /// The GUID from the communication header if available. - /// - /// # Errors - /// - /// Returns an error if the communication buffer header cannot be read. - pub fn header_guid(&self) -> Guid<'_> { - Guid::from_ref(&self.header_guid) - } - - /// Returns the message length from this communicate header. - /// - /// The length represents the size of the message data that follows the header. - /// - /// # Returns - /// - /// The length in bytes of the message data (excluding the header size). - pub const fn message_length(&self) -> usize { - self.message_length - } -} - /// MM Communicator Service Status Codes #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum CommunicateBufferStatus { diff --git a/components/patina_mm/src/lib.rs b/components/patina_mm/src/lib.rs index 7ab1d99e1..e90a80f09 100644 --- a/components/patina_mm/src/lib.rs +++ b/components/patina_mm/src/lib.rs @@ -6,7 +6,8 @@ #![cfg_attr(all(not(feature = "std"), not(test), not(feature = "mockall")), no_std)] #![feature(coverage_attribute)] +extern crate alloc; + pub mod component; pub mod config; -pub mod protocol; pub mod service; diff --git a/components/patina_mm/tests/patina_mm_integration/common/constants.rs b/components/patina_mm/tests/patina_mm_integration/common/constants.rs index fe6aea748..d9011a868 100644 --- a/components/patina_mm/tests/patina_mm_integration/common/constants.rs +++ b/components/patina_mm/tests/patina_mm_integration/common/constants.rs @@ -11,52 +11,16 @@ use patina::base::SIZE_4KB; /// Standard test buffer size pub const TEST_BUFFER_SIZE: usize = SIZE_4KB; -/// MM Supervisor constants and definitions +/// MM Supervisor constants and definitions for testing /// /// Note: These values are only used for testing. They're not meant to be /// accurate or used in production code. pub mod mm_supv { - /// Supervisor signature bytes - pub const SIGNATURE: [u8; 4] = [b'M', b'S', b'U', b'P']; - - /// Communication protocol revision - pub const REVISION: u32 = 1; - - /// Request signature as a DWORD - pub const REQUEST_SIGNATURE: u32 = 0x5055534D; // 'MSUP' - - /// Supervisor version + /// Mock supervisor version for testing pub const VERSION: u32 = 0x00130008; - /// Supervisor patch level + /// Mock supervisor patch level for testing pub const PATCH_LEVEL: u32 = 0x00010001; - - /// Maximum request level supported - pub const MAX_REQUEST_LEVEL: u64 = 0x0000000000000004; // COMM_UPDATE - - /// Request type constants - pub mod requests { - /// Request for unblocking memory regions - pub const UNBLOCK_MEM: u32 = 0x0001; - - /// Request to fetch security policy - pub const FETCH_POLICY: u32 = 0x0002; - - /// Request version information - pub const VERSION_INFO: u32 = 0x0003; - - /// Request to update the communication buffer address - pub const COMM_UPDATE: u32 = 0x0004; - } - - /// Response code constants - pub mod responses { - /// Operation completed successfully - pub const SUCCESS: u64 = 0; - - /// Operation failed with error - pub const ERROR: u64 = 0xFFFFFFFFFFFFFFFF; - } } /// Test GUIDs for different handlers diff --git a/components/patina_mm/tests/patina_mm_integration/common/handlers.rs b/components/patina_mm/tests/patina_mm_integration/common/handlers.rs index d0def8752..ec77c40a3 100644 --- a/components/patina_mm/tests/patina_mm_integration/common/handlers.rs +++ b/components/patina_mm/tests/patina_mm_integration/common/handlers.rs @@ -17,11 +17,16 @@ //! SPDX-License-Identifier: Apache-2.0 use crate::patina_mm_integration::common::constants::*; +use r_efi::efi; extern crate alloc; use alloc::{string::String, vec::Vec}; -use zerocopy::{FromBytes, IntoBytes}; -use zerocopy_derive::*; +pub use zerocopy::IntoBytes; + +pub use patina::management_mode::protocol::{ + mm_supervisor_request, + mm_supervisor_request::{MmSupervisorRequestHeader, MmSupervisorVersionInfo, RequestType, ResponseType}, +}; /// Standardized error type for MM handlers #[derive(Debug, Clone, PartialEq, Eq)] @@ -109,64 +114,6 @@ impl MmHandler for VersionInfoHandler { } } -/// MM Supervisor request header -#[derive(Debug, Clone, Copy, IntoBytes, FromBytes, Immutable)] -#[repr(C)] -pub struct MmSupervisorRequestHeader { - pub signature: u32, - pub revision: u32, - pub request: u32, - pub reserved: u32, - pub result: u64, -} - -impl MmSupervisorRequestHeader { - const SIZE: usize = core::mem::size_of::(); - - /// Converts a byte slice to a MmSupervisorRequestHeader - #[allow(dead_code)] // // Usage not recognized - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() < Self::SIZE { - return Err(MmHandlerError::InvalidInput("Buffer too small for header".to_string())); - } - - Self::read_from_bytes(&bytes[..Self::SIZE]) - .map_err(|_| MmHandlerError::InvalidInput("Failed to parse header from bytes".to_string())) - } - - /// Converts a MmSupervisorRequestHeader instance to a byte vector - pub fn to_bytes(self) -> Vec { - self.as_bytes().to_vec() - } -} - -/// MM Supervisor version information -#[derive(Debug, Clone, Copy, IntoBytes, FromBytes, Immutable)] -#[repr(C)] -pub struct MmSupervisorVersionInfo { - pub version: u32, - pub patch_level: u32, - pub max_supervisor_request_level: u64, -} - -impl MmSupervisorVersionInfo { - const SIZE: usize = core::mem::size_of::(); - - #[allow(dead_code)] // Usage not recognized - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() < Self::SIZE { - return Err(MmHandlerError::InvalidInput("Buffer too small for version info".to_string())); - } - - Self::read_from_bytes(&bytes[..Self::SIZE]) - .map_err(|_| MmHandlerError::InvalidInput("Failed to parse version info from bytes".to_string())) - } - - fn to_bytes(self) -> Vec { - self.as_bytes().to_vec() - } -} - /// MM Supervisor handler for testing supervisor communication patterns pub struct MmSupervisorHandler { #[allow(dead_code)] // Usage not recognized @@ -180,9 +127,9 @@ impl MmSupervisorHandler { fn handle_get_info_request(&self) -> MmHandlerResult> { let response_header = MmSupervisorRequestHeader { - signature: mm_supv::REQUEST_SIGNATURE, - revision: mm_supv::REVISION, - request: mm_supv::requests::VERSION_INFO, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::VersionInfo as u32, reserved: 0, result: 0, // Success }; @@ -190,12 +137,12 @@ impl MmSupervisorHandler { let version_info = MmSupervisorVersionInfo { version: mm_supv::VERSION, patch_level: mm_supv::PATCH_LEVEL, - max_supervisor_request_level: mm_supv::MAX_REQUEST_LEVEL, + max_supervisor_request_level: RequestType::MAX_REQUEST_TYPE, }; let mut response = Vec::new(); - response.extend_from_slice(&response_header.to_bytes()); - response.extend_from_slice(&version_info.to_bytes()); + response.extend_from_slice(response_header.as_bytes()); + response.extend_from_slice(version_info.as_bytes()); log::debug!(target: "supervisor_handler", "Generated get info response: {} bytes", response.len()); Ok(response) @@ -203,9 +150,9 @@ impl MmSupervisorHandler { fn handle_get_capabilities_request(&self) -> MmHandlerResult> { let response_header = MmSupervisorRequestHeader { - signature: mm_supv::REQUEST_SIGNATURE, - revision: mm_supv::REVISION, - request: mm_supv::requests::FETCH_POLICY, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::FetchPolicy as u32, reserved: 0, result: 0, // Success }; @@ -213,7 +160,7 @@ impl MmSupervisorHandler { let capabilities: u64 = 0x00000007; // Mock capabilities value let mut response = Vec::new(); - response.extend_from_slice(&response_header.to_bytes()); + response.extend_from_slice(response_header.as_bytes()); response.extend_from_slice(&capabilities.to_le_bytes()); log::debug!(target: "supervisor_handler", "Generated get capabilities response: {} bytes", response.len()); @@ -222,9 +169,9 @@ impl MmSupervisorHandler { fn handle_comm_update_request(&self) -> MmHandlerResult> { let response_header = MmSupervisorRequestHeader { - signature: mm_supv::REQUEST_SIGNATURE, - revision: mm_supv::REVISION, - request: mm_supv::requests::COMM_UPDATE, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::CommUpdate as u32, reserved: 0, result: 0, // Success }; @@ -233,7 +180,7 @@ impl MmSupervisorHandler { let update_result: u32 = 0x00000001; // Success status let mut response = Vec::new(); - response.extend_from_slice(&response_header.to_bytes()); + response.extend_from_slice(response_header.as_bytes()); response.extend_from_slice(&update_result.to_le_bytes()); log::debug!(target: "supervisor_handler", "Generated comm update response: {} bytes", response.len()); @@ -242,9 +189,9 @@ impl MmSupervisorHandler { fn handle_unblock_mem_request(&self) -> MmHandlerResult> { let response_header = MmSupervisorRequestHeader { - signature: mm_supv::REQUEST_SIGNATURE, - revision: mm_supv::REVISION, - request: mm_supv::requests::UNBLOCK_MEM, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::UnblockMem as u32, reserved: 0, result: 0, // Success }; @@ -253,7 +200,7 @@ impl MmSupervisorHandler { let unblock_status: u64 = 0x0000000000000001; // Success - memory regions unblocked let mut response = Vec::new(); - response.extend_from_slice(&response_header.to_bytes()); + response.extend_from_slice(response_header.as_bytes()); response.extend_from_slice(&unblock_status.to_le_bytes()); log::debug!(target: "supervisor_handler", "Generated unblock mem response: {} bytes", response.len()); @@ -273,58 +220,59 @@ impl MmHandler for MmSupervisorHandler { ))); } - let request_header = MmSupervisorRequestHeader::from_bytes(data)?; + let request_header = MmSupervisorRequestHeader::from_bytes(data) + .ok_or_else(|| MmHandlerError::InvalidInput("Failed to parse header from bytes".to_string()))?; // Validate signature - if request_header.signature != mm_supv::REQUEST_SIGNATURE { + if request_header.signature != mm_supervisor_request::SIGNATURE { return Err(MmHandlerError::InvalidInput(format!( "Invalid signature: 0x{:08X}, expected 0x{:08X}", request_header.signature, - mm_supv::REQUEST_SIGNATURE + mm_supervisor_request::SIGNATURE ))); } // Validate revision - if request_header.revision != mm_supv::REVISION { + if request_header.revision != mm_supervisor_request::REVISION { return Err(MmHandlerError::InvalidInput(format!( "Invalid revision: 0x{:08X}, expected 0x{:08X}", request_header.revision, - mm_supv::REVISION + mm_supervisor_request::REVISION ))); } // Process based on request type - match request_header.request { - mm_supv::requests::VERSION_INFO => { + match RequestType::try_from(request_header.request) { + Ok(RequestType::VersionInfo) => { log::debug!(target: "supervisor_handler", "Processing get info request"); self.handle_get_info_request() } - mm_supv::requests::FETCH_POLICY => { + Ok(RequestType::FetchPolicy) => { log::debug!(target: "supervisor_handler", "Processing fetch policy request"); self.handle_get_capabilities_request() } - mm_supv::requests::COMM_UPDATE => { + Ok(RequestType::CommUpdate) => { log::debug!(target: "supervisor_handler", "Processing comm update request"); self.handle_comm_update_request() } - mm_supv::requests::UNBLOCK_MEM => { + Ok(RequestType::UnblockMem) => { log::debug!(target: "supervisor_handler", "Processing unblock mem request"); self.handle_unblock_mem_request() } - _ => { + Err(_) => { log::warn!(target: "supervisor_handler", "Unsupported request type: 0x{:08X}", request_header.request); // Return error response let error_header = MmSupervisorRequestHeader { - signature: mm_supv::REQUEST_SIGNATURE, - revision: mm_supv::REVISION, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, request: request_header.request, reserved: 0, - result: 0xFFFFFFFFFFFFFFFF, // Error + result: efi::Status::from(ResponseType::InvalidRequest).as_usize() as u64, // Error }; let mut response = Vec::new(); - response.extend_from_slice(&error_header.to_bytes()); + response.extend_from_slice(error_header.as_bytes()); Ok(response) } } @@ -482,11 +430,11 @@ mod tests { result: 0x123456789ABCDEF0, }; - let bytes = original.to_bytes(); + let bytes = original.as_bytes(); assert_eq!(bytes.len(), MmSupervisorRequestHeader::SIZE); - let recovered = MmSupervisorRequestHeader::from_bytes(&bytes); - assert!(recovered.is_ok(), "Should successfully parse the header"); + let recovered = MmSupervisorRequestHeader::from_bytes(bytes); + assert!(recovered.is_some(), "Should successfully parse the header"); let recovered = recovered.unwrap(); assert_eq!(recovered.signature, original.signature); diff --git a/components/patina_mm/tests/patina_mm_integration/framework/core_functionality_tests.rs b/components/patina_mm/tests/patina_mm_integration/framework/core_functionality_tests.rs index 3702f34e8..f80ac028b 100644 --- a/components/patina_mm/tests/patina_mm_integration/framework/core_functionality_tests.rs +++ b/components/patina_mm/tests/patina_mm_integration/framework/core_functionality_tests.rs @@ -9,17 +9,16 @@ //! //! SPDX-License-Identifier: Apache-2.0 -use patina::{BinaryGuid, Guid}; +use patina::{BinaryGuid, Guid, pi::protocols::communication::EfiMmCommunicateHeader}; use patina_mm::{ component::communicator::{MmCommunication, MmCommunicator, MmExecutor, Status}, - config::{CommunicateBuffer, EfiMmCommunicateHeader}, + config::CommunicateBuffer, }; use core::pin::Pin; use std::collections::HashMap; -extern crate alloc; -use alloc::{boxed::Box, vec::Vec}; +use std::vec::Vec; /// Lightweight MM handler used for testing struct TestHandler { diff --git a/components/patina_mm/tests/patina_mm_integration/mm_communicator/component_integration_tests.rs b/components/patina_mm/tests/patina_mm_integration/mm_communicator/component_integration_tests.rs index e175f7b91..d10871edf 100644 --- a/components/patina_mm/tests/patina_mm_integration/mm_communicator/component_integration_tests.rs +++ b/components/patina_mm/tests/patina_mm_integration/mm_communicator/component_integration_tests.rs @@ -16,6 +16,7 @@ use patina::{ Guid, component::{IntoComponent, Storage}, + management_mode::protocol::{mm_supervisor_request, mm_supervisor_request::RequestType}, }; use patina_mm::{ component::{communicator::MmCommunicator, sw_mmi_manager::SwMmiManager}, @@ -24,6 +25,8 @@ use patina_mm::{ use core::pin::Pin; +use r_efi::efi; + use crate::patina_mm_integration::common::*; #[test] @@ -156,17 +159,17 @@ fn test_real_component_mm_supervisor_version_request() { // Create MM Supervisor version request using the actual structures let version_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::VERSION_INFO, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::VersionInfo.into(), reserved: 0, result: 0, }; - let request_bytes = version_request.to_bytes(); + let request_bytes = version_request.as_bytes(); // Send the request using the real component framework - let result = framework.communicate(&Guid::from_ref(&test_guids::MM_SUPERVISOR), &request_bytes); + let result = framework.communicate(&Guid::from_ref(&test_guids::MM_SUPERVISOR), request_bytes); assert!(result.is_ok(), "Real component MM Supervisor communication should succeed: {:?}", result.err()); let response = result.unwrap(); @@ -181,10 +184,10 @@ fn test_real_component_mm_supervisor_version_request() { MmSupervisorRequestHeader::from_bytes(&response).expect("Should parse response header from real component"); // Verify header fields - assert_eq!(response_header.signature, mm_supv::REQUEST_SIGNATURE, "Response signature should match"); - assert_eq!(response_header.revision, mm_supv::REVISION, "Response revision should match"); - assert_eq!(response_header.request, mm_supv::requests::VERSION_INFO, "Response request type should match"); - assert_eq!(response_header.result, 0, "Response should indicate success"); + assert_eq!(response_header.signature, mm_supervisor_request::SIGNATURE, "Response signature should match"); + assert_eq!(response_header.revision, mm_supervisor_request::REVISION, "Response revision should match"); + assert_eq!(response_header.request, RequestType::VersionInfo.into(), "Response request type should match"); + assert_eq!(response_header.result, efi::Status::SUCCESS.as_usize() as u64, "Response should indicate success"); // Parse version info from response let version_info_offset = core::mem::size_of::(); @@ -201,7 +204,7 @@ fn test_real_component_mm_supervisor_version_request() { assert_eq!( version_info.max_supervisor_request_level, - mm_supv::MAX_REQUEST_LEVEL, + RequestType::MAX_REQUEST_TYPE, "Max request level should match expected value" ); } @@ -275,15 +278,15 @@ fn test_real_component_multiple_handlers() { // Test MM supervisor handler let supervisor_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::FETCH_POLICY, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::FetchPolicy.into(), reserved: 0, result: 0, }; let supervisor_result = - framework.communicate(&Guid::from_ref(&test_guids::MM_SUPERVISOR), &supervisor_request.to_bytes()); + framework.communicate(&Guid::from_ref(&test_guids::MM_SUPERVISOR), supervisor_request.as_bytes()); assert!(supervisor_result.is_ok(), "Supervisor communication should succeed"); // Both handlers should work independently through the real component infrastructure diff --git a/components/patina_mm/tests/patina_mm_integration/mm_communicator/stress_tests.rs b/components/patina_mm/tests/patina_mm_integration/mm_communicator/stress_tests.rs index 933306ef5..4873361b3 100644 --- a/components/patina_mm/tests/patina_mm_integration/mm_communicator/stress_tests.rs +++ b/components/patina_mm/tests/patina_mm_integration/mm_communicator/stress_tests.rs @@ -10,8 +10,6 @@ use crate::patina_mm_integration::common::{constants::*, framework::*}; -extern crate alloc; -use alloc::{boxed::Box, format, vec, vec::Vec}; use core::pin::Pin; use patina::{ Guid, diff --git a/components/patina_mm/tests/patina_mm_integration/mm_supervisor/communication_tests.rs b/components/patina_mm/tests/patina_mm_integration/mm_supervisor/communication_tests.rs index c4cb5bd3c..9cdec6711 100644 --- a/components/patina_mm/tests/patina_mm_integration/mm_supervisor/communication_tests.rs +++ b/components/patina_mm/tests/patina_mm_integration/mm_supervisor/communication_tests.rs @@ -15,6 +15,8 @@ //! SPDX-License-Identifier: Apache-2.0 use crate::patina_mm_integration::common::*; +use patina::management_mode::protocol::{mm_supervisor_request, mm_supervisor_request::RequestType}; +use r_efi::efi; #[test] fn test_mm_supervisor_version_request_integration() { @@ -25,17 +27,17 @@ fn test_mm_supervisor_version_request_integration() { // Create MM Supervisor version request using safe operations let version_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::VERSION_INFO, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::VersionInfo.into(), reserved: 0, result: 0, }; - let request_bytes = version_request.to_bytes(); + let request_bytes = version_request.as_bytes(); // Send the request using framework - let result = framework.communicate(&test_guids::MM_SUPERVISOR, &request_bytes); + let result = framework.communicate(&test_guids::MM_SUPERVISOR, request_bytes); assert!(result.is_ok(), "MM Supervisor communication should succeed: {:?}", result.err()); let response = result.unwrap(); @@ -51,7 +53,7 @@ fn test_mm_supervisor_version_request_integration() { assert_eq!(response_header.signature, version_request.signature, "Signature should match"); assert_eq!(response_header.revision, version_request.revision, "Revision should match"); assert_eq!(response_header.request, version_request.request, "Request type should match"); - assert_eq!(response_header.result, mm_supv::responses::SUCCESS, "Result should be success"); + assert_eq!(response_header.result, efi::Status::SUCCESS.as_usize() as u64, "Result should be success"); // Parse version info safely let version_info_offset = core::mem::size_of::(); @@ -75,17 +77,17 @@ fn test_mm_supervisor_capabilities_request() { // Create capabilities request using safe operations let capabilities_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::FETCH_POLICY, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::FetchPolicy.into(), reserved: 0, result: 0, }; - let request_bytes = capabilities_request.to_bytes(); + let request_bytes = capabilities_request.as_bytes(); // Send the request using framework - let result = framework.communicate(&test_guids::MM_SUPERVISOR, &request_bytes); + let result = framework.communicate(&test_guids::MM_SUPERVISOR, request_bytes); assert!(result.is_ok(), "MM Supervisor capabilities communication should succeed"); let response = result.unwrap(); @@ -97,7 +99,7 @@ fn test_mm_supervisor_capabilities_request() { // Parse response header safely let response_header = MmSupervisorRequestHeader::from_bytes(&response).expect("Should parse response header"); - assert_eq!(response_header.result, mm_supv::responses::SUCCESS, "Capabilities request should succeed"); + assert_eq!(response_header.result, efi::Status::SUCCESS.as_usize() as u64, "Capabilities request should succeed"); // Parse capabilities safely let capabilities_offset = core::mem::size_of::(); @@ -124,17 +126,17 @@ fn test_mm_supervisor_invalid_request() { // Create invalid request using safe operations let invalid_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, request: 0xFFFF, // Invalid request type reserved: 0, result: 0, }; - let request_bytes = invalid_request.to_bytes(); + let request_bytes = invalid_request.as_bytes(); // Send the request using framework - let result = framework.communicate(&test_guids::MM_SUPERVISOR, &request_bytes); + let result = framework.communicate(&test_guids::MM_SUPERVISOR, request_bytes); assert!(result.is_ok(), "Communication should succeed even with invalid request"); let response = result.unwrap(); @@ -142,7 +144,11 @@ fn test_mm_supervisor_invalid_request() { // Parse response header safely let response_header = MmSupervisorRequestHeader::from_bytes(&response).expect("Should parse response header"); - assert_eq!(response_header.result, mm_supv::responses::ERROR, "Invalid request should return error"); + assert_eq!( + response_header.result, + efi::Status::INVALID_PARAMETER.as_usize() as u64, + "Invalid request should return error" + ); } #[test] @@ -153,16 +159,16 @@ fn test_mm_supervisor_invalid_signature() { // Create request with invalid signature using safe operations let invalid_request = MmSupervisorRequestHeader { signature: u32::from_le_bytes([b'I', b'N', b'V', b'D']), // Invalid signature - revision: mm_supv::REVISION, - request: mm_supv::requests::VERSION_INFO, + revision: mm_supervisor_request::REVISION, + request: RequestType::VersionInfo.into(), reserved: 0, result: 0, }; - let request_bytes = invalid_request.to_bytes(); + let request_bytes = invalid_request.as_bytes(); // Test handler directly - let result = mm_supervisor.handle_request(&request_bytes); + let result = mm_supervisor.handle_request(request_bytes); assert!(result.is_err(), "Invalid signature should cause handler to fail"); let error_msg = format!("{}", result.unwrap_err()); @@ -207,16 +213,16 @@ fn test_mm_supervisor_builder_integration() { // Test MM Supervisor handler as well let version_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::VERSION_INFO, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::VersionInfo.into(), reserved: 0, result: 0, }; // Use framework directly instead of mm_comm_service - let request_data = version_request.to_bytes(); // Convert to bytes - let supervisor_result = framework.communicate(&test_guids::MM_SUPERVISOR, &request_data); + let request_data = version_request.as_bytes(); // Convert to bytes + let supervisor_result = framework.communicate(&test_guids::MM_SUPERVISOR, request_data); assert!(supervisor_result.is_ok(), "MM Supervisor should work"); // Verify both triggers worked (we made 2 communication calls: echo + supervisor) @@ -229,26 +235,26 @@ fn test_safe_message_parsing_with_mm_supervisor() { let mut buffer = vec![0u8; TEST_BUFFER_SIZE]; let version_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::VERSION_INFO, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::VersionInfo.into(), reserved: 0, result: 0, }; - let request_data = version_request.to_bytes(); + let request_data = version_request.as_bytes(); // Test writing MM Supervisor message safely let mut parser = MmMessageParser::new(&mut buffer); parser - .write_message(&test_guids::MM_SUPERVISOR, &request_data) + .write_message(&test_guids::MM_SUPERVISOR, request_data) .expect("Should write MM Supervisor message successfully"); // Test parsing the message back safely let (parsed_guid, parsed_data) = parser.parse_message().expect("Should parse MM Supervisor message successfully"); assert_eq!(parsed_guid, test_guids::MM_SUPERVISOR); - assert_eq!(parsed_data, &request_data); + assert_eq!(parsed_data, request_data); // Verify we can parse the MM Supervisor request from the parsed data let parsed_request = @@ -267,17 +273,17 @@ fn test_mm_supervisor_comm_update_request() { // Create communication buffer update request using COMM_UPDATE constant let comm_update_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::COMM_UPDATE, + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::CommUpdate.into(), reserved: 0, result: 0, }; - let request_bytes = comm_update_request.to_bytes(); + let request_bytes = comm_update_request.as_bytes(); // Send the request using framework - let result = framework.communicate(&test_guids::MM_SUPERVISOR, &request_bytes); + let result = framework.communicate(&test_guids::MM_SUPERVISOR, request_bytes); assert!(result.is_ok(), "MM Supervisor comm update communication should succeed"); let response = result.unwrap(); @@ -289,8 +295,8 @@ fn test_mm_supervisor_comm_update_request() { // Parse response header safely let response_header = MmSupervisorRequestHeader::from_bytes(&response).expect("Should parse response header"); - assert_eq!(response_header.request, mm_supv::requests::COMM_UPDATE, "Response should be for COMM_UPDATE request"); - assert_eq!(response_header.result, mm_supv::responses::SUCCESS, "Comm update request should succeed"); + assert_eq!(response_header.request, RequestType::CommUpdate.into(), "Response should be for COMM_UPDATE request"); + assert_eq!(response_header.result, efi::Status::SUCCESS.as_usize() as u64, "Comm update request should succeed"); // Parse update result safely let update_result_offset = core::mem::size_of::(); @@ -315,17 +321,17 @@ fn test_mm_supervisor_unblock_mem_request() { // Create memory unblock request using UNBLOCK_MEM constant let unblock_mem_request = MmSupervisorRequestHeader { - signature: u32::from_le_bytes(mm_supv::SIGNATURE), - revision: mm_supv::REVISION, - request: mm_supv::requests::UNBLOCK_MEM, // This uses the constant! + signature: mm_supervisor_request::SIGNATURE, + revision: mm_supervisor_request::REVISION, + request: RequestType::UnblockMem.into(), reserved: 0, result: 0, }; - let request_bytes = unblock_mem_request.to_bytes(); + let request_bytes = unblock_mem_request.as_bytes(); // Send the request using framework - let result = framework.communicate(&test_guids::MM_SUPERVISOR, &request_bytes); + let result = framework.communicate(&test_guids::MM_SUPERVISOR, request_bytes); assert!(result.is_ok(), "MM Supervisor unblock mem communication should succeed"); let response = result.unwrap(); @@ -337,8 +343,8 @@ fn test_mm_supervisor_unblock_mem_request() { // Parse response header safely let response_header = MmSupervisorRequestHeader::from_bytes(&response).expect("Should parse response header"); - assert_eq!(response_header.request, mm_supv::requests::UNBLOCK_MEM, "Response should be for UNBLOCK_MEM request"); - assert_eq!(response_header.result, mm_supv::responses::SUCCESS, "Unblock mem request should succeed"); + assert_eq!(response_header.request, RequestType::UnblockMem.into(), "Response should be for UNBLOCK_MEM request"); + assert_eq!(response_header.result, efi::Status::SUCCESS.as_usize() as u64, "Unblock mem request should succeed"); // Parse unblock status safely let unblock_status_offset = core::mem::size_of::(); diff --git a/components/patina_mm/tests/patina_mm_integration/mod.rs b/components/patina_mm/tests/patina_mm_integration/mod.rs index 768468cc3..db638cf76 100644 --- a/components/patina_mm/tests/patina_mm_integration/mod.rs +++ b/components/patina_mm/tests/patina_mm_integration/mod.rs @@ -31,6 +31,8 @@ //! //! SPDX-License-Identifier: Apache-2.0 +extern crate alloc; + // Common utilities available to all test modules mod common; diff --git a/sdk/patina/src/lib.rs b/sdk/patina/src/lib.rs index e23abf2a3..ee0fb7a34 100644 --- a/sdk/patina/src/lib.rs +++ b/sdk/patina/src/lib.rs @@ -49,6 +49,7 @@ pub mod error; pub mod guids; pub mod hash; pub mod log; +pub mod management_mode; #[cfg(any(test, feature = "alloc"))] pub mod performance; pub mod pi; diff --git a/sdk/patina/src/management_mode.rs b/sdk/patina/src/management_mode.rs new file mode 100644 index 000000000..d99a5c5ab --- /dev/null +++ b/sdk/patina/src/management_mode.rs @@ -0,0 +1,16 @@ +//! Management Mode (MM) SDK for Patina +//! +//! This crate provides the Management Mode (MM) related definitions for Patina. +//! +//! ## License +//! +//! Copyright (C) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +pub mod comm_buffer_hob; +pub mod protocol; + +// Re-export commonly used items for easier access +pub use comm_buffer_hob::MmCommBufferStatus; diff --git a/sdk/patina/src/management_mode/comm_buffer_hob.rs b/sdk/patina/src/management_mode/comm_buffer_hob.rs new file mode 100644 index 000000000..cc6db44a0 --- /dev/null +++ b/sdk/patina/src/management_mode/comm_buffer_hob.rs @@ -0,0 +1,91 @@ +//! Management Mode (MM) Header and Buffer HOB Definitions +//! +//! Defines the header and buffer HOB structures necessary for the MM environment to be initialized and used by components +//! dependent on MM details. +//! +//! ## MM HOB Usage +//! +//! It is expected that the MM HOB buffer will be initialized by the environment that registers services for the +//! platform. The HOBs can have platform-fixed values assigned during their initialization. It should be common +//! for at least the communication buffers to be populated as a mutable HOB during boot time. It is +//! recommended for a "MM HOB" component to handle all MM HOB details with minimal other MM related +//! dependencies and lock the HOBs so they are available for components that depend on the immutable HOB +//! to perform MM operations. +//! +//! ## License +//! +//! Copyright (C) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use crate::BinaryGuid; +use zerocopy_derive::{FromBytes, Immutable, KnownLayout}; + +/// GUID for the MM communication buffer HOB (`gMmCommBufferHobGuid`). +/// +/// `{ 0x6c2a2520, 0x0131, 0x4aee, { 0xa7, 0x50, 0xcc, 0x38, 0x4a, 0xac, 0xe8, 0xc6 } }` +pub const MM_COMM_BUFFER_HOB_GUID: BinaryGuid = BinaryGuid::from_string("6c2a2520-0131-4aee-a750-cc384aace8c6"); + +/// MM Common Buffer HOB Data Structure. +/// +/// Describes the communication buffer region passed via HOB from PEI to MM. +#[repr(C, packed)] +#[derive(Debug, Clone, Copy)] +pub struct MmCommonBufferHobData { + /// Physical start address of the common region. + pub physical_start: u64, + /// Number of pages in the communication buffer region. + pub number_of_pages: u64, + /// Pointer to `MmCommBufferStatus` structure. + pub status_buffer: u64, +} + +/// MM Communication Buffer Status +/// +/// Shared structure between DXE and MM environments to communicate the status +/// of MM communication operations. This structure is written by DXE before +/// triggering an MMI and read/written by MM during MMI processing. +/// +/// This is a structure currently used in some MM Supervisor MM implementations. +#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)] +#[repr(C)] +pub struct MmCommBufferStatus { + /// Whether the data in the fixed MM communication buffer is valid when entering from non-MM to MM. + /// Must be set to TRUE before triggering MMI, will be set to FALSE by MM after processing. + pub is_comm_buffer_valid: u8, + + /// The channel used to communicate with MM. + /// FALSE = user buffer, TRUE = supervisor buffer + pub talk_to_supervisor: u8, + + /// Padding to align to 8 bytes. + /// This padding is necessary to match the structure layout defined in edk2 and mu_basecore. + pub _padding: [u8; 6], + + /// The return status when returning from MM to non-MM. + pub return_status: u64, + + /// The size in bytes of the output buffer when returning from MM to non-MM. + pub return_buffer_size: u64, +} + +impl Default for MmCommBufferStatus { + #[coverage(off)] + fn default() -> Self { + Self::new() + } +} + +impl MmCommBufferStatus { + /// Create a new mailbox status with all fields zeroed + pub const fn new() -> Self { + Self { + is_comm_buffer_valid: 0, + talk_to_supervisor: 0, + _padding: [0; 6], + return_status: 0, + return_buffer_size: 0, + } + } +} diff --git a/components/patina_mm/src/protocol.rs b/sdk/patina/src/management_mode/protocol.rs similarity index 90% rename from components/patina_mm/src/protocol.rs rename to sdk/patina/src/management_mode/protocol.rs index cf73116f5..89493e062 100644 --- a/components/patina_mm/src/protocol.rs +++ b/sdk/patina/src/management_mode/protocol.rs @@ -9,3 +9,4 @@ //! SPDX-License-Identifier: Apache-2.0 pub mod mm_comm_buffer_update; +pub mod mm_supervisor_request; diff --git a/components/patina_mm/src/protocol/mm_comm_buffer_update.rs b/sdk/patina/src/management_mode/protocol/mm_comm_buffer_update.rs similarity index 98% rename from components/patina_mm/src/protocol/mm_comm_buffer_update.rs rename to sdk/patina/src/management_mode/protocol/mm_comm_buffer_update.rs index 4e8ce0ea1..609669c70 100644 --- a/components/patina_mm/src/protocol/mm_comm_buffer_update.rs +++ b/sdk/patina/src/management_mode/protocol/mm_comm_buffer_update.rs @@ -10,7 +10,7 @@ //! SPDX-License-Identifier: Apache-2.0 //! -use patina::BinaryGuid; +use crate::BinaryGuid; use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout}; /// GUID for the MM Communication Buffer Update Protocol diff --git a/sdk/patina/src/management_mode/protocol/mm_supervisor_request.rs b/sdk/patina/src/management_mode/protocol/mm_supervisor_request.rs new file mode 100644 index 000000000..443495cf3 --- /dev/null +++ b/sdk/patina/src/management_mode/protocol/mm_supervisor_request.rs @@ -0,0 +1,221 @@ +//! MM Supervisor Request Protocol Definitions +//! +//! This module provides the shared protocol structures and constants for MM Supervisor +//! request handling. These types define the communication contract between the supervisor +//! and its clients (DXE, tests, etc.). +//! +//! ## Overview +//! +//! The MM Supervisor uses a structured request/response protocol. Requests are sent via +//! the MM communicate buffer and consist of an [`MmSupervisorRequestHeader`] followed by +//! request-specific payload data. The supervisor processes the request and writes back +//! a response header (with result status) followed by response-specific data. +//! +//! ## License +//! +//! Copyright (C) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 + +use crate::BinaryGuid; +use r_efi::efi; +use zerocopy::FromBytes; + +/// Signature value for the request header ('MSUP' as little-endian u32). +pub const SIGNATURE: u32 = u32::from_le_bytes([b'M', b'S', b'U', b'P']); + +/// Current revision of the request protocol. +pub const REVISION: u32 = 1; + +// GUID for gMmSupervisorRequestHandlerGuid +// { 0x8c633b23, 0x1260, 0x4ea6, { 0x83, 0xf, 0x7d, 0xdc, 0x97, 0x38, 0x21, 0x11 } } +/// GUID for the MM Supervisor Request Handler protocol. +pub const MM_SUPERVISOR_REQUEST_HANDLER_GUID: BinaryGuid = + BinaryGuid::from_string("8c633b23-1260-4ea6-830f-7ddc97382111"); + +/// MM Supervisor request header. +/// +/// This header is present at the start of every supervisor request buffer. It identifies +/// the request type and carries the result status on response. +/// +/// ## Layout +/// +/// ```text +/// Offset Size Field +/// 0x00 4 signature - Must be [`SIGNATURE`] ('MSUP' as little-endian u32) +/// 0x04 4 revision - Protocol revision, must be <= [`REVISION`] +/// 0x08 4 request - Request type (see [`RequestType`] enum) +/// 0x0C 4 reserved - Reserved for alignment, must be 0 +/// 0x10 8 result - Return status (0 = success, set by supervisor on response) +/// ``` +#[derive(Debug, Clone, Copy, zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable)] +#[repr(C)] +pub struct MmSupervisorRequestHeader { + /// Signature to identify the request ('MSUP' as little-endian). + pub signature: u32, + /// Revision of the request protocol. + pub revision: u32, + /// The specific request type (see [`RequestType`] enum). + pub request: u32, + /// Reserved for alignment, must be 0. + pub reserved: u32, + /// Result status. The value of this field follows the [`efi::Status`] definitions. + pub result: u64, +} + +impl MmSupervisorRequestHeader { + /// Size of the header in bytes. + pub const SIZE: usize = core::mem::size_of::(); + + /// Validates the header signature and revision. + pub fn is_valid(&self) -> bool { + self.signature == SIGNATURE && self.revision <= REVISION + } + + /// Reads a header from a byte slice. + /// + /// Returns `None` if the slice is too small or misaligned. + pub fn from_bytes(bytes: &[u8]) -> Option { + Self::read_from_bytes(bytes.get(..Self::SIZE)?).ok() + } +} + +/// Response from MM Supervisor version info request. +/// +/// Returned as the payload following an [`MmSupervisorRequestHeader`] when the request +/// type is [`RequestType::VersionInfo`]. +/// +/// ## Layout +/// +/// ```text +/// Offset Size Field +/// 0x00 4 version - Supervisor version +/// 0x04 4 patch_level - Supervisor patch level +/// 0x08 8 max_supervisor_request_level - Highest supported request type +/// ``` +#[derive( + Debug, + Clone, + Copy, + zerocopy_derive::FromBytes, + zerocopy_derive::IntoBytes, + zerocopy_derive::Immutable, + zerocopy_derive::KnownLayout +)] +#[repr(C)] +pub struct MmSupervisorVersionInfo { + /// Version of the MM Supervisor. + pub version: u32, + /// Patch level. + pub patch_level: u32, + /// Maximum supported supervisor request level (highest valid request type value). + pub max_supervisor_request_level: u64, +} + +impl MmSupervisorVersionInfo { + /// Size of the version info structure in bytes. + pub const SIZE: usize = core::mem::size_of::(); + + /// Reads version info from a byte slice. + /// + /// Returns `None` if the slice is too small or misaligned. + pub fn from_bytes(bytes: &[u8]) -> Option { + Self::read_from_bytes(bytes.get(..Self::SIZE)?).ok() + } +} + +/// MM Supervisor request types. +/// +/// Each variant corresponds to a specific supervisor operation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RequestType { + /// Request to unblock memory regions. + UnblockMem = 0x0001, + /// Request to fetch security policy. + FetchPolicy = 0x0002, + /// Request for version information. + VersionInfo = 0x0003, + /// Request to update communication buffer. + CommUpdate = 0x0004, +} + +impl RequestType { + /// Tries to convert a raw u64 value into a `RequestType`. + /// + /// Returns `Err(value)` if the value does not correspond to a valid request type. + pub const MAX_REQUEST_TYPE: u64 = Self::CommUpdate as u64; +} + +impl TryFrom for RequestType { + type Error = u32; + + fn try_from(value: u32) -> Result { + match value { + 0x0001 => Ok(Self::UnblockMem), + 0x0002 => Ok(Self::FetchPolicy), + 0x0003 => Ok(Self::VersionInfo), + 0x0004 => Ok(Self::CommUpdate), + other => Err(other), + } + } +} + +impl From for u32 { + fn from(request_type: RequestType) -> Self { + request_type as u32 + } +} + +/// Standard MM Supervisor response types. +/// +/// Each variant corresponds to a specific response status that the supervisor can return. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ResponseType { + /// Error: Invalid request index. + InvalidRequest, + /// Error: Invalid data buffer. + InvalidDataBuffer, + /// Error: Communication buffer initialization failed. + CommBufferInitError, +} + +/// Maps `ResponseType` variants to corresponding [`efi::Status`] codes, because this is how +/// the supervisor request handlers map the [`MmSupervisorRequestHeader::result`] field in the +/// response header. +impl From for efi::Status { + fn from(response_type: ResponseType) -> Self { + match response_type { + ResponseType::InvalidRequest => efi::Status::INVALID_PARAMETER, + ResponseType::InvalidDataBuffer => efi::Status::BUFFER_TOO_SMALL, + ResponseType::CommBufferInitError => efi::Status::DEVICE_ERROR, + } + } +} + +/// MM Supervisor Unblock Memory Parameters. +/// +/// Matches the C `MM_SUPERVISOR_UNBLOCK_MEMORY_PARAMS` layout. The C header +/// defines this under `#pragma pack(push, 1)`, but because `efi::MemoryDescriptor` +/// (40 bytes) and `Guid` (16 bytes) are both naturally aligned, the packed +/// and natural layouts are identical (56 bytes total). +/// +/// ## Layout +/// +/// ```text +/// Offset Size Field +/// 0x00 40 memory_descriptor - EFI_MEMORY_DESCRIPTOR (r-efi efi::MemoryDescriptor) +/// 0x28 16 identifier_guid - Requester identification GUID +/// ``` +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct MmSupervisorUnblockMemoryParams { + /// Memory descriptor identifying the region to unblock. + pub memory_descriptor: efi::MemoryDescriptor, + /// GUID identifying the requesting driver/module. + pub identifier_guid: BinaryGuid, +} + +impl MmSupervisorUnblockMemoryParams { + /// Size of this structure in bytes. + pub const SIZE: usize = core::mem::size_of::(); +} diff --git a/sdk/patina/src/pi.rs b/sdk/patina/src/pi.rs index b17489d34..aa41fea47 100644 --- a/sdk/patina/src/pi.rs +++ b/sdk/patina/src/pi.rs @@ -35,9 +35,11 @@ pub mod error_codes; pub mod fw_fs; pub mod hob; pub mod list_entry; +pub mod mm_cis; pub mod protocols; #[cfg(feature = "serde")] pub mod serializable; +pub mod spec_version; pub mod status_code; pub use boot_mode::Mode as BootMode; diff --git a/sdk/patina/src/pi/mm_cis.rs b/sdk/patina/src/pi/mm_cis.rs new file mode 100644 index 000000000..7b2aff6ba --- /dev/null +++ b/sdk/patina/src/pi/mm_cis.rs @@ -0,0 +1,397 @@ +//! Management Mode (MM) Core Interface Definitions +//! +//! This module contains definitions related to the MM Core Interface as defined +//! in the UEFI Platform Initialization Specification. +//! +//! ## License +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use crate::pi::spec_version; +use core::ffi::c_void; +use r_efi::{ + efi, + efi::{ + BootAllocatePages, BootAllocatePool, BootFreePages, BootFreePool, BootHandleProtocol, + BootInstallProtocolInterface, BootLocateHandle, BootLocateProtocol, BootUninstallProtocolInterface, + }, +}; + +/// MMST signature: `'S', 'M', 'S', 'T'` (same as C `MM_MMST_SIGNATURE`). +pub const MM_MMST_SIGNATURE: u32 = u32::from_le_bytes([b'S', b'M', b'S', b'T']); + +/// MMST major revision, the same as the PI Specification major revision. +pub const MM_MMST_REVISION_MAJOR: u32 = spec_version::PI_SPECIFICATION_MAJOR_REVISION; + +/// MMST minor revision, the same as the PI Specification minor revision. +pub const MM_MMST_REVISION_MINOR: u32 = spec_version::PI_SPECIFICATION_MINOR_REVISION; + +/// PI Specification version encoded as `(major << 16) | minor`. +pub const MM_SYSTEM_TABLE_REVISION: u32 = (MM_MMST_REVISION_MAJOR << 16) | MM_MMST_REVISION_MINOR; + +// +// This EFI_MM_CPU_IO_PROTOCOL is embedded in MMST, so we need to define it here to be able to parse the MMST correctly. +// + +/// A single MM I/O access function pointer. +/// +/// Matches the C typedef `EFI_MM_CPU_IO`: +/// ```c +/// typedef EFI_STATUS (EFIAPI *EFI_MM_CPU_IO)( +/// IN CONST EFI_MM_CPU_IO_PROTOCOL *This, +/// IN EFI_MM_IO_WIDTH Width, +/// IN UINT64 Address, +/// IN UINTN Count, +/// IN OUT VOID *Buffer +/// ); +/// ``` +pub type MmCpuIoFn = unsafe extern "efiapi" fn( + this: *const MmCpuIoAccess, + width: usize, + address: u64, + count: usize, + buffer: *mut c_void, +) -> efi::Status; + +/// MM CPU I/O access pair (Read + Write). +/// +/// Matches `EFI_MM_IO_ACCESS`. +/// ```c +/// typedef struct { +/// /// +/// /// This service provides the various modalities of memory and I/O read. +/// /// +/// EFI_MM_CPU_IO Read; +/// /// +/// /// This service provides the various modalities of memory and I/O write. +/// /// +/// EFI_MM_CPU_IO Write; +/// } EFI_MM_IO_ACCESS; +/// ``` +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct MmCpuIoAccess { + /// This service provides the various modalities of memory and I/O read. + pub read: MmCpuIoFn, + /// This service provides the various modalities of memory and I/O write. + pub write: MmCpuIoFn, +} + +/// The `EFI_MM_CPU_IO_PROTOCOL` embedded in the system table. +/// +/// ```c +/// typedef struct _EFI_MM_CPU_IO_PROTOCOL { +/// EFI_MM_IO_ACCESS Mem; +/// EFI_MM_IO_ACCESS Io; +/// } EFI_MM_CPU_IO_PROTOCOL; +/// ``` +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct MmCpuIoProtocol { + /// MMIO access pair (Read + Write). + pub mem: MmCpuIoAccess, + /// I/O port access functions. + pub io: MmCpuIoAccess, +} + +/// Adds, updates, or removes a configuration table entry from the Management Mode System Table. +/// +/// This function matches the C typedef `EFI_MM_INSTALL_CONFIGURATION_TABLE`: +/// ```c +/// typedef +/// EFI_STATUS +/// (EFIAPI *EFI_MM_INSTALL_CONFIGURATION_TABLE)( +/// IN CONST EFI_MM_SYSTEM_TABLE *SystemTable, +/// IN CONST EFI_GUID *Guid, +/// IN VOID *Table, +/// IN UINTN TableSize +/// ); +/// ``` +pub type MmInstallConfigurationTableFn = unsafe extern "efiapi" fn( + system_table: *const EfiMmSystemTable, + guid: *const efi::Guid, + table: *mut c_void, + table_size: usize, +) -> efi::Status; + +/// Allocates pool memory from the specified memory type. +/// +/// This function matches the C typedef `EFI_MM_ALLOCATE_POOL`, which is already defined in `r_efi::efi` as `BootAllocatePool`. +pub type MmAllocatePoolFn = BootAllocatePool; + +/// Frees pool memory. +/// +/// This function matches the C typedef `EFI_MM_FREE_POOL`, which is already defined in `r_efi::efi` as `BootFreePool`. +pub type MmFreePoolFn = BootFreePool; + +/// Allocates memory pages from the system. +/// +/// This function matches the C typedef `EFI_ALLOCATE_PAGES`, which is already defined in `r_efi::efi` as `BootAllocatePages`. +pub type MmAllocatePagesFn = BootAllocatePages; + +/// Frees memory pages. +/// +/// This function matches the C typedef `EFI_FREE_PAGES`, which is already defined in `r_efi::efi` as `BootFreePages`. +pub type MmFreePagesFn = BootFreePages; + +/// `EFI_MM_STARTUP_THIS_AP` +pub type MmStartupThisApFn = + unsafe extern "efiapi" fn(procedure: usize, cpu_number: usize, proc_arguments: *mut c_void) -> efi::Status; + +/// Installs a protocol interface on a device handle. +/// +/// This function matches the C typedef `EFI_INSTALL_PROTOCOL_INTERFACE`, which is already defined in `r_efi::efi` as `BootInstallProtocolInterface`. +pub type MmInstallProtocolInterfaceFn = BootInstallProtocolInterface; + +/// Removes a protocol interface from a device handle. +/// +/// This function matches the C typedef `EFI_UNINSTALL_PROTOCOL_INTERFACE`, which is already defined in `r_efi::efi` as `BootUninstallProtocolInterface`. +pub type MmUninstallProtocolInterfaceFn = BootUninstallProtocolInterface; + +/// Queries a handle to determine if it supports a specified protocol. +/// +/// This function matches the C typedef `EFI_HANDLE_PROTOCOL`, which is already defined in `r_efi::efi` as `BootHandleProtocol`. +pub type MmHandleProtocolFn = BootHandleProtocol; + +/// Register a callback function be called when a particular protocol interface is installed. +/// +/// This function matches the C typedef `EFI_MM_REGISTER_PROTOCOL_NOTIFY`: +/// ```c +/// typedef +/// EFI_STATUS +/// (EFIAPI *EFI_MM_REGISTER_PROTOCOL_NOTIFY)( +/// IN CONST EFI_GUID *Protocol, +/// IN EFI_MM_NOTIFY_FN Function, +/// OUT VOID **Registration +/// ); +/// ``` +pub type MmRegisterProtocolNotifyFn = unsafe extern "efiapi" fn( + protocol: *const efi::Guid, + function: usize, + registration: *mut *mut c_void, +) -> efi::Status; + +/// Returns an array of handles that support a specified protocol. +/// +/// This function matches the C typedef `EFI_LOCATE_HANDLE`, which is already defined in `r_efi::efi` as `BootLocateHandle`. +pub type MmLocateHandleFn = BootLocateHandle; + +/// Returns the first protocol instance that matches the given protocol. +/// +/// This function matches the C typedef `EFI_LOCATE_PROTOCOL`, which is already defined in `r_efi::efi` as `BootLocateProtocol`. +pub type MmLocateProtocolFn = BootLocateProtocol; + +/// Manage MMI of a particular type. +/// +/// This function matches the C typedef `EFI_MM_INTERRUPT_MANAGE`: +/// ```c +/// typedef +/// EFI_STATUS +/// (EFIAPI *EFI_MM_INTERRUPT_MANAGE)( +/// IN CONST EFI_GUID *HandlerType, +/// IN CONST VOID *Context OPTIONAL, +/// IN OUT VOID *CommBuffer OPTIONAL, +/// IN OUT UINTN *CommBufferSize OPTIONAL +/// ); +/// ``` +pub type MmiManageFn = unsafe extern "efiapi" fn( + handler_type: *const efi::Guid, + context: *const c_void, + comm_buffer: *mut c_void, + comm_buffer_size: *mut usize, +) -> efi::Status; + +/// Main entry point for an MM handler dispatch or communicate-based callback. +/// +/// This function matches the C typedef `EFI_MM_HANDLER_ENTRY_POINT`: +/// ```c +/// typedef +/// EFI_STATUS +/// (EFIAPI *EFI_MM_HANDLER_ENTRY_POINT)( +/// IN EFI_HANDLE DispatchHandle, +/// IN CONST VOID *Context OPTIONAL, +/// IN OUT VOID *CommBuffer OPTIONAL, +/// IN OUT UINTN *CommBufferSize OPTIONAL +/// ); +/// ``` +pub type MmiHandlerEntryPoint = unsafe extern "efiapi" fn( + dispatch_handle: efi::Handle, + context: *const c_void, + comm_buffer: *mut c_void, + comm_buffer_size: *mut usize, +) -> efi::Status; + +/// Registers a handler entry point for a particular MMI handler type. +/// +/// This function matches the C typedef `EFI_MM_INTERRUPT_REGISTER`: +/// ```c +/// typedef +/// EFI_STATUS +/// (EFIAPI *EFI_MM_INTERRUPT_REGISTER)( +/// IN EFI_MM_HANDLER_ENTRY_POINT Handler, +/// IN CONST EFI_GUID *HandlerType OPTIONAL, +/// OUT EFI_HANDLE *DispatchHandle +/// ); +/// ``` +pub type MmiHandlerRegisterFn = unsafe extern "efiapi" fn( + handler: MmiHandlerEntryPoint, + handler_type: *const efi::Guid, + dispatch_handle: *mut efi::Handle, +) -> efi::Status; + +/// Unregister a handler in MM. +/// +/// This function matches the C typedef `EFI_MM_INTERRUPT_UNREGISTER`: +/// ```c +/// typedef +/// EFI_STATUS +/// (EFIAPI *EFI_MM_INTERRUPT_UNREGISTER)( +/// IN EFI_HANDLE DispatchHandle +/// ); +/// ``` +pub type MmiHandlerUnregisterFn = unsafe extern "efiapi" fn(dispatch_handle: efi::Handle) -> efi::Status; + +/// EFI_MM_ENTRY_CONTEXT structure. +/// +/// Processor information and functionality needed by MM Foundation. +/// Matches the C `EFI_MM_ENTRY_CONTEXT` from PI specification. +/// +/// Layout (x86_64, all fields 8 bytes): +/// - `mm_startup_this_ap`: Function pointer for `EFI_MM_STARTUP_THIS_AP` +/// - `currently_executing_cpu`: Index of the processor executing the MM Foundation +/// - `number_of_cpus`: Total number of possible processors in the platform (1-based) +/// - `cpu_save_state_size`: Pointer to array of save state sizes per CPU +/// - `cpu_save_state`: Pointer to array of CPU save state pointers +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct EfiMmEntryContext { + /// Function pointer for EFI_MM_STARTUP_THIS_AP. + pub mm_startup_this_ap: u64, + /// Index of the currently executing CPU. + pub currently_executing_cpu: u64, + /// Total number of CPUs (1-based). + pub number_of_cpus: u64, + /// Pointer to array of per-CPU save state sizes. + pub cpu_save_state_size: u64, + /// Pointer to array of per-CPU save state pointers. + pub cpu_save_state: u64, +} + +/// The Management Mode System Table (MMST). +/// +/// This is the `#[repr(C)]` Rust definition of the C `EFI_MM_SYSTEM_TABLE` structure +/// from `PiMmCis.h`. The table pointer is passed as the second argument to +/// every MM driver's entry point: +/// +/// ```c +/// EFI_STATUS EFIAPI DriverEntry(EFI_HANDLE ImageHandle, EFI_MM_SYSTEM_TABLE *MmSt); +/// ``` +#[repr(C)] +pub struct EfiMmSystemTable { + /// + /// The table header for the SMST. + /// + pub hdr: efi::TableHeader, + + /// + /// A pointer to a NULL-terminated Unicode string containing the vendor name. + /// It is permissible for this pointer to be NULL. + /// + pub mm_firmware_vendor: *mut u16, + /// + /// The particular revision of the firmware. + /// + pub mm_firmware_revision: u32, + + /// Function to add, update, or remove a configuration table entry from the MMST. + pub mm_install_configuration_table: MmInstallConfigurationTableFn, + + /// + /// I/O Service + /// + pub mm_io: MmCpuIoProtocol, + + /// + /// Runtime memory services + /// + /// This function matches the C typedef `EFI_MM_ALLOCATE_POOL`, which allocates pool memory from the specified memory type. + pub mm_allocate_pool: MmAllocatePoolFn, + /// This function matches the C typedef `EFI_MM_FREE_POOL`, which frees pool memory. + pub mm_free_pool: MmFreePoolFn, + /// This function matches the C typedef `EFI_ALLOCATE_PAGES`, which allocates memory pages from the system. + pub mm_allocate_pages: MmAllocatePagesFn, + /// This function matches the C typedef `EFI_FREE_PAGES`, which frees memory pages. + pub mm_free_pages: MmFreePagesFn, + + /// + /// MP service + /// + pub mm_startup_this_ap: MmStartupThisApFn, + + /// + /// CPU information records + /// + /// A number between zero and and the NumberOfCpus field. This field designates + /// which processor is executing the MM infrastructure. + /// + pub currently_executing_cpu: usize, + /// + /// The number of possible processors in the platform. This is a 1 based counter. + /// + pub number_of_cpus: usize, + /// + /// Points to an array, where each element describes the number of bytes in the + /// corresponding save state specified by CpuSaveState. There are always + /// NumberOfCpus entries in the array. + /// + pub cpu_save_state_size: *mut usize, + /// + /// Points to an array, where each element is a pointer to a CPU save state. The + /// corresponding element in CpuSaveStateSize specifies the number of bytes in the + /// save state area. There are always NumberOfCpus entries in the array. + /// + pub cpu_save_state: *mut *mut c_void, + + /// + /// Extensibility table + /// + /// + /// The number of UEFI Configuration Tables in the buffer MmConfigurationTable. + /// + pub number_of_table_entries: usize, + /// + /// A pointer to the UEFI Configuration Tables. The number of entries in the table is + /// NumberOfTableEntries. + /// + pub mm_configuration_table: *mut efi::ConfigurationTable, + + /// + /// Protocol services + /// + /// + /// This function matches the C typedef `EFI_INSTALL_PROTOCOL_INTERFACE`, which installs a protocol interface on a device handle. + pub mm_install_protocol_interface: MmInstallProtocolInterfaceFn, + /// This function matches the C typedef `EFI_UNINSTALL_PROTOCOL_INTERFACE`, which removes a protocol interface from a device handle. + pub mm_uninstall_protocol_interface: MmUninstallProtocolInterfaceFn, + /// This function matches the C typedef `EFI_HANDLE_PROTOCOL`, which queries a handle to determine if it supports a specified protocol. + pub mm_handle_protocol: MmHandleProtocolFn, + /// This function matches the C typedef `EFI_MM_REGISTER_PROTOCOL_NOTIFY`, which registers a callback function be called when a particular protocol interface is installed. + pub mm_register_protocol_notify: MmRegisterProtocolNotifyFn, + /// This function matches the C typedef `EFI_LOCATE_HANDLE`, which returns an array of handles that support a specified protocol. + pub mm_locate_handle: MmLocateHandleFn, + /// This function matches the C typedef `EFI_LOCATE_PROTOCOL`, which returns the first protocol instance that matches the given protocol. + pub mm_locate_protocol: MmLocateProtocolFn, + + /// + /// MMI Management functions + /// + /// This function matches the C typedef `EFI_MM_INTERRUPT_MANAGE`, which manages MMI of a particular type. + pub mmi_manage: MmiManageFn, + /// This function matches the C typedef `EFI_MM_HANDLER_REGISTER`, which registers a handler entry point for a particular MMI handler type. + pub mmi_handler_register: MmiHandlerRegisterFn, + /// This function matches the C typedef `EFI_MM_INTERRUPT_UNREGISTER`, which unregisters a handler in MM. + pub mmi_handler_unregister: MmiHandlerUnregisterFn, +} diff --git a/sdk/patina/src/pi/protocols/communication.rs b/sdk/patina/src/pi/protocols/communication.rs index 3a431b739..5658f49dc 100644 --- a/sdk/patina/src/pi/protocols/communication.rs +++ b/sdk/patina/src/pi/protocols/communication.rs @@ -11,8 +11,9 @@ //! SPDX-License-Identifier: Apache-2.0 //! +use crate::{BinaryGuid, Guid}; use core::ffi::c_void; -use r_efi::{efi, system}; +use r_efi::efi; /// MM Communication Protocol GUID. pub const PROTOCOL_GUID: crate::BinaryGuid = crate::BinaryGuid::from_string("C68ED8E2-9DC6-4CBD-9D94-DB65ACC5C332"); @@ -91,18 +92,54 @@ pub struct Protocol { /// MM communication header structure. pub struct EfiMmCommunicateHeader { /// To avoid confusion in interpreting frames, the communication buffer should always begin with the header. - pub header_guid: r_efi::base::Guid, + pub header_guid: BinaryGuid, /// Describes the size of Data (in bytes) and does not include the size of the header. pub message_length: usize, // Comm buffer data follows the header } -#[repr(C)] -#[derive(Copy, Clone, Debug)] -/// MM initialization header structure. -pub struct EfiMmInitializationHeader { - /// To avoid confusion in interpreting frames, the communication buffer should always begin with the header. - pub comm_header: EfiMmCommunicateHeader, - /// Describes the size of Data (in bytes) and does not include the size of the header. - pub system_table: *mut system::SystemTable, +impl EfiMmCommunicateHeader { + /// Create a new communicate header with the specified GUID and message length. + pub fn new(header_guid: Guid, message_length: usize) -> Self { + Self { header_guid: header_guid.to_efi_guid().into(), message_length } + } + + /// Returns the communicate header as a slice of bytes using safe conversion. + /// Useful if byte-level access to the header structure is needed. + /// + /// # Returns + /// + /// A slice of bytes representing the header. + pub fn as_bytes(&self) -> &[u8] { + // SAFETY: EfiMmCommunicateHeader is repr(C) with well-defined layout and size + unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, Self::size()) } + } + + /// Function to get the size of the header in bytes. + /// + /// # Returns + /// + /// The size of the header in bytes. + pub const fn size() -> usize { + core::mem::size_of::() + } + + /// Get the header GUID from the communication buffer. + /// + /// # Returns + /// + /// The GUID from the communication header. + pub fn header_guid(&self) -> Guid<'_> { + Guid::from_ref(&self.header_guid) + } + + /// Returns the message length from this communicate header. + /// The length represents the size of the message data that follows the header. + /// + /// # Returns + /// + /// The length in bytes of the message data (excluding the header size). + pub const fn message_length(&self) -> usize { + self.message_length + } } diff --git a/sdk/patina/src/pi/spec_version.rs b/sdk/patina/src/pi/spec_version.rs new file mode 100644 index 000000000..68d219221 --- /dev/null +++ b/sdk/patina/src/pi/spec_version.rs @@ -0,0 +1,16 @@ +//! Platform Initialization Specification Version +//! +//! This module contains definitions related to the version of the Platform Initialization (PI) +//! Specification that this implementation targets. +//! +//! ## License +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +/// The major revision number of the PI Specification that this implementation targets. +pub const PI_SPECIFICATION_MAJOR_REVISION: u32 = 1; +/// The minor revision number of the PI Specification that this implementation targets. +pub const PI_SPECIFICATION_MINOR_REVISION: u32 = 80;