Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions Documentation/ABI/testing/sysfs-driver-apple-sep
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ Date: October 2026
KernelVersion: 7.1
Contact: Chris Kearney <chris@kearneymail.com>
Description:
(RO) Version of the diag interface: "2" for the attributes
(RO) Version of the diag interface: "3" for the attributes
and values described in this file. Version 2 added
sensor_attach and restore; version 1 had neither.
sensor_attach and restore; version 1 had neither. Version 3
adds the policy snapshot.

Within a version, attributes are only added, and an existing
attribute keeps its values and their meaning. A reader tests
Expand All @@ -19,7 +20,8 @@ Description:
bring-up got without reading the kernel log. It exists while
the driver is bound to the SEP: it is created at probe and
removed at unbind. Every attribute in it is read-only and
reads as one ASCII token followed by a newline.
reads as ASCII followed by a newline. The policy snapshot
contains space-separated key=value tokens.

Each value is the state the driver recorded when it made the
corresponding decision. Reading an attribute sends nothing
Expand All @@ -30,6 +32,26 @@ Description:
by the boot-time bring-up, so the sensor and touchid
attributes only move on once something has opened it.

This deployment uses the existing T8122 profile and v4
/dev/sep-bio interface. It does not add a new sensor protocol.
OS firmware and system firmware are separate: qualify both.

For an existing bag, select xart_writes=1, provision_keybag=0,
xart_guard=1 and xarm_trace=1. trusted_keys=0 and
filevault_api=0 omit the optional kernel APIs; both default
to 1 to preserve existing deployments. There is no boot_uuid
parameter: the OS identity comes from /chosen/apfs-preboot-uuid.
Writable J613 26.6.2 attachment rejects a missing DT identity,
explicit UUID override or disabled guard before opening xART.
Host-store or identity-index read failure refuses attachment.

Preserve the current keybag, host store, reference key, three
Catacombs, ownership journal and fprint records. Different
Linux roots must share the same current files globally, before
module load; the kernel opens them from init_task's root.
Never replace them with a snapshot or enrol again to migrate.
Readiness diagnostics do not prove a successful finger match.

What: /sys/bus/platform/devices/<unit>.sep/diag/profile
Date: October 2026
KernelVersion: 7.1
Expand Down Expand Up @@ -364,3 +386,24 @@ Description:
tools/aurora-sep/XART-SAFETY.md. During a boot the value
can only move from "unverified" to "blocked", once the guard
refuses a write.

What: /sys/bus/platform/devices/<unit>.sep/diag/policy
Date: November 2026
KernelVersion: 7.2
Contact: Satya Benson <satya@benson.earth>
Description:
(RO, ABI 3) A space-separated key=value snapshot of the
driver policy selected at probe: xart_writes (effective store
writability), provision_keybag, xart_guard (effective level),
trusted_keys, filevault_api, xarm_trace, refkey_v2 and
identity_override (0 or 1). API flags describe the selected
policy, not successful registration if that API later fails.

identity_source is pending before the driver selects it, dt
for /chosen/apfs-preboot-uuid, override for explicit parameters,
or missing. It is updated at the actual identity decision.
No UUID value, state, identity or key bytes are exposed.
Reads only use cached facts and never contact the enclave.
Module-parameter sysfs files need not exist; their absence
is not evidence of a mismatched policy. Older diagnostic
ABIs retain xart/xart_guard and kernel decision messages.
15 changes: 6 additions & 9 deletions drivers/soc/apple/bio.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
// Copyright 2026 Dj

