mirror of
https://github.com/freemkv/freemkv-unlock.git
synced 2026-10-07 18:13:11 +00:00
unlock: drop Feature::Bd, rename Uhd→Unrestricted (mirror fw 0.9.2 ABI)
This commit is contained in:
+37
-50
@@ -210,18 +210,16 @@ pub enum Feature {
|
|||||||
/// [`REGION_BD_A`]/`_B`/`_C` (`0x0A`/`0x0B`/`0x0C`) = force BD region A/B/C;
|
/// [`REGION_BD_A`]/`_B`/`_C` (`0x0A`/`0x0B`/`0x0C`) = force BD region A/B/C;
|
||||||
/// [`REGION_FREE`] (`0x0F`) = region-free (any disc plays).
|
/// [`REGION_FREE`] (`0x0F`) = region-free (any disc plays).
|
||||||
Region = 0x02,
|
Region = 0x02,
|
||||||
/// UHD (AACS 2.0) capability gate. [`STATE_PASSTHROUGH`] (`0xFF`) = OEM (as
|
/// Unrestricted (BD/UHD) capability widen gate. fw 0.9.2 unified the former
|
||||||
/// shipped); [`STATE_OFF`] (`0x00`) = No (refuse UHD discs); [`STATE_ON`]
|
/// separate BD (AACS 1.0) and UHD (AACS 2.0) accept gates into this single
|
||||||
/// (`0x01`) = Yes (accept UHD — the mode gate is neutralized so the drive
|
/// lever. [`STATE_PASSTHROUGH`] (`0xFF`) = OEM (as shipped); [`STATE_OFF`]
|
||||||
/// engages UHD discs). On the byte-extraction classifier (e.g. BU40N) the No
|
/// (`0x00`) = No (refuse BD/UHD discs); [`STATE_ON`] (`0x01`) = Yes (accept
|
||||||
/// (OFF) leg currently reads back but behaves == OEM (boot-safe no-op, pending
|
/// BD/UHD — the mode gate is neutralized so the drive engages both). On the
|
||||||
/// deeper RE) — do NOT treat OFF as a proven UHD disable there.
|
/// byte-extraction classifier (e.g. BU40N) the No (OFF) leg currently reads
|
||||||
Uhd = 0x03,
|
/// back but behaves == OEM (boot-safe no-op, pending deeper RE) — do NOT
|
||||||
/// Blu-ray (AACS 1.0) capability gate. [`STATE_PASSTHROUGH`] (`0xFF`) = OEM (BD
|
/// treat OFF as a proven disable there. Wire id `0x04` (was `Bd`) is retired:
|
||||||
/// engaged as shipped); [`STATE_OFF`] (`0x00`) = No (force-refuse BD discs — the
|
/// the BD slot is now unused/reserved, the firmware treats it as a no-op.
|
||||||
/// drive raises its own `6F` refusal sense); [`STATE_ON`] (`0x01`) = Yes (accept
|
Unrestricted = 0x03,
|
||||||
/// BD — an enable direction, not a no-op).
|
|
||||||
Bd = 0x04,
|
|
||||||
/// Host Revocation List handling on the cert path. [`STATE_PASSTHROUGH`]
|
/// Host Revocation List handling on the cert path. [`STATE_PASSTHROUGH`]
|
||||||
/// (`0xFF`) = OEM enforce; [`STATE_OFF`] (`0x00`) = off (skip the HRL lookup —
|
/// (`0xFF`) = OEM enforce; [`STATE_OFF`] (`0x00`) = off (skip the HRL lookup —
|
||||||
/// revoked certs accepted, non-destructive, the unlock direction); [`STATE_ON`]
|
/// revoked certs accepted, non-destructive, the unlock direction); [`STATE_ON`]
|
||||||
@@ -239,11 +237,10 @@ pub enum Feature {
|
|||||||
|
|
||||||
/// The set of all features, in the canonical order the IDENTITY feature-state
|
/// The set of all features, in the canonical order the IDENTITY feature-state
|
||||||
/// table serialises them (and the order [`FirmwareControl::states`] probes).
|
/// table serialises them (and the order [`FirmwareControl::states`] probes).
|
||||||
pub const ALL_FEATURES: [Feature; 6] = [
|
pub const ALL_FEATURES: [Feature; 5] = [
|
||||||
Feature::Speed,
|
Feature::Speed,
|
||||||
Feature::Region,
|
Feature::Region,
|
||||||
Feature::Uhd,
|
Feature::Unrestricted,
|
||||||
Feature::Bd,
|
|
||||||
Feature::Hrl,
|
Feature::Hrl,
|
||||||
Feature::Encryption,
|
Feature::Encryption,
|
||||||
];
|
];
|
||||||
@@ -446,10 +443,8 @@ pub struct FeatureStates {
|
|||||||
pub speed: u8,
|
pub speed: u8,
|
||||||
/// [`Feature::Region`] state.
|
/// [`Feature::Region`] state.
|
||||||
pub region: u8,
|
pub region: u8,
|
||||||
/// [`Feature::Uhd`] state.
|
/// [`Feature::Unrestricted`] state.
|
||||||
pub uhd: u8,
|
pub unrestricted: u8,
|
||||||
/// [`Feature::Bd`] state.
|
|
||||||
pub bd: u8,
|
|
||||||
/// [`Feature::Hrl`] state.
|
/// [`Feature::Hrl`] state.
|
||||||
pub hrl: u8,
|
pub hrl: u8,
|
||||||
/// [`Feature::Encryption`] state (the consolidated cert/bus bypass).
|
/// [`Feature::Encryption`] state (the consolidated cert/bus bypass).
|
||||||
@@ -462,8 +457,7 @@ impl FeatureStates {
|
|||||||
FeatureStates {
|
FeatureStates {
|
||||||
speed: STATE_PASSTHROUGH,
|
speed: STATE_PASSTHROUGH,
|
||||||
region: STATE_PASSTHROUGH,
|
region: STATE_PASSTHROUGH,
|
||||||
uhd: STATE_PASSTHROUGH,
|
unrestricted: STATE_PASSTHROUGH,
|
||||||
bd: STATE_PASSTHROUGH,
|
|
||||||
hrl: STATE_PASSTHROUGH,
|
hrl: STATE_PASSTHROUGH,
|
||||||
encryption: STATE_PASSTHROUGH,
|
encryption: STATE_PASSTHROUGH,
|
||||||
}
|
}
|
||||||
@@ -474,8 +468,7 @@ impl FeatureStates {
|
|||||||
match feature {
|
match feature {
|
||||||
Feature::Speed => self.speed,
|
Feature::Speed => self.speed,
|
||||||
Feature::Region => self.region,
|
Feature::Region => self.region,
|
||||||
Feature::Uhd => self.uhd,
|
Feature::Unrestricted => self.unrestricted,
|
||||||
Feature::Bd => self.bd,
|
|
||||||
Feature::Hrl => self.hrl,
|
Feature::Hrl => self.hrl,
|
||||||
Feature::Encryption => self.encryption,
|
Feature::Encryption => self.encryption,
|
||||||
}
|
}
|
||||||
@@ -485,8 +478,7 @@ impl FeatureStates {
|
|||||||
match feature {
|
match feature {
|
||||||
Feature::Speed => self.speed = state,
|
Feature::Speed => self.speed = state,
|
||||||
Feature::Region => self.region = state,
|
Feature::Region => self.region = state,
|
||||||
Feature::Uhd => self.uhd = state,
|
Feature::Unrestricted => self.unrestricted = state,
|
||||||
Feature::Bd => self.bd = state,
|
|
||||||
Feature::Hrl => self.hrl = state,
|
Feature::Hrl => self.hrl = state,
|
||||||
Feature::Encryption => self.encryption = state,
|
Feature::Encryption => self.encryption = state,
|
||||||
}
|
}
|
||||||
@@ -533,6 +525,10 @@ pub enum ArmRecipe {
|
|||||||
BypassBd,
|
BypassBd,
|
||||||
/// [`FirmwareControl::arm_bypass_uhd`].
|
/// [`FirmwareControl::arm_bypass_uhd`].
|
||||||
BypassUhd,
|
BypassUhd,
|
||||||
|
// NOTE: `Feature::Unrestricted` now unifies the former BD+UHD accept gates
|
||||||
|
// (fw 0.9.2); the OemBd/OemUhd/BypassBd/BypassUhd recipe names above are
|
||||||
|
// kept as-is (they name the disc TYPE being ripped, not the retired
|
||||||
|
// per-format feature), see `arm_oem_uhd`/`arm_bypass_uhd` below.
|
||||||
/// [`FirmwareControl::arm_stealth_oem`].
|
/// [`FirmwareControl::arm_stealth_oem`].
|
||||||
StealthOem,
|
StealthOem,
|
||||||
}
|
}
|
||||||
@@ -697,24 +693,15 @@ impl<'a> FirmwareControl<'a> {
|
|||||||
|
|
||||||
// ── Typed setters ────────────────────────────────────────────────────────
|
// ── Typed setters ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Force the UHD (AACS 2.0) capability gate on (drive engages UHD discs).
|
/// Force the Unrestricted (BD/UHD) capability gate on (drive engages both
|
||||||
pub fn enable_uhd(&mut self) -> Result<()> {
|
/// BD and UHD discs). fw 0.9.2 unified the former separate UHD/BD gates
|
||||||
self.set(Feature::Uhd, STATE_ON)
|
/// into this single lever.
|
||||||
|
pub fn enable_unrestricted(&mut self) -> Result<()> {
|
||||||
|
self.set(Feature::Unrestricted, STATE_ON)
|
||||||
}
|
}
|
||||||
/// Force the UHD capability gate off.
|
/// Force the Unrestricted (BD/UHD) capability gate off.
|
||||||
pub fn disable_uhd(&mut self) -> Result<()> {
|
pub fn disable_unrestricted(&mut self) -> Result<()> {
|
||||||
self.set(Feature::Uhd, STATE_OFF)
|
self.set(Feature::Unrestricted, STATE_OFF)
|
||||||
}
|
|
||||||
/// Force the Blu-ray (AACS 1.0) capability gate on.
|
|
||||||
pub fn enable_bd(&mut self) -> Result<()> {
|
|
||||||
self.set(Feature::Bd, STATE_ON)
|
|
||||||
}
|
|
||||||
/// Force the Blu-ray capability gate off (drive refuses BD discs). Sends the
|
|
||||||
/// uniform [`STATE_OFF`] (`0x00`) — an unarmed image boots to [`STATE_PASSTHROUGH`]
|
|
||||||
/// (`0xFF`, OEM) via the firmware power-on hook, so `0x00` no longer collides
|
|
||||||
/// with the boot value (the retired `0x02` sentinel is gone).
|
|
||||||
pub fn disable_bd(&mut self) -> Result<()> {
|
|
||||||
self.set(Feature::Bd, STATE_OFF)
|
|
||||||
}
|
}
|
||||||
/// Skip the HRL lookup (revoked certs accepted; non-destructive). The unlock
|
/// Skip the HRL lookup (revoked certs accepted; non-destructive). The unlock
|
||||||
/// direction is now [`STATE_OFF`] (`0x00` = HRL enforcement off).
|
/// direction is now [`STATE_OFF`] (`0x00` = HRL enforcement off).
|
||||||
@@ -766,12 +753,12 @@ impl<'a> FirmwareControl<'a> {
|
|||||||
|
|
||||||
/// **arm_oem_uhd** — OEM-style UHD (AACS 2.0) rip.
|
/// **arm_oem_uhd** — OEM-style UHD (AACS 2.0) rip.
|
||||||
///
|
///
|
||||||
/// Sets: `Uhd = on` (mode-gate neutralized so the drive engages the UHD
|
/// Sets: `Unrestricted = on` (mode-gate neutralized so the drive engages
|
||||||
/// disc), `Hrl = skip` (revocation off), `Encryption = off` (content
|
/// the UHD disc), `Hrl = skip` (revocation off), `Encryption = off`
|
||||||
/// de-bussed — the consolidated cert/bus bypass). Rips: UHD via the OEM
|
/// (content de-bussed — the consolidated cert/bus bypass). Rips: UHD via
|
||||||
/// path.
|
/// the OEM path.
|
||||||
pub fn arm_oem_uhd(&mut self) -> Result<()> {
|
pub fn arm_oem_uhd(&mut self) -> Result<()> {
|
||||||
self.set_verify(Feature::Uhd, STATE_ON)?;
|
self.set_verify(Feature::Unrestricted, STATE_ON)?;
|
||||||
self.set_verify(Feature::Hrl, STATE_OFF)?;
|
self.set_verify(Feature::Hrl, STATE_OFF)?;
|
||||||
self.set_verify(Feature::Encryption, STATE_OFF)
|
self.set_verify(Feature::Encryption, STATE_OFF)
|
||||||
}
|
}
|
||||||
@@ -788,11 +775,11 @@ impl<'a> FirmwareControl<'a> {
|
|||||||
|
|
||||||
/// **arm_bypass_uhd** — full-bypass UHD rip (NO host cert needed).
|
/// **arm_bypass_uhd** — full-bypass UHD rip (NO host cert needed).
|
||||||
///
|
///
|
||||||
/// Sets: `Uhd = on` (engage the UHD disc), `Encryption = off` (the
|
/// Sets: `Unrestricted = on` (engage the UHD disc), `Encryption = off`
|
||||||
/// consolidated cert/bus bypass), and `Hrl = skip` (revocation off).
|
/// (the consolidated cert/bus bypass), and `Hrl = skip` (revocation off).
|
||||||
/// Rips: UHD with no cert.
|
/// Rips: UHD with no cert.
|
||||||
pub fn arm_bypass_uhd(&mut self) -> Result<()> {
|
pub fn arm_bypass_uhd(&mut self) -> Result<()> {
|
||||||
self.set_verify(Feature::Uhd, STATE_ON)?;
|
self.set_verify(Feature::Unrestricted, STATE_ON)?;
|
||||||
self.set_verify(Feature::Hrl, STATE_OFF)?;
|
self.set_verify(Feature::Hrl, STATE_OFF)?;
|
||||||
self.set_verify(Feature::Encryption, STATE_OFF)
|
self.set_verify(Feature::Encryption, STATE_OFF)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-22
@@ -24,12 +24,13 @@ fn verb_values_match_abi() {
|
|||||||
fn feature_values_match_abi() {
|
fn feature_values_match_abi() {
|
||||||
assert_eq!(Feature::Speed as u8, 0x01);
|
assert_eq!(Feature::Speed as u8, 0x01);
|
||||||
assert_eq!(Feature::Region as u8, 0x02);
|
assert_eq!(Feature::Region as u8, 0x02);
|
||||||
assert_eq!(Feature::Uhd as u8, 0x03);
|
assert_eq!(Feature::Unrestricted as u8, 0x03);
|
||||||
assert_eq!(Feature::Bd as u8, 0x04);
|
|
||||||
assert_eq!(Feature::Hrl as u8, 0x05);
|
assert_eq!(Feature::Hrl as u8, 0x05);
|
||||||
assert_eq!(Feature::Encryption as u8, 0x06);
|
assert_eq!(Feature::Encryption as u8, 0x06);
|
||||||
|
// Wire id 0x04 (was `Bd`) is retired — fw 0.9.2 unified BD+UHD into
|
||||||
|
// Unrestricted; the BD slot is now unused/reserved (fw no-ops it).
|
||||||
// Wire id 0x07 (was `Bus`) is retired — proven inert on BU40N/MT1959.
|
// Wire id 0x07 (was `Bus`) is retired — proven inert on BU40N/MT1959.
|
||||||
assert_eq!(ALL_FEATURES.len(), 6);
|
assert_eq!(ALL_FEATURES.len(), 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -425,10 +426,11 @@ fn states_probes_every_feature_in_order() {
|
|||||||
}
|
}
|
||||||
assert_eq!(states.speed, SPEED_MAX);
|
assert_eq!(states.speed, SPEED_MAX);
|
||||||
assert_eq!(states.hrl, STATE_ON);
|
assert_eq!(states.hrl, STATE_ON);
|
||||||
// Six GETs, one per feature, in ALL_FEATURES order (Bus id 0x07 retired).
|
// Five GETs, one per feature, in ALL_FEATURES order (Bd id 0x04 and Bus id
|
||||||
assert_eq!(m.cdbs.len(), 6);
|
// 0x07 both retired).
|
||||||
|
assert_eq!(m.cdbs.len(), 5);
|
||||||
let features: Vec<u8> = m.cdbs.iter().map(|c| c[CDB_FEATURE]).collect();
|
let features: Vec<u8> = m.cdbs.iter().map(|c| c[CDB_FEATURE]).collect();
|
||||||
assert_eq!(features, vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
|
assert_eq!(features, vec![0x01, 0x02, 0x03, 0x05, 0x06]);
|
||||||
assert!(m.verbs().iter().all(|&v| v == Verb::Get as u8));
|
assert!(m.verbs().iter().all(|&v| v == Verb::Get as u8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,8 +454,7 @@ fn typed_setters_issue_expected_set_cdbs() {
|
|||||||
let mut m = FwMock::new();
|
let mut m = FwMock::new();
|
||||||
{
|
{
|
||||||
let mut fw = FirmwareControl::new(&mut m);
|
let mut fw = FirmwareControl::new(&mut m);
|
||||||
fw.enable_uhd().unwrap();
|
fw.enable_unrestricted().unwrap();
|
||||||
fw.disable_bd().unwrap();
|
|
||||||
fw.skip_hrl().unwrap();
|
fw.skip_hrl().unwrap();
|
||||||
fw.disable_encryption().unwrap();
|
fw.disable_encryption().unwrap();
|
||||||
fw.region_free().unwrap();
|
fw.region_free().unwrap();
|
||||||
@@ -461,17 +462,16 @@ fn typed_setters_issue_expected_set_cdbs() {
|
|||||||
fw.force_region_dvd(2).unwrap();
|
fw.force_region_dvd(2).unwrap();
|
||||||
fw.unlock_speed().unwrap();
|
fw.unlock_speed().unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(m.cdbs[0], build_set_cdb(Feature::Uhd, STATE_ON));
|
assert_eq!(m.cdbs[0], build_set_cdb(Feature::Unrestricted, STATE_ON));
|
||||||
assert_eq!(m.cdbs[1], build_set_cdb(Feature::Bd, STATE_OFF));
|
|
||||||
// The HRL/Encryption unlock direction is STATE_OFF (0x00), not STATE_ON.
|
// The HRL/Encryption unlock direction is STATE_OFF (0x00), not STATE_ON.
|
||||||
assert_eq!(m.cdbs[2], build_set_cdb(Feature::Hrl, STATE_OFF));
|
assert_eq!(m.cdbs[1], build_set_cdb(Feature::Hrl, STATE_OFF));
|
||||||
assert_eq!(m.cdbs[3], build_set_cdb(Feature::Encryption, STATE_OFF));
|
assert_eq!(m.cdbs[2], build_set_cdb(Feature::Encryption, STATE_OFF));
|
||||||
// Region-free is REGION_FREE (0x0F) — 0x01 now forces DVD region 1.
|
// Region-free is REGION_FREE (0x0F) — 0x01 now forces DVD region 1.
|
||||||
assert_eq!(m.cdbs[4], build_set_cdb(Feature::Region, REGION_FREE));
|
assert_eq!(m.cdbs[3], build_set_cdb(Feature::Region, REGION_FREE));
|
||||||
assert_eq!(m.cdbs[5], build_set_cdb(Feature::Region, REGION_BD_B));
|
assert_eq!(m.cdbs[4], build_set_cdb(Feature::Region, REGION_BD_B));
|
||||||
// force_region_dvd(2) = REGION_DVD_BASE + 2 = 0x02 under the new base.
|
// force_region_dvd(2) = REGION_DVD_BASE + 2 = 0x02 under the new base.
|
||||||
assert_eq!(m.cdbs[6], build_set_cdb(Feature::Region, 0x02));
|
assert_eq!(m.cdbs[5], build_set_cdb(Feature::Region, 0x02));
|
||||||
assert_eq!(m.cdbs[7], build_set_cdb(Feature::Speed, SPEED_MAX));
|
assert_eq!(m.cdbs[6], build_set_cdb(Feature::Speed, SPEED_MAX));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -511,10 +511,10 @@ fn arm_oem_uhd_sets_uhd_hrl_encryption_each_verified() {
|
|||||||
vec![0x02, 0x03, 0x02, 0x03, 0x02, 0x03],
|
vec![0x02, 0x03, 0x02, 0x03, 0x02, 0x03],
|
||||||
"each set is verified by a get"
|
"each set is verified by a get"
|
||||||
);
|
);
|
||||||
assert_eq!(m.cdbs[0], build_set_cdb(Feature::Uhd, STATE_ON));
|
assert_eq!(m.cdbs[0], build_set_cdb(Feature::Unrestricted, STATE_ON));
|
||||||
assert_eq!(m.cdbs[2], build_set_cdb(Feature::Hrl, STATE_OFF));
|
assert_eq!(m.cdbs[2], build_set_cdb(Feature::Hrl, STATE_OFF));
|
||||||
assert_eq!(m.cdbs[4], build_set_cdb(Feature::Encryption, STATE_OFF));
|
assert_eq!(m.cdbs[4], build_set_cdb(Feature::Encryption, STATE_OFF));
|
||||||
assert_eq!(m.states.uhd, STATE_ON);
|
assert_eq!(m.states.unrestricted, STATE_ON);
|
||||||
assert_eq!(m.states.hrl, STATE_OFF);
|
assert_eq!(m.states.hrl, STATE_OFF);
|
||||||
assert_eq!(m.states.encryption, STATE_OFF);
|
assert_eq!(m.states.encryption, STATE_OFF);
|
||||||
}
|
}
|
||||||
@@ -542,11 +542,11 @@ fn arm_bypass_uhd_sets_uhd_hrl_encryption() {
|
|||||||
let mut fw = FirmwareControl::new(&mut m);
|
let mut fw = FirmwareControl::new(&mut m);
|
||||||
fw.arm_bypass_uhd().expect("armed");
|
fw.arm_bypass_uhd().expect("armed");
|
||||||
}
|
}
|
||||||
// Recipe order: Uhd=on, Hrl=off, Encryption=off (each SET then verifying GET).
|
// Recipe order: Unrestricted=on, Hrl=off, Encryption=off (each SET then verifying GET).
|
||||||
assert_eq!(m.cdbs[0], build_set_cdb(Feature::Uhd, STATE_ON));
|
assert_eq!(m.cdbs[0], build_set_cdb(Feature::Unrestricted, STATE_ON));
|
||||||
assert_eq!(m.cdbs[2], build_set_cdb(Feature::Hrl, STATE_OFF));
|
assert_eq!(m.cdbs[2], build_set_cdb(Feature::Hrl, STATE_OFF));
|
||||||
assert_eq!(m.cdbs[4], build_set_cdb(Feature::Encryption, STATE_OFF));
|
assert_eq!(m.cdbs[4], build_set_cdb(Feature::Encryption, STATE_OFF));
|
||||||
assert_eq!(m.states.uhd, STATE_ON);
|
assert_eq!(m.states.unrestricted, STATE_ON);
|
||||||
assert_eq!(m.states.hrl, STATE_OFF);
|
assert_eq!(m.states.hrl, STATE_OFF);
|
||||||
assert_eq!(m.states.encryption, STATE_OFF);
|
assert_eq!(m.states.encryption, STATE_OFF);
|
||||||
}
|
}
|
||||||
@@ -555,7 +555,7 @@ fn arm_bypass_uhd_sets_uhd_hrl_encryption() {
|
|||||||
fn arm_stealth_oem_resets_then_verifies_all_passthrough() {
|
fn arm_stealth_oem_resets_then_verifies_all_passthrough() {
|
||||||
let mut m = FwMock::new();
|
let mut m = FwMock::new();
|
||||||
m.states.encryption = STATE_ON;
|
m.states.encryption = STATE_ON;
|
||||||
m.states.uhd = STATE_ON;
|
m.states.unrestricted = STATE_ON;
|
||||||
{
|
{
|
||||||
let mut fw = FirmwareControl::new(&mut m);
|
let mut fw = FirmwareControl::new(&mut m);
|
||||||
fw.arm_stealth_oem().expect("disarmed");
|
fw.arm_stealth_oem().expect("disarmed");
|
||||||
|
|||||||
Reference in New Issue
Block a user