Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,17 @@ check_rustup_target "aarch64-unknown-none"
set -x

# cargo deny --log-level=warn check
cargo clippy --target=aarch64-unknown-none -p ihv1-ec-sp -p qemu-ec-sp
cargo clippy
cargo clippy --target=aarch64-unknown-none -p ihv1-ec-sp -p qemu-ec-sp -- -D warnings
cargo clippy -- -D warnings
cargo fmt --check
cargo hack check --feature-powerset --exclude=ihv1-ec-sp --exclude=qemu-ec-sp
cargo hack check --feature-powerset --target=aarch64-unknown-none
cargo test -q
HOST_TARGET=$(rustc -vV | sed -n 's/host: //p')
if [ -z "$HOST_TARGET" ]; then
echo "Failed to determine Rust host target from \`rustc -vV\` output."
echo "Please verify that \`rustc -vV\` prints a valid \`host:\` line."
exit 1
fi
cargo test --target "$HOST_TARGET" -q
cd "$PROJECT_DIR/platform/qemu-sp" && cargo build --target=aarch64-unknown-none
cd "$PROJECT_DIR/platform/ihv1-sp" && cargo build --target=aarch64-unknown-none
134 changes: 134 additions & 0 deletions ec-service-lib/src/message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,137 @@ where
}
}
}

