| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // 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, SIGINT}; |
| use sys_util::{error, pipe}; |
| 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 Chrome OS 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(&[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(&[SIGINT]); |
| DONE_FLAG.store(true, Ordering::Release); |
| watcher |
| .join() |
| .map_err(|_| dispatcher::Error::CommandReturnedError)?; |
| |
| Ok(()) |
| } |