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
//! Module for [`ApmTag`].
use crate::{TagHeader, TagType};
use multiboot2_common::{MaybeDynSized, Tag};
/// The Advanced Power Management (APM) tag.
#[derive(Debug)]
#[repr(C, align(8))]
pub struct ApmTag {
header: TagHeader,
version: u16,
cseg: u16,
offset: u32,
cset_16: u16,
dseg: u16,
flags: u16,
cseg_len: u16,
cseg_16_len: u16,
dseg_len: u16,
}
impl ApmTag {
/// Creates a new tag.
#[expect(clippy::too_many_arguments)]
#[must_use]
pub fn new(
version: u16,
cseg: u16,
offset: u32,
cset_16: u16,
dset: u16,
flags: u16,
cseg_len: u16,
cseg_16_len: u16,
dseg_len: u16,
) -> Self {
Self {
header: TagHeader::new(Self::ID, Self::BASE_SIZE as u32),
version,
cseg,
offset,
cset_16,
dseg: dset,
flags,
cseg_len,
cseg_16_len,
dseg_len,
}
}
/// The version number of the APM BIOS.
#[must_use]
pub const fn version(&self) -> u16 {
self.version
}
/// Contains the 16-bit code segment (CS) address for the APM entry point.
#[must_use]
pub const fn cseg(&self) -> u16 {
self.cseg
}
/// Represents the offset address within the code segment (`cseg`) for the
/// APM entry point.
#[must_use]
pub const fn offset(&self) -> u32 {
self.offset
}
/// Contains the 16-bit code segment (CS) address used for 16-bit protected
/// mode APM functions.
#[must_use]
pub const fn cset_16(&self) -> u16 {
self.cset_16
}
/// Holds the 16-bit data segment (DS) address used by the APM BIOS for
/// data operations.
#[must_use]
pub const fn dseg(&self) -> u16 {
self.dseg
}
/// Indicates the status and characteristics of the APM connection, such as
/// if APM is present and its capabilities.
#[must_use]
pub const fn flags(&self) -> u16 {
self.flags
}
/// Indicates the length, in bytes, of the 32-bit code segment (`cseg`).
#[must_use]
pub const fn cseg_len(&self) -> u16 {
self.cseg_len
}
/// Provides the length, in bytes, of the 16-bit code segment (`cseg_16`)
/// used for APM functions.
#[must_use]
pub const fn cseg_16_len(&self) -> u16 {
self.cseg_16_len
}
/// Indicates the length, in bytes, of the data segment (`dseg`) used by
/// the APM BIOS.
#[must_use]
pub const fn dseg_len(&self) -> u16 {
self.dseg_len
}
}
// SAFETY: The tag is repr(C) with the header as first field, any bit
// pattern is valid, and `BASE_SIZE`/`dst_len` match the ABI.
unsafe impl MaybeDynSized for ApmTag {
type Header = TagHeader;
// Spec size (28), excluding the trailing padding that `size_of::<Self>()`
// (32) would add. Payload: eight `u16` fields plus the `u32` offset.
const BASE_SIZE: usize = size_of::<TagHeader>() + size_of::<[u16; 8]>() + size_of::<u32>();
}
impl Tag for ApmTag {
type IDType = TagType;
const ID: TagType = TagType::Apm;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reported_size_matches_spec() {
// The Multiboot2 spec mandates size 28 (excluding padding), not the
// 32 bytes that `size_of::<ApmTag>()` would report.
let tag = ApmTag::new(1, 2, 3, 4, 5, 6, 7, 8, 9);
assert_eq!(tag.header.size, 28);
}
/// Representative test for all stack-constructed sized tags: the bytes
/// exposed for a tag built via its constructor must cover exactly the
/// reported tag size and exclude the implicit trailing padding of the
/// Rust type layout, which is uninitialized memory that must never be
/// read (verified by Miri).
#[test]
fn as_bytes_covers_exactly_the_reported_tag_size() {
let tag = ApmTag::new(1, 2, 3, 4, 5, 6, 7, 8, 9);
let bytes = tag.as_bytes();
assert_eq!(bytes.len(), 28);
assert!(bytes.len() < size_of::<ApmTag>());
// Reading every single byte must be defined behavior.
let byte_sum = bytes.iter().map(|&byte| byte as u64).sum::<u64>();
assert!(byte_sum > 0);
}
}