From 332f0d25e2d3411c77500f0f5847e36b106be508 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Dec 2024 12:06:31 -0700 Subject: [PATCH] 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); } }