From 332f0d25e2d3411c77500f0f5847e36b106be508 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Dec 2024 12:06:31 -0700 Subject: [PATCH 1/2] Disable CSME by CMOS option on meer9 before flashing Signed-off-by: Tim Crawford --- src/app/bios.rs | 38 ++++++++++++++++++++----- src/app/cmos.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/src/app/bios.rs b/src/app/bios.rs index 4d4e538..179300f 100644 --- a/src/app/bios.rs +++ b/src/app/bios.rs @@ -217,9 +217,36 @@ impl Component for BiosComponent { fn validate(&self) -> Result { let data = load(self.path())?; + + if self.system_version.as_str() == "meer9" { + // HACK: + // CSME must be disabled or in read-only mode to write + // CSME region of SPI flash. PCH reset does not trigger + // CSME reset, so ME_OVERRIDE will not be in effect on + // cold reset. HECI reset can't be requested after End + // Of Post (before payload runs), so disable CSME as a + // workaround. + let mut cmos_options = cmos::CmosOptionTable::new(); + // XXX: Probably better to check for HECI device. + if cmos_options.me_state() { + println!("\nDisabling CSME for writing SPI flash"); + unsafe { cmos_options.set_me_state(false); } + + println!("System will reboot in 5 seconds"); + let _ = (std::system_table().BootServices.Stall)(5_000_000); + + (std::system_table().RuntimeServices.ResetSystem)( + ResetType::Cold, + Status(0), + 0, + ptr::null(), + ); + } + } + if let Some((mut spi, _hsfsts_ctl)) = self.spi() { // if hsfsts_ctl.contains(HsfStsCtl::FDOPSS) { - // println!("SPI currently locked, attempting to unlock"); + // println!("\nSPI currently locked, attempting to unlock"); // Self::spi_unlock(); // } @@ -484,12 +511,9 @@ impl Component for BiosComponent { println!(); } - // Invalidate the 2-byte CMOS checksum to force writing the option defaults. - let mut cmos = cmos::Cmos::default(); - let old_hi = cmos.read(123); - let old_lo = cmos.read(124); - cmos.write(123, !old_hi); - cmos.write(124, !old_lo); + // Have coreboot reset the option table to the defaults. + let mut cmos_options = cmos::CmosOptionTable::new(); + unsafe { cmos_options.invalidate_checksum(); } } else { find(FIRMWARENSH)?; diff --git a/src/app/cmos.rs b/src/app/cmos.rs index ba2ce25..80270bd 100644 --- a/src/app/cmos.rs +++ b/src/app/cmos.rs @@ -8,6 +8,8 @@ pub struct Cmos { } impl Cmos { + pub const PORT_BANK0: u16 = 0x70; + pub fn new(port: u16) -> Self { Self { port: Pio::::new(port), @@ -28,6 +30,78 @@ impl Cmos { impl Default for Cmos { fn default() -> Self { - Self::new(0x70) + Self::new(Self::PORT_BANK0) + } +} + +// HACK: All boards use the same option table layout, so hard-code the logic +// so we can get meer9 working. + +pub struct CmosOptionTable { + cmos: Cmos, +} + +impl CmosOptionTable { + /// Offset into CMOS RAM of the table `check_sum`: Bit 984 + const CHECKSUM_OFFSET: u8 = (984 / 8) as u8; + /// Offset into CMOS RAM of the option `me_state`: Bit 416 + const ME_STATE_OFFSET: u8 = (416 / 8) as u8; + + pub fn new() -> Self { + Self { + cmos: Cmos::default(), + } + } + + /// Read the checksum from the CMOS option table. + pub fn checksum(&mut self) -> u16 { + let hi = u16::from(self.cmos.read(Self::CHECKSUM_OFFSET)); + let lo = u16::from(self.cmos.read(Self::CHECKSUM_OFFSET + 1)); + + hi << 8 | lo + } + + /// Write the checksum to the CMOS option table. + pub unsafe fn set_checksum(&mut self, cksum: u16) { + let hi = (cksum >> 8) as u8; + let lo = cksum as u8; + + self.cmos.write(Self::CHECKSUM_OFFSET, hi); + self.cmos.write(Self::CHECKSUM_OFFSET + 1, lo); + } + + // Get CSME state in CMOS option table. + pub fn me_state(&mut self) -> bool { + let state = self.cmos.read(Self::ME_STATE_OFFSET); + + // me_state + // 0: Enable + // 1: Disable + state & 0x01 == 0x00 + } + + /// Set CSME state via CMOS option table. + pub unsafe fn set_me_state(&mut self, state: bool) { + let old_state = self.cmos.read(Self::ME_STATE_OFFSET); + let old_cksum = self.checksum(); + + // me_state + // 0: Enable + // 1: Disable + let (new_state, new_cksum) = if state { + (old_state & 0xFE, old_cksum - 1) + } else { + (old_state | 0x01, old_cksum + 1) + }; + + self.cmos.write(Self::ME_STATE_OFFSET, new_state); + self.set_checksum(new_cksum); + } + + /// Invalidate the 2-byte CMOS checksum to have coreboot erase the option + /// table and write out the defaults. + pub unsafe fn invalidate_checksum(&mut self) { + let cksum = self.checksum(); + self.set_checksum(!cksum); } } From 5fb19de1ef235a7b9355aa90076d15b86967ea15 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Dec 2024 14:43:55 -0700 Subject: [PATCH 2/2] Defer ME disable until update is accepted Move disabling CSME from BIOS validation to main app update logic. This allows users to accept updating before disabling, or cancel without disabling. Signed-off-by: Tim Crawford --- src/app/bios.rs | 26 -------------------------- src/app/mod.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/app/bios.rs b/src/app/bios.rs index 179300f..7c39496 100644 --- a/src/app/bios.rs +++ b/src/app/bios.rs @@ -218,32 +218,6 @@ impl Component for BiosComponent { fn validate(&self) -> Result { let data = load(self.path())?; - if self.system_version.as_str() == "meer9" { - // HACK: - // CSME must be disabled or in read-only mode to write - // CSME region of SPI flash. PCH reset does not trigger - // CSME reset, so ME_OVERRIDE will not be in effect on - // cold reset. HECI reset can't be requested after End - // Of Post (before payload runs), so disable CSME as a - // workaround. - let mut cmos_options = cmos::CmosOptionTable::new(); - // XXX: Probably better to check for HECI device. - if cmos_options.me_state() { - println!("\nDisabling CSME for writing SPI flash"); - unsafe { cmos_options.set_me_state(false); } - - println!("System will reboot in 5 seconds"); - let _ = (std::system_table().BootServices.Stall)(5_000_000); - - (std::system_table().RuntimeServices.ResetSystem)( - ResetType::Cold, - Status(0), - 0, - ptr::null(), - ); - } - } - if let Some((mut spi, _hsfsts_ctl)) = self.spi() { // if hsfsts_ctl.contains(HsfStsCtl::FDOPSS) { // println!("\nSPI currently locked, attempting to unlock"); diff --git a/src/app/mod.rs b/src/app/mod.rs index 4b24da8..2e06ad2 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -265,6 +265,34 @@ fn inner() -> Result<()> { if c == '\n' || c == '\r' { success = true; + for c in &components { + if c.model() == "meer9" { + // HACK: + // CSME must be disabled or in read-only mode to write + // CSME region of SPI flash. PCH reset does not trigger + // CSME reset, so ME_OVERRIDE will not be in effect on + // cold reset. HECI reset can't be requested after End + // Of Post (before payload runs), so disable CSME as a + // workaround. + let mut cmos_options = cmos::CmosOptionTable::new(); + // XXX: Probably better to check for HECI device. + if cmos_options.me_state() { + println!("Disabling CSME for writing SPI flash"); + unsafe { cmos_options.set_me_state(false); } + + println!("System will reboot in 5 seconds"); + let _ = (std::system_table().BootServices.Stall)(5_000_000); + + (std::system_table().RuntimeServices.ResetSystem)( + ResetType::Cold, + Status(0), + 0, + ptr::null(), + ); + } + } + } + { let ec_kind = unsafe { EcKind::new(true) }; // If EC tag does not exist, unlock the firmware