xref: /aosp_15_r20/external/crosvm/base/src/sys/linux/acpi_event.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::str;
6 
7 use thiserror::Error;
8 use zerocopy::FromBytes;
9 use zerocopy::FromZeroes;
10 
11 use super::netlink::*;
12 
13 const ACPI_EVENT_SIZE: usize = std::mem::size_of::<AcpiGenlEvent>();
14 const GENL_HDRLEN: usize = std::mem::size_of::<GenlMsgHdr>();
15 const NLA_HDRLEN: usize = std::mem::size_of::<NlAttr>();
16 
17 #[derive(Error, Debug)]
18 pub enum AcpiEventError {
19     #[error("GenmsghdrCmd or NlAttrType inappropriate for acpi event")]
20     TypeAttrMissmatch,
21     #[error("Something goes wrong: msg_len {0} is not correct")]
22     InvalidMsgLen(usize),
23 }
24 type Result<T> = std::result::Result<T, AcpiEventError>;
25 
26 /// attributes of AcpiGenlFamily
27 #[allow(dead_code)]
28 enum NlAttrType {
29     AcpiGenlAttrUnspec,
30     AcpiGenlAttrEvent, // acpi_event (needed by user space)
31     AcpiGenlAttrMax,
32 }
33 
34 /// commands supported by the AcpiGenlFamily
35 #[allow(dead_code)]
36 enum GenmsghdrCmd {
37     AcpiGenlCmdUnspec,
38     AcpiGenlCmdEvent, // kernel->user notifications for acpi_events
39     AcpiGenlCmdMax,
40 }
41 
42 #[repr(C)]
43 #[derive(Copy, Clone, FromZeroes, FromBytes)]
44 struct AcpiGenlEvent {
45     device_class: [::std::os::raw::c_char; 20usize],
46     bus_id: [::std::os::raw::c_char; 15usize],
47     _type: u32,
48     data: u32,
49 }
50 
51 pub struct AcpiNotifyEvent {
52     pub device_class: String,
53     pub bus_id: String,
54     pub _type: u32,
55     pub data: u32,
56 }
57 
58 impl AcpiNotifyEvent {
59     /// Create acpi event by decapsulating it from NetlinkMessage.
new(netlink_message: NetlinkMessage) -> Result<Self>60     pub fn new(netlink_message: NetlinkMessage) -> Result<Self> {
61         let msg_len = netlink_message.data.len();
62         if msg_len != GENL_HDRLEN + NLA_HDRLEN + ACPI_EVENT_SIZE {
63             return Err(AcpiEventError::InvalidMsgLen(msg_len));
64         }
65 
66         let genl_hdr = GenlMsgHdr::read_from(&netlink_message.data[..GENL_HDRLEN])
67             .expect("unable to get GenlMsgHdr from slice");
68 
69         let nlattr_end = GENL_HDRLEN + NLA_HDRLEN;
70         let nl_attr = NlAttr::read_from(&netlink_message.data[GENL_HDRLEN..nlattr_end])
71             .expect("unable to get NlAttr from slice");
72 
73         // Sanity check that the headers have correct for acpi event `cmd` and `_type`
74         if genl_hdr.cmd != GenmsghdrCmd::AcpiGenlCmdEvent as u8
75             || nl_attr._type != NlAttrType::AcpiGenlAttrEvent as u16
76         {
77             return Err(AcpiEventError::TypeAttrMissmatch);
78         }
79 
80         let acpi_event = AcpiGenlEvent::read_from(&netlink_message.data[nlattr_end..msg_len])
81             .expect("unable to get AcpiGenlEvent from slice");
82 
83         // The raw::c_char is either i8 or u8 which is known portability issue:
84         // https://github.com/rust-lang/rust/issues/79089,
85         // before using device_class further cast it to u8.
86         let device_class: &[u8; 20usize] =
87             // SAFETY: trivially safe
88             unsafe { ::std::mem::transmute(&acpi_event.device_class) };
89         // SAFETY: trivially safe
90         let bus_id: &[u8; 15usize] = unsafe { ::std::mem::transmute(&acpi_event.bus_id) };
91 
92         Ok(AcpiNotifyEvent {
93             device_class: strip_padding(device_class).to_owned(),
94             bus_id: strip_padding(bus_id).to_owned(),
95             _type: acpi_event._type,
96             data: acpi_event.data,
97         })
98     }
99 }
100 
101 // Like `CStr::from_bytes_with_nul` but strips any bytes starting from first '\0'-byte and
102 // returns &str. Panics if `b` doesn't contain any '\0' bytes.
strip_padding(b: &[u8]) -> &str103 fn strip_padding(b: &[u8]) -> &str {
104     // It would be nice if we could use memchr here but that's locked behind an unstable gate.
105     let pos = b
106         .iter()
107         .position(|&c| c == 0)
108         .expect("`b` doesn't contain any nul bytes");
109 
110     str::from_utf8(&b[..pos]).unwrap()
111 }
112