1use anyhow::{ensure, Context, Error};
6use serde_derive::Deserialize;
7use std::fs::File;
8use std::io::Read as _;
9use std::path::Path;
10use zx_types::{
11 zx_cpu_set_t, zx_processor_power_domain_t, zx_processor_power_level_t,
12 zx_processor_power_level_transition_t, ZX_CPU_SET_BITS_PER_WORD, ZX_CPU_SET_MAX_CPUS,
13 ZX_MAX_NAME_LEN, ZX_PROCESSOR_POWER_CONTROL_ARM_PSCI, ZX_PROCESSOR_POWER_CONTROL_ARM_WFI,
14 ZX_PROCESSOR_POWER_CONTROL_CPU_DRIVER, ZX_PROCESSOR_POWER_CONTROL_RISCV_SBI,
15 ZX_PROCESSOR_POWER_CONTROL_RISCV_WFI, ZX_PROCESSOR_POWER_LEVEL_OPTIONS_DOMAIN_INDEPENDENT,
16};
17
18#[derive(Deserialize, Debug, Clone, PartialEq)]
83pub struct PowerLevelDomainJson {
84 pub power_domain: PowerDomain,
86
87 pub power_levels: Vec<PowerLevel>,
89
90 pub power_level_transitions: Vec<PowerLevelTransition>,
95}
96
97#[derive(Deserialize, Debug, Clone, PartialEq)]
98pub struct PowerDomain {
99 pub cpu_set: Vec<u16>,
101 pub domain_id: u32,
103}
104
105#[derive(Deserialize, Debug, Clone, PartialEq)]
106pub struct PowerLevel {
107 pub option: Option<PowerLevelOption>,
108 pub processing_rate: u64,
109 pub power_coefficient_nw: u64,
110 pub control_interface: ControlInterface,
111 pub control_argument: u64,
112 pub diagnostic_name: String,
113}
114
115#[derive(Deserialize, Debug, Clone, PartialEq)]
116pub struct PowerLevelTransition {
117 pub from: u8,
118 pub to: u8,
119 pub duration_ns: i64,
120 pub energy_nj: u64,
121}
122
123#[derive(Deserialize, Debug, Clone, PartialEq)]
124pub enum ControlInterface {
125 CpuDriver,
126 ArmPsci,
127 ArmWfi,
128 RiscvSbi,
129 RiscvWfi,
130}
131
132#[derive(Deserialize, Debug, Clone, PartialEq)]
133pub enum PowerLevelOption {
134 DomainIndependent,
135}
136
137impl PowerLevelDomainJson {
138 pub fn validate(&self) -> Result<(), Error> {
139 let power_level_num = self.power_levels.len();
140 for power_level_transition in self.power_level_transitions.iter() {
141 let from_index = power_level_transition.from as usize;
142 let to_index = power_level_transition.to as usize;
143 ensure!(
144 from_index != to_index
145 && from_index < power_level_num
146 && to_index < power_level_num,
147 "Power level transition need to be between two different valid indexes",
148 );
149 let from_processing_rate = self.power_levels[from_index].processing_rate;
150 let to_processing_rate = self.power_levels[to_index].processing_rate;
151 ensure!(
152 from_processing_rate != 0 || to_processing_rate != 0,
153 "Transitions directly from one idle power level to another idle power level are invalid",
154 );
155 }
156 Ok(())
157 }
158}
159
160#[derive(Debug, Clone, PartialEq)]
162pub struct EnergyModel(pub Vec<PowerLevelDomain>);
163
164#[derive(Debug, Clone, PartialEq)]
166pub struct PowerLevelDomain {
167 pub power_domain: zx_processor_power_domain_t,
168 pub power_levels: Vec<zx_processor_power_level_t>,
169 pub power_level_transitions: Vec<zx_processor_power_level_transition_t>,
170}
171
172impl EnergyModel {
173 pub fn new(json_file_path: &Path) -> Result<EnergyModel, Error> {
174 let mut buffer = String::new();
175 File::open(&json_file_path)?.read_to_string(&mut buffer)?;
176
177 let power_level_domains = serde_json5::from_str::<Vec<PowerLevelDomainJson>>(&buffer)?;
178
179 for power_level_domain in power_level_domains.iter() {
181 power_level_domain.validate().context(format!(
182 "Validation failed for power_domain {}",
183 power_level_domain.power_domain.domain_id
184 ))?;
185 }
186
187 Ok(Self(
188 power_level_domains
189 .into_iter()
190 .map(|s| s.try_into())
191 .collect::<Result<Vec<PowerLevelDomain>, _>>()?,
192 ))
193 }
194}
195
196impl TryFrom<PowerLevelDomainJson> for PowerLevelDomain {
197 type Error = anyhow::Error;
198 fn try_from(e: PowerLevelDomainJson) -> Result<Self, Error> {
199 Ok(Self {
200 power_domain: e.power_domain.try_into()?,
201 power_levels: e
202 .power_levels
203 .into_iter()
204 .map(|s| s.try_into())
205 .collect::<Result<Vec<zx_processor_power_level_t>, _>>()?,
206 power_level_transitions: e
207 .power_level_transitions
208 .into_iter()
209 .map(|s| s.into())
210 .collect(),
211 })
212 }
213}
214
215impl TryFrom<PowerDomain> for zx_processor_power_domain_t {
216 type Error = anyhow::Error;
217 fn try_from(p: PowerDomain) -> Result<Self, Error> {
218 let mut ret = Self::default();
219 ret.cpus = get_cpu_mask(p.cpu_set)?;
220 ret.domain_id = p.domain_id;
221 Ok(ret)
222 }
223}
224
225impl From<PowerLevelTransition> for zx_processor_power_level_transition_t {
226 fn from(p: PowerLevelTransition) -> Self {
227 let mut ret = Self::default();
228 ret.from = p.from;
229 ret.to = p.to;
230 ret.latency = p.duration_ns;
231 ret.energy = p.energy_nj;
232 ret
233 }
234}
235
236impl TryFrom<PowerLevel> for zx_processor_power_level_t {
237 type Error = anyhow::Error;
238 fn try_from(p: PowerLevel) -> Result<Self, Error> {
239 let control_interface = match p.control_interface {
240 ControlInterface::CpuDriver => ZX_PROCESSOR_POWER_CONTROL_CPU_DRIVER,
241 ControlInterface::ArmPsci => ZX_PROCESSOR_POWER_CONTROL_ARM_PSCI,
242 ControlInterface::ArmWfi => ZX_PROCESSOR_POWER_CONTROL_ARM_WFI,
243 ControlInterface::RiscvSbi => ZX_PROCESSOR_POWER_CONTROL_RISCV_SBI,
244 ControlInterface::RiscvWfi => ZX_PROCESSOR_POWER_CONTROL_RISCV_WFI,
245 };
246
247 let options = match p.option {
248 Some(PowerLevelOption::DomainIndependent) => {
249 ZX_PROCESSOR_POWER_LEVEL_OPTIONS_DOMAIN_INDEPENDENT
250 }
251 _ => 0,
252 };
253
254 let bytes = p.diagnostic_name.as_bytes();
255 ensure!(
256 bytes.len() <= ZX_MAX_NAME_LEN,
257 "diagnostic_name {:?} must be shorter than {:?} bytes",
258 p.diagnostic_name,
259 ZX_MAX_NAME_LEN,
260 );
261 let mut diagnostic_name = [0u8; ZX_MAX_NAME_LEN];
262 diagnostic_name[..bytes.len()].copy_from_slice(bytes);
263
264 let mut ret = Self::default();
265 ret.options = options;
266 ret.processing_rate = p.processing_rate;
267 ret.power_coefficient_nw = p.power_coefficient_nw;
268 ret.control_interface = control_interface;
269 ret.control_argument = p.control_argument;
270 ret.diagnostic_name = diagnostic_name;
271 Ok(ret)
272 }
273}
274
275fn get_cpu_mask(cpu_set: Vec<u16>) -> Result<zx_cpu_set_t, anyhow::Error> {
285 let mut cpu_mask = [0 as u64; ZX_CPU_SET_MAX_CPUS / ZX_CPU_SET_BITS_PER_WORD];
286 for cpu in cpu_set {
287 let cpu_index = cpu as usize;
288 ensure!(
289 cpu_index < ZX_CPU_SET_MAX_CPUS,
290 "CPU index must be smaller than {:?}",
291 ZX_CPU_SET_MAX_CPUS,
292 );
293
294 let i = cpu_index / ZX_CPU_SET_BITS_PER_WORD;
295 cpu_mask[i] = cpu_mask[i] | (1 << (cpu_index % ZX_CPU_SET_BITS_PER_WORD));
296 }
297 Ok(zx_cpu_set_t { mask: cpu_mask })
298}
299
300#[cfg(test)]
301mod tests {
302 use crate::*;
303 use assert_matches::assert_matches;
304
305 #[test]
306 fn test_convert_cpu_mask() {
307 assert_eq!(
308 get_cpu_mask(vec![0u16, 1u16, 2u16, 3u16]).unwrap().mask,
309 [15u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64]
310 );
311
312 fn is_nth_set(
313 cpu_mask: [u64; ZX_CPU_SET_MAX_CPUS / ZX_CPU_SET_BITS_PER_WORD],
314 n: usize,
315 ) -> bool {
316 cpu_mask[n / ZX_CPU_SET_BITS_PER_WORD] & (1 << (n % ZX_CPU_SET_BITS_PER_WORD)) != 0
317 }
318
319 let mut cpu_set = vec![15u16];
320 let mut cpu_mask = get_cpu_mask(cpu_set).unwrap().mask;
321 assert!(is_nth_set(cpu_mask, 15));
322
323 cpu_set = vec![32u16];
324 cpu_mask = get_cpu_mask(cpu_set).unwrap().mask;
325 assert!(is_nth_set(cpu_mask, 32));
326
327 cpu_set = vec![ZX_CPU_SET_MAX_CPUS as u16];
329 assert_matches!(get_cpu_mask(cpu_set), Err(_));
330 }
331
332 #[test]
333 fn test_processor_power_level() {
334 let power_level = PowerLevel {
335 option: Some(PowerLevelOption::DomainIndependent),
336 processing_rate: 0,
337 power_coefficient_nw: 0,
338 control_interface: ControlInterface::ArmPsci,
339 control_argument: 0,
340 diagnostic_name:
341 "This is a very very very long diagnositc name which exceeds 32 bytes.".to_string(),
342 };
343 assert_matches!(zx_processor_power_level_t::try_from(power_level), Err(_));
344
345 let mut zx_power_level = zx_processor_power_level_t::default();
346 zx_power_level.control_interface = ZX_PROCESSOR_POWER_CONTROL_CPU_DRIVER;
347 let power_level = PowerLevel {
348 option: None,
349 processing_rate: 0,
350 power_coefficient_nw: 0,
351 control_interface: ControlInterface::CpuDriver,
352 control_argument: 0,
353 diagnostic_name: "".to_string(),
354 };
355
356 assert!(zx_processor_power_level_t::try_from(power_level).unwrap() == zx_power_level);
357 }
358}