fuchsia_zbi_abi/
lib.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use num_derive::{FromPrimitive, ToPrimitive};
6use zerocopy::byteorder::little_endian::U32;
7use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
8
9const ZBI_MAX_SMT: usize = 4;
10
11/// Must be in the extra field for headers of type ZbiType::Container.
12pub const ZBI_CONTAINER_MAGIC: u32 = 0x868c_f7e6;
13
14/// Every header type must have this magic value.
15pub const ZBI_ITEM_MAGIC: u32 = 0xb578_1729;
16
17/// Always required.
18pub const ZBI_FLAGS_VERSION: u32 = 0x0001_0000;
19
20/// If the header contains this flag, the CRC32 field must contain a valid CRC32. Otherwise, the
21/// CRC32 field must contain ZBI_ITEM_NO_CRC32.
22pub const ZBI_FLAGS_CRC32: u32 = 0x0002_0000;
23
24/// The CRC32 field must be set to this when not using CRC32.
25pub const ZBI_ITEM_NO_CRC32: u32 = 0x4a87_e8d6;
26
27// Each item is padded to be a multiple of 8 bytes.
28pub const ZBI_ALIGNMENT_BYTES: u32 = 0x8;
29
30pub fn is_zbi_type_driver_metadata(zbi_type_raw: u32) -> bool {
31    (zbi_type_raw & 0xFF) == ZbiType::DriverMetadata.into_raw()
32}
33
34#[repr(u32)]
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36/// Defines the types supported by the Rust ZBI parser. This is a subset from
37/// sdk/lib/zbi-format/include/lib/zbi-format/zbi.h, and should be updated as needed.
38pub enum ZbiType {
39    Container = 0x544f_4f42,            // 'BOOT'
40    Cmdline = 0x4c44_4d43,              // 'CMDL'
41    Crashlog = 0x4d4f_4f42,             // 'BOOM'
42    KernelDriver = 0x5652_444B,         // 'KDRV'
43    PlatformId = 0x4449_4C50,           // 'PLID'
44    StorageBootfsFactory = 0x4653_4642, // 'BFSF'
45    StorageRamdisk = 0x4b53_4452,       // 'RDSK'
46    ImageArgs = 0x4752_4149,            // 'IARG'
47    SerialNumber = 0x4e4c_5253,         // 'SRLN'
48    BootloaderFile = 0x4C46_5442,       // 'BTFL'
49    DeviceTree = 0xd00d_feed,           // ??
50    CpuTopology = 0x544f_504f,          // 'TOPO'
51    AcpiRsdp = 0x5044_5352,             // 'RSDP'
52    Smbios = 0x4942_4d53,               // 'SMBI'
53    Framebuffer = 0x4246_5753,          // 'SWFB'
54
55    // DriverMetadata is a special case, where only the LSB of the u32 needs to match this
56    // value. See the IsZbiTypeDriverMetadata function for details.
57    DriverMetadata = 0x6D,
58    Unknown,
59}
60
61impl ZbiType {
62    pub fn from_raw(raw: u32) -> Self {
63        match raw {
64            x if x == ZbiType::Container.into_raw() => ZbiType::Container,
65            x if x == ZbiType::Cmdline.into_raw() => ZbiType::Cmdline,
66            x if x == ZbiType::Crashlog.into_raw() => ZbiType::Crashlog,
67            x if x == ZbiType::KernelDriver.into_raw() => ZbiType::KernelDriver,
68            x if x == ZbiType::PlatformId.into_raw() => ZbiType::PlatformId,
69            x if x == ZbiType::StorageBootfsFactory.into_raw() => ZbiType::StorageBootfsFactory,
70            x if x == ZbiType::StorageRamdisk.into_raw() => ZbiType::StorageRamdisk,
71            x if x == ZbiType::ImageArgs.into_raw() => ZbiType::ImageArgs,
72            x if x == ZbiType::SerialNumber.into_raw() => ZbiType::SerialNumber,
73            x if x == ZbiType::BootloaderFile.into_raw() => ZbiType::BootloaderFile,
74            x if x == ZbiType::DeviceTree.into_raw() => ZbiType::DeviceTree,
75            x if x == ZbiType::CpuTopology.into_raw() => ZbiType::CpuTopology,
76            x if x == ZbiType::AcpiRsdp.into_raw() => ZbiType::AcpiRsdp,
77            x if x == ZbiType::Smbios.into_raw() => ZbiType::Smbios,
78            x if x == ZbiType::Framebuffer.into_raw() => ZbiType::Framebuffer,
79            x if is_zbi_type_driver_metadata(x) => ZbiType::DriverMetadata,
80            _ => ZbiType::Unknown,
81        }
82    }
83
84    pub fn into_raw(self) -> u32 {
85        self as u32
86    }
87}
88
89#[repr(C)]
90#[derive(Debug, Default, Copy, Clone, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
91pub struct zbi_header_t {
92    pub zbi_type: U32,
93    pub length: U32,
94    pub extra: U32,
95    pub flags: U32,
96    pub reserved_0: U32,
97    pub reserved_1: U32,
98    pub magic: U32,
99    pub crc32: U32,
100}
101
102/// Define a container header that describes a container content length of `length`.
103pub fn zbi_container_header(length: u32) -> zbi_header_t {
104    zbi_header_t {
105        zbi_type: U32::new(ZbiType::Container.into_raw()),
106        length: U32::new(length),
107        extra: U32::new(ZBI_CONTAINER_MAGIC),
108        flags: U32::new(ZBI_FLAGS_VERSION),
109        reserved_0: U32::new(0),
110        reserved_1: U32::new(0),
111        magic: U32::new(ZBI_ITEM_MAGIC),
112        crc32: U32::new(ZBI_ITEM_NO_CRC32),
113    }
114}
115
116#[repr(C)]
117#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
118/// Defines the Rust version of `zbi_topology_node_v2_t` in
119/// sdk/lib/zbi-format/include/lib/zbi-format/zbi.h.
120pub struct ZbiTopologyNode {
121    // Should be one of ZbiTopologyEntityType.
122    pub entity_type: u8,
123    pub parent_index: u16,
124    pub entity: Entity,
125}
126
127#[repr(u8)]
128#[derive(FromPrimitive, ToPrimitive)]
129pub enum ZbiTopologyEntityType {
130    ZbiTopologyEntityUndefined = 0,
131    ZbiTopologyEntityProcessor = 1,
132    ZbiTopologyEntityCluster = 2,
133    ZbiTopologyEntityCache = 3,
134    ZbiTopologyEntityDie = 4,
135    ZbiTopologyEntitySocket = 5,
136    ZbiTopologyEntityPowerPlane = 6,
137    ZbiTopologyEntityNumaRegion = 7,
138}
139
140#[repr(u8)]
141#[derive(FromPrimitive, ToPrimitive)]
142pub enum ZbiTopologyArchitecture {
143    ZbiTopologyArchUndefined = 0,
144    ZbiTopologyArchX64 = 1,
145    ZbiTopologyArchArm64 = 2,
146}
147
148#[repr(C)]
149#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
150pub union Entity {
151    pub processor: ZbiTopologyProcessor,
152    pub cluster: ZbiTopologyCluster,
153    pub numa_region: ZbiTopologyNumaRegion,
154    pub cache: ZbiTopologyCache,
155}
156
157#[repr(C)]
158#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
159pub struct ZbiTopologyProcessor {
160    pub logical_ids: [u16; ZBI_MAX_SMT],
161    pub logical_id_count: u8,
162    pub flags: u16,
163
164    // Should be one of ZbiTopologyArchitecture.
165    // If UNDEFINED then nothing will be set in arch_info.
166    pub architecture: u8,
167    pub architecture_info: ArchitectureInfo,
168}
169
170#[repr(C)]
171#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
172pub union ArchitectureInfo {
173    pub arm64: ZbiTopologyArm64Info,
174    pub x64: ZbiTopologyX64Info,
175}
176
177#[repr(C)]
178#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
179pub struct ZbiTopologyCluster {
180    // Relative performance level of this processor in the system.
181    // Refer to sdk/lib/zbi-format/include/lib/zbi-format/zbi.h for more details.
182    pub performance_class: u8,
183}
184
185#[repr(C)]
186#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
187pub struct ZbiTopologyNumaRegion {
188    // Starting and ending memory addresses of this numa region.
189    pub start_address: u64,
190    pub end_address: u64,
191}
192
193#[repr(C)]
194#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
195pub struct ZbiTopologyCache {
196    // Unique id of this cache node. No other semantics are assumed.
197    pub cache_id: u32,
198}
199
200#[repr(C)]
201#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
202pub struct ZbiTopologyArm64Info {
203    // Cluster ids for each level, one being closest to the cpu.
204    // These map to aff1, aff2, and aff3 values in the ARM registers.
205    pub cluster_1_id: u8,
206    pub cluster_2_id: u8,
207    pub cluster_3_id: u8,
208
209    // Id of the cpu inside of the bottom-most cluster, aff0 value.
210    pub cpu_id: u8,
211
212    // The GIC interface number for this processor.
213    // In GIC v3+ this is not necessary as the processors are addressed by their
214    // affinity routing (all cluster ids followed by cpu_id).
215    pub gic_id: u8,
216}
217
218#[repr(C)]
219#[derive(Copy, Clone, KnownLayout, FromBytes, Immutable)]
220pub struct ZbiTopologyX64Info {
221    // Indexes here correspond to the logical_ids index for the thread.
222    pub apic_ids: [u32; ZBI_MAX_SMT],
223    pub apic_id_count: u32,
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229
230    const MAX_SERIALIZATION_BUFFER_SIZE: usize = 128;
231
232    // From libtest-lib.a
233    extern "C" {
234        pub fn serialize_zbi_topology_x64_info_t(
235            buffer: &[u8; MAX_SERIALIZATION_BUFFER_SIZE],
236            apic_ids: &[u32; 4],
237            apic_id_count: u32,
238        ) -> usize;
239
240        pub fn serialize_zbi_topology_arm64_info_t(
241            buffer: &[u8; MAX_SERIALIZATION_BUFFER_SIZE],
242            cluster_1_id: u8,
243            cluster_2_id: u8,
244            cluster_3_id: u8,
245            cpu_id: u8,
246            gic_id: u8,
247        ) -> usize;
248
249        pub fn serialize_zbi_topology_cache_t(
250            buffer: &[u8; MAX_SERIALIZATION_BUFFER_SIZE],
251            cache_id: u32,
252        ) -> usize;
253
254        pub fn serialize_zbi_topology_numa_region_v2_t(
255            buffer: &[u8; MAX_SERIALIZATION_BUFFER_SIZE],
256            start_address: u64,
257            end_address: u64,
258        ) -> usize;
259
260        pub fn serialize_zbi_topology_cluster_t(
261            buffer: &[u8; MAX_SERIALIZATION_BUFFER_SIZE],
262            performance_class: u8,
263        ) -> usize;
264
265        pub fn serialize_zbi_topology_processor_v2_t(
266            buffer: &[u8; MAX_SERIALIZATION_BUFFER_SIZE],
267            logical_ids: &[u16; 4],
268            logical_id_count: u8,
269            flags: u16,
270            architecture: u8,
271            architecture_info: ArchitectureInfo,
272        ) -> usize;
273
274        pub fn serialize_zbi_topology_node_v2_t(
275            buffer: &[u8; MAX_SERIALIZATION_BUFFER_SIZE],
276            entity_type: u8,
277            parent_index: u16,
278            entity: Entity,
279        ) -> usize;
280    }
281
282    unsafe fn as_u8_slice<T: Sized>(p: &T) -> &[u8] {
283        std::slice::from_raw_parts((p as *const T) as *const u8, ::std::mem::size_of::<T>())
284    }
285
286    #[fuchsia::test]
287    fn zbi_topology_x64_info_in_sync() {
288        let apic_ids = [0, 1, 3, 4];
289        let apic_id_count = 4;
290
291        let buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
292        let size = unsafe { serialize_zbi_topology_x64_info_t(&buffer, &apic_ids, apic_id_count) };
293        assert_eq!(size, std::mem::size_of::<ZbiTopologyX64Info>());
294
295        let x64_info = ZbiTopologyX64Info { apic_ids, apic_id_count };
296        assert_eq!(unsafe { as_u8_slice(&x64_info) }, &buffer[0..size]);
297    }
298
299    #[fuchsia::test]
300    fn zbi_topology_arm_info_in_sync() {
301        let cluster_1_id = 1;
302        let cluster_2_id = 2;
303        let cluster_3_id = 3;
304        let cpu_id = 0;
305        let gic_id = 1;
306
307        let buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
308        let size = unsafe {
309            serialize_zbi_topology_arm64_info_t(
310                &buffer,
311                cluster_1_id,
312                cluster_2_id,
313                cluster_3_id,
314                cpu_id,
315                gic_id,
316            )
317        };
318        assert_eq!(size, std::mem::size_of::<ZbiTopologyArm64Info>());
319
320        let arm_info =
321            ZbiTopologyArm64Info { cluster_1_id, cluster_2_id, cluster_3_id, cpu_id, gic_id };
322        assert_eq!(unsafe { as_u8_slice(&arm_info) }, &buffer[0..size]);
323    }
324
325    #[fuchsia::test]
326    fn zbi_topology_cache_in_sync() {
327        let cache_id = 1221;
328
329        let buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
330        let size = unsafe { serialize_zbi_topology_cache_t(&buffer, cache_id) };
331        assert_eq!(size, std::mem::size_of::<ZbiTopologyCache>());
332
333        let cache = ZbiTopologyCache { cache_id };
334        assert_eq!(unsafe { as_u8_slice(&cache) }, &buffer[0..size]);
335    }
336
337    #[fuchsia::test]
338    fn zbi_topology_numa_region_in_sync() {
339        let start_address = 1221;
340        let end_address = 1223;
341
342        let buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
343        let size =
344            unsafe { serialize_zbi_topology_numa_region_v2_t(&buffer, start_address, end_address) };
345        assert_eq!(size, std::mem::size_of::<ZbiTopologyNumaRegion>());
346
347        let numa_region = ZbiTopologyNumaRegion { start_address, end_address };
348        assert_eq!(unsafe { as_u8_slice(&numa_region) }, &buffer[0..size]);
349    }
350
351    #[fuchsia::test]
352    fn zbi_topology_cluster_in_sync() {
353        let performance_class = 2;
354
355        let buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
356        let size = unsafe { serialize_zbi_topology_cluster_t(&buffer, performance_class) };
357        assert_eq!(size, std::mem::size_of::<ZbiTopologyCluster>());
358
359        let cluster = ZbiTopologyCluster { performance_class };
360        assert_eq!(unsafe { as_u8_slice(&cluster) }, &buffer[0..size]);
361    }
362
363    #[fuchsia::test]
364    fn zbi_topology_processor_in_sync() {
365        let logical_ids = [0, 1, 3, 4];
366        let logical_id_count = 4;
367        let flags = 1;
368        let arm_info = ZbiTopologyArm64Info {
369            cluster_1_id: 1,
370            cluster_2_id: 1,
371            cluster_3_id: 0,
372            cpu_id: 0,
373            gic_id: 1,
374        };
375        let x64_info = ZbiTopologyX64Info { apic_ids: [0, 1, 3, 4], apic_id_count: 4 };
376        let mut architecture_info = ArchitectureInfo { arm64: arm_info };
377
378        let mut buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
379        let mut size = unsafe {
380            serialize_zbi_topology_processor_v2_t(
381                &buffer,
382                &logical_ids,
383                logical_id_count,
384                flags,
385                ZbiTopologyArchitecture::ZbiTopologyArchArm64 as u8,
386                architecture_info,
387            )
388        };
389        assert_eq!(size, std::mem::size_of::<ZbiTopologyProcessor>());
390
391        // Ensure that the padding introduced by union are all zeroed.
392        let mut arm_processor: ZbiTopologyProcessor = unsafe { std::mem::zeroed() };
393        arm_processor.logical_ids = logical_ids;
394        arm_processor.logical_id_count = logical_id_count;
395        arm_processor.flags = flags;
396        arm_processor.architecture = ZbiTopologyArchitecture::ZbiTopologyArchArm64 as u8;
397        arm_processor.architecture_info.arm64 = arm_info;
398        assert_eq!(unsafe { as_u8_slice(&arm_processor) }, &buffer[0..size]);
399
400        buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
401        architecture_info = ArchitectureInfo { x64: x64_info };
402        size = unsafe {
403            serialize_zbi_topology_processor_v2_t(
404                &buffer,
405                &logical_ids,
406                logical_id_count,
407                flags,
408                ZbiTopologyArchitecture::ZbiTopologyArchX64 as u8,
409                architecture_info,
410            )
411        };
412        assert_eq!(size, std::mem::size_of::<ZbiTopologyProcessor>());
413
414        // Ensure that the padding introduced by union are all zeroed.
415        let mut x64_processor: ZbiTopologyProcessor = unsafe { std::mem::zeroed() };
416        x64_processor.logical_ids = logical_ids;
417        x64_processor.logical_id_count = logical_id_count;
418        x64_processor.flags = flags;
419        x64_processor.architecture = ZbiTopologyArchitecture::ZbiTopologyArchX64 as u8;
420        x64_processor.architecture_info.x64 = x64_info;
421        assert_eq!(unsafe { as_u8_slice(&x64_processor) }, &buffer[0..size]);
422
423        buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
424        size = unsafe {
425            serialize_zbi_topology_processor_v2_t(
426                &buffer,
427                &logical_ids,
428                logical_id_count,
429                flags,
430                ZbiTopologyArchitecture::ZbiTopologyArchUndefined as u8,
431                architecture_info,
432            )
433        };
434        assert_eq!(size, std::mem::size_of::<ZbiTopologyProcessor>());
435
436        // Ensure that the padding introduced by union are all zeroed.
437        let mut undefined_processor: ZbiTopologyProcessor = unsafe { std::mem::zeroed() };
438        undefined_processor.logical_ids = logical_ids;
439        undefined_processor.logical_id_count = logical_id_count;
440        undefined_processor.flags = flags;
441        undefined_processor.architecture = ZbiTopologyArchitecture::ZbiTopologyArchUndefined as u8;
442        assert_eq!(unsafe { as_u8_slice(&undefined_processor) }, &buffer[0..size]);
443    }
444
445    #[fuchsia::test]
446    fn zbi_topology_node_in_sync() {
447        let parent_index = 5;
448        // Ensure that the padding introduced by union are all zeroed.
449        let mut processor: ZbiTopologyProcessor = unsafe { std::mem::zeroed() };
450        processor.logical_ids = [0, 0, 3, 0];
451        processor.logical_id_count = 4;
452        processor.flags = 2;
453        processor.architecture = ZbiTopologyArchitecture::ZbiTopologyArchUndefined as u8;
454        let mut entity = Entity { processor: processor };
455
456        let mut buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
457        let mut size = unsafe {
458            serialize_zbi_topology_node_v2_t(
459                &buffer,
460                ZbiTopologyEntityType::ZbiTopologyEntityProcessor as u8,
461                parent_index,
462                entity,
463            )
464        };
465        assert_eq!(size, std::mem::size_of::<ZbiTopologyNode>());
466
467        // Ensure that the padding introduced by union are all zeroed.
468        let mut processor_node: ZbiTopologyNode = unsafe { std::mem::zeroed() };
469        processor_node.parent_index = parent_index;
470        processor_node.entity_type = ZbiTopologyEntityType::ZbiTopologyEntityProcessor as u8;
471        processor_node.entity.processor = processor;
472        assert_eq!(unsafe { as_u8_slice(&processor_node) }, &buffer[0..size]);
473
474        let cluster = ZbiTopologyCluster { performance_class: 1 };
475        entity = Entity { cluster: cluster };
476        buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
477        size = unsafe {
478            serialize_zbi_topology_node_v2_t(
479                &buffer,
480                ZbiTopologyEntityType::ZbiTopologyEntityCluster as u8,
481                parent_index,
482                entity,
483            )
484        };
485        assert_eq!(size, std::mem::size_of::<ZbiTopologyNode>());
486
487        // Ensure that the padding introduced by union are all zeroed.
488        let mut cluster_node: ZbiTopologyNode = unsafe { std::mem::zeroed() };
489        cluster_node.parent_index = parent_index;
490        cluster_node.entity_type = ZbiTopologyEntityType::ZbiTopologyEntityCluster as u8;
491        cluster_node.entity.cluster = cluster;
492        assert_eq!(unsafe { as_u8_slice(&cluster_node) }, &buffer[0..size]);
493
494        let numa_region = ZbiTopologyNumaRegion { start_address: 2233, end_address: 3333 };
495        entity = Entity { numa_region: numa_region };
496        buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
497        size = unsafe {
498            serialize_zbi_topology_node_v2_t(
499                &buffer,
500                ZbiTopologyEntityType::ZbiTopologyEntityNumaRegion as u8,
501                parent_index,
502                entity,
503            )
504        };
505        assert_eq!(size, std::mem::size_of::<ZbiTopologyNode>());
506
507        // Ensure that the padding introduced by union are all zeroed.
508        let mut numa_region_node: ZbiTopologyNode = unsafe { std::mem::zeroed() };
509        numa_region_node.parent_index = parent_index;
510        numa_region_node.entity_type = ZbiTopologyEntityType::ZbiTopologyEntityNumaRegion as u8;
511        numa_region_node.entity.numa_region = numa_region;
512        assert_eq!(unsafe { as_u8_slice(&numa_region_node) }, &buffer[0..size]);
513
514        let cache = ZbiTopologyCache { cache_id: 3 };
515        entity = Entity { cache: cache };
516        buffer = [0 as u8; MAX_SERIALIZATION_BUFFER_SIZE];
517        size = unsafe {
518            serialize_zbi_topology_node_v2_t(
519                &buffer,
520                ZbiTopologyEntityType::ZbiTopologyEntityCache as u8,
521                parent_index,
522                entity,
523            )
524        };
525        assert_eq!(size, std::mem::size_of::<ZbiTopologyNode>());
526
527        // Ensure that the padding introduced by union are all zeroed.
528        let mut cache_node: ZbiTopologyNode = unsafe { std::mem::zeroed() };
529        cache_node.parent_index = parent_index;
530        cache_node.entity_type = ZbiTopologyEntityType::ZbiTopologyEntityCache as u8;
531        cache_node.entity.cache = cache;
532        assert_eq!(unsafe { as_u8_slice(&cache_node) }, &buffer[0..size]);
533    }
534}