1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
use crate::core::{Handle, UndefinedStruct};
use crate::SMBiosStruct;
use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::fmt;

/// # Memory Device Mapped Address (Type 20)
///
/// This structure maps memory address space usually to a device-level granularity.
///
/// Compliant with:
/// DMTF SMBIOS Reference Specification 3.4.0 (DSP0134)
/// Document Date: 2020-07-17
pub struct SMBiosMemoryDeviceMappedAddress<'a> {
    parts: &'a UndefinedStruct,
}

impl<'a> SMBiosStruct<'a> for SMBiosMemoryDeviceMappedAddress<'a> {
    const STRUCT_TYPE: u8 = 20u8;

    fn new(parts: &'a UndefinedStruct) -> Self {
        Self { parts }
    }

    fn parts(&self) -> &'a UndefinedStruct {
        self.parts
    }
}

impl<'a> SMBiosMemoryDeviceMappedAddress<'a> {
    /// Physical address, in kilobytes, of a range of
    /// memory mapped to the referenced [SMBiosMemoryDevice]
    /// When the field value is FFFF FFFFh the actual
    /// address is stored in the Extended Starting
    /// Address field. When this field contains a valid
    /// address, Ending Address must also contain a
    /// valid address. When this field contains FFFF
    /// FFFFh, Ending Address must also contain FFFF
    /// FFFFh.
    pub fn starting_address(&self) -> Option<u32> {
        self.parts.get_field_dword(0x4)
    }

    /// Physical ending address of the last kilobyte of a
    /// range of addresses mapped to the referenced
    /// [SMBiosMemoryDevice]
    /// When the field value is FFFF FFFFh the actual
    /// address is stored in the Extended Ending Address
    /// field. When this field contains a valid address,
    /// Starting Address must also contain a valid
    /// address.
    pub fn ending_address(&self) -> Option<u32> {
        self.parts.get_field_dword(0x8)
    }

    /// Handle, or instance number, associated with the
    /// [SMBiosMemoryDevice] structure to which this address
    /// range is mapped
    /// Multiple address ranges can be mapped to a
    /// single [SMBiosMemoryDevice]
    pub fn memory_device_handle(&self) -> Option<Handle> {
        self.parts.get_field_handle(0xC)
    }

    /// Handle, or instance number, associated with the
    /// Memory Array Mapped Address structure to which
    /// this device address range is mapped
    /// Multiple address ranges can be mapped to a
    /// single [SMBiosMemoryArrayMappedAddress].
    pub fn memory_array_mapped_address_handle(&self) -> Option<Handle> {
        self.parts.get_field_handle(0xE)
    }

    /// Position of the referenced [SMBiosMemoryDevice] in a row
    /// of the address partition
    /// For example, if two 8-bit devices form a 16-bit row,
    /// this field’s value is either 1 or 2.
    /// The value 0 is reserved. If the position is
    /// unknown, the field contains FFh.
    pub fn partition_row_position(&self) -> Option<u8> {
        self.parts.get_field_byte(0x10)
    }

    /// Position of the referenced [SMBiosMemoryDevice] in an
    /// interleave
    /// The value 0 indicates non-interleaved, 1 indicates
    /// first interleave position, 2 the second interleave
    /// position, and so on. If the position is unknown, the
    /// field contains FFh.
    /// EXAMPLES: In a 2:1 interleave, the value 1 indicates
    /// the device in the ”even” position. In a 4:1 interleave, the
    /// value 1 indicates the first of four possible positions.
    pub fn interleave_position(&self) -> Option<u8> {
        self.parts.get_field_byte(0x11)
    }

    /// Maximum number of consecutive rows from the
    /// referenced [SMBiosMemoryDevice] that are accessed in a
    /// single interleaved transfer
    /// If the device is not part of an interleave, the field
    /// contains 0; if the interleave configuration is
    /// unknown, the value is FFh.
    /// EXAMPLES: If a device transfers two rows each time it
    /// is read, its Interleaved Data Depth is set to 2. If that
    /// device is 2:1 interleaved and in Interleave Position 1, the
    /// rows mapped to that device are 1, 2, 5, 6, 9, 10, etc.
    pub fn interleaved_data_depth(&self) -> Option<u8> {
        self.parts.get_field_byte(0x12)
    }