// ===========================================================================
// MessageHandler Unit Tests
// ===========================================================================
#[cfg(test)]
mod tests {
use super::*;
use crate::Service;
use odp_ffa::{DirectMessagePayload, HasRegisterPayload};
use uuid::{uuid, Uuid};

// ===================================================================
// Mock Services
// ===================================================================

/// A mock service that echoes the first payload register + marker 0xAA.
struct MockServiceA;

impl Service for MockServiceA {
const UUID: Uuid = uuid!("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
const NAME: &'static str = "MockA";

fn ffa_msg_send_direct_req2(
&mut self,
msg: MsgSendDirectReq2,
) -> core::result::Result<MsgSendDirectResp2, odp_ffa::Error> {
let input = msg.payload().register_at(0);
let regs = [input, 0xAA];
let payload = DirectMessagePayload::from_iter(regs.iter().flat_map(|r| r.to_le_bytes()));
Ok(MsgSendDirectResp2::from_req_with_payload(&msg, payload))
}
}

/// A mock service that echoes the first payload register + marker 0xBB.
struct MockServiceB;

impl Service for MockServiceB {
const UUID: Uuid = uuid!("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
const NAME: &'static str = "MockB";

fn ffa_msg_send_direct_req2(
&mut self,
msg: MsgSendDirectReq2,
) -> core::result::Result<MsgSendDirectResp2, odp_ffa::Error> {
let input = msg.payload().register_at(0);
let regs = [input, 0xBB];
let payload = DirectMessagePayload::from_iter(regs.iter().flat_map(|r| r.to_le_bytes()));
Ok(MsgSendDirectResp2::from_req_with_payload(&msg, payload))
}
}

// ===================================================================
// Helpers
// ===================================================================

fn make_msg(uuid: Uuid, value: u64) -> MsgSendDirectReq2 {
let regs = [value];
let payload = DirectMessagePayload::from_iter(regs.iter().flat_map(|r| r.to_le_bytes()));
MsgSendDirectReq2::new(0x0001, 0x8001, uuid, payload)
}

// ===================================================================
// UUID Routing Tests (MHD-01)
// ===================================================================
#[test]
fn test_routes_to_correct_service_a() {
let mut handler = MessageHandler::new().append(MockServiceA).append(MockServiceB);
let msg = make_msg(MockServiceA::UUID, 42);
let resp = handler.node.handle(msg).unwrap();
assert_eq!(resp.payload().register_at(0), 42); // echoed value
assert_eq!(resp.payload().register_at(1), 0xAA); // MockA marker
}

#[test]
fn test_routes_to_correct_service_b() {
let mut handler = MessageHandler::new().append(MockServiceA).append(MockServiceB);
let msg = make_msg(MockServiceB::UUID, 99);
let resp = handler.node.handle(msg).unwrap();
assert_eq!(resp.payload().register_at(0), 99);
assert_eq!(resp.payload().register_at(1), 0xBB);
}

// ===================================================================
// Unknown UUID Tests (MHD-02)
// ===================================================================
#[test]
fn test_unknown_uuid_returns_error() {
let mut handler = MessageHandler::new().append(MockServiceA).append(MockServiceB);
let unknown_uuid = uuid!("cccccccc-cccc-cccc-cccc-cccccccccccc");
let msg = make_msg(unknown_uuid, 0);
let result = handler.node.handle(msg);
assert!(result.is_err());
match result.unwrap_err() {
odp_ffa::Error::Other(s) => assert_eq!(s, "Unknown UUID"),
e => panic!("Expected Error::Other(\"Unknown UUID\"), got {:?}", e),
}
}

#[test]
fn test_empty_handler_returns_error() {
let mut handler = MessageHandler::new();
let msg = make_msg(MockServiceA::UUID, 0);
let result = handler.node.handle(msg);
assert!(result.is_err());
}

// ===================================================================
// Service Chain Ordering Tests (MHD-03)
// ===================================================================
#[test]
fn test_service_chain_ordering_both_reachable() {
// append order: A first, B second
// linked list order: B -> A -> Terminal (LIFO)
let mut handler = MessageHandler::new().append(MockServiceA).append(MockServiceB);

// Both should be reachable regardless of position in chain
let resp_a = handler.node.handle(make_msg(MockServiceA::UUID, 1)).unwrap();
assert_eq!(resp_a.payload().register_at(1), 0xAA);

let resp_b = handler.node.handle(make_msg(MockServiceB::UUID, 2)).unwrap();
assert_eq!(resp_b.payload().register_at(1), 0xBB);
}

#[test]
fn test_single_service_routes_correctly() {
let mut handler = MessageHandler::new().append(MockServiceA);
let resp = handler.node.handle(make_msg(MockServiceA::UUID, 7)).unwrap();
assert_eq!(resp.payload().register_at(1), 0xAA);

// Unknown UUID should still error
let unknown = uuid!("dddddddd-dddd-dddd-dddd-dddddddddddd");
assert!(handler.node.handle(make_msg(unknown, 0)).is_err());
}
}
149 changes: 103 additions & 46 deletions ec-service-lib/src/services/fw_mgmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,58 +119,24 @@ impl FwMgmt {
}
}

fn map_share(&self, _address: u64, _length: u64) -> GenericRsp {
fn map_share(&self, _address: u64, _length: u64) -> Result<DirectMessagePayload> {
// TODO - do not hardcode address and length in MemRetrieveReq
MemRetrieveReq::new().exec().unwrap();
GenericRsp { _status: 0x0 }
MemRetrieveReq::new().exec()?;
Ok(DirectMessagePayload::from(GenericRsp { _status: 0x0 }))
}

fn test_notify(&self, msg: MsgSendDirectReq2) -> GenericRsp {
// let nfy = FfaNotify {
// function_id: FunctionId::NotificationSet.into(),
// source_id: msg.destination_id,
// destination_id: msg.source_id,
// args64: [
// 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
// ],
// };

// let _result = nfy.exec();

fn test_notify(&self, msg: MsgSendDirectReq2) -> Result<DirectMessagePayload> {
let flags = 0b10;
let notification_bitmap = 0b10;
NotificationSet::new(msg.destination_id(), msg.source_id(), flags, notification_bitmap)
.exec()
.unwrap();
NotificationSet::new(msg.destination_id(), msg.source_id(), flags, notification_bitmap).exec()?;

Comment thread
dymk marked this conversation as resolved.
// Return status success
GenericRsp { _status: 0x0 }
Ok(DirectMessagePayload::from(GenericRsp { _status: 0x0 }))
}

fn process_indirect(&self, seq_num: u16, _rx_buffer: u64, _tx_buffer: u64) -> GenericRsp {
fn process_indirect(&self, seq_num: u16, _rx_buffer: u64, _tx_buffer: u64) -> Result<DirectMessagePayload> {
debug!("Processing indirect message: 0x{:x}", seq_num);
// let msg = FfaIndirectMsg::new();
// let mut in_buf: [u8; 256] = [0; 256];
// let mut status;

// unsafe {
// status = msg.read_indirect_msg(rx_buffer, seq_num, &mut in_buf);
// };

// if status == FfaError::Ok {
// error!("Indirect Message: {:?}", in_buf);
// }

// // Populate TX buffer with response and matching seq num
// let buf: [u8; 16] = [
// 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
// ];
// unsafe {
// status = msg.write_indirect_msg(tx_buffer, seq_num, &buf);
// };

// GenericRsp { _status: status.into() }
GenericRsp { _status: 0x0 }
Err(odp_ffa::Error::Other("process_indirect not supported"))
}
}

Expand All @@ -183,18 +149,18 @@ impl Service for FwMgmt {
debug!("Received FwMgmt command 0x{:x}", cmd);

let payload = match cmd {
EC_CAP_INDIRECT_MSG => DirectMessagePayload::from(self.process_indirect(
EC_CAP_INDIRECT_MSG => self.process_indirect(
msg.payload().u8_at(1) as u16,
msg.payload().register_at(4),
msg.payload().register_at(5),
)),
)?,
EC_CAP_GET_FW_STATE => DirectMessagePayload::from(self.get_fw_state()),
EC_CAP_GET_SVC_LIST => DirectMessagePayload::from(self.get_svc_list()),
EC_CAP_GET_BID => DirectMessagePayload::from(self.get_bid()),
EC_CAP_TEST_NFY => DirectMessagePayload::from(self.test_notify(msg.clone())),
EC_CAP_TEST_NFY => self.test_notify(msg.clone())?,
EC_CAP_MAP_SHARE => {
// First parameter is pointer to memory descriptor
DirectMessagePayload::from(self.map_share(msg.payload().register_at(1), msg.payload().register_at(2)))
self.map_share(msg.payload().register_at(1), msg.payload().register_at(2))?
}
_ => {
error!("Unknown FwMgmt Command: {}", cmd);
Expand All @@ -205,3 +171,94 @@ impl Service for FwMgmt {
Ok(MsgSendDirectResp2::from_req_with_payload(&msg, payload))
}
}

// ===========================================================================
// FwMgmt Unit Tests
// ===========================================================================
#[cfg(test)]
mod tests {
use super::*;
use odp_ffa::{DirectMessagePayload, HasRegisterPayload};

const FWMGMT_UUID: Uuid = uuid!("330c1273-fde5-4757-9819-5b6539037502");

/// Build a FwMgmt request with the given command byte.
fn fwmgmt_req(cmd: u8) -> MsgSendDirectReq2 {
let mut bytes = [0u8; 14 * 8];
bytes[0] = cmd;
let payload = DirectMessagePayload::from_iter(bytes);
MsgSendDirectReq2::new(0x0001, 0x8001, FWMGMT_UUID, payload)
}

/// Extract status (i64) from response payload register 0.
fn resp_status_i64(resp: &MsgSendDirectResp2) -> i64 {
resp.payload().u64_at(0) as i64
}

// ===================================================================
// FwMgmt::get_fw_state Test
// ===================================================================
#[test]
fn test_get_fw_state() {
let mut svc = FwMgmt::new();
let resp = svc.ffa_msg_send_direct_req2(fwmgmt_req(EC_CAP_GET_FW_STATE)).unwrap();
let p = resp.payload();
// FwStateRsp serializes: fw_version(u16 LE) + secure_state(u8) + boot_status(u8) = 4 bytes
assert_eq!(p.u8_at(0), 0x00); // fw_version low byte
assert_eq!(p.u8_at(1), 0x01); // fw_version high byte (0x0100 LE)
assert_eq!(p.u8_at(2), 0x00); // secure_state
assert_eq!(p.u8_at(3), 0x01); // boot_status
}

// ===================================================================
// FwMgmt::get_svc_list Test
// ===================================================================
#[test]
fn test_get_svc_list() {
let mut svc = FwMgmt::new();
let resp = svc.ffa_msg_send_direct_req2(fwmgmt_req(EC_CAP_GET_SVC_LIST)).unwrap();
// ServiceListRsp: status(i64) + debug_mask(u16) + battery_mask(u8) + fan_mask(u8) +
// thermal_mask(u8) + hid_mask(u8) + key_mask(u16)
assert_eq!(resp_status_i64(&resp), 0x0); // status
let p = resp.payload();
assert_eq!(p.u8_at(8), 0x01); // debug_mask low byte
assert_eq!(p.u8_at(10), 0x01); // battery_mask
assert_eq!(p.u8_at(11), 0x01); // fan_mask
assert_eq!(p.u8_at(12), 0x01); // thermal_mask
assert_eq!(p.u8_at(13), 0x00); // hid_mask
assert_eq!(p.u8_at(14), 0x07); // key_mask low byte
}

// ===================================================================
// FwMgmt::get_bid Test
// ===================================================================
#[test]
fn test_get_bid() {
let mut svc = FwMgmt::new();
let resp = svc.ffa_msg_send_direct_req2(fwmgmt_req(EC_CAP_GET_BID)).unwrap();
assert_eq!(resp_status_i64(&resp), 0x0);
let p = resp.payload();
let bid = p.u64_at(8); // _bid starts at offset 8 (after i64 status)
assert_eq!(bid, 0xdead0001);
}

// ===================================================================
// FwMgmt::process_indirect Test
// ===================================================================
#[test]
fn test_process_indirect_returns_error() {
let mut svc = FwMgmt::new();
let result = svc.ffa_msg_send_direct_req2(fwmgmt_req(EC_CAP_INDIRECT_MSG));
assert!(result.is_err(), "process_indirect should return an error");
}

// ===================================================================
// FwMgmt Unknown Command Test
// ===================================================================
#[test]
fn test_unknown_command_returns_error() {
let mut svc = FwMgmt::new();
let result = svc.ffa_msg_send_direct_req2(fwmgmt_req(0xFF));
assert!(result.is_err());
}
}
Loading
Loading