xref: /aosp_15_r20/external/flashrom/util/flashrom_tester/src/utils.rs (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1 //
2 // Copyright 2019, Google Inc.
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //    * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //    * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //    * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 //
31 // Alternatively, this software may be distributed under the terms of the
32 // GNU General Public License ("GPL") version 2 as published by the Free
33 // Software Foundation.
34 //
35 
36 use std::io::prelude::*;
37 use std::process::Command;
38 
39 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
40 pub enum LayoutNames {
41     TopQuad,
42     TopHalf,
43     BottomHalf,
44     BottomQuad,
45 }
46 
47 impl LayoutNames {
48     // Return a section that does not overlap
get_non_overlapping_section(&self) -> LayoutNames49     pub fn get_non_overlapping_section(&self) -> LayoutNames {
50         match self {
51             LayoutNames::TopQuad => LayoutNames::BottomQuad,
52             LayoutNames::TopHalf => LayoutNames::BottomHalf,
53             LayoutNames::BottomHalf => LayoutNames::TopHalf,
54             LayoutNames::BottomQuad => LayoutNames::TopQuad,
55         }
56     }
57 }
58 
59 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
60 pub struct LayoutSizes {
61     half_sz: i64,
62     quad_sz: i64,
63     rom_top: i64,
64     bottom_half_top: i64,
65     bottom_quad_top: i64,
66     top_quad_bottom: i64,
67 }
68 
get_layout_sizes(rom_sz: i64) -> Result<LayoutSizes, String>69 pub fn get_layout_sizes(rom_sz: i64) -> Result<LayoutSizes, String> {
70     if rom_sz <= 0 {
71         return Err("invalid rom size provided".into());
72     }
73     if rom_sz & (rom_sz - 1) != 0 {
74         return Err("invalid rom size, not a power of 2".into());
75     }
76     Ok(LayoutSizes {
77         half_sz: rom_sz / 2,
78         quad_sz: rom_sz / 4,
79         rom_top: rom_sz - 1,
80         bottom_half_top: (rom_sz / 2) - 1,
81         bottom_quad_top: (rom_sz / 4) - 1,
82         top_quad_bottom: (rom_sz / 4) * 3,
83     })
84 }
85 
layout_section(ls: &LayoutSizes, ln: LayoutNames) -> (&'static str, i64, i64)86 pub fn layout_section(ls: &LayoutSizes, ln: LayoutNames) -> (&'static str, i64, i64) {
87     match ln {
88         LayoutNames::TopQuad => ("TOP_QUAD", ls.top_quad_bottom, ls.quad_sz),
89         LayoutNames::TopHalf => ("TOP_HALF", ls.half_sz, ls.half_sz),
90         LayoutNames::BottomHalf => ("BOTTOM_HALF", 0, ls.half_sz),
91         LayoutNames::BottomQuad => ("BOTTOM_QUAD", 0, ls.quad_sz),
92     }
93 }
94 
construct_layout_file<F: Write>(mut target: F, ls: &LayoutSizes) -> std::io::Result<()>95 pub fn construct_layout_file<F: Write>(mut target: F, ls: &LayoutSizes) -> std::io::Result<()> {
96     writeln!(target, "000000:{:x} BOTTOM_QUAD", ls.bottom_quad_top)?;
97     writeln!(target, "000000:{:x} BOTTOM_HALF", ls.bottom_half_top)?;
98     writeln!(target, "{:x}:{:x} TOP_HALF", ls.half_sz, ls.rom_top)?;
99     writeln!(target, "{:x}:{:x} TOP_QUAD", ls.top_quad_bottom, ls.rom_top)
100 }
101 
toggle_hw_wp(dis: bool) -> Result<(), String>102 pub fn toggle_hw_wp(dis: bool) -> Result<(), String> {
103     // The easist way to toggle the hardware write-protect is
104     // to {dis}connect the battery (and/or {open,close} the WP screw).
105     let s = if dis { "dis" } else { "" };
106     let screw_state = if dis { "open" } else { "close" };
107     // Print a failure message, but not on the first try.
108     let mut fail_msg = None;
109     while dis == get_hardware_wp()? {
110         if let Some(msg) = fail_msg {
111             eprintln!("{msg}");
112         }
113         fail_msg = Some(format!("Hardware write protect is still {}!", !dis));
114         // The following message is read by the tast test. Do not modify.
115         info!("Prompt for hardware WP {}able", s);
116         eprintln!(" > {}connect the battery (and/or {} the WP screw)", s, screw_state);
117         pause();
118     }
119     Ok(())
120 }
121 
ac_power_warning()122 pub fn ac_power_warning() {
123     info!("*****************************");
124     info!("AC power *must be* connected!");
125     info!("*****************************");
126     pause();
127 }
128 
pause()129 fn pause() {
130     // The following message is read by the tast test. Do not modify.
131     println!("Press enter to continue...");
132     // Rust stdout is always LineBuffered at time of writing.
133     // But this is not guaranteed, so flush anyway.
134     std::io::stdout().flush().unwrap();
135     // This reads one line, there is no guarantee the line came
136     // after the above prompt. But it is good enough.
137     if std::io::stdin().read_line(&mut String::new()).unwrap() == 0 {
138         panic!("stdin closed");
139     }
140 }
141 
get_hardware_wp() -> std::result::Result<bool, String>142 pub fn get_hardware_wp() -> std::result::Result<bool, String> {
143     let wp_s_val = collect_crosssystem(&["wpsw_cur"])?.parse::<u32>();
144     match wp_s_val {
145         Ok(v) => {
146             if v == 1 {
147                 Ok(true)
148             } else if v == 0 {
149                 Ok(false)
150             } else {
151                 Err("Unknown write protect value".into())
152             }
153         }
154         Err(_) => Err("Cannot parse write protect value".into()),
155     }
156 }
157 
collect_crosssystem(args: &[&str]) -> Result<String, String>158 pub fn collect_crosssystem(args: &[&str]) -> Result<String, String> {
159     let cmd = match Command::new("crossystem").args(args).output() {
160         Ok(x) => x,
161         Err(e) => return Err(format!("Failed to run crossystem: {}", e)),
162     };
163 
164     if !cmd.status.success() {
165         return Err(translate_command_error(&cmd).to_string());
166     };
167 
168     Ok(String::from_utf8_lossy(&cmd.stdout).into_owned())
169 }
170 
translate_command_error(output: &std::process::Output) -> std::io::Error171 pub fn translate_command_error(output: &std::process::Output) -> std::io::Error {
172     use std::io::{Error, ErrorKind};
173     // There is two cases on failure;
174     //  i. ) A bad exit code,
175     //  ii.) A SIG killed us.
176     match output.status.code() {
177         Some(code) => {
178             let e = format!(
179                 "{}\nExited with error code: {}",
180                 String::from_utf8_lossy(&output.stderr),
181                 code
182             );
183             Error::new(ErrorKind::Other, e)
184         }
185         None => Error::new(
186             ErrorKind::Other,
187             "Process terminated by a signal".to_string(),
188         ),
189     }
190 }
191 
192 #[cfg(test)]
193 mod tests {
194     use super::*;
195 
196     #[test]
construct_layout_file()197     fn construct_layout_file() {
198         use super::{construct_layout_file, get_layout_sizes};
199 
200         let mut buf = Vec::new();
201         construct_layout_file(
202             &mut buf,
203             &get_layout_sizes(0x10000).expect("64k is a valid chip size"),
204         )
205         .expect("no I/O errors expected");
206 
207         assert_eq!(
208             &buf[..],
209             &b"000000:3fff BOTTOM_QUAD\n\
210                000000:7fff BOTTOM_HALF\n\
211                8000:ffff TOP_HALF\n\
212                c000:ffff TOP_QUAD\n"[..]
213         );
214     }
215 
216     #[test]
get_layout_sizes()217     fn get_layout_sizes() {
218         use super::get_layout_sizes;
219 
220         assert_eq!(
221             get_layout_sizes(-128).err(),
222             Some("invalid rom size provided".into())
223         );
224 
225         assert_eq!(
226             get_layout_sizes(3 << 20).err(),
227             Some("invalid rom size, not a power of 2".into())
228         );
229 
230         assert_eq!(
231             get_layout_sizes(64 << 10).unwrap(),
232             LayoutSizes {
233                 half_sz: 0x8000,
234                 quad_sz: 0x4000,
235                 rom_top: 0xFFFF,
236                 bottom_half_top: 0x7FFF,
237                 bottom_quad_top: 0x3FFF,
238                 top_quad_bottom: 0xC000,
239             }
240         );
241     }
242 }
243