diff --git a/CHANGELOG.md b/CHANGELOG.md index de4c177..4259329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [1.7.3] — UNRELEASED + +### Changed + +- firmware: mirror the fw 0.8.3 RESET ABI — added `RESET_TO_DEFAULTS` (`0x01`, restore the baked create-time defaults into RAM) and `FirmwareControl::reset_to_defaults()`; `RESET_TO_OEM` (`0xFF`) is now documented as TRUE OEM (RAM passthrough **and** the NV block blanked to all-`0xFF`, so the drive is traceless), and `RESET_TO_FLASH` as marker-gated (a never-saved drive loads the defaults). Wire values unchanged. + ## [1.7.2] — UNRELEASED ### Fixed diff --git a/src/firmware/mod.rs b/src/firmware/mod.rs index 2adf999..fc5041e 100644 --- a/src/firmware/mod.rs +++ b/src/firmware/mod.rs @@ -31,7 +31,9 @@ //! //! Every feature defaults to [`STATE_PASSTHROUGH`] (firmware does not touch the //! subsystem → byte-identical to OEM); [`Verb::Reset`] with [`RESET_TO_OEM`] -//! returns every feature to passthrough, while [`RESET_TO_FLASH`] reloads the last +//! returns every feature to passthrough (and, firmware ≥0.8.3, blanks the NV block +//! so the drive is traceless), [`RESET_TO_DEFAULTS`] restores the baked create-time +//! defaults, while [`RESET_TO_FLASH`] reloads the last //! [`Verb::Save`]d config block. use crate::scsi::{DataDirection, ScsiTransport}; @@ -114,11 +116,21 @@ pub const REGION_FREE: u8 = 0x0F; /// [`Verb::Reset`] mode (rides in the state slot `cdb[6]`): reload the saved flash /// config block into the RAM feature-state table, discarding un-saved RAM changes -/// — the non-destructive "revert to last SAVE". +/// — the non-destructive "revert to last SAVE". Marker-gated in firmware ≥0.8.3: a +/// never-saved drive loads the baked create-time defaults instead. pub const RESET_TO_FLASH: u8 = 0x00; -/// [`Verb::Reset`] mode (rides in the state slot `cdb[6]`): force every feature to -/// [`STATE_PASSTHROUGH`] (OEM behaviour everywhere), regardless of the saved config. +/// [`Verb::Reset`] mode (rides in the state slot `cdb[6]`): restore the baked +/// per-create **defaults** (the create-time flags, e.g. UHD/BD on) into the RAM +/// feature-state table. RAM-only — does not touch flash. This is what a never-saved +/// drive boots to. Firmware ≥0.8.3. +pub const RESET_TO_DEFAULTS: u8 = 0x01; + +/// [`Verb::Reset`] mode (rides in the state slot `cdb[6]`): TRUE OEM. Forces every +/// feature to [`STATE_PASSTHROUGH`] in RAM **and** (firmware ≥0.8.3) writes the NV +/// config block all-`0xFF` — marker included — so the drive is byte-for-byte like a +/// never-saved/never-configured drive (no trace it was ever set). Because the marker +/// is then `0xFF`, the NEXT boot loads the baked defaults, exactly like a fresh drive. pub const RESET_TO_OEM: u8 = 0xFF; /// The verb selector in `cdb[4]`. These numeric values ARE the wire protocol. @@ -133,8 +145,10 @@ pub enum Verb { Get = 0x03, /// Restore the RAM feature-state table. The mode rides in `cdb[6]` (the state /// slot): [`RESET_TO_FLASH`] (`0x00`) reloads the saved flash config block back - /// into RAM (discarding un-saved changes), [`RESET_TO_OEM`] (`0xFF`) forces every - /// feature to [`STATE_PASSTHROUGH`] (out-of-the-box behaviour). Ignores feature. + /// into RAM (discarding un-saved changes; marker-gated in fw ≥0.8.3), + /// [`RESET_TO_DEFAULTS`] (`0x01`) restores the baked create-time defaults, and + /// [`RESET_TO_OEM`] (`0xFF`) forces every feature to [`STATE_PASSTHROUGH`] AND + /// blanks the NV block (traceless, fw ≥0.8.3). Ignores feature. Reset = 0x04, /// Diagnostic RAM peek: [`MEMREAD_LEN`] bytes at the 32-bit address in /// `cdb[5..9]` (big-endian). @@ -267,8 +281,9 @@ pub fn build_get_cdb(feature: Feature) -> [u8; CDB_LEN] { /// Build a `RESET` CDB. `mode` rides in the state slot (`cdb[6]`): /// [`RESET_TO_FLASH`] (`0x00`) reloads the saved flash config into RAM, -/// [`RESET_TO_OEM`] (`0xFF`) forces every feature to passthrough. Floors its -/// data-in at [`MIN_ALLOC_LEN`] for the same HW min-transfer reason as +/// [`RESET_TO_DEFAULTS`] (`0x01`) restores the baked create-time defaults, and +/// [`RESET_TO_OEM`] (`0xFF`) forces every feature to passthrough (and blanks NV). +/// Floors its data-in at [`MIN_ALLOC_LEN`] for the same HW min-transfer reason as /// [`build_set_cdb`]. pub fn build_reset_cdb(mode: u8) -> [u8; CDB_LEN] { build_cdb(Verb::Reset, None, Some(mode), MIN_ALLOC_LEN) @@ -528,14 +543,24 @@ impl<'a> FirmwareControl<'a> { self.exec_in(&cdb).map(|_| ()) } - /// Restore every feature to [`STATE_PASSTHROUGH`] (RESET with - /// [`RESET_TO_OEM`]). Data-in phase for the same reason as [`set`](Self::set) — - /// a no-data RESET is aborted. + /// Restore every feature to [`STATE_PASSTHROUGH`] (RESET with [`RESET_TO_OEM`]) + /// — TRUE OEM: firmware ≥0.8.3 also blanks the NV block so the drive is + /// traceless. Data-in phase for the same reason as [`set`](Self::set) — a no-data + /// RESET is aborted. pub fn reset(&mut self) -> Result<()> { let cdb = build_reset_cdb(RESET_TO_OEM); self.exec_in(&cdb).map(|_| ()) } + /// Restore the baked create-time defaults into the RAM feature-state table + /// (RESET with [`RESET_TO_DEFAULTS`]) — what a never-saved drive boots to; does + /// not touch flash. Firmware ≥0.8.3. Data-in phase for the same reason as + /// [`reset`](Self::reset). + pub fn reset_to_defaults(&mut self) -> Result<()> { + let cdb = build_reset_cdb(RESET_TO_DEFAULTS); + self.exec_in(&cdb).map(|_| ()) + } + /// Reload the saved flash config block back into the RAM feature-state table /// (RESET with [`RESET_TO_FLASH`]), discarding any un-saved RAM changes — the /// non-destructive "revert to last SAVE". Data-in phase for the same reason as diff --git a/src/firmware/mod_tests.rs b/src/firmware/mod_tests.rs index ecf1fe7..3f53ab5 100644 --- a/src/firmware/mod_tests.rs +++ b/src/firmware/mod_tests.rs @@ -44,6 +44,7 @@ fn state_and_frame_constants_match_abi() { assert_eq!(REGION_DVD_BASE, 0x00); assert_eq!(REGION_FREE, 0x0F); assert_eq!(RESET_TO_FLASH, 0x00); + assert_eq!(RESET_TO_DEFAULTS, 0x01); assert_eq!(RESET_TO_OEM, 0xFF); } @@ -332,6 +333,16 @@ fn reset_sends_reset_cdb_and_clears_state() { assert_eq!(m.states, FeatureStates::all_passthrough()); } +#[test] +fn reset_to_defaults_sends_the_defaults_mode_cdb() { + let mut m = FwMock::new(); + { + let mut fw = FirmwareControl::new(&mut m); + fw.reset_to_defaults().expect("reset to defaults"); + } + assert_eq!(m.cdbs[0], build_reset_cdb(RESET_TO_DEFAULTS)); +} + #[test] fn save_then_reset_to_flash_restores_the_saved_value() { // SET a feature, SAVE it, force all-passthrough with RESET(to-OEM), then