Expand Down Expand Up @@ -279,22 +279,19 @@
let Some(raw) = store.read(&identity_key())? else {
return Ok(index);
};
if raw.len() < 4 {
return Ok(index);
if raw.len() < 8 {
return Err(EINVAL);
}
if u32::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]) != INDEX_FORMAT {
return Err(EINVAL);
}
if raw.len() < 8 {
return Ok(index);
}
let count = u32::from_le_bytes([raw[4], raw[5], raw[6], raw[7]]) as usize;
let stride = UUID_LEN + LABEL_LEN;
for i in 0..count.min(MAX_IDENTITIES) {
if count > MAX_IDENTITIES || raw.len() != 8 + count * stride {
return Err(EINVAL);
}
for i in 0..count {
let at = 8 + i * stride;
if at + stride > raw.len() {
break;
}
let mut entry = Identity::ZERO;
entry.uuid.copy_from_slice(&raw[at..at + UUID_LEN]);
entry
Expand Down
43 changes: 30 additions & 13 deletions drivers/soc/apple/catacomb.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
// Copyright 2026 Dj

Expand Down Expand Up @@ -46,28 +46,45 @@
file.sync()
}

pub(crate) fn read(kind: u8) -> Option<KVec<u8>> {
let path = path_for(kind)?;
let file = shim::StoreFile::open_readonly(path).ok()?;
let size = file.size().ok()?;
/// `Ok(None)` only when the file is missing. A file that exists but cannot be
/// read or fails validation is an error: "cannot tell" must never read as
/// "nothing saved", or a caller may select a fresh context over a saved one.
pub(crate) fn read(kind: u8) -> Result<Option<KVec<u8>>> {
let path = path_for(kind).ok_or(EINVAL)?;
let file = match shim::StoreFile::open_readonly(path) {
Ok(file) => file,
Err(e) if e == ENOENT => return Ok(None),
Err(e) => return Err(e),
};
let size = file.size()?;
if size < HEADER as u64 {
return None;
return Err(EINVAL);
}
let mut head = [0u8; HEADER];
file.read_exact(0, &mut head).ok()?;
file.read_exact(0, &mut head)?;
if head[..MAGIC.len()] != MAGIC {
return None;
return Err(EINVAL);
}
let len = u32::from_le_bytes([head[8], head[9], head[10], head[11]]) as usize;
let crc = u16::from_le_bytes([head[12], head[13]]);
if len == 0 || len > MAX_CATACOMB || size != (HEADER + len) as u64 {
return None;
return Err(EINVAL);
}
let mut blob: KVec<u8> = KVec::with_capacity(len, GFP_KERNEL).ok()?;
blob.resize(len, 0, GFP_KERNEL).ok()?;
file.read_exact(HEADER as u64, &mut blob).ok()?;
let mut blob: KVec<u8> = KVec::with_capacity(len, GFP_KERNEL)?;
blob.resize(len, 0, GFP_KERNEL)?;
file.read_exact(HEADER as u64, &mut blob)?;
if crc16_ccitt_false(&blob) != crc {
return None;
return Err(EINVAL);
}
Some(blob)
Ok(Some(blob))
}

/// True only for a present, valid file.
pub(crate) fn is_saved(kind: u8) -> bool {
matches!(read(kind), Ok(Some(_)))
}

/// True only when the file is provably missing.
pub(crate) fn is_absent(kind: u8) -> bool {
matches!(read(kind), Ok(None))
}
44 changes: 44 additions & 0 deletions drivers/soc/apple/diag.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT

//! Rust side of `diag_shim.c`: the passive diagnostics in sysfs.
Expand Down Expand Up @@ -30,6 +30,15 @@
sepos13: bool,
xart: bool,
) -> c_int;
fn sep_diag_set_policy(
provision_keybag: bool,
trusted_keys: bool,
filevault_api: bool,
xarm_trace: bool,
refkey_v2: bool,
identity_override: bool,
);
fn sep_diag_set_identity_source(source: c_uint);
fn sep_diag_set_attach(attached: bool);
fn sep_diag_set_endpoints(count: c_uint);
fn sep_diag_set_keystore_open();
Expand Down Expand Up @@ -66,6 +75,28 @@
Unproven = 3,
}

/// Boot-time policy, without identity values or key material.
pub(crate) struct Policy {
pub provision_keybag: bool,
pub trusted_keys: bool,
pub filevault_api: bool,
pub xarm_trace: bool,
pub refkey_v2: bool,
pub identity_override: bool,
}

/// The source selected by prepare_os_uuid, not the UUID itself.
pub(crate) enum IdentitySource {
DeviceTree = 1,
Override = 2,
Missing = 3,
}

pub(crate) fn identity_source(source: IdentitySource) {
// SAFETY: this only stores the source enum in the passive cache.
unsafe { sep_diag_set_identity_source(source as c_uint) };
}

