Skip to content
Merged
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
20 changes: 18 additions & 2 deletions ASFW.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,14 @@
isa = XCBuildConfiguration;
buildSettings = {
AD_HOC_CODE_SIGNING_ALLOWED = YES;
ASFW_DEXT_ENTITLEMENTS_NO = ASFWDriver/ASFWDriver.entitlements;
ASFW_DEXT_ENTITLEMENTS_YES = "ASFWDriver/ASFWDriver+SCSI.entitlements";
ASFW_ENABLE_SCSI = NO;
ASFW_INFOPLIST_DEFS_NO = "";
ASFW_INFOPLIST_DEFS_YES = "ASFW_ENABLE_SCSI=1";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++23";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = ASFWDriver/ASFWDriver.entitlements;
CODE_SIGN_ENTITLEMENTS = "$(ASFW_DEXT_ENTITLEMENTS_$(ASFW_ENABLE_SCSI))";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
Expand All @@ -612,6 +617,9 @@
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ASFWDriver/Info.plist;
INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional";
INFOPLIST_PREPROCESS = YES;
INFOPLIST_PREPROCESSOR_DEFINITIONS = "$(ASFW_INFOPLIST_DEFS_$(ASFW_ENABLE_SCSI))";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = "-framework SCSIControllerDriverKit";
Expand All @@ -631,9 +639,14 @@
isa = XCBuildConfiguration;
buildSettings = {
AD_HOC_CODE_SIGNING_ALLOWED = YES;
ASFW_DEXT_ENTITLEMENTS_NO = ASFWDriver/ASFWDriver.entitlements;
ASFW_DEXT_ENTITLEMENTS_YES = "ASFWDriver/ASFWDriver+SCSI.entitlements";
ASFW_ENABLE_SCSI = NO;
ASFW_INFOPLIST_DEFS_NO = "";
ASFW_INFOPLIST_DEFS_YES = "ASFW_ENABLE_SCSI=1";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++23";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = ASFWDriver/ASFWDriver.entitlements;
CODE_SIGN_ENTITLEMENTS = "$(ASFW_DEXT_ENTITLEMENTS_$(ASFW_ENABLE_SCSI))";
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
Expand All @@ -647,6 +660,9 @@
);
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ASFWDriver/Info.plist;
INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional";
INFOPLIST_PREPROCESS = YES;
INFOPLIST_PREPROCESSOR_DEFINITIONS = "$(ASFW_INFOPLIST_DEFS_$(ASFW_ENABLE_SCSI))";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = "-framework SCSIControllerDriverKit";
Expand Down
16 changes: 16 additions & 0 deletions ASFWDriver/ASFWDriver+SCSI.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.transport.pci</key>
<true/>
<key>com.apple.developer.driverkit.transport.pci.bridge</key>
<true/>
<key>com.apple.developer.driverkit.family.audio</key>
<true/>
<key>com.apple.developer.driverkit.family.scsicontroller</key>
<true/>
</dict>
</plist>
2 changes: 0 additions & 2 deletions ASFWDriver/ASFWDriver.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
<true/>
<key>com.apple.developer.driverkit.family.audio</key>
<true/>
<key>com.apple.developer.driverkit.family.scsicontroller</key>
<true/>
</dict>
</plist>

2 changes: 2 additions & 0 deletions ASFWDriver/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
<key>IOUserServerOneProcess</key>
<true/>
</dict>
#if ASFW_ENABLE_SCSI
<key>ASFWSCSIControllerService</key>
<dict>
<key>CFBundleIdentifier</key>
Expand All @@ -124,6 +125,7 @@
<key>IOUserServerOneProcess</key>
<true/>
</dict>
#endif
</dict>
<key>OSBundleUsageDescription</key>
<string>AS FireWire Driver</string>
Expand Down
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,48 @@ NOTE: You need an Apple Developer account (paid) and appropriate entitlements

Enabling `systemextensionsctl developer on` is recommended — it allows installing system extensions from the build

### SCSI HBA (SBP-2 scanners/disks) — opt-in

The SCSI HBA (`ASFWSCSIControllerService`, for SBP-2 devices such as FireWire film
scanners and disks) is **excluded from the default build**, because shipping it by
default can **panic the machine into a boot loop** (~60 s registry busy-timeout,
`IOService.cpp:5986`, no device attached) through two independent paths:

- Its Info.plist personality instantiates a kernel-side
`IOUserSCSIParallelInterfaceController` as soon as the FireWire card matches at
boot. The HBA currently reports SCSI target 0 as present unconditionally, and the
probe INQUIRY is then held with no deadline waiting for an SBP-2 login that never
arrives when no SBP-2 device is on the bus (audio interfaces are not SBP-2). The
stalled target registration keeps the PCI nub busy past watchdogd's 60 s boot
quiesce. This happens **regardless of SIP/AMFI state**.
- It requires the restricted
`com.apple.developer.driverkit.family.scsicontroller` entitlement. On a machine
where AMFI enforces entitlements, the ad-hoc-signed dext carrying it is killed at
launch (taking the audio driver down with it, since everything runs in one
process), and the orphaned kernel stub strands the same busy chain.

The default build carries neither the personality nor the entitlement and cannot
trigger either path.

To include the HBA, opt in explicitly:

```bash
./build.sh --scsi # or: xcodebuild … ASFW_ENABLE_SCSI=YES
./sign.sh # picks the +SCSI entitlements automatically
```

> **Warning:** `--scsi` builds are currently **not cold-boot-safe**: cold-booting
> with the FireWire controller attached and no powered-on SBP-2 device on the bus
> can hit the 60 s boot panic even with SIP fully disabled. Power the SBP-2 device
> on before booting. A stalled probe on a running system does not panic
> immediately, but can panic later when the adapter is unplugged or the extension
> is torn down. The HBA-side fix (create the target at SBP-2 login instead of
> boot) will follow in a separate PR.

If a machine ever ends up in a panic loop: boot into Recovery, `csrutil disable`,
boot normally, uninstall the extension
(`systemextensionsctl uninstall - net.mrmidi.ASFW.ASFWDriver`), then re-enable SIP.

## Installing a prebuilt build (testers)

If you want to test ASFireWire without building it yourself, tagged releases attach a
Expand All @@ -366,6 +408,12 @@ for experimental testing only — not general use.
> Only do this on a machine you are comfortable using for testing, and re-enable SIP
> (`csrutil enable`) when you are done. The build is unsigned/un-notarized and provided
> as-is; run it only if you understand and accept that.
>
> **Uninstall the extension _before_ re-enabling SIP.** With SIP back on, AMFI refuses
> to launch the ad-hoc-signed dext; an installed build that includes the SCSI HBA then
> leaves an orphaned kernel-side SCSI stub behind at every boot, which can panic the
> machine into a boot loop (recovery: Recovery → `csrutil disable` → boot → uninstall
> → `csrutil enable`).

**Requirements:** an Apple Silicon Mac running macOS 26 (Tahoe), and FireWire hardware
(a PCIe FireWire/OHCI card, or an Apple Thunderbolt-to-FireWire adapter).
Expand Down
20 changes: 20 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ SWIFT_TEST_ONLY=false
# When true, generate Swift code coverage
SWIFT_COVERAGE=false
SWIFT_COVERAGE_LCOV="${BUILD_DIR}/swift_coverage.lcov"
# When true, include the SCSI HBA: the ASFWSCSIControllerService personality in
# Info.plist and the restricted scsicontroller entitlement in the dext signature.
# Default OFF: the personality matches the FireWire card at boot, and with no
# powered SBP-2 device on the bus the HBA's probe INQUIRY stalls target-0
# registration past watchdogd's 60 s boot quiesce (registry busy-timeout panic,
# IOService.cpp:5986) — regardless of SIP state. On AMFI-enforcing machines the
# ad-hoc signature carrying the restricted entitlement additionally gets the dext
# killed at launch, stranding the orphaned kernel stub in the same panic.
# See README "SCSI HBA — opt-in".
ENABLE_SCSI=false

usage() {
cat <<EOF
Expand All @@ -61,6 +71,9 @@ Usage: $0 [--verbose] [--no-bump] [--scheme NAME] [--config CONFIG] [--arch ARCH
--swift-coverage Run Swift tests with coverage and export to LCOV
--commands Generate compile_commands.json via xcpretty
--analyze Run PVS-Studio static analyzer after build
--scsi Include the SCSI HBA (personality + restricted entitlement);
requires SIP disabled, and is NOT cold-boot-safe without a
powered-on SBP-2 device attached — see README
--scheme NAME Override scheme (default: ${SCHEME_NAME})
--config CONFIG Override configuration (default: ${CONFIGURATION})
--arch ARCH Override architecture passed to xcodebuild (default: ${ARCH_NAME})
Expand All @@ -79,6 +92,7 @@ while [[ $# -gt 0 ]]; do
--test-filter) SELECTED_TESTS_PATTERN="$2"; shift 2;;
--commands) GENERATE_COMMANDS=true; shift;;
--analyze) RUN_ANALYZER=true; shift;;
--scsi) ENABLE_SCSI=true; shift;;
--scheme) SCHEME_NAME="$2"; shift 2;;
--config) CONFIGURATION="$2"; shift 2;;
--arch) ARCH_NAME="$2"; shift 2;;
Expand Down Expand Up @@ -290,6 +304,11 @@ run_build() {
log "Building ${PROJECT_NAME} (scheme=${SCHEME_NAME}, config=${CONFIGURATION})…"
local QUIET_FLAG=()
$VERBOSE || QUIET_FLAG=(-quiet)
local SCSI_FLAG=()
if $ENABLE_SCSI; then
SCSI_FLAG=(ASFW_ENABLE_SCSI=YES)
log "SCSI HBA enabled (ASFW_ENABLE_SCSI=YES): personality + scsicontroller entitlement included"
fi

# Run xcodebuild. We capture everything to RAW_LOG.
set +e
Expand All @@ -303,6 +322,7 @@ run_build() {
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY="" \
${SCSI_FLAG[@]+"${SCSI_FLAG[@]}"} \
${QUIET_FLAG[@]+"${QUIET_FLAG[@]}"} \
build \
2>&1 | tee "${RAW_LOG}"
Expand Down
16 changes: 16 additions & 0 deletions sign.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ ok() { echo "[OK] $*"; }
DEXT_PATH="$(find "${APP_PATH}/Contents/Library/SystemExtensions" -maxdepth 1 -name '*.dext' -print -quit 2>/dev/null || true)"
[[ -n "${DEXT_PATH}" ]] || { err "No .dext found under ${APP_PATH}/Contents/Library/SystemExtensions"; exit 1; }

# The entitlement set must match what the build put in Info.plist: a dext whose
# signature carries the restricted scsicontroller entitlement is killed by AMFI
# on enforcing machines, and the orphaned kernel-side SCSI stub then panics the
# kernel at boot. Detect the SCSI personality (present only when the app was
# built with ASFW_ENABLE_SCSI=YES / ./build.sh --scsi) and pick the matching file
# — never sign the SCSI entitlement onto a build without the personality, or
# vice versa.
if /usr/libexec/PlistBuddy -c "Print :IOKitPersonalities:ASFWSCSIControllerService" \
"${DEXT_PATH}/Info.plist" >/dev/null 2>&1; then
DEXT_ENTITLEMENTS="ASFWDriver/ASFWDriver+SCSI.entitlements"
log "SCSI HBA personality detected — signing with ${DEXT_ENTITLEMENTS}"
[[ -f "${DEXT_ENTITLEMENTS}" ]] || { err "Missing ${DEXT_ENTITLEMENTS}"; exit 1; }
else
log "No SCSI HBA personality — signing with ${DEXT_ENTITLEMENTS} (audio only)"
fi

# Sign inner-first: the dext, then the enclosing app.
log "Signing dext: ${DEXT_PATH}"
codesign --force --sign - --timestamp=none \
Expand Down
Loading