-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverify_ro.rs
143 lines (124 loc) · 4.66 KB
/
verify_ro.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Provides the command "verify_ro" for crosh which checks the ro of Cr50 firmware.
use std::fs::metadata;
use std::io::{copy, stdout};
use std::os::unix::io::IntoRawFd;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{sleep, spawn};
use std::time::Duration;
use dbus::arg::OwnedFd;
use dbus::blocking::Connection;
use libc::c_int;
use libchromeos::pipe;
use log::error;
use nix::sys::signal::Signal;
use system_api::client::OrgChromiumDebugd;
use crate::dispatcher::{self, Arguments, Command, Dispatcher};
use crate::util::{clear_signal_handlers, set_signal_handlers, DEFAULT_DBUS_TIMEOUT};
const SCRIPT: &str = "/usr/share/cros/cr50-verify-ro.sh";
pub fn register(dispatcher: &mut Dispatcher) {
// Only register the verify_ro command if the script is present.
if !Path::new(SCRIPT).exists() {
return;
}
dispatcher.register_command(
Command::new(
"verify_ro".to_string(),
"".to_string(),
"Verify AP and EC RO firmware on a ChromeOS device connected over SuzyQ
cable, if supported by the device."
.to_string(),
)
.set_command_callback(Some(execute_verify_ro)),
);
}
fn stop_verify_ro(handle: &str) -> Result<(), dispatcher::Error> {
let connection = Connection::new_system().map_err(|err| {
error!("ERROR: Failed to get D-Bus connection: {}", err);
dispatcher::Error::CommandReturnedError
})?;
let conn_path = connection.with_proxy(
"org.chromium.debugd",
"/org/chromium/debugd",
DEFAULT_DBUS_TIMEOUT,
);
conn_path
.update_and_verify_fwon_usb_stop(handle)
.map_err(|err| {
println!("ERROR: Got unexpected result: {}", err);
dispatcher::Error::CommandReturnedError
})?;
Ok(())
}
// Set to true when SIGINT is received and triggers sending a stop command over D-Bus.
static STOP_FLAG: AtomicBool = AtomicBool::new(false);
// Set to true when the original D-Bus command closes the pipe signalling completion.
static DONE_FLAG: AtomicBool = AtomicBool::new(false);
// Handle Ctrl-c/SIGINT by sending a stop over D-Bus.
extern "C" fn sigint_handler(_: c_int) {
STOP_FLAG.store(true, Ordering::Release);
}
fn execute_verify_ro(_cmd: &Command, args: &Arguments) -> Result<(), dispatcher::Error> {
if args.get_tokens().len() != 1 {
eprintln!("too many arguments");
return Err(dispatcher::Error::CommandReturnedError);
}
const CR50_IMAGE: &str = "/opt/google/cr50/firmware/cr50.bin.prod";
const RO_DB: &str = "/opt/google/cr50/ro_db";
if match metadata(CR50_IMAGE) {
Ok(data) => !data.is_file(),
_ => true,
} && match metadata(RO_DB) {
Ok(data) => !data.is_dir(),
_ => true,
} {
eprintln!("This device can not be used for RO verification");
return Err(dispatcher::Error::CommandReturnedError);
}
let connection = Connection::new_system().map_err(|err| {
error!("ERROR: Failed to get D-Bus connection: {}", err);
dispatcher::Error::CommandReturnedError
})?;
let conn_path = connection.with_proxy(
"org.chromium.debugd",
"/org/chromium/debugd",
DEFAULT_DBUS_TIMEOUT,
);
// Safe because sigint_handler is async-signal safe.
unsafe { set_signal_handlers(&[Signal::SIGINT], sigint_handler) }
// Pass a pipe through D-Bus to collect the response.
let (mut read_pipe, write_pipe) = pipe(true).unwrap();
let handle = conn_path
.update_and_verify_fwon_usb_start(
// Safe because write_pipe isn't copied elsewhere.
unsafe { OwnedFd::new(write_pipe.into_raw_fd()) },
CR50_IMAGE,
RO_DB,
)
.map_err(|err| {
println!("ERROR: Got unexpected result: {}", err);
dispatcher::Error::CommandReturnedError
})?;
// Start a thread to send a stop on SIGINT, or stops when DONE_FLAG is set.
let watcher = spawn(move || loop {
if STOP_FLAG.load(Ordering::Acquire) {
stop_verify_ro(&handle).unwrap_or(());
break;
}
if DONE_FLAG.load(Ordering::Acquire) {
break;
}
sleep(Duration::from_millis(50));
});
// Print the response.
copy(&mut read_pipe, &mut stdout()).map_err(|_| dispatcher::Error::CommandReturnedError)?;
clear_signal_handlers(&[Signal::SIGINT]);
DONE_FLAG.store(true, Ordering::Release);
watcher
.join()
.map_err(|_| dispatcher::Error::CommandReturnedError)?;
Ok(())
}