use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
#[cfg(feature = "uefi")]
use core::prelude::rust_2021::derive;
use num_derive::FromPrimitive;
use std::fmt;
use zerocopy::byteorder::little_endian::{U16, U32};
use zerocopy::{FromBytes, KnownLayout};
use crate::chromium_ec::{CrosEc, EcResult};
use crate::smbios;
use crate::util::Platform;
use self::device::{FwMode, PdController, PdPort};
pub mod binary;
pub mod device;
#[cfg(feature = "hidapi")]
pub mod hid;
const FW1_METADATA_ROW: u32 = 0x1FE;
const FW1_METADATA_ROW_CCG8: u32 = 0x3FE;
const FW2_METADATA_ROW_CCG5: u32 = 0x1FF;
const FW2_METADATA_ROW_CCG6: u32 = 0x1FD;
const FW2_METADATA_ROW_CCG8: u32 = 0x3FF;
const METADATA_OFFSET: usize = 0xC0; const CCG8_METADATA_OFFSET: usize = 0x80;
const CCG3_METADATA_OFFSET: usize = 0x40;
const METADATA_MAGIC: u16 = u16::from_le_bytes([b'Y', b'C']); const CCG8_METADATA_MAGIC: u16 = u16::from_le_bytes([b'F', b'I']);
#[repr(C, packed)]
#[derive(FromBytes, KnownLayout, Debug, Copy, Clone)]
struct CyAcdMetadata {
_fw_checksum: u8,
_fw_entry: u32,
boot_last_row: U16,
_reserved1: [u8; 2],
fw_size: U32,
_reserved2: [u8; 3],
_active_boot_app: u8,
_boot_app_ver_status: u8,
_boot_app_version: u16,
_boot_app_id: u16,
metadata_valid: U16,
_fw_version: u32,
_boot_seq: u32,
}
#[repr(C, packed)]
#[derive(FromBytes, KnownLayout, Debug, Copy, Clone)]
struct CyAcd2Metadata {
fw_start: U32,
fw_size: U32,
_boot_app_id: u16,
_boot_last_row: u16,
_config_fw_start: u32,
_config_fw_size: u32,
_boot_seq: u32,
_reserved_1: [u32; 15],
metadata_version: U16,
metadata_valid: U16,
_fw_crc32: u32,
_reserved_2: [u32; 8],
_md_crc32: u32,
}
#[non_exhaustive]
#[derive(Debug, PartialEq, FromPrimitive, Clone, Copy)]
pub enum SiliconId {
Ccg3 = 0x1D00,
Ccg5 = 0x2100,
Ccg6Adl = 0x3000,
Ccg6 = 0x30A0,
Ccg8 = 0x3580,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct BaseVersion {
pub major: u8,
pub minor: u8,
pub patch: u8,
pub build_number: u16,
}
impl BaseVersion {
pub fn to_dec_string(&self) -> String {
format!(
"{}.{}.{}.{:0>3}",
self.major, self.minor, self.patch, self.build_number
)
}
}
impl fmt::Display for BaseVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:X}.{:X}.{:X}.{:03X}",
self.major, self.minor, self.patch, self.build_number
)
}
}
impl From<&[u8]> for BaseVersion {
fn from(data: &[u8]) -> Self {
Self {
build_number: u16::from_le_bytes([data[0], data[1]]),
patch: data[2],
major: (data[3] & 0xF0) >> 4,
minor: data[3] & 0x0F,
}
}
}
impl From<u32> for BaseVersion {
fn from(data: u32) -> Self {
Self::from(u32::to_le_bytes(data).as_slice())
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub enum Application {
Notebook,
Monitor,
AA,
Invalid,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct AppVersion {
pub application: Application,
pub major: u8,
pub minor: u8,
pub circuit: u8,
}
impl fmt::Display for AppVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:X}.{:X}.{:02X}", self.major, self.minor, self.circuit)
}
}
impl From<&[u8]> for AppVersion {
fn from(data: &[u8]) -> Self {
let application = match &[data[1], data[0]] {
b"nb" => Application::Notebook,
b"md" => Application::Monitor,
b"aa" => Application::AA,
_ => Application::Invalid,
};
Self {
application,
circuit: data[2],
major: (data[3] & 0xF0) >> 4,
minor: data[3] & 0x0F,
}
}
}
impl From<u32> for AppVersion {
fn from(data: u32) -> Self {
Self::from(u32::to_le_bytes(data).as_slice())
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct ControllerVersion {
pub base: BaseVersion,
pub app: AppVersion,
}
#[derive(Debug, PartialEq)]
pub struct ControllerFirmwares {
pub active_fw: FwMode,
pub bootloader: ControllerVersion,
pub backup_fw: ControllerVersion,
pub main_fw: ControllerVersion,
}
impl ControllerFirmwares {
pub fn active_fw(&self) -> ControllerVersion {
match self.active_fw {
FwMode::MainFw => self.main_fw,
FwMode::BackupFw => self.backup_fw,
FwMode::BootLoader => self.bootloader,
}
}
pub fn active_fw_ver(&self) -> String {
let active = self.active_fw();
if let Some(Platform::IntelGen11) = smbios::get_platform() {
active.base.to_dec_string()
} else {
active.app.to_string()
}
}
}
#[derive(Debug, PartialEq)]
pub enum PdVersions {
RightLeft((ControllerFirmwares, ControllerFirmwares)),
Single(ControllerFirmwares),
Many(Vec<ControllerFirmwares>),
}
#[derive(Debug)]
pub enum MainPdVersions {
RightLeft((ControllerVersion, ControllerVersion)),
Single(ControllerVersion),
Many(Vec<ControllerVersion>),
}
pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult<PdVersions> {
let pd01 = PdController::new(PdPort::Right01, ec.clone()).get_fw_versions();
let pd23 = PdController::new(PdPort::Left23, ec.clone()).get_fw_versions();
let pd_back = PdController::new(PdPort::Back, ec.clone()).get_fw_versions();
match (pd01, pd23, pd_back) {
(Err(_), Err(_), Ok(pd_back)) => Ok(PdVersions::Single(pd_back)),
(Ok(pd01), Ok(pd23), Err(_)) => Ok(PdVersions::RightLeft((pd01, pd23))),
(Ok(pd01), Ok(pd23), Ok(pd_back)) => Ok(PdVersions::Many(vec![pd01, pd23, pd_back])),
(Err(err), _, _) => Err(err),
(_, Err(err), _) => Err(err),
}
}
fn parse_metadata_ccg3(buffer: &[u8]) -> Option<(u32, u32)> {
let buffer = &buffer[CCG3_METADATA_OFFSET..];
let (metadata, _) = CyAcdMetadata::read_from_prefix(buffer).ok()?;
trace!("Metadata: {:X?}", metadata);
if metadata.metadata_valid.get() == METADATA_MAGIC {
Some((
1 + metadata.boot_last_row.get() as u32,
metadata.fw_size.get(),
))
} else {
None
}
}
fn parse_metadata_cyacd(buffer: &[u8]) -> Option<(u32, u32)> {
let buffer = &buffer[METADATA_OFFSET..];
let (metadata, _) = CyAcdMetadata::read_from_prefix(buffer).ok()?;
trace!("Metadata: {:X?}", metadata);
if metadata.metadata_valid.get() == METADATA_MAGIC {
Some((
1 + metadata.boot_last_row.get() as u32,
metadata.fw_size.get(),
))
} else {
None
}
}
fn parse_metadata_cyacd2(buffer: &[u8]) -> Option<(u32, u32)> {
let buffer = &buffer[CCG8_METADATA_OFFSET..];
let (metadata, _) = CyAcd2Metadata::read_from_prefix(buffer).ok()?;
trace!("Metadata: {:X?}", metadata);
if metadata.metadata_valid.get() == CCG8_METADATA_MAGIC {
if metadata.metadata_version.get() == 1 {
Some((metadata.fw_start.get(), metadata.fw_size.get()))
} else {
println!("Unknown CCG8 metadata version");
None
}
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn derive_ord() {
let v0_0_0 = AppVersion {
application: Application::Notebook,
major: 0,
minor: 0,
circuit: 0,
};
let v1_0_1 = AppVersion {
application: Application::Notebook,
major: 1,
minor: 0,
circuit: 1,
};
let v0_1_0 = AppVersion {
application: Application::Notebook,
major: 0,
minor: 1,
circuit: 0,
};
let v1_1_1 = AppVersion {
application: Application::Notebook,
major: 1,
minor: 1,
circuit: 1,
};
assert_eq!(v0_0_0, v0_0_0.clone());
assert!(v0_0_0 < v1_0_1);
assert!(v0_1_0 < v1_0_1);
assert!(v1_0_1 < v1_1_1);
assert!(v1_1_1 > v1_0_1);
}
}