blob: 13ef3ba3208162450902f1d160812173b3577c1f [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fidl::Error as FidlError;
use fidl_fuchsia_mem::Buffer;
#[cfg(not(test))]
use fidl_fuchsia_paver::PaverMarker;
use fidl_fuchsia_paver::{SysconfigMarker, SysconfigProxy};
#[cfg(not(test))]
use fuchsia_component::client::connect_to_service;
use fuchsia_zircon::Status;
use thiserror::Error;
pub mod channel;
#[derive(Debug, Error)]
pub enum SysconfigError {
#[error("Out of range: {}", _0)]
OutOfRange(usize),
#[error("Sysconfig Fidl Error: {:?}", _0)]
SysconfigFidl(FidlError),
#[error("Zircon Error: {:?}", _0)]
Zircon(Status),
#[error("Service Connection")]
ServiceConnection,
#[error("Sysconfig Proxy Creation")]
SysconfigProxy,
}
impl From<FidlError> for SysconfigError {
fn from(e: FidlError) -> Self {
SysconfigError::SysconfigFidl(e)
}
}
impl From<Status> for SysconfigError {
fn from(e: Status) -> Self {
SysconfigError::Zircon(e)
}
}
#[cfg(not(test))]
pub fn get_sysconfig_proxy() -> Result<SysconfigProxy, SysconfigError> {
let paver =
connect_to_service::<PaverMarker>().map_err(|_| SysconfigError::ServiceConnection)?;
let (sysconfig, server_end) = fidl::endpoints::create_proxy::<SysconfigMarker>()
.map_err(|_| SysconfigError::SysconfigProxy)?;
let () = paver.find_sysconfig(server_end)?;
Ok(sysconfig)
}
#[cfg(test)]
use sysconfig_mock::get_sysconfig_proxy;
pub async fn read_sysconfig_partition() -> Result<Vec<u8>, SysconfigError> {
let sysconfig = get_sysconfig_proxy()?;
let read_result = sysconfig.read().await?;
let buffer = read_result.map_err(|status| Status::from_raw(status))?;
let get_size_result = sysconfig.get_partition_size().await?;
let ptn_size = get_size_result.map_err(|status| Status::from_raw(status))?;
let mut ret = vec![0u8; ptn_size as usize];
buffer.vmo.read(ret.as_mut_slice(), 0)?;
Ok(ret)
}
pub async fn write_sysconfig_partition(data: &[u8]) -> Result<(), SysconfigError> {
let sysconfig = get_sysconfig_proxy()?;
let get_size_result = sysconfig.get_partition_size().await?;
let ptn_size = get_size_result.map_err(|status| Status::from_raw(status))?;
if data.len() > ptn_size as usize {
return Err(SysconfigError::OutOfRange(data.len()));
}
let mut buf =
Buffer { vmo: fuchsia_zircon::Vmo::create(ptn_size as u64)?, size: ptn_size as u64 };
buf.vmo.write(data, 0)?;
let write_result = sysconfig.write(&mut buf).await?;
Status::ok(write_result)?;
let flush_result = sysconfig.flush().await?;
Status::ok(flush_result)?;
Ok(())
}
#[cfg(test)]
mod sysconfig_mock {
use super::*;
use fasync::futures::TryStreamExt;
use fidl_fuchsia_paver::SysconfigRequest;
use fuchsia_async as fasync;
use std::cell::RefCell;
pub const SYSCONFIG_PARTITION_SIZE: usize = 4096;
thread_local!(pub static DATA: RefCell<Vec<u8>> = RefCell::new(vec![0; SYSCONFIG_PARTITION_SIZE]));
pub fn get_data() -> Vec<u8> {
DATA.with(|data| data.borrow().clone())
}
pub fn set_data(new_data: Vec<u8>) {
DATA.with(|data| *data.borrow_mut() = new_data);
}
pub fn spawn_sysconfig_service() -> SysconfigProxy {
let (sysconfig_proxy, mut sysconfig_stream) =
fidl::endpoints::create_proxy_and_stream::<SysconfigMarker>().unwrap();
fasync::Task::spawn(async move {
while let Some(req) = sysconfig_stream.try_next().await.unwrap() {
#[allow(unreachable_patterns)]
match req {
SysconfigRequest::Read { responder } => {
let buf = Buffer {
vmo: fuchsia_zircon::Vmo::create(SYSCONFIG_PARTITION_SIZE as u64)
.unwrap(),
size: SYSCONFIG_PARTITION_SIZE as u64,
};
buf.vmo.write(get_data().as_mut_slice(), 0).unwrap();
responder.send(&mut Ok(buf)).expect("sysconfig response to send");
}
SysconfigRequest::Write { payload, responder } => {
assert_eq!(payload.size as usize, SYSCONFIG_PARTITION_SIZE);
let mut temp: Vec<u8> = vec![0; SYSCONFIG_PARTITION_SIZE];
payload.vmo.read(temp.as_mut_slice(), 0).unwrap();
set_data(temp);
responder.send(Status::OK.into_raw()).expect("sysconfig response to send");
}
SysconfigRequest::GetPartitionSize { responder } => {
responder
.send(&mut Ok(SYSCONFIG_PARTITION_SIZE as u64))
.expect("sysconfig response to send");
}
SysconfigRequest::Flush { responder } => {
responder.send(Status::OK.into_raw()).expect("sysconfig response to send");
}
_ => {}
}
}
})
.detach();
sysconfig_proxy
}
pub fn get_sysconfig_proxy() -> Result<SysconfigProxy, SysconfigError> {
Ok(spawn_sysconfig_service())
}
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_async as fasync;
use matches::assert_matches;
use sysconfig_mock::*;
#[fasync::run_singlethreaded(test)]
async fn test_read_sysconfig_partition() {
let expected: Vec<u8> = vec![0x4a; SYSCONFIG_PARTITION_SIZE];
set_data(expected.clone());
assert_eq!(read_sysconfig_partition().await.unwrap(), expected)
}
#[fasync::run_singlethreaded(test)]
async fn test_write_sysconfig_partition_out_of_range() {
const LEN: usize = SYSCONFIG_PARTITION_SIZE * 2;
let data: [u8; LEN] = [0; LEN];
assert_matches!(
write_sysconfig_partition(&data).await,
Err(SysconfigError::OutOfRange(LEN))
)
}
#[fasync::run_singlethreaded(test)]
async fn test_write_sysconfig_partition() {
let mut expected: Vec<u8> = vec![0x4a; SYSCONFIG_PARTITION_SIZE];
write_sysconfig_partition(&expected.as_mut_slice()).await.unwrap();
assert_eq!(expected, get_data().to_vec())
}
}