From 7700f4b585d2c22946e39e5dcfed28dc998f3642 Mon Sep 17 00:00:00 2001 From: Oliver Anderson Date: Fri, 20 Mar 2026 11:57:01 +0100 Subject: [PATCH] arch: Helper functions for u32 hex (de-) serialization These helper functions will later be used to (de-) serialize CPUID leaves and MSR register addresses. Signed-off-by: Oliver Anderson On-behalf-of: SAP oliver.anderson@sap.com --- Cargo.lock | 1 + arch/Cargo.toml | 1 + arch/src/x86_64/helpers.rs | 80 ++++++++++++++++++++++++++++++++++++++ arch/src/x86_64/mod.rs | 2 + 4 files changed, 84 insertions(+) create mode 100644 arch/src/x86_64/helpers.rs diff --git a/Cargo.lock b/Cargo.lock index a2003576e..77607a481 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,6 +121,7 @@ dependencies = [ "log", "proptest", "serde", + "serde_json", "thiserror", "uuid", "vm-fdt", diff --git a/arch/Cargo.toml b/arch/Cargo.toml index b208eb9a5..87fb2e0fd 100644 --- a/arch/Cargo.toml +++ b/arch/Cargo.toml @@ -27,6 +27,7 @@ vmm-sys-util = { workspace = true, features = ["with-serde"] } [dev-dependencies] proptest = "1.0.0" +serde_json = { workspace = true } [target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies] fdt_parser = { version = "0.1.5", package = "fdt" } diff --git a/arch/src/x86_64/helpers.rs b/arch/src/x86_64/helpers.rs new file mode 100644 index 000000000..8343517e2 --- /dev/null +++ b/arch/src/x86_64/helpers.rs @@ -0,0 +1,80 @@ +// Copyright © 2026 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +use serde::{Deserialize, Deserializer, Serializer}; + +/// Serializes the given `input` as a hex string (starting with "0x"). +/// +/// As an example if `input:=5` then this function will feed the given +/// `serializer` the string "0x5". +pub(crate) fn serialize_u32_hex( + input: &u32, + serializer: S, +) -> std::result::Result { + serializer.serialize_str(&format!("{input:#x}")) +} + +/// Deserializes a u32 from a hex string representation. +pub(crate) fn deserialize_u32_hex<'de, D: Deserializer<'de>>( + deserializer: D, +) -> std::result::Result { + let hex: &str = <&str>::deserialize(deserializer)?; + u32::from_str_radix(hex.strip_prefix("0x").unwrap_or(""), 16).map_err(|_| { + ::custom(format!("{hex} is not a hex encoded 32 bit integer")) + }) +} + +#[cfg(test)] +mod unit_tests { + use proptest::prelude::*; + use serde::{Deserialize, Serialize}; + + use super::*; + + #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] + struct TestStruct { + #[serde( + serialize_with = "serialize_u32_hex", + deserialize_with = "deserialize_u32_hex" + )] + foo: u32, + #[serde( + serialize_with = "serialize_u32_hex", + deserialize_with = "deserialize_u32_hex" + )] + bar: u32, + } + + // Check that our hex serializers satisfy the two following invariants + // 1. Serialization followed by deserialization is the identity. + // 2. Values of type u32 are serialized to strings starting with "0x" and then + // a sub-string where all characters are ascii hex digits (with the letters [a-f] always in lowercase). + proptest! { + #[test] + fn hex_serialization_works(foo in any::(), bar in any::()) { + let t = TestStruct { foo , bar }; + + let t_string = serde_json::to_string(&t).unwrap(); + let t_deserialized = serde_json::from_str(&t_string).unwrap(); + prop_assert_eq!(t, t_deserialized); + + let t_json = serde_json::to_value(t).unwrap(); + + let check_str_invariants = |value: &str| { + prop_assert!(value.starts_with("0x")); + prop_assert!(value.as_bytes()[2..].iter().all(u8::is_ascii_hexdigit)); + prop_assert!(!value.as_bytes()[2..].iter().any(u8::is_ascii_uppercase)); + Ok(()) + }; + + let foo_str = t_json.get("foo").unwrap().as_str().unwrap(); + let bar_str = t_json.get("bar").unwrap().as_str().unwrap(); + + check_str_invariants(foo_str)?; + check_str_invariants(bar_str)?; + + } + } +} diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index b16bca3d1..b60b430cf 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -14,6 +14,7 @@ pub mod regs; #[cfg(feature = "tdx")] pub mod tdx; +mod helpers; mod mpspec; mod mptable; mod smbios; @@ -21,6 +22,7 @@ mod smbios; use std::arch::x86_64; use std::mem; +use helpers::{deserialize_u32_hex, serialize_u32_hex}; use hypervisor::arch::x86::{CPUID_FLAG_VALID_INDEX, CpuIdEntry}; use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError}; use linux_loader::loader::bootparam::{boot_params, setup_header};