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