opentitanlib/util/
status.rs1use std::convert::TryFrom;
9use std::ffi::CString;
10use std::path::PathBuf;
11
12use anyhow::{Context, Result, bail};
13use bindgen::status::{ot_status_create_record_t, status_create, status_err, status_extract};
14use object::{Object, ObjectSection};
15use zerocopy::FromBytes;
16
17pub use bindgen::status::absl_status_t as RawStatusCode;
18pub use bindgen::status::status_t as RawStatus;
19
20#[derive(Debug, serde::Serialize, serde::Deserialize, strum::FromRepr, PartialEq, Eq)]
23#[repr(u32)]
24pub enum StatusCode {
25 Ok = bindgen::status::absl_status_code_kOk,
26 Cancelled = bindgen::status::absl_status_code_kCancelled,
27 Unknown = bindgen::status::absl_status_code_kUnknown,
28 InvalidArgument = bindgen::status::absl_status_code_kInvalidArgument,
29 DeadlineExceeded = bindgen::status::absl_status_code_kDeadlineExceeded,
30 NotFound = bindgen::status::absl_status_code_kNotFound,
31 AlreadyExists = bindgen::status::absl_status_code_kAlreadyExists,
32 PermissionDenied = bindgen::status::absl_status_code_kPermissionDenied,
33 ResourceExhausted = bindgen::status::absl_status_code_kResourceExhausted,
34 FailedPrecondition = bindgen::status::absl_status_code_kFailedPrecondition,
35 Aborted = bindgen::status::absl_status_code_kAborted,
36 OutOfRange = bindgen::status::absl_status_code_kOutOfRange,
37 Unimplemented = bindgen::status::absl_status_code_kUnimplemented,
38 Internal = bindgen::status::absl_status_code_kInternal,
39 Unavailable = bindgen::status::absl_status_code_kUnavailable,
40 DataLoss = bindgen::status::absl_status_code_kDataLoss,
41 Unauthenticated = bindgen::status::absl_status_code_kUnauthenticated,
42}
43
44fn status_create_safe(code: StatusCode, mod_id: u32, file: String, arg: i32) -> RawStatus {
46 let file = CString::new(file).expect("CString::new failed");
48 unsafe { status_create(code as u32, mod_id, file.as_ptr(), arg) }
51 }
53
54fn c_string_to_string(array: &[i8]) -> String {
57 let array = array
58 .iter()
59 .map(|c| *c as u8)
60 .take_while(|c| *c != 0u8)
61 .collect::<Vec<_>>();
62 String::from_utf8_lossy(&array).to_string()
63}
64
65#[derive(Debug, serde::Serialize, serde::Deserialize)]
66pub struct Status {
68 pub module_id: String,
70 pub arg: i32,
73 pub code: StatusCode,
75}
76
77impl Status {
79 pub fn from_raw_status(status: RawStatus) -> Result<Status> {
80 let mut code_str: *const std::os::raw::c_char = std::ptr::null();
82 let mut arg = 0i32;
83 let mut mod_id: [std::os::raw::c_char; 3] = [0; 3];
84 let mod_id_ptr = &mut mod_id as *mut i8;
85 let is_err_status = unsafe { status_extract(status, &mut code_str, &mut arg, mod_id_ptr) };
91 let code = match is_err_status {
92 false => StatusCode::Ok,
93 true => {
94 let raw_code = unsafe { status_err(status) };
96 StatusCode::from_repr(raw_code)
97 .with_context(|| format!("invalid status code value {raw_code}"))?
98 }
99 };
100 Ok(Status {
101 module_id: c_string_to_string(&mod_id),
102 arg,
103 code,
104 })
105 }
106
107 pub fn from_u32(status: u32) -> Result<Status> {
108 Self::from_raw_status(RawStatus {
109 value: status as i32,
110 })
111 }
112}
113
114#[cfg(test)]
115mod test {
116 use super::*;
117
118 #[test]
119 fn status_manip_test_good() {
120 const CODE: StatusCode = StatusCode::PermissionDenied;
123 const MOD_ID: &str = "abc";
124 const ARG: i32 = 42;
125 let mod_id_bytes = MOD_ID.as_bytes();
126 assert_eq!(mod_id_bytes.len(), 3);
127 let mod_id_val = ((mod_id_bytes[0].to_ascii_uppercase() as u32) << 16)
131 | ((mod_id_bytes[1].to_ascii_uppercase() as u32) << 21)
132 | ((mod_id_bytes[2].to_ascii_uppercase() as u32) << 26);
133 let raw_status = status_create_safe(CODE, mod_id_val, "".to_string(), ARG);
134
135 let decoded_status = Status::from_raw_status(raw_status);
137 assert!(decoded_status.is_ok());
138 let decoded_status = decoded_status.unwrap();
139 assert_eq!(decoded_status.module_id, MOD_ID.to_ascii_uppercase());
140 assert_eq!(decoded_status.code, CODE);
141 assert_eq!(decoded_status.arg, ARG);
142 }
143}
144
145#[derive(Debug, serde::Serialize, serde::Deserialize)]
146pub struct StatusCreateRecord {
148 pub module_id: Option<u32>,
151 pub filename: String,
153}
154
155impl StatusCreateRecord {
156 pub fn get_module_id(&self) -> Result<String> {
159 const DEFAULT_MODULE_ID: u32 = 0;
166
167 let mod_id = self.module_id.map_or(DEFAULT_MODULE_ID, |mod_id| mod_id);
168 let status = status_create_safe(StatusCode::Unknown, mod_id, self.filename.clone(), 0);
170 let status = Status::from_raw_status(status)?;
171 Ok(status.module_id)
172 }
173}
174
175const UNKNOWN_MODULE_ID: u32 = bindgen::status::ot_status_create_record_magic_OT_SCR_UNKNOWN_MOD_ID;
177
178impl TryFrom<ot_status_create_record_t> for StatusCreateRecord {
179 type Error = anyhow::Error;
180
181 fn try_from(record: ot_status_create_record_t) -> Result<StatusCreateRecord> {
182 let filename = c_string_to_string(&record.filename);
186
187 Ok(StatusCreateRecord {
188 module_id: Some(record.module_id).filter(|mod_id| mod_id != &UNKNOWN_MODULE_ID),
189 filename,
190 })
191 }
192}
193
194#[derive(Debug, serde::Serialize, serde::Deserialize)]
195pub struct StatusCreateRecords {
197 pub records: Vec<StatusCreateRecord>,
198}
199
200impl StatusCreateRecords {
201 pub fn find_module_id(&self, mod_id: &String) -> Vec<String> {
203 let iter = self
204 .records
205 .iter()
206 .filter(|rec| rec.get_module_id().is_ok_and(|id| id == *mod_id))
207 .map(|rec| rec.filename.clone());
208 std::collections::HashSet::<String>::from_iter(iter)
209 .into_iter()
210 .collect()
211 }
212}
213
214pub fn load_elf(elf_file: &PathBuf) -> Result<StatusCreateRecords> {
215 let file_data = std::fs::read(elf_file)
216 .with_context(|| format!("Could not read ELF file {}.", elf_file.display()))?;
217 let file = object::File::parse(&*file_data)
218 .with_context(|| format!("Could not parse ELF file {}", elf_file.display()))?;
219 let section = file
221 .section_by_name(".ot.status_create_record")
222 .ok_or_else(|| {
223 anyhow::anyhow!("ELF file should have a .ot.status_create_record section")
224 })?;
225 let status_create_records = section
226 .data()
227 .context("cannot read .ot.status_create_record section data")?;
228 const RECORD_SIZE: usize = std::mem::size_of::<ot_status_create_record_t>();
230 if status_create_records.len() % RECORD_SIZE != 0 {
231 bail!(
232 ".ot.status_create_record section size ({}) is not a multiple of the ot_status_create_record_t size ({})",
233 status_create_records.len(),
234 RECORD_SIZE
235 );
236 }
237 let records = status_create_records
240 .chunks(RECORD_SIZE)
241 .map(|chunk| ot_status_create_record_t::read_from_bytes(chunk).unwrap())
242 .map(StatusCreateRecord::try_from)
243 .collect::<Result<_>>()?;
244 Ok(StatusCreateRecords { records })
245}