Skip to main content

counters_abi/
abi.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#![no_std]
8
9//! This module describes how the kernel exposes its internal counters to userland.
10//! This is a PRIVATE UNSTABLE ABI that may change at any time! The layouts used
11//! here; the set of counters; their names, meanings, and types; and the set of
12//! available types; are all subject to change in every kernel version and are
13//! not meant to be any kind of stable ABI between the kernel and userland.
14//!
15//! The expectation is that these layouts will be used only by a single
16//! privileged service that is tightly-coupled with the kernel, i.e. always built
17//! from source when building the kernel.
18//!
19//! The counters exist only for kernel-specific diagnostic and logging purposes.
20
21use core::mem::{align_of, offset_of, size_of};
22use counters_bindings as bindings;
23
24/// The aggregation type of a kernel counter.
25///
26/// This specifies how the diagnostic tools should combine the per-CPU slot values
27/// of the counter to produce a single diagnostic value.
28#[repr(u64)]
29#[derive(Copy, Clone, Debug, Eq, PartialEq)]
30pub enum Type {
31    /// Padding element (unused).
32    Padding = 0,
33    /// Standard summation counter (aggregates the sum across all CPUs).
34    Sum = 1,
35    /// Minimum tracker counter (finds the minimum value across all CPUs).
36    Min = 2,
37    /// Maximum tracker counter (finds the maximum value across all CPUs).
38    Max = 3,
39}
40
41zr::static_assert!(Type::Padding as u64 == bindings::counters_Type_kPadding);
42zr::static_assert!(Type::Sum as u64 == bindings::counters_Type_kSum);
43zr::static_assert!(Type::Min as u64 == bindings::counters_Type_kMin);
44zr::static_assert!(Type::Max as u64 == bindings::counters_Type_kMax);
45
46/// Binary-stable C-compatible representation of a kernel counter descriptor.
47///
48/// The memory layout of this structure matches Zircon's `counters::Descriptor` exactly,
49/// enabling the linker and userspace diagnostic tools to parse Rust-declared counters
50/// seamlessly from the kernel's binary segments.
51#[repr(C, align(8))]
52#[derive(Copy, Clone, Debug, Eq, PartialEq)]
53pub struct Descriptor {
54    pub name: [u8; 56],
55    pub type_: u64,
56}
57
58// kernel.ld uses this size to ASSERT that enough space has been reserved in the counters arena.
59zr::static_assert!(size_of::<Descriptor>() == 64);
60// kernel.ld knows there is no alignment padding between the VMO header and the descriptor table.
61zr::static_assert!(align_of::<Descriptor>() == 8);
62zr::static_assert_size_and_align!(
63    Descriptor,
64    size_of::<bindings::counters_Descriptor>(),
65    align_of::<bindings::counters_Descriptor>(),
66);
67zr::static_assert!(offset_of!(Descriptor, name) == offset_of!(bindings::counters_Descriptor, name));
68zr::static_assert!(
69    offset_of!(Descriptor, type_) == offset_of!(bindings::counters_Descriptor, type_)
70);
71
72impl Descriptor {
73    /// Create a new raw `Descriptor` instance with the given packed name and type value.
74    pub const fn new(name: [u8; 56], type_: u64) -> Self {
75        Self { name, type_ }
76    }
77}
78
79/// Header layout of the kernel counter descriptor VMO (`counters::DescriptorVmo`).
80///
81/// Followed in the VMO by `num_counters()` entries of [`Descriptor`], sorted by name.
82/// Each index into that table corresponds to an index into a per-CPU array in the arena.
83#[repr(C)]
84#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
85pub struct DescriptorVmo {
86    /// Magic number identifying the `DescriptorVmo` layout (`DescriptorVmo::MAGIC`).
87    pub magic: u64,
88    /// Maximum number of CPUs (`SMP_MAX_CPUS`).
89    pub max_cpus: u64,
90    /// Size in bytes of the trailing descriptor table (`sizeof(descriptor_table)`).
91    pub descriptor_table_size: u64,
92}
93
94impl DescriptorVmo {
95    /// `PA_VMO_KERNEL_FILE` with this name has the `DescriptorVmo` layout.
96    pub const VMO_NAME: &[u8] = b"counters/desc";
97
98    /// This is `time_t` as of writing. Change it when changing this layout.
99    // TODO(mcgrathr): Maybe generate these uniquely at build time from
100    // the kernel version info or something?
101    pub const MAGIC: u64 = 1547273975;
102
103    /// Returns the number of [`Descriptor`] entries in the descriptor table.
104    pub const fn num_counters(&self) -> usize {
105        (self.descriptor_table_size as usize) / size_of::<Descriptor>()
106    }
107}
108
109// kernel.ld knows the layout of DescriptorVmo.
110zr::static_assert!(
111    offset_of!(DescriptorVmo, descriptor_table_size) == 16 && size_of::<DescriptorVmo>() == 24
112);
113zr::static_assert_size_and_align!(
114    DescriptorVmo,
115    size_of::<bindings::counters_DescriptorVmo>(),
116    align_of::<bindings::counters_DescriptorVmo>(),
117);
118zr::static_assert!(
119    offset_of!(DescriptorVmo, magic) == offset_of!(bindings::counters_DescriptorVmo, magic)
120);
121zr::static_assert!(
122    offset_of!(DescriptorVmo, max_cpus) == offset_of!(bindings::counters_DescriptorVmo, max_cpus)
123);
124zr::static_assert!(
125    offset_of!(DescriptorVmo, descriptor_table_size)
126        == offset_of!(bindings::counters_DescriptorVmo, descriptor_table_size)
127);
128zr::static_assert!(
129    size_of::<DescriptorVmo>() == offset_of!(bindings::counters_DescriptorVmo, descriptor_table)
130);
131zr::static_assert!(DescriptorVmo::MAGIC == bindings::counters_DescriptorVmo_kMagic);
132
133/// Name of the kernel counter descriptor VMO (`counters::DescriptorVmo::kVmoName`).
134pub const DESCRIPTOR_VMO_NAME: &[u8] = DescriptorVmo::VMO_NAME;
135
136/// `PA_VMO_KERNEL_FILE` with this name holds an array of `SMP_MAX_CPUS`
137/// arrays, each of which is `i64[num_counters()]` indexed by the
138/// index into `DescriptorVmo`'s descriptor table (`counters::kArenaVmoName`).
139pub const ARENA_VMO_NAME: &[u8] = b"counters/arena";