/// Adds the `diag` group to the SEP device, with the facts fixed at probe. The
/// group goes away when the driver unbinds.
///
Expand All @@ -76,6 +107,7 @@
profile: &profile::PlatformProfile,
xart_writable: bool,
guard: Option<(u8, bool)>,
policy: Policy,
) -> Result {
// Exhaustive, so a new bootstrap or key store fails to build here until
// the ABI has a value for it.
Expand All @@ -98,6 +130,18 @@
// SAFETY: no preconditions; stores three words.
unsafe { sep_diag_set_xart_guard(c_uint::from(level), list_loaded, matching() as c_int) };

// SAFETY: the facts are fixed before the attribute group is published.
unsafe {
sep_diag_set_policy(
policy.provision_keybag,
policy.trusted_keys,
policy.filevault_api,
policy.xarm_trace,
policy.refkey_v2,
policy.identity_override,
)
};

// SAFETY: `dev` is bound for the duration of the call, which is what devres
// needs. `profile.name` is a `str`, valid for reads of `profile.name.len()`
// bytes; the shim copies at most that many, needs no NUL terminator, and
Expand Down
58 changes: 57 additions & 1 deletion drivers/soc/apple/diag_shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "shim.h"

/* The contract version; see Documentation/ABI/testing/sysfs-driver-apple-sep. */
#define SEP_DIAG_ABI 2
#define SEP_DIAG_ABI 3

#define SEP_DIAG_ATTACH_PENDING 0
#define SEP_DIAG_ATTACH_ATTACHED 1
Expand Down Expand Up @@ -64,6 +64,13 @@ static struct {
bool cold;
bool sepos13;
bool xart;
bool provision_keybag;
bool trusted_keys;
bool filevault_api;
bool xarm_trace;
bool refkey_v2;
bool identity_override;
atomic_t identity_source;
/* The xART guard's effective level (0: none runs), and its list. */
unsigned int xart_guard;
bool xart_guard_list;
Expand Down Expand Up @@ -120,6 +127,54 @@ static ssize_t xart_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(xart);

/* Parameter policy is captured at probe; the identity choice is cached later. */
static ssize_t policy_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
const char *identity;

switch (atomic_read(&sep_diag.identity_source)) {
case 1:
identity = "dt";
break;
case 2:
identity = "override";
break;
case 3:
identity = "missing";
break;
default:
identity = "pending";
break;
}
return sysfs_emit(buf,
"xart_writes=%u provision_keybag=%u xart_guard=%u "
"trusted_keys=%u filevault_api=%u xarm_trace=%u "
"refkey_v2=%u identity_override=%u identity_source=%s\n",
sep_diag.xart, sep_diag.provision_keybag, sep_diag.xart_guard,
sep_diag.trusted_keys, sep_diag.filevault_api, sep_diag.xarm_trace,
sep_diag.refkey_v2, sep_diag.identity_override, identity);
}
static DEVICE_ATTR_RO(policy);

void sep_diag_set_policy(bool provision_keybag, bool trusted_keys,
bool filevault_api, bool xarm_trace, bool refkey_v2,
bool identity_override)
{
sep_diag.provision_keybag = provision_keybag;
sep_diag.trusted_keys = trusted_keys;
sep_diag.filevault_api = filevault_api;
sep_diag.xarm_trace = xarm_trace;
sep_diag.refkey_v2 = refkey_v2;
sep_diag.identity_override = identity_override;
atomic_set(&sep_diag.identity_source, 0);
}

void sep_diag_set_identity_source(unsigned int source)
{
atomic_set(&sep_diag.identity_source, source);
}

static ssize_t attach_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
Expand Down Expand Up @@ -346,6 +401,7 @@ static struct attribute *sep_diag_attrs[] = {
&dev_attr_boot.attr,
&dev_attr_protocol.attr,
&dev_attr_xart.attr,
&dev_attr_policy.attr,
&dev_attr_attach.attr,
&dev_attr_endpoints.attr,
&dev_attr_keystore.attr,
Expand Down
9 changes: 9 additions & 0 deletions drivers/soc/apple/keybag.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
// Copyright 2026 Dj

Expand Down Expand Up @@ -177,6 +177,15 @@
head[OFF_STATE + 3],
]);
if state == STATE_REFUSED {
let expected_crc = crc16_ccitt_false(&checksum_input(state, &[], &[0; UUID_LEN], &[])?);
let crc = u16::from_le_bytes([head[OFF_CRC], head[OFF_CRC + 1]]);
if size != HEADER as u64
|| crc != expected_crc
|| head[OFF_LEN..OFF_CRC].iter().any(|byte| *byte != 0)
|| head[OFF_CRC + 2..].iter().any(|byte| *byte != 0)
{
return Err(EINVAL);
}
return Ok(State::Absent(NoStoredKeyBag { slot }));
}
let Some(provenance) = UuidProvenance::from_state(state) else {
Expand Down
27 changes: 27 additions & 0 deletions drivers/soc/apple/profile.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
// Copyright 2026 Dj
// Copyright 2026 Ryan Murray
Expand Down Expand Up @@ -666,3 +666,30 @@
}
}
}

/// The owner-qualified J613 OS-firmware lane; system firmware may differ.
pub(crate) fn j613_26_6_2() -> bool {
machine_has(b"apple,j613")
&& chosen_string(c"asahi,os-fw-version")
.as_deref()
.is_some_and(os_firmware_26_6_2)
}

fn os_firmware_26_6_2(raw: &[u8]) -> bool {
raw == b"26.6.2" || raw == b"26.6.2\0"
}

#[cfg(CONFIG_APPLE_SEP_KUNIT_TEST)]
#[kernel::macros::kunit_tests(apple_sep_j613_firmware)]
mod j613_firmware_tests {
use super::*;

#[test]
fn exact_os_firmware_only() {
assert!(os_firmware_26_6_2(b"26.6.2"));
assert!(os_firmware_26_6_2(b"26.6.2\0"));
for raw in [&b"14.8.3"[..], &b"26.6.20"[..], &b"27.0"[..], &b""[..]] {
assert!(!os_firmware_26_6_2(raw));
}
}
}
14 changes: 13 additions & 1 deletion drivers/soc/apple/refkey.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
// Copyright 2026 Dj
//! Machine ref-key: Secure-Enclave key sealing, signing, and attestation.
Expand Down Expand Up @@ -177,7 +177,19 @@
return;
}

if let Ok(f) = shim::StoreFile::open_readonly(path) {
let existing = match shim::StoreFile::open_readonly(path) {
Ok(file) => Some(file),
Err(ENOENT) => None,
Err(error) => {
dev_err!(
self.dev,
"sks: cannot read existing machine ref-key: {:?}; refusing replacement\n",
error
);
return;
}
};
if let Some(f) = existing {
let sz = f.size().unwrap_or(0);
if (1..=8192).contains(&sz) {
let mut blob: KVec<u8> = KVec::new();
Expand Down
Loading
Loading