summaryrefslogtreecommitdiff
path: root/rust/kernel/sizes.rs
blob: 521b2b38bfe7722f457a70b7621d6083dafcc245 (plain)
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
// SPDX-License-Identifier: GPL-2.0

//! Commonly used sizes.
//!
//! C headers: [`include/linux/sizes.h`](srctree/include/linux/sizes.h).
//!
//! The top-level `SZ_*` constants are [`usize`]-typed, for use in kernel page
//! arithmetic and similar CPU-side work.
//!
//! The [`SizeConstants`] trait provides the same constants as associated constants
//! on [`u32`], [`u64`], and [`usize`], for use in device address spaces where
//! the address width depends on the hardware. Device drivers frequently need
//! these constants as [`u64`] (or [`u32`]) rather than [`usize`], because
//! device address spaces are sized independently of the CPU pointer width.
//!
//! # Examples
//!
//! ```
//! use kernel::{
//!     page::PAGE_SIZE,
//!     sizes::{
//!         SizeConstants,
//!         SZ_1M, //
//!     }, //
//! };
//!
//! // Module-level constants continue to work without a type qualifier.
//! let num_pages_in_1m = SZ_1M / PAGE_SIZE;
//!
//! // Trait associated constants require a type qualifier.
//! let heap_size = 14 * u64::SZ_1M;
//! let small = u32::SZ_4K;
//! ```

macro_rules! define_sizes {
    ($($type:ty),* $(,)?) => {
        define_sizes!(@internal [$($type),*]
            /// `0x0000_0400`.
            SZ_1K,
            /// `0x0000_0800`.
            SZ_2K,
            /// `0x0000_1000`.
            SZ_4K,
            /// `0x0000_2000`.
            SZ_8K,
            /// `0x0000_4000`.
            SZ_16K,
            /// `0x0000_8000`.
            SZ_32K,
            /// `0x0001_0000`.
            SZ_64K,
            /// `0x0002_0000`.
            SZ_128K,
            /// `0x0004_0000`.
            SZ_256K,
            /// `0x0008_0000`.
            SZ_512K,
            /// `0x0010_0000`.
            SZ_1M,
            /// `0x0020_0000`.
            SZ_2M,
            /// `0x0040_0000`.
            SZ_4M,
            /// `0x0080_0000`.
            SZ_8M,
            /// `0x0100_0000`.
            SZ_16M,
            /// `0x0200_0000`.
            SZ_32M,
            /// `0x0400_0000`.
            SZ_64M,
            /// `0x0800_0000`.
            SZ_128M,
            /// `0x1000_0000`.
            SZ_256M,
            /// `0x2000_0000`.
            SZ_512M,
            /// `0x4000_0000`.
            SZ_1G,
            /// `0x8000_0000`.
            SZ_2G,
        );
    };

    (@internal [$($type:ty),*] $($names_and_metas:tt)*) => {
        define_sizes!(@consts_and_trait $($names_and_metas)*);
        define_sizes!(@impls [$($type),*] $($names_and_metas)*);
    };

    (@consts_and_trait $($(#[$meta:meta])* $name:ident,)*) => {
        $(
            $(#[$meta])*
            pub const $name: usize = bindings::$name as usize;
        )*

        /// Size constants for device address spaces.
        ///
        /// Implemented for [`u32`], [`u64`], and [`usize`] so drivers can
        /// choose the width that matches their hardware. All `SZ_*` values fit
        /// in a [`u32`], so all implementations are lossless.
        ///
        /// # Examples
        ///
        /// ```
        /// use kernel::sizes::SizeConstants;
        ///
        /// let gpu_heap = 14 * u64::SZ_1M;
        /// let mmio_window = u32::SZ_16M;
        /// ```
        pub trait SizeConstants {
            $(
                $(#[$meta])*
                const $name: Self;
            )*
        }
    };

    (@impls [] $($(#[$meta:meta])* $name:ident,)*) => {};

    (@impls [$first:ty $(, $rest:ty)*] $($(#[$meta:meta])* $name:ident,)*) => {
        impl SizeConstants for $first {
            $(
                const $name: Self = {
                    assert!((self::$name as u128) <= (<$first>::MAX as u128));
                    self::$name as $first
                };
            )*
        }

        define_sizes!(@impls [$($rest),*] $($(#[$meta])* $name,)*);
    };
}

define_sizes!(u32, u64, usize);