    /// Physical address, in bytes, of a range of memory
    /// mapped to the referenced [SMBiosMemoryDevice]
    /// This field is valid when Starting Address contains
    /// the value FFFF FFFFh. If Starting Address
    /// contains a value other than FFFF FFFFh, this field
    /// contains zeros. When this field contains a valid
    /// address, Extended Ending Address must also
    /// contain a valid address.
    pub fn extended_starting_address(&self) -> Option<u64> {
        self.parts.get_field_qword(0x13)
    }

    /// Physical ending address, in bytes, of the last of a
    /// range of addresses mapped to the referenced
    /// [SMBiosMemoryDevice]
    /// This field is valid when both Starting Address and
    /// Ending Address contain the value FFFF FFFFh. If
    /// Ending Address contains a value other than FFFF
    /// FFFFh, this field contains zeros. When this field
    /// contains a valid address, Extended Starting
    /// Address must also contain a valid address
    pub fn extended_ending_address(&self) -> Option<u64> {
        self.parts.get_field_qword(0x1B)
    }
}

impl fmt::Debug for SMBiosMemoryDeviceMappedAddress<'_> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.debug_struct(std::any::type_name::<SMBiosMemoryDeviceMappedAddress<'_>>())
            .field("header", &self.parts.header)
            .field("starting_address", &self.starting_address())
            .field("ending_address", &self.ending_address())
            .field("memory_device_handle", &self.memory_device_handle())
            .field(
                "memory_array_mapped_address_handle",
                &self.memory_array_mapped_address_handle(),
            )
            .field("partition_row_position", &self.partition_row_position())
            .field("interleave_position", &self.interleave_position())
            .field("interleaved_data_depth", &self.interleaved_data_depth())
            .field(
                "extended_starting_address",
                &self.extended_starting_address(),
            )
            .field("extended_ending_address", &self.extended_ending_address())
            .finish()
    }
}

impl Serialize for SMBiosMemoryDeviceMappedAddress<'_> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("SMBiosMemoryDeviceMappedAddress", 10)?;
        state.serialize_field("header", &self.parts.header)?;
        state.serialize_field("starting_address", &self.starting_address())?;
        state.serialize_field("ending_address", &self.ending_address())?;
        state.serialize_field("memory_device_handle", &self.memory_device_handle())?;
        state.serialize_field(
            "memory_array_mapped_address_handle",
            &self.memory_array_mapped_address_handle(),
        )?;
        state.serialize_field("partition_row_position", &self.partition_row_position())?;
        state.serialize_field("interleave_position", &self.interleave_position())?;
        state.serialize_field("interleaved_data_depth", &self.interleaved_data_depth())?;
        state.serialize_field(
            "extended_starting_address",
            &self.extended_starting_address(),
        )?;
        state.serialize_field("extended_ending_address", &self.extended_ending_address())?;
        state.end()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn unit_test() {
        let struct_type20 = vec![
            0x14, 0x23, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x40, 0x00,
            0x3F, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        ];

        let parts = UndefinedStruct::new(&struct_type20);
        let test_struct = SMBiosMemoryDeviceMappedAddress::new(&parts);

        assert_eq!(test_struct.starting_address(), Some(0));
        assert_eq!(test_struct.ending_address(), Some(8388607));
        assert_eq!(*test_struct.memory_device_handle().unwrap(), 64);
        assert_eq!(
            *test_struct.memory_array_mapped_address_handle().unwrap(),
            63
        );
        assert_eq!(test_struct.partition_row_position(), Some(1));
        assert_eq!(test_struct.interleave_position(), Some(1));
        assert_eq!(test_struct.interleaved_data_depth(), Some(2));
        assert_eq!(test_struct.extended_starting_address(), Some(0));
        assert_eq!(test_struct.extended_ending_address(), Some(0));
    }
}