Skip to content

Commit

Permalink
RustAdvancedLoggerDxe: Update to use mu_rust_helpers (#583)
Browse files Browse the repository at this point in the history
## Description

- Updated `RustAdvancedLoggerDxe` to use the `boot_services` crate from
  `mu_rust_helpers`. The `boot_services` crate provides a common
  rust-style wrapper of the raw boot services.
- Updated tests to use mockall for cleaner test code

- [ ] Impacts functionality?
- [ ] Impacts security?
- [ ] Breaking change?
- [x] Includes tests?
- [ ] Includes documentation?

## How This Was Tested

+ Updated tests and ran them.
+ Built and booted QEMU: Booted to shell and got logs in the terminal.

## Integration Instructions

N/A
  • Loading branch information
bkarstens authored Sep 25, 2024
1 parent 0c553da commit ffffac7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 72 deletions.
6 changes: 5 additions & 1 deletion AdvLoggerPkg/Crates/RustAdvancedLoggerDxe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ name = "rust_advanced_logger_dxe"
path = "src/lib.rs"

[dependencies]
r-efi = {workspace=true}
r-efi = { workspace = true }
boot_services = { workspace = true }

[dev-dependencies]
boot_services = { workspace = true, features = ["mockall"] }

[features]
std = []
146 changes: 75 additions & 71 deletions AdvLoggerPkg/Crates/RustAdvancedLoggerDxe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@
extern crate std; //allow rustdoc links to reference std (e.g. println docs below).

use core::{
ffi::c_void,
fmt::{self, Write},
ops::Deref,
ptr,
sync::atomic::{AtomicPtr, Ordering},
};
use r_efi::{
efi::{Guid, Status},
system::BootServices,
};
use r_efi::efi;

use boot_services::{self, protocol_handler::Protocol};

//Global static logger instance - this is a singleton.
static LOGGER: AdvancedLogger = AdvancedLogger::new();
Expand All @@ -58,22 +57,41 @@ pub const DEBUG_VERBOSE: usize = 0x00400000;
pub const DEBUG_ERROR: usize = 0x80000000;

// AdvancedLogger protocol definition. Mirrors C definition in AdvLoggerPkg/Include/Protocol/AdvancedLogger.h
const ADVANCED_LOGGER_PROTOCOL_GUID: Guid =
Guid::from_fields(0x434f695c, 0xef26, 0x4a12, 0x9e, 0xba, &[0xdd, 0xef, 0x00, 0x97, 0x49, 0x7c]);
const ADVANCED_LOGGER_PROTOCOL_GUID: efi::Guid =
efi::Guid::from_fields(0x434f695c, 0xef26, 0x4a12, 0x9e, 0xba, &[0xdd, 0xef, 0x00, 0x97, 0x49, 0x7c]);

type AdvancedLoggerWriteProtocol = extern "efiapi" fn(*const AdvancedLoggerProtocolInterface, usize, *const u8, usize);

type AdvancedLoggerWriteProtocol = extern "efiapi" fn(*const AdvancedLoggerProtocol, usize, *const u8, usize);
const ADVANCED_LOGGER_PROTOCOL: AdvancedLoggerProtocol = AdvancedLoggerProtocol {};

#[repr(C)]
struct AdvancedLoggerProtocol {
struct AdvancedLoggerProtocolInterface {
signature: u32,
version: u32,
write_log: AdvancedLoggerWriteProtocol,
}

struct AdvancedLoggerProtocol;

unsafe impl Protocol for AdvancedLoggerProtocol {
type Interface = AdvancedLoggerProtocolInterface;
fn protocol_guid(&self) -> &'static efi::Guid {
&ADVANCED_LOGGER_PROTOCOL_GUID
}
}

impl Deref for AdvancedLoggerProtocol {
type Target = efi::Guid;

fn deref(&self) -> &Self::Target {
self.protocol_guid()
}
}

// Private un-synchronized AdvancedLogger wrapper. Provides implementation of fmt::Write for AdvancedLogger.
#[derive(Debug)]
struct AdvancedLogger {
protocol: AtomicPtr<AdvancedLoggerProtocol>,
protocol: AtomicPtr<AdvancedLoggerProtocolInterface>,
}

impl AdvancedLogger {
Expand All @@ -83,22 +101,14 @@ impl AdvancedLogger {
}

// initialize the AdvancedLogger by acquiring a pointer to the AdvancedLogger protocol.
fn init(&self, bs: *mut BootServices) {
assert!(!bs.is_null(), "BootServices should not be NULL");
let boot_services = unsafe { &mut ptr::read(bs) };

let mut ptr: *mut c_void = ptr::null_mut();

let status = (boot_services.locate_protocol)(
&ADVANCED_LOGGER_PROTOCOL_GUID as *const _ as *mut _,
ptr::null_mut(),
ptr::addr_of_mut!(ptr),
);

self.protocol.store(
if status == Status::SUCCESS { ptr as *mut AdvancedLoggerProtocol } else { ptr::null_mut() },
Ordering::SeqCst,
)
fn init(&self, boot_services_impl: &impl boot_services::BootServices) {
let protocol_ptr = match boot_services_impl.locate_protocol(&ADVANCED_LOGGER_PROTOCOL, None) {
Ok(Some(interface)) => interface as *mut AdvancedLoggerProtocolInterface,
Ok(None) => ptr::null_mut(),
Err(_status) => ptr::null_mut(),
};

self.protocol.store(protocol_ptr, Ordering::SeqCst)
}

// log the debug output in `args` at the given log level.
Expand All @@ -112,14 +122,14 @@ impl AdvancedLogger {
}

struct LogTransactor<'a> {
protocol: &'a mut AdvancedLoggerProtocol,
protocol: &'a mut AdvancedLoggerProtocolInterface,
level: usize,
}

impl<'a> fmt::Write for LogTransactor<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
(self.protocol.write_log)(
self.protocol as *const AdvancedLoggerProtocol,
self.protocol as *const AdvancedLoggerProtocolInterface,
self.level,
s.as_ptr(),
s.as_bytes().len(),
Expand All @@ -130,8 +140,9 @@ impl<'a> fmt::Write for LogTransactor<'a> {

/// Initializes the logging subsystem. The `debug` and `debugln` macros may be called before calling this function, but
/// output is discarded if the logger has not yet been initialized via this routine.
pub fn init_debug(bs: *mut BootServices) {
LOGGER.init(bs);
pub fn init_debug(efi_boot_services: *mut efi::BootServices) {
let standard_boot_services = unsafe { boot_services::StandardBootServices::new(&*efi_boot_services) };
LOGGER.init(&standard_boot_services);
}

#[doc(hidden)]
Expand Down Expand Up @@ -252,26 +263,23 @@ macro_rules! function {
mod tests {
extern crate std;
use crate::{
debug, init_debug, AdvancedLogger, AdvancedLoggerProtocol, ADVANCED_LOGGER_PROTOCOL_GUID, DEBUG_ERROR,
DEBUG_INFO, DEBUG_INIT, DEBUG_VERBOSE, DEBUG_WARN, LOGGER,
};
use core::{ffi::c_void, mem::MaybeUninit, slice::from_raw_parts, sync::atomic::Ordering};
use r_efi::{
efi::{Guid, Status},
system::BootServices,
debug, AdvancedLogger, AdvancedLoggerProtocol, AdvancedLoggerProtocolInterface, DEBUG_ERROR, DEBUG_INFO,
DEBUG_INIT, DEBUG_VERBOSE, DEBUG_WARN, LOGGER,
};
use boot_services;
use core::{slice::from_raw_parts, sync::atomic::Ordering};
use std::{println, str};

static ADVANCED_LOGGER_INSTANCE: AdvancedLoggerProtocol =
AdvancedLoggerProtocol { signature: 0, version: 0, write_log: mock_advanced_logger_write };
static ADVANCED_LOGGER_INSTANCE: AdvancedLoggerProtocolInterface =
AdvancedLoggerProtocolInterface { signature: 0, version: 0, write_log: mock_advanced_logger_write };

extern "efiapi" fn mock_advanced_logger_write(
this: *const AdvancedLoggerProtocol,
this: *const AdvancedLoggerProtocolInterface,
error_level: usize,
buffer: *const u8,
buffer_size: usize,
) {
assert_eq!(this, &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol);
assert_eq!(this, &ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocolInterface);
//to avoid dealing with complicated mock state, we assume that tests will produce the same output string:
//"This is a <x> test.\n", where <x> depends on the debug level (e.g. "DEBUG_INFO"). In addition,
//the string might be built with multiple calls to write, so we just check that it is a substring
Expand All @@ -289,47 +297,43 @@ mod tests {
}
}

extern "efiapi" fn mock_locate_protocol(
protocol: *mut Guid,
_registration: *mut c_void,
interface: *mut *mut c_void,
) -> Status {
let protocol = unsafe { protocol.as_mut().unwrap() };
assert_eq!(protocol, &ADVANCED_LOGGER_PROTOCOL_GUID);
assert!(!interface.is_null());
unsafe {
interface.write(&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol as *mut c_void);
}
Status::SUCCESS
}

fn mock_boot_services() -> BootServices {
let boot_services = MaybeUninit::zeroed();
let mut boot_services: BootServices = unsafe { boot_services.assume_init() };
boot_services.locate_protocol = mock_locate_protocol;
boot_services
}

#[test]
fn init_should_initialize_logger() {
let mut boot_services = mock_boot_services();
let mut mock_boot_services = boot_services::MockBootServices::new();
mock_boot_services.expect_locate_protocol().returning(|_: &AdvancedLoggerProtocol, registration| unsafe {
assert_eq!(registration, None);
Ok(Some(
(&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocolInterface
as *mut AdvancedLoggerProtocolInterface)
.as_mut()
.unwrap(),
))
});
static TEST_LOGGER: AdvancedLogger = AdvancedLogger::new();
TEST_LOGGER.init(&mut boot_services);

TEST_LOGGER.init(&mock_boot_services);
assert_eq!(
TEST_LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocol,
&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol
TEST_LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocolInterface,
&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocolInterface
);
}

#[test]
fn debug_macro_should_log_things() {
let mut boot_services = mock_boot_services();
init_debug(&mut boot_services);
let mut mock_boot_services = boot_services::MockBootServices::new();
mock_boot_services.expect_locate_protocol().returning(|_: &AdvancedLoggerProtocol, registration| unsafe {
assert_eq!(registration, None);
Ok(Some(
(&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocolInterface
as *mut AdvancedLoggerProtocolInterface)
.as_mut()
.unwrap(),
))
});
LOGGER.init(&mock_boot_services);

assert_eq!(
LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocol,
&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocol
LOGGER.protocol.load(Ordering::SeqCst) as *const AdvancedLoggerProtocolInterface,
&ADVANCED_LOGGER_INSTANCE as *const AdvancedLoggerProtocolInterface
);

debugln!(DEBUG_INIT, "This is a DEBUG_INIT test.");
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ RustBootServicesAllocatorDxe = {path = "MsCorePkg/Crates/RustBootServicesAllocat
HidIo = {path = "HidPkg/Crates/HidIo"}
hidparser = {git = "https://github.com/microsoft/mu_rust_hid.git", branch = "main"}
HiiKeyboardLayout = {path = "HidPkg/Crates/HiiKeyboardLayout"}
mu_rust_helpers = { git = "https://github.com/microsoft/mu_rust_helpers.git", tag = "v1.0.0" }
boot_services = { git = "https://github.com/microsoft/mu_rust_helpers.git", tag = "v1.0.0" }

memoffset = "0.9.0"
num-traits = { version = "0.2", default-features = false}
Expand Down

0 comments on commit ffffac7

Please sign in to